IBOutlet properties from Swift framework are missing from Objective-C app - ios

I have a framework I've written in Swift that contains a class that extends UIView and that has an IBOutlet property.
The class is visible from my Objective-C app, but Interface Builder won't see the IBOutlet. Here's the code:
#objc protocol MyFrameworkDelegate {
// My delegate methods here
...
}
#objc class MyFrameworkClass: UIView {
#objc #IBOutlet var delegate:MyFrameworkDelegate?
// The rest of my class here
...
}
I set my view's class to MyFrameworkClass in my storyboard, but the delegate outlet isn't showing.
I tried with and without the #objc modifier, no luck. When I inspect my class from Objective-C, the property comes up without the IBOutlet.
Anyone else came across something like that?
EDIT
Here's the code generated by Xcode for the Swift -> Objective-C conversion:
#protocol MyFrameworkDelegate;
SWIFT_CLASS("_Nc9a0xD6MyFrameworkClass")
#interface MyFrameworkClass : UIView
#property (nonatomic) id <MyFrameworkDelegate> delegate;
...
Also, here's the error message I get when I try to run the app:
2014-06-22 21:41:59.095 MyApp Test[75211:17812266] Unknown class MyFrameworkClass in Interface Builder file.
2014-06-22 21:41:59.099 MyApp Test[75211:17812266] -[UIView setDelegate:]: unrecognized selector sent to instance 0xcf5d650
2014-06-22 21:41:59.101 MyApp Test[75211:17812266] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView setDelegate:]: unrecognized selector sent to instance 0xcf5d650'
EDIT
Here's a screenshot of my storyboard:

Your module is None, you should select it to your corresponding value.

reason: '-[UIView setDelegate:]: unrecognized selector
This makes it look like the object in question is an instance of UIView, not MyFrameworkClass. Check the type of the object in IB.

I suspect the word delegate itself is triggering some kind of underlying bug/mechanism/leaky abstraction.
How about calling it myFrameworkDelegate?
I am also wondering if dragging from IB shouldn't always give the best results by itself but I need to fire up Xcode 6 and test manually first to see what happens. New technology :)

Instead of
#objc #IBOutlet var delegate:MyFrameworkDelegate?
use
#IBOutlet var delegate:AnyObject?
than you'll be able to link your class to this property.

Related

Swift CocoaPods Library in Objective-C Project

I have found several online resources on how to include an Objective-C library into a Swift project and a few limited resources on how to do the reverse (which is what I'm after).
I have managed to get my project to compile and run based on the work I did here in this question: Swift CocoaPod Library in Objective-C Project Migration from Swift 3 to 4/5
However whenever I try to access anything from the Swift library in my ObjC project my app crashes with the following...
2020-01-29 14:42:09.756352-0700 Hyperion[13547:2723315] -[HGCircularSlider.RangeCircularSlider setStartPointValue:]: unrecognized selector sent to instance 0x1074088c0
2020-01-29 14:42:09.763045-0700 Hyperion[13547:2723315] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[HGCircularSlider.RangeCircularSlider setStartPointValue:]: unrecognized selector sent to instance 0x1074088c0'
*** First throw call stack:
(0x18e32ea48 0x18e055fa4 0x18e2325a8 0x1923cf86c 0x18e332af4 0x18e334a7c 0x10208f554 0x10208d4d4 0x192d15dec 0x192be580c 0x10208d388 0x10208b570 0x191d8cab8 0x191d8d160 0x191d11b40 0x191d11c6c 0x191d192c0 0x191d23b84 0x191d23f70 0x191d1716c 0x191d119a0 0x191d87134 0x191d87838 0x1924f3d74 0x1924f7224 0x1924f70d0 0x1924f739c 0x191d87290 0x191d87838 0x1924f3d74 0x1924f7224 0x1924f70d0 0x1924f739c 0x191d87290 0x191d87838 0x191cceaec 0x191ccdf08 0x191cca9c8 0x191cca7e0 0x191ccde54 0x1923a2e50 0x191b3e148 0x1923a2e50 0x191dd9494 0x191dd97f8 0x191b40b58 0x1923a2e50 0x191dd9494 0x191dd97f8 0x191dd8814 0x1923dc3d4 0x1923dd714 0x1923b9e2c 0x192431fa4 0x192434500 0x19242d374 0x18e2aca00 0x18e2ac958 0x18e2ac0f0 0x18e2a723c 0x18e2a6adc 0x19822c328 0x1923a1ae0 0x1020de520 0x18e130360)
libc++abi.dylib: terminating with uncaught exception of type NSException
As I state in the answer of my question linked above I have written a Swift class that exposes the CocoaPod's library functions I need which allows my project to compile...
import Foundation
import HGCircularSlider
#objc public class CircularSliderObjc: RangeCircularSlider
{
#objc override open var startPointValue: CGFloat {
get {
return super.startPointValue;
}
set {
super.startPointValue = newValue;
}
}
#objc override open var endPointValue: CGFloat {
get {
return super.endPointValue;
}
set {
super.endPointValue = newValue;
}
}
#objc override open var endThumbImage: UIImage? {
get {
return super.endThumbImage;
}
set {
super.endThumbImage = newValue;
}
}
#objc override open var startThumbImage: UIImage? {
get {
return super.startThumbImage;
}
set {
super.startThumbImage = newValue;
}
}
}
As you can see in the project I have exposed startPointValue as both a set and a get. However invoking this function at runtime causes the crash.
Here is the calling class's header...
#import "BaseViewController.h"
#import "Hyperion-Swift.h"
#interface TemperatureDeviceViewController : BaseViewController
#property (weak, nonatomic) IBOutlet CircularSliderObjc *rangeSlider;
And the call in my .m file...
[self.rangeSlider setStartPointValue:[self getSliderValueForTemp:startValue]];
I'm assuming I'm missing one critical step here?
UPDATE #1
I've tried the answer suggested by Max which makes a lot of sense. However because this is an IBOutlet it in my ViewController his solution really doesn't work for my use case here.
I did some further debugging and put breakpoints all over my startPointValue function in order to see what the "super" was referencing. However none of the breakpoints are triggered before the crash happens. Therefore I'm not sure that the call to "super" is the actual cause? Verdict is still out on this one I think.
Try redesigning your wrapper class to encapsulate rather than inherit from the framework class. For example:
#objc public class CircularSliderObjc
{
let slider: RangeCircularSlider // NOT #objc!
#objc var startPointValue: CGFloat {
get {
return slider.startPointValue;
}
set {
slider.startPointValue = newValue;
}
}
That way Objective-C doesn't even need to know that the Swift class exists at all. My guess is that this is crashing on the super, where something Swift-specific is being accessed from Objective-C.

