I have a large app with LOTS of ObjC classes that we've written over the years. I'm trying to migrate over to Swift a bit at a time because I can't risk a mass shift that could break too many things.
This is my class hierarchy for my current problem:
OverFreeAnimalLimitViewController.swift (was originally ObjC) ->
FixBillingIssueViewController.swift (was originally ObjC) ->
BaseViewController.m ->
UIViewController
BaseViewController.m
has:
@property(nullable, strong, nonatomic) IBOutletCollection(UIButton) NSArray<UIButton*> *appstring_buttons;
In the storyboard, I attach all of the buttons to that collection so that I can style them all programmatically instead of having to do that same configuration in the storyboard over and over again.
The underlying xml code in the storyboard looks like this:
<connections>
<outletCollection property="appstring_buttons" destination="Kwf-Mv-Gis" id="ZzW-k6-Ydm"/>
<outletCollection property="appstring_buttons" destination="f7s-5H-81g" id="xRb-XN-pNs"/>
</connections>
When OverFreeAnimalLimitViewController
and FixBillingIssueViewController
were written in objc, this worked properly.
I've converted them to swift and now the app crashes when processing the storyboard. When I run the app and the storyboard tries to attach all of the outlets and outletcollections, I get:
'NSUnknownKeyException', reason: '[<UIViewController 0x12b662370> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key appstring_buttons.'
I noticed the error message says UIViewController
instead of OverFreeAnimalLimitViewController
, so I checked the storyboard and the storyboard correctly lists the custom class as OverFreeAnimalLimitViewController
for this view controller.
I've seen that swift only has @IBOutlet
and doesn't explicitly have @IBOutletCollection
because when you want to use a collection, you simply use an array type with the @IBOutlet
annotation. But does that mean it'll hard-crash if it tries to use an underlying @IBOutletCollection
?
In OverFreeAnimalLimitViewController.swift
if I try to write code to reference self.appstring_buttons
, Xcode will offer me this autocomplete:
Is there a way to fix this or is not possible to use a Swift class that extends from an ObjC class with @IBOutletCollections?
CodePudding user response:
Ok, I figured it out. It had to do with the internals of the Storyboard and not with the IBOutletCollection
definitions.
I created a brand new project in Xcode to test this problem out and the outlet collections worked as intended, so I knew it wasn't a bug and there should be a proper solution for my real project.
One thing that had kept bugging me was that the error message said [<UIViewController 0x12b662370> setValue:forUndefinedKey:]
with UIViewController
instead of OverFreeAnimalLimitViewController
. When I saw that problem at the very start of the issue, I immediately checked the storyboard to make sure the custom class had been set, because it looked like it was just loaded a bare UIViewController instead of my subclass. But I checked and it was set in the storyboard, so I dismissed that as the problem.
What Fixed It
I went into the storyboard and cleared out the Custom Class setting, so that it was just back to a standard UIViewController. Then I set OverFreeAnimalLimitViewController
as the Custom Class again. Then it worked.
What it looks like is that when Xcode was setting the custom class, it looked for the referenced class and when it saw that it was a swift class, it added some extra attributes to the storyboard, i.e. the customModule
and customModuleProvider
which is what would allow it to load the proper class.
<viewController customClass="OverFreeAnimalLimitViewController" title="subscribe_to_sync" id="CMN-i2-ISu" sceneMemberID="viewController">
<viewController customClass="OverFreeAnimalLimitViewController" customModule="HerdBoss" customModuleProvider="target" title="subscribe_to_sync" id="CMN-i2-ISu" sceneMemberID="viewController">
After that, it was able to load the correct view controller subclass and then the outlets attached correctly.