skip to Main Content

TL;DR: Why is an Objectiv-C object created as Swift Optional when assigned to a var before returning it as func result and as Non-Optional when returning it directly?

Details:

I would like to use some existing Objectiv-C code in Swift. A Swift methods creates and returns an UIViewController. This does not work as expected when working with a UIViewController class implemented in Objectiv-C

// SomeViewController.h
@interface SomeViewController : UIViewController { }
- (id)initWithData:(MyData *)data;
@end

// SomeViewController.m
@implementation SomeViewController { }

- (id)initWithData:(MyData *)data {
    self = [super init];
    if (self) {}
    return self;
}

@end


// Swift
func foo() -> UIViewController {
    return SomeViewController(data: someData)   // Works fine

}

func foo() -> UIViewController {
    // ERROR: Value of optional type 'SomeViewController?' must be 
    // unwrapped to a value of type 'SomeViewController'
    let vs = SomeViewController(data: someData)  
    return vc

    ---
    // No problem when using standard init
    let vs = SomeViewController()   
    return vc                                   // Works fine
}

2

Answers


  1. Because the initialization method provided by the CocoaTouch frameworks can never be return nil value。However, custom initializers method can return nil value。
    for example

    // SomeViewController.h

    @interface SomeViewController : UIViewController { }
    - (id)initWithData:(MyData *)data;
    @end
    
    // SomeViewController.m
    @implementation SomeViewController { }
    
    - (id)initWithData:(MyData *)data {
        return nil;
    }
    
    @end
    

    or overwrite init method

    @interface SomeViewController : UIViewController { }
    - (id)init;
    @end
    
    // SomeViewController.m
    @implementation SomeViewController { }
    
    - (id)init {
        return nil;
    }
    
    @end
    

    Xcode BUG

    func foo() -> UIViewController {
        // returns nil will be crash!!!
        // Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
        return SomeViewController(data: someData)   // unsafe
    }
    

    error message

    Login or Signup to reply.
  2. Objective-C initializers can return nil unless you specify otherwise in the initializer’s declaration, so Swift has no choice but to infer its type to be Optional.

    The easiest way to specify non-nullability is to bracket your .h file with nullability assumptions:

    NS_ASSUME_NONNULL_BEGIN
    
    @interface SomeViewController : UIViewController
    
    // This initializer is specified to not return `nil`. It is up to you to ensure that this is the case in your implementation. 
    - (instancetype)initWithData:(NSData*)data;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    

    The reason that the built-in initializers don’t infer type as optional is because they have all been audited and explicitly declared to be either nullable on non-nullable.

    More details: https://developer.apple.com/swift/blog/?id=25

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search