I've been thinking about the best way to target mobile with clojure for a while. I saw a couple people join and thought it might be worth sharing what I've learned so far. Now that graalvm has added iOS and android targets, I don't think there are any technical roadblocks that would prevent clojure on mobile, but there is plenty of work to do. Recently, I was able to build a simple todo app in clojure that ran on my iPhone. I intend to publish the example once I clean it up. I published a very bare bones proof of concept at https://github.com/phronmophobic/mobiletest. It even can eval clojure code at runtime thanks to sci, :sci: . The next steps, as I see them, are: • Decide on a strategy for UI graphics and events (SwiftUI, Epoxy, React Native, membrane, something else?) • Decide on a strategy to interop with platform APIs • Improve tooling and documentation • Decide on a strategy for repl driven development (or else, why bother 😛). I've started with iOS since that's the mobile device I regularly use, but I haven't seen anything to indicate that the same process wouldn't apply to android (before clojure, I was working on cross-platform mobile games in c++). I would love to hear any feedback or ideas: • What do people think would be a good fit for building UIs? • What kinds of apps are folks hoping to build? • I'm also curious why people are interested in using graalvm+clojure vs cljs+react native. Personally, I just don't like html/css/javascript, but realistically, it seems like folks have found cljs+react native to be productive.
You could still do the control logic from the REPL
And then compile to native as the very last step
> I think the interop with platform API is extremely important and a sustainable way of maintaining the bridge must be thought of. Java objC bridges have failed to be maintained in the past. Apple's documentation has an API (eg. https://developer.apple.com/tutorials/data/documentation/technologies.json?language=objc ) that I think provides enough info to automatically generate an API. Objective-c is dynamic enough that stubs shouldn't be necessary. Swift only APIs will probably require stubs.
> I'm also curious why people are interested in using graalvm+clojure vs cljs+react native
Idk if this counts as justification, but it feels like an unexplored space
and I "trust" jvm clojure more
That's neat, the Apple documentation API, creating stubs would be straight forward. But I'm not able wrap my head around how the data structures will move in and out of the G+C compiled code to ObjC/Swift, if you get what I mean. Not sure how to put it clearly, but with stubs I'll know the types and signatures and all that metadata but how do I program against those data-structures when I'm writing in Clojure.
It's still a rough plan, but the idea is to have clj->objc
and objc->clj
functions for structures like NSDictionary, NSArray, NSString, etc. For classes, you can use https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/index.html, to set and get properties. That should make working with objective-c APIs possible from clojure, but not necessarily pleasant. There will probably be some amount of effort needed to make working with platform APIs more comfortable.
In babashka I worked my way around all of this native stuff by using JSON or Transit serialization ;)
One use-case that I think can be super useful is calling native APIs from the REPL. I had to build a custom camera component in Swift and I felt like going back to the dark ages, using the debugger, setting break points, etc. If I had a Clojure REPL into Swift/Objective-C (ideally Swift) the experience could potentially be much better.
I’m not sure how possible that would be… Since you’d have to compile some Objective-C on the fly and execute it? I believe that is possible (but I have never tried it myself, have only heard/read stories). Not sure about that strategy with Swift.
Just found this: https://github.com/johnno1962/InjectionIII
Perhaps worth a look.
I was able to get some basic objective-c interop working using https://developer.apple.com/documentation/objectivec/1456712-objc_msgsend?language=objc (along with a few other https://developer.apple.com/documentation/objectivec/objective-c_runtime?language=objc calls). I think that should enable most objective c interop, but I'm not sure how how easy it is to make a repl for Swift. I'll check out the project the InjectionIII project, but Swift seems overall less friendly for dynamic runtime interaction.
This project (InjectionIII) actually seems like the real deal… It looks like you can eval Swift/Obj-C on the fly.
I’ve been learning iOS too lately and story boarding is different from HTML/CSS. Currently finding it better than the flow-spoiling experience of tweaking HTML/CSS. But that could just be the high of learning something new. But not sure how the tooling will work for this since the swift code and story boarding experience seem very coupled. Graalvm+Clojure native than Cljs+react since I think intuitively that the former might be far more performant. I think the interop with platform API is extremely important and a sustainable way of maintaining the bridge must be thought of. Java objC bridges have failed to be maintained in the past. It would also be an interesting approach if I could write the UI bits in Xcode and controllerish logic in Clojure. If perf is great with g+c then it would be the best thing ever.
I'm getting the following error when I run https://github.com/phronmophobic/mobiletest/blob/main/scripts/compile-shared The build folder seems to have downloaded the deps.
Exception in thread "main" com.oracle.svm.core.util.UserError$UserException: Could not find platform class org.graalvm.nativeimage.Platform$IOS_AARCH64 that was specified explicitly on the command line using the system property svm.platform
at com.oracle.svm.core.util.UserError.abort(UserError.java:68)
at com.oracle.svm.hosted.NativeImageGenerator.defaultPlatform(NativeImageGenerator.java:332)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.installNativeImageClassLoader(NativeImageGeneratorRunner.java:172)
at com.oracle.svm.hosted.NativeImageGeneratorRunner.main(NativeImageGeneratorRunner.java:114)
at com.oracle.svm.hosted.NativeImageGeneratorRunner$JDK9Plus.main(NativeImageGeneratorRunner.java:541)
Error: Image build request failed with exit status 1
My Graal isn't the latest version, if that matters.
You should first run the download script
ah yes, graal version may also matter, use 21.1.0
I did run the download script, trying it with the latest Graal
not sure if this is causing the error, but you'll also have to install the llvm tool chain:
gu install llvm-toolchain
I had that installed too. But I guess the version was the issue, the compilation has started with the latest version. 🤞:skin-tone-4:
The build fails in xcode. Any idea what is causing this?
(I'm on Xcode 12.5.1 )
you have to build for device.
I haven’t tried building for simulator, but it requires a different architecture
@kslvsunil , hopefully, that unblocks you, but that’s probably a good note for me to put in the Readme!
Woohoo, works! This is really cool
Thank you 🙂
:bananadance::toot::clojure-spin:
it should start a sci repl that you can connect to
not super useful since there's no interop yet, but it's still neat
Oh, haha. I was scratching my head as to what was even compiled when I saw that "Hello, world!" was coming from ContentView
Coool, will hack around with it, cheers
Yep, mobiletest is computational only. No UI yet.
Yup, figured 🙂
Ok, just updated the readme to add a step for selecting a non simulator target.