UIViewController throws 'unrecognized selector' error

I've created a XIB containing only a UITableView. I changed the parameters of the project to load this view at launch. But I get this "unrecognized selector sent to instance" error I don't know what it is. How do I fix this and get my app to show my view ?
I've searched the web and it seems that might come from Objective-C libraries ?
EDIT
I did some testing, it's look like it crashes when I link my TableView with my IBOutlet in the ViewController. Am I doing something wrong with the type TableView ?
The viewcontroller :
class Liste: UIViewController {
#IBOutlet weak var maListe: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
}
The error stack :
2019-06-12 14:06:11.083905+0200 GuildWar[5905:8796162]
-[GuildWar.Liste _finishDecodingLayoutGuideConnections:]: unrecognized selector sent to instance 0x104f0ef40 2019-06-12 14:06:17.511280+0200
GuildWar[5905:8796162] * Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[GuildWar.Liste
_finishDecodingLayoutGuideConnections:]: unrecognized selector sent to instance 0x104f0ef40'
* First throw call stack: (0x21230f518 0x2114ea9f8 0x21222c278 0x23e4c2ef8 0x212314d60 0x2123169fc 0x23e874110 0x21c955f28
0x21c8f5304 0x212cdba4c 0x21c955f28 0x21c95616c 0x21c8f5304
0x23e151dcc 0x23e152958 0x23e4971e4 0x23e4977c0 0x23e495e24
0x23dd5a104 0x23dd6269c 0x23dd59d88 0x23dd5a678 0x23dd589c4
0x23dd5868c 0x23dd5d1cc 0x23dd5dfb0 0x23dd5d084 0x23dd61d84
0x23e494518 0x23e090f0c 0x214c89d44 0x214c93754 0x214c92f5c
0x104cbcc74 0x104cc0840 0x214cc40bc 0x214cc3d58 0x214cc4310
0x2122a12bc 0x2122a123c 0x2122a0b24 0x21229ba60 0x21229b354
0x21449b79c 0x23e497b68 0x1045ea10c 0x211d618e0) libc++abi.dylib:
terminating with uncaught exception of type NSException
It looks like, you might have created IBOutlet or IBAction and deleted later in the code. Revisit the connections in the storyboard connection inspectors and check if there is any warning symbol like the attached screenshot, then you need to remove that connection.
Or if you can share your code I can look into.

invalidateLayout with invalidateSupplementaryElements and UICollectionViewLayoutInvalidationContext

I am trying to refresh a Footer ( supplementaryElement). I am using the invalidatelayout method. Based on SO suggestions here. Apple documentation here.
I am getting the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'the invalidation context
() sent to
-[UICollectionViewFlowLayout invalidateLayoutWithContext:] is not an instance of type UICollectionViewFlowLayoutInvalidationContext or a
subclass'
Implemented Code:
let footer_context = UICollectionViewLayoutInvalidationContext()
footer_context.invalidateSupplementaryElements(ofKind: "ActivitiesSelectMembersFooterView", at: [indexPath])
self.collectionView?.collectionViewLayout.invalidateLayout(with: footer_context)
Looks like invalidateLayout method is expecting UICollectionViewFlowLayoutInvalidationContext instead of UICollectionViewLayoutInvalidationContext.
I am using Xcode 8 and Swift 3.
My Storyboard in Xcode is here -
"Next" is the Footer that has the custom class "ActivitiesSelectMembersFooterView"
The error message accurately describes the problem. When calling invalidateLayout(with:) on a UICollectionViewFlowLayout object, you need to pass an instance of UICollectionViewFlowLayoutInvalidationContext.
Change this line:
let footer_context = UICollectionViewLayoutInvalidationContext()
To this:
let footer_context = UICollectionViewFlowLayoutInvalidationContext()

