js' blog

ObjFW, Frameworks, the App Store and Swift
Created: 20.10.2017 23:30 UTC

This week, I finally got scrypt-pwgen into the App Store (except France, because they have insane crypto regulations). This was actually quite a fun exercise, as this (to the best of my knowledge) is the first app to make it into the App Store that uses ObjFW. Initially, I was using ObjFW built as a .framework for this and it worked fine on my local device, but it confused the App Store a lot, and while the build would upload, it would then fail the processing on Apple's side. The immediate remedy was to just use ObjFW as a static library, which worked and pretty much let me get the app into the App Store immediately (except the first version being rejected, because it crashed as a category wasn't loaded due to the switch from a .framework to static linking). However, I really wanted this working with a .framework. Since copying the Xcode-built .frameworks over was annoying, I tried adding a subproject with ObjFW itself, but that was also annoying. I ended up adding support for building .frameworks to buildsys.mk and a script that builds a universal ObjFW.framework and ObjFW_Bridge.framework for all architectures + all simulator architectures, so that I can easily switch between device and simulator. This now works beautifully, and I can successfully upload it to the App Store, with just one caveat: Before uploading it to the App Store, I need to use lipo to strip the simulator architectures, as Apple does not like you uploading a .framework that contains support for unsupported architectures :).

All this looking into .frameworks gave me the idea to look into how well ObjFW.framework and ObjFW_Bridge.framework work with Swift. And after minor fixes, it worked exceptionally (pun intended!) well, except for one thing: Exceptions! But there was an easy solution, as Swift supports Blocks, and now there's a category on OFException in the bridge that allows you to do this:

import ObjFW
import ObjFW_Bridge

OFException.try({
	OFOutOfMemoryException(requestedSize: 1234).throw()
}, catch: { (e: OFException) in
	print("Caught \(e.class())")
}, finally: {
	print("Finally")
})

So now it's even possible to use ObjC exceptions in Swift! Initially, I was converting OFExceptions into NSErrors, but while this is the most "native" mapping, it felt very ugly using it. The way it is now allows using it in a way that is almost the same as in ObjC.