Fixing issues caused by future SDKs

What if you need to fix an issue caused by an iOS version newer than the Xcode version you are using?

With the release of iOS13 imminent, we’ve found some critical issues that can only be fixed by using new APIs only available via Xcode 11. However our project uses Xcode 10 — building from Xcode 11 introduces even more issues. Of course, we will eventually need to move to the newest IDE, but for now we need a low-risk solution.

The Problem

We have a UINavigationBar with the following appearance traits:

Swift

This combination of customization works perfectly on iOS12 and earlier, but on iOS13 the background of the bar appears white (when building from both Xcode 10 and 11). Thus, no buttons or titles can be seen in the navigation bar.

There are some new APIs that we must use to customize navigation bars, as seen in the documentation diff:

The Unwanted Solution

The code that fixes the issue can be placed in an if #available(iOS 13.0, *) and compiled with Xcode 11:

Swift

But as we were forced to remain on Xcode 10 in order to reduce risk, how can we access these APIs?

Teaching Xcode about future iOS versions

This is pretty standard but I’ll go over it briefly anyway.

You can allow Xcode 10 to build to your attached iOS13 device by copying the relevant supporting files from Xcode 11. To do this, visit the Xcode11.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport directory. Inside there is a folder called 13.0 — you must copy it to the same directory inside of your Xcode10.app .

If you need to support a beta, you may need to duplicate the folder in the older Xcode and name it something like 13.1 (17A5831c)

Whilst this is quite hacky, it does allow you to build directly from an older version of Xcode to a device running a newer OS.

Objective-C is your friend

With the popularity of Swift sky rocketing, Objective-C is not only being forgotten but also never being taught. There are some good developers out there who have only been doing iOS since after Swift’s launch!

Yet, it’s the power Objective-C wields that will help us in our quest today.

Did you know that in Objective-C you can dynamically instantiate a class at runtime and send messages to it? With proper checks in place to ensure we are running under iOS13, we can use NSClassFromString to gain access to the new UINavigationBarAppearance we found earlier, even though it’s only available in a future Xcode!

Objective-C, but also C

Now we need to follow our “unwanted solution” from above and translate it into Objective-C. In order to set properties on ios13Appearance , let’s define a function to help us:

C function, but Objective-C contents

This takes an object target , a function selectorString and an object that will become the argument of the function selectorString . We can then create a selector and check if the target responds to it — if so, then run it with the argument.

In this way, we can set the properties required using setters:

C, but also Objective-C

Unfortunately, we can’t use UIAppearance with the UINavigationBarAppearance instance we created. Instead, we’ll need to run this code on every navigation bar we create in our app:

C

Getting the gist of it

I’ve wrapped up this particular case in a horribly named class that will stand out in your code as needing to be removed. Of course, there may be other issues you encounter that you can fix in the same way, so remember: whilst Objective-C is considered old and clunky, it can in some cases provide you with a lot more power than Swift can!