Menu Home

Dude, where’s my view? Unknown class in iOS 12

When XCFramework was announced, and “Build Libraries For Distribution” has made available to developers, everything were too perfect to be real. No more “work arounds” with fat frameworks, allowing just one package to contain many architectures, simplifying builds. So magical! And the best of it all: you would use it even supporting old versions of iOS. Isn’t it wonderful?

It is, if you don’t have to deal with storyboards and custom views. 🥲

Dude, where’s my view?

That’s exactly what you read. Your view, beautifully shown and totally functional, becomes simply invisible to storyboard/xib in iOS 12.

But hold a sec. It’s not that simple, and I’ll explain what is happening here.

Before I start, let’s take a look in a little recipe to reproduce this error.

  1. Create a framework, add a struct and change the Build Libraries for Distribution flag to YES.
  2. Import that framework to your app’s project.
  3. Create a custom view (using XIB or View Code, it’s up to you) and add it to a XIB or Storyboard.
  4. In your custom view class, add the struct that you created in the previous framework.
  5. Run your code in a device or simulador running iOS 12 or previous.

It seems a very specific scenario we have here. One of those that doesn’t show up everyday, right? But believe me, it’s commoner that you may think!

For example: if you have a framework where your Models (usually structs) are placed, and one of those structs are used in a View to hold some value or just as a transport object, you may face that problem!

If you think that a View is not shown, and that problem happens just in iOS 12, take a look in your app’s logs. Do you see anything like the below message?

I know. You’ll read the message and instantly think: “DAMN YOU INTERFACE BUILDER”. But hold on a sec bud… 

Interface Builder: the troublemaker or just another victim?

The Interface Builder is so innocent as you here. Let’s understand why.

The Interface Builder says that the class is unknown to it. But the class is there. You can use it. You can access it. So why is it unknown?

Why don’t we ask to objc runtime and see what it says?

Well, it’s true. It’s unknown! But hold a sec. If I ask him to get the class, using the format “Module.ClassName”, the objc runtime founds it. And after that, if I try to get the class once more, it founds the damn class! How is that possible? 

Is it happening in iOS 13 too? Let’s take a look.

Nope, in iOS 13 the objc runtime founds the class at first try. No workarounds needed. No problems.

What’a hell is happening here????

The logic explanation

When you build a framework using Build Libraries for Distribution, you are telling that the framework should enable the ABI Stability feature from Swift. This feature generates a swiftinterface file, literally a text file, holding a description of your public classes, so the future versions of Swift and its compiler can understand the framework contents, without needing a new compilation. That’s fantastic, but with a cost. The objc runtime from previous versions may not be so prepared to this new technology.

As this tech showed up with iOS 13 and Xcode 11, it’s natural that the previous versions couldn’t use all available resources. However, when this happens, the Xcode itself shows an alert informing that this resource is only available to iOS 13 or later, but that is not the case here.

There is not much to do here, because it is a OS limitation, but we have some “workarounds” in that case.

The “workaround” solutions

We have three possible “workarounds” here: we may change our struct to a class, force objc runtime to find our class “on our own”, or we may leave Build Libraries for Distribution.

Shifting struct to class

The first solution is really self-explained, without any mystery: we’ll shift our struct to a class.

Using this solution, your code, that looks like this:

public struct MyStruct { … }

will turn into this:

public class MyStruct { … }

Horrible, isn’t it? But prepare yourself, our next solution is even worse! 😈

Forcing objc runtime to find your class “on your own”

There is basically two ways of doing this. You may use objc_getClass, passing a string with your module’s name (target) and your custom view’s name (if your custom view belongs to your app’s target, the module name will be your app name, like MyApp.MyCustomView), separated by a dot.

if #available(iOS 13, *) { } else {
    objc_getClass(“TesteFramework2.MyCustomViewClass”)
}

If you don’t want to work with objc runtime, you may easily use NSClassFromString, passing the name of the class you want to load in a very same way as the previous use.

if #available(iOS 13, *) { } else {
    NSClassFromString(“TesteFramework2.MyCustomViewClass”)
}

This two ways assumes that you know exactly which classes you need to force load at runtime, which may be impractical during development, but may be very useful to fix a specific bug.

Leaving Build Libraries for Distribution

Another possible solution is stop using Build Libraries for Distribution, just setting this flag to NO in Build Settings.

We have a warning here, because leaving Build Libraries for Distribution implies in not being able to use Xcframeworks and the ABI Stability; in other words, you will lose two really useful features if you choose this path.

Think twice before taking this path, and take it just as a last resource.

BONUS: no more iOS 12 supporting (???)

In a perfect world, where the flowers bloom at the fields, people sing with the wind through their hairs, and unicorns fly leaving a rainbow path in the sky, you may simply drop iOS 12 support from your app 🌈🦄.

But we know that reality is pretty different. Whatever can go wrong, will go wrong! 👊💣

Leaving iOS 12 support means that you’ll not support two important devices anymore, which have a expressive user-base, mainly in emerging economies: iPhone 5S and iPhone 6.

Is your user base for those devices so small that it’s worth it to be dropped? Then this is a good choice.

Just a little tip: Apple stills releasing some security updates for iOS 12 (the last one was released in June, 14). If even Apple stills working in security fixes to this version, is it a really good idea to drop it?

Don’t you believe it? Check it out!

If you want to try and see this error with your own eyes, I uploaded a sample in a Github repo, so you may check it TOTALLY LIVE!!!

https://github.com/AndreasLS/struct-ib-ios12-bug

Clone the repo (and download iOS 12 simulator too) and test it. 😉

Categories: Quick Tips

Tagged as:

André Salla