We came across this issue after we upgraded to AFNetworking 2.6.0, which added nullability annotations, and UIWebView's extension -loadRequest:progress:success:failure: started crashing. Below is minimal example causing the crash.
Obj-C class:
NS_ASSUME_NONNULL_BEGIN
#interface Foo : NSObject
- (void) fooWithSuccess:(NSString* (^)(NSString* string))success;
#end
NS_ASSUME_NONNULL_END
#implementation Foo
- (void)fooWithSuccess:(NSString* (^)(NSString*))success
{
// EXC_BAD_ACCESS on the following line
NSLog(#"%#", success(#"Foo"));
}
#end
Calling the code above from swift (e.g. in viewDidAppear()):
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
foo.fooWithSuccess { string in
return string
}
}
However, it works without problems if Obj-c code is either not annotated or return parameter of success block is nullable:
- (void) fooWithSuccess:(NSString* __nullable (^)(NSString* string))success;
It seems to us that this behaviour is bug in Obj-c and Swift2 interoperability, and we are wondering if anyone has any insight / we are missing something.
Update
Issue is only with Swift2. When running in Swift 1.2 program behaves as expected.
Radar
Radar has been filed: rdar://22911182
Related
i am working on a project and i am implementing this SDK https://docs.drivequant.com/trip-analysis/ios/get-started and I am implementing it in appdelegeate.m
here how I import the module
#import DriveKitCoreModule;
#import DriveKitTripAnalysisModule;
and here how I initialise it
[[DriveKit shared] initialize];
[[DriveKit shared] setApiKeyWithKey: #""];
[[DriveKit shared] setUserIdWithUserId: #""];
[[DriveKit shared] enableLoggingWithShowInConsole:YES];
[[DriveKitTripAnalysis shared] initializeWithTripListener: self appLaunchOptions:launchOptions];
but I always get this warning with initializeWithTripListener
and after running I got this error
does anyone know why I am getting this error? and how to fix it?
See this link for a description of how you declare that an Objective-C class conforms to a protocol. (Specifically the section titled "Conforming to Protocols".)
Looking at the readme for the framework you are using, it says:
Make the AppDelegate class implement TripListener protocol. This protocol contains 6 methods to implement:
func tripPoint(tripPoint: TripPoint) {
}
func tripStarted(startMode: StartMode) {
}
func tripCancelled(cancelTrip: CancelTrip) {
}
func tripFinished(post: PostGeneric, response: PostGenericResponse) {
}
func tripSavedForRepost(){
}
func beaconDetected(){
}
func significantLocationChangeDetected(location: CLLocation){
}
func sdkStateChanged(state: State){
}
So you will need to add implementations for those 6 methods to your app delegate. (Those method declarations are in Swift. If you're writing your app delegate in Objective-C you'll need to find the Objective-C function prototypes.)
I have a project with swift and ObjC sources and have a strange message in autogenerated header file:
The code, that marked with error:
//interface in ModuleName-Swift.h file
#interface PointsList : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
- (nonnull instancetype)initWithPoints:(NSArray<NSValue *> * /*this marked with warning*/_Nonnull)points OBJC_DESIGNATED_INITIALIZER; //Warning:Pointer is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)
- (nonnull instancetype)initWithValues:(NSArray<NSValue *> * /*this marked with warning*/_Nonnull)pointValues //Warning:Pointer is missing a nullability type specifier (_Nonnull, _Nullable, or _Null_unspecified)
#end
//source PointsList.swift
#objc public class PointsList : NSObject {
var points = Array<CGPoint>()
#objc public override init() {
super.init()
}
#objc public init(withPoints points: Array<CGPoint>) {
self.points.append(contentsOf: points)
super.init()
}
#objc public init(withValues pointValues: Array<NSValue>) {
super.init()
pointValues.forEach { (pointValue) in
self.points.append(pointValue.cgPointValue)
}
}
}
This file is generated by Xcode from swift's classes. I see _Nunnull modifier but XCode complains that I should add that modifier
. Maybe I've forgot some setting in project configuration.
How I can fix this strange behaviour?
UPD
I have same issue with Swift 5 and Xcode 10.2.
It is strange because other files in my project are processed in a correct way.
Problem only with _Nonnull and _Nullable modifiers. Xcode ignore them, if I press to a button Fix in the warning message, Xcode adds second modifier to the pointer and the warning does not move away.
In pure ObjC you shouldn't really need to use _Nullable and _Nonnull. These are lower lever calls than nullable and nonull that should only be necessary when doing C++ things (like typedefs and blocks).
Really you shouldn't ever have to use nonnull either. The only identifier I generally use is nullable. The reason for this is you should be using NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END wrappers in your ObjC code, not only for readability, but for more verbose compiler warnings/errors and better Swift interoperability.
Recommend writing your ObjC classes similar to this:
// SomeClass.h
NS_ASSUME_NONNULL_BEGIN
#interface SomeClass : NSObject
-(instancetype)initWithRequiredSomething:(NSString *)requiredString optionalSomething:(nullable NSString *)optionalString;
#end
NS_ASSUME_NONNULL_END
The return value and requiredString are both implicitly nonnull and optionalString is nullable
Try going through your project and removing all instances of _Nullable, _Nonnull and nonnull, you may find it to be quite satisfying (and hopefully fix the problem!).
Using this Objective-C property:
#interface TSOnboardingPersonalizeViewController
#property (nonatomic, weak) NSObject<PersonalizeContentCoordinatorDelegate> * _Nullable delegate;
#end
crashes in a Swift extension like this:
extension TSOnboardingPersonalizeViewController {
func next() {
self.delegate?.performAction(.Forward)
}
}
POing self.delegate shows:
(lldb) po self.delegate
▿ Optional<protocol<PersonalizeContentCoordinatorDelegate>>
▿ Some : <MyApp.PersonalizeContentCoordinator: 0x8e964cf58140>
But it doesn't crash if I cast the property to the protocol type:
extension TSOnboardingPersonalizeViewController {
func next() {
if let delegate = self.delegate as? PersonalizeContentCoordinatorDelegate {
delegate.performAction(.Forward)
}
}
}
POing delegate shows:
(lldb) po delegate
<MyApp.PersonalizeContentCoordinator: 0x7f86bcf59930>
Why do I need to explicity cast the property to the PersonalizeContentCoordinatorDelegate protocol type?
I'm using Swift 2.2 and Xcode 7.3
I think it could be due to the fact that the delegate is an NSObject, which probably doesn't have a performAction method. So, even though the delegate conforms to the protocol in Objective-C, Swift probably sees an NSObject (which doesn't include that method).
This seems like a "Lost in Translation" sort of thing, where Objective-C is telling Swift "This object conforms to this protocol." Then when executed, Swift tries calling that method on an NSObject which obviously doesn't include that method and crashes. This would explain why casting would work, because until you tell Swift you have an object of a certain type, it can't execute any of that type's specific methods (even if the underlying object actually is that type).
This is just a guess, though and should definitely be taken with a grain of salt (and maybe a better answer if one comes along).
In Objective-C I am often using the following "design pattern" in my code to "broadcast" the scenes input callbacks:
#protocol RPSceneDelegate <NSObject>
-(void)someMethod;
-(void)didTap;
#end
#interface RPScene : SKScene
#property (nonatomic, strong) NSMutableArray<id<RPSceneDelegate>>* delegates;
-(void)addDelegate:(id<RPSceneDelegate>)delegate_;
-(void)removeDelegate:(id<RPSceneDelegate>)delegate_;
#end
#implementation RPScene
#pragma mark - Delegate Handling
-(void)addDelegate:(id<RPSceneDelegate>)delegate_ {
if ([self.delegates containsObject:delegate_]) return;
[self.delegates addObject:delegate_];
}
-(void)removeDelegate:(id<RPSceneDelegate>)delegate_ {
if (![self.delegates containsObject:delegate_]) return;
[self.delegates removeObject:delegate_];
}
-(void)didTap {
for (id<RPSceneDelegate> delegate_ in self.delegates) {
// Just a small example to show what I am doing
[delegate_ performSelector:#selector(didTap)];
}
}
#end
Last night I tried to convert this to swift using Swift-Arrays (not NSMutableArray) but I ended up totally frustrating when checking:
array.contains(theObject)
To get a better understanding of Swift it would help me if someone could convert that little piece of code to Swift. So how do I implement that in Swift!?
EDIT: My code in Swift
protocol RPSceneDelegate {
func someMethod()
}
class RPScene: SKScene {
var delegates = [RPSceneDelegate]()
func addDelegate(delegate: RPSceneDelegate) {
if !delegates.contains(delegate) {
delegates.append(delegate)
}
}
}
What works fine is:
delegates.append(delegate)
What gives me an error is:
delegates.contains(delegate)
The Compiler says:
Cannot convert value of type 'RPSceneDelegate' to expected argument
type '#noescape (RPSceneDelegate) throws -> Bool'
Problem is. I just don't know what that means ... My first idea was that .contains() requires a function, not an instance / object but I have no clue how to implement that...
It's not a helpful error message...
What is means is that in order to check if an array contains something the compiler needs to be able to compare them. At the moment the objects only respond to a simple protocol so the compiler has no idea how to compare them.
Your protocol needs to extend Equatable, and depending on exactly what your objects conforming to this protocol are you may need to implement the equality function.
I want to implement subscripting in my custom class and thus implemented the following methods:
- (id)objectForKeyedSubscript:(id<NSCopying>)key;
- (void)setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key;
But I have a strange issue, because I've never met it before: [(id)obj isKindOfClass:] throws an ARC Semantic Issue:
No known instance method for selector 'isKindOfClass:'
As far as I remember, I didn't have any problems with id before... Is it a bug with Xcode (I'm using the Xcode 5 Developer Preview 2), or have I forgotten something important?
- (id)objectForKeyedSubscript:(id<NSCopying>)key {
if(![key isKindOfClass:[NSString class]]) { // error
...
} else {
...
}
}
isKindOfClass: is a method of the NSObject protocol, so you can either
declare key as conforming to the protocol
- (id)objectForKeyedSubscript:(id <NSCopying, NSObject> )key { ...
or require key to be derived from NSObject (which conforms to that protocol):
- (id)objectForKeyedSubscript:(NSObject <NSCopying> *)key { ...