How to reuse complex xib-designed views in storyboards using modern swift generics, property wrappers and dynamic member lookup?

Matthias Lamoureux
6 min readJan 14, 2020

--

To answer this rather long question (take a deep breath and try not to say it three times in a row at home, we are professionals), you’ll need at least Xcode 11 and a project using Swift 5.1.

I ran into this problem many times, and the last one was when I wanted to display and reuse a message view for our new B2B application ExtraDoc, here at Extrabat. I have read many things about how reusing xib files in storyboards and unfortunately none completely satisfied me, especially because none really used the whole power of the latest swift release.

We will develop a simple project, containing a complex message view designed in a xib file. Then, we’ll use it and preview it in a storyboard. Finally, we’ll try to access its properties using an outlet in an elegant way.

Project creation

First let’s create a single view app called ReusableXibViews using Swift and Storyboards.

Xcode | File | New | Project | Single View App

Xib creation

Well, let’s dive in with the creation and the design of our message view. Create and add the MessageView.xib file to your project.

File | New | File… | User Interface | View

Choose the Freeform size in the simulated metrics.

Attributes inspector | Simulated Metrics

Then, design your view the way you want.

MessageView.xib

The next classical step consists of creating the associated UIViewclass, let’s call it MessageView.

Note: It is important to name the xib file with the name of the class. Later we’ll use a helper function using this as a convention.

MessageView.swift

Once done, just assign the class in the Identity inspector, and connect the outlets in the Connection inspector.

Identity inspector
Connection inspector

Well, well, well… Nothing really new, so let the fun begin…

Storyboard inclusion

So, we’d like to have our view in our storyboard, first step : load the view from the xib. To do it, we’ll add a little extension to UIView to load the view from the xib named after the class.

Add the file UIView+Nib.swift.

UIView+Nib.swift

Note: We’re talking about xib files and everywhere we use in code the term nib. That’s just an historical convention. A long time ago, Interface Builder files were stored in nib files, then Apple changed its format for XML, and gave a new name: xib. Thank you for reading this little historical break…

The really important part is the next step: we will create a generic class designed to wrap the view, whatever its type. We need that for the storyboard to preview our view, thanks to the prepareForInterfaceBuilder method. Here we simply load the content view from the xib file and add it as a subview of the wrapper.

NibWrapperView.swift

As our old friend Interface Builder does not really like generics (probably too fancy for him and his old buddy Objective-C) we have to define a class to specialize generics and to be used in storyboards.

Go back to the MessageView.swift and add this little class definition:

MessageView.swift

So now it’s easy to figure out what we’ll do: go to your storyboard, add a plain UIView and assign our brand new MessageViewWrapper custom class.

And voilà!

Main.storyboard

Well, at least it should… Xcode being a bit picky with @IBDesignable views it may:

  1. Take some time to appear
  2. Just do not appear… 🤷‍♂️

To check if there’s been a failure, check the designable state of the view in the Identity inspector.

Message View Wrapper | Identity Inspector

If you have this, just wait…

Message View Wrapper | Identity Inspector

And if you have a “failed” message… Well… Try cleaning the project, closing Xcode and the Simulator, it may be better after… You can try to turn your computer off and on again… Or run in underwear around a cemetery during a full moon night… (Please don’t! Try and fill a feedback to Apple instead it may be more efficient… perhaps…)

Anyways… Congratulations! You’ve succeeded to insert a custom xib view inside a storyboard… Great, but now, what if you want an outlet out of it?

To outlets and beyond…

So let’s try to do it as we would do with any view…

ViewController.swift

Damnit! Interface Builder has its revenge to prevent us from using Swift generics… (I’m sure you can hear its evil laugh!) Indeed, we can’t make a @objc generics class, and @IBOutlet must be…

But we are smarter than that, aren’t we?

Ok IB, you want an Objective-C class, let’s give you one!

ViewController.swift

Yay! It works…

So let’s use it:

ViewController.swift
It’s aliiiiiive!

Oh wait…

We’ll have to perform all those casts all the time and we will loose all the beauty we’ve been working for… Happily Swift 5.1 adds this cool feature: Property Wrappers… And it will make our day.

So let’s define a Property Wrapper to do all the casting for us!

NibWrapped.swift

So now we can use it this way:

ViewController.swift

You can access the casted value of your view with _messageView.unwrapped and write things like this:

ViewController.swift
Nice…

Thats great!!! But… Can’t we go further?

Dynamic member lookup to the rescue

Thanks to this little addition to Swift 4.2, we will be able to call the properties directly instead of using the unwrapped property.

Just update the NibWrapped.swift file with this:

NibWrapped.swift

So now it is (almost) completely transparent and you can use directly the _messageView as if it were a MessageView class:

ViewController.swift
Woooot!

Note: I said almost transparent, because we still need to use the property wrapper _messageView and not the proper outlet messageView but I still think it’s a fair compromise…

Conclusion

So thanks to all this, when you want a new reusable and previewable xib view, just:

  1. Add a class to specialize the generics and set it as the a view’s custom class in a storyboard
  2. Add a simple UIView outlet with the property wrapper indicating the target class
  3. And that’s it!

Well, I hope you’ve liked the elegant way we can use modern Swift features along with our good old Interface Builder. (Who’s laughing now?!?)

Note: You can find this sample project on github: https://github.com/letatas/ReusableXibViews

--

--