Stats

Contributors: 2 Thursday, December 1, 2016
Licensed under: CC-BY-SA
Not affiliated with Stack Overflow
Rip Tutorial: riptutorial@gmail.com
Roadmap: roadmap

Fine-grained interoperation between Objective-C and Swift

Download swift eBook

Example

When an API is marked with NS_REFINED_FOR_SWIFT, it will be prefixed with two underscores (__) when imported to Swift:

@interface MyClass : NSObject
- (NSInteger)indexOfObject:(id)obj NS_REFINED_FOR_SWIFT;
@end

The generated interface looks like this:

public class MyClass : NSObject {
    public func __indexOfObject(obj: AnyObject) -> Int
}

Now you can replace the API with a more "Swifty" extension. In this case, we can use an optional return value, filtering out NSNotFound:

extension MyClass {
    // Rather than returning NSNotFound if the object doesn't exist,
    // this "refined" API returns nil.
    func indexOfObject(obj: AnyObject) -> Int? {
        let idx = __indexOfObject(obj)
        if idx == NSNotFound { return nil }
        return idx
    }
}

// Swift code, using "if let" as it should be:
let myobj = MyClass()
if let idx = myobj.indexOfObject(something) {
    // do something with idx
}

In most cases you might want to restrict whether or not an argument to an Objective-C function could be nil. This is done using _Nonnull keyword, which qualifies any pointer or block reference:

void
doStuff(const void *const _Nonnull data, void (^_Nonnull completion)())
{
    // complex asynchronous code
}

With that written, the compiler shall emit an error whenever we try to pass nil to that function from our Swift code:

doStuff(
    nil,  // error: nil is not compatible with expected argument type 'UnsafeRawPointer'
    nil)  // error: nil is not compatible with expected argument type '() -> Void'

The opposite of _Nonnull is _Nullable, which means that it is acceptable to pass nil in this argument. _Nullable is also the default; however, specifying it explicitly allows for more self-documented and future-proof code.

To further help the compiler with optimising your code, you also might want to specify if the block is escaping:

void
callNow(__attribute__((noescape)) void (^_Nonnull f)())
{
    // f is not stored anywhere
}

With this attribute we promise not to save the block reference and not to call the block after the function has finished execution.