According to the Xcode release notes, Apple has been "auditing" their existing APIs to remove implicitly unwrapped optionals. That means that instead of T!
, their APIs will return T
or T?
where appropriate.
Where do they do this? How can I annotate/wrap my existing Objective-C code (especially libraries) to make it cleaner to use from Swift?
Xcode 6.3 added official support for annotating nullability in Objective-C.
The nullability of a value can be declared by annotating the type with the keywords __nullable
, __nonnull
and __null_unspecified
(the default). In properties and methods the keywords are nullable
, nonnull
and null_unspecified
.
Examples from the Xcode release notes
- (void)registerNib:(nonnull UINib *)nib
forCellReuseIdentifier:(nonnull NSString *)identifier;
- (nullable UITableViewCell *)cellForRowAtIndexPath:(nonnull NSIndexPath)indexPath;
@property (nonatomic, readwrite, retain, nullable) UIView *backgroundView;
null_unspecified
(which translates to T!
) is the default for all existing code. A useful feature is the ability to change the default for sections of your API.
NS_ASSUME_NONNULL_BEGIN
// nonnull is the default here
NS_ASSUME_NONNULL_END
This removes a lot of noise, since methods that accept and handle nil usually are the exception, not the rule. Personally, I would use this for all audited APIs.
null_resettable
is an additional annotation that is used for the uncommon case where you can set a property to nil, but it will never be nil (because it resets to a default value).
@property (nonatomic, retain, null_resettable) UIColor *tintColor;
Personally, I would avoid this behavior for new code. The hybrid nature of such properties isn't a good fit for Swift.
Xcode 7 adds support for annotating generic types in Objective-C.
NSArray
, NSSet
and NSDictionary
(which are automatically bridged to Swift's Array
, Set
and Dictionary
can be annotated with the type of their contents.
@property NSArray<NSString *> *stringArray;
@property NSSet<NSString *> *stringSet;
@property NSDictionary<NSString *, NSString *> *stringDict;
There's also the __kindof
keyword that tells the Objective-C compiler to be less strict and allow downcasting. But it doesn't affect the Swift side.
@interface MyArray1<__covariant T> : NSObject
- (void)addObject:(T)object;
@end
@interface MyArray2<__covariant T : NSObject *> : NSObject
- (void)addObject:(T)object;
@end
@interface MyArray3<__covariant T : id<NSCopying>> : NSObject
- (void)addObject:(T)object;
@end
The @implementation
doesn't know about T
and uses id
/NSObject *
/id<NSCopying>
as it always did.
I believe there's a way to do this at the moment. It looks like the information is being compiled to
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator
in files like
UIKit.apinotesc
UIKit.swiftdoc
UIKit.swiftmodule
The *.swiftmodule
classes are generated by Xcode as part of the swift build but the additional information may be in the .apinotesc
file, which there doesn't appear to be a documented form of generating it at the moment.
However, the swift
command has an -apinotes
option, which is used to generate this. You can see from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator/UIKit.apinotesc
that you can parse this with:
xcrun swift -apinotes -binary-to-yaml -o=- /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator/UIKit.apinotesc
This generates a YAML file looking like:
Classes:
- Name: NSFileProviderExtension
Availability: available
AvailabilityMsg: ''
Methods:
- Selector: 'URLForItemWithPersistentIdentifier:'
MethodKind: Instance
Nullability: [ N ]
NullabilityOfRet: U
Availability: available
AvailabilityMsg: ''
If I were a betting man (and I'm not), I'd say that the MethodKind determines whether it's an Instance
or a Class
and that the Nullability is being used by Swift to determine whether it's an optional or not. I would suspect:
U
- (implicitly) unwrapped optionalO
- optionalN
- non-optional?These are wild guesses though.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With