NSInvalidArgumentException - XCode 6.0.1

I realize this error has been discussed many times on stackoverflow, but I'm still not sure what I'm missing. Here's the console output:
Unknown class CRBarGraphController in Interface Builder file.
2014-11-18 18:40:05.796 Test[7319:60b] -[UIImageView start2]: unrecognized selector sent to instance 0x16562ac0
2014-11-18 18:40:06.256 Test[7319:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIImageView start2]: unrecognized selector sent to instance 0x16562ac0'
CRBarGraphController.m defines a function (void)start2; also declared in the interface for CRBarGraphController.h.
start2 is called from CRGraphViewController.m using the following code:
-(void)viewWillAppear:(BOOL)animated {
self.navigationController.navigationBarHidden = NO;
[self.bar_Graph start2];
}
where, bar_Graph is an object of CRBarGraphController, declared in CRGraphViewController.h as:
#property (weak, nonatomic) IBOutlet CRBarGraphController *bar_Graph;
CRGraphViewController.h does import CRBarGraphController.h
Also, in the storyboard, bar_Graph outlet is set to class, CRBarGraphController.
Why does XCode still think bar_Graph is an instance of UIImageView and not CRBarGraphComtroller? I included an exception breakpoint to make sure the exception originates from where start2 is being called.

Connect to an IOS IBAction which is not at a "valid connection destination"

I have been spending days to make connection from a button on the storyboard to a new NSObject class in an IOS project. Methods I used in OS X no longer work in IOS.
I have no trouble connection the button to the IBAction on the original UIViewController class. According to the Apple documentation my new class is not a "valid connection destination".
To work around the issue I tried to call the method in the new class from the UIViewController class. I tried notification and delegate schemes, using the posts on Stack Overflow, but could not get them to work.
The closest I came linking to a +method in the target class, such as
+ (void) startToneGenerator
{
NSLog(#"Arrived in startToneGenerator");
// [self startPlay:nil];
}
However, from inside that method I can not call the -startPlay in the same class. Because the -StartPlay class is part of an audio unit construct I can not change it to a +starPlay class without breaking the audio unit.
Can anyone point me to some documentation which describes what "valid connection destination" really means, and how to make the attempted connection a valid one.
I used the method suggested in answer 1 in OS X, where it does work, but it no longer works in IOS.
I am restating the code you clarify the problem.
// code in VC
- (IBAction)RunPauseStop:(id)sender
{
NSLog(#"arrived in RunPauseSTop"); // OK
[uToneGenerator testMethod]; // OK
// [uToneGenerator startPlay:self]; // crashes
}
// code in uToneGenerator
+ (void) testMethod
{
NSLog(#"arrived in <testMethod>"); // OK
// [uToneGenerator startPlay:nil]; // crashes
}
- (IBAction)startPlay:(id)sender
{
NSLog(#"arrived in <startPlay>"); // Don't get here
}
The line [uToneGenerator startPlay:nil]; compiles (with a warning) but crashes with:
2012-03-06 13:20:25.509 Tinnitus Tamer IP[863:f803] +[uToneGenerator startPlay:]: unrecognized selector sent to class 0x6c40
2012-03-06 13:20:25.509 Tinnitus Tamer IP[863:f803] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[uToneGenerator startPlay:]: unrecognized selector sent to class 0x6c40'
calling from testMethod; compiles but crashes with:
2012-03-06 13:22:31.413 Tinnitus Tamer IP[885:f803] +[uToneGenerator startPlay:]: unrecognized selector sent to class 0x6c44
2012-03-06 13:22:31.414 Tinnitus Tamer IP[885:f803] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[uToneGenerator startPlay:]: unrecognized selector sent to class 0x6c44'
I noted that the error message can not find +[uToneGenerator startPlay:, but the method I am trying to call is -[uToneGenerator startPlay:
uToneGenerator *utg = [[uToneGenerator alloc]init]; works OK. In spite of the compiler warning "NSProject may not respond to 'startPlay'.
Thanks for helping me out. Much appreciated. I really was stuck.
You need to call whatever class method you want inside the IBAction method in your VC:
- (IBAction)myButtonPressed:(id)sender {
[MyClass myClassMethod];
}
If you want that code to work you must declare - (IBAction)startPlay:(id)sender with a +. Otherwise you will have to make an instance of your class:
uToneGenerator *utg = [[uToneGenerator alloc]init]; //or whatever you use
and then use that to call your startPlay method:
[utg startPlay:self];
Hope it helps.

Resources