Avoid Swift importing Objective-C as Implicitly Unwrapped by Default

This continues the discussion from https://devforums.apple.com/thread/271101


Summary:

  • In a mixed Swift/Objective-C project, when Objective-C code is brought to Swift as implicitly unwrapped optional by default [1]
  • E.g. the Objective-C method signature:
    - (NSString *)giveMeAString

    will be seen by Swift like this:

    func giveMeAString() -> NSString!
  • The method giveMeAString might return nil
  • As Swift unwraps the method result implicitly, the following code will crash:
    // Objective-C
    - (NSString *)giveMeAString { return nil; }
    
    // Swift
    func thisWillCrash() {
        let string = someObjectiveCObject.giveMeAString()
        let length = string.length // crash
    }
  • The bad thing is that the compiler does not give any warnings in this case
  • Of course the Objective-C code could be annotated with nullability annotations to avoid these issues, but it is unrealistic to do it at once for a large code base (as also seen for Apple SDKs)
  • This issue makes a mixed Swift/Objective-C app much more error prone than a pure Objective-C or a pure Swift app
  • To avoid this issue, I would like to suggest changing Swift compiler behavior for importing Objective-C code, assuming `nullable` as default for return types
  • @gparker made another interesting suggestion that would also relieve the problem (but need more work from the app developers): "un-annotated declarations are not imported"
  • Are any changes in this direction planned for Swift 2.0? At least some compiler switch to enable this behavior?


[1] https://developer.apple.com/swift/blog/?id=25

Until such a compiler option is implemented by Apple, any workaround suggestions to cope with this problem? Is it somehow possible to see all the places where implicitly unwrapped optionals are accessed without using the `?` operator?

By analogy with NS_ASSUME_NONNULL…, I tried this:


#define NS_ASSUME_NULL_BEGIN _Pragma("clang assume_null begin")
#define NS_ASSUME_NULL_END   _Pragma("clang assume_null end")


and it at least appears to be recognized. Whether it does what you want or not, I don't know, but it'd be worth a try.

_Pragma("clang assume_null begin") seems not to do anything unfortunately. Swift still imports method return types and parameters as implicitly unwrapped. Did you try this?

I have filed radar 21459831.

Unfortunately no response yet. I hope for the best in Swift 2.0

I correct: for Swift 3.0 :-(

Looking at the open source Swift code, the behavior of importing Objective-C code as implicitly unwrapped by default seems to be hard coded into the ClangImporter. The value OTK_ImplicitlyUnwrappedOptional is used as a default in many places in ClangImporter.cpp, ImportDecl.cpp ImportType.cpp and ImporterImpl.h.


A PR for making this default dependent on a compiler option would be awesome.


https://github.com/apple/swift/blob/master/lib/ClangImporter/ClangImporter.cpp

https://github.com/apple/swift/blob/master/lib/ClangImporter/ImportDecl.cpp

https://github.com/apple/swift/blob/master/lib/ClangImporter/ImportType.cpp

https://github.com/apple/swift/blob/master/lib/ClangImporter/ImporterImpl.h

Any news on this? Instead of a safer code, now my mixed project can crash without even a warning 😮

The following news:

I ended up writing a script searching for headers missing nullability: https://github.com/fabb/check_nullability

Avoid Swift importing Objective-C as Implicitly Unwrapped by Default
 
 
Q