Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dancing with Dinosaurs: Objective-C and Swift Interop

Bas Broek
November 18, 2021

Dancing with Dinosaurs: Objective-C and Swift Interop

In this presentation, we meet at an old friend (Objective-C) and how it interacts with its new friend (Swift).

From making our Objective-C code work great with Swift, to making our Swift code available in Objective-C as well; covering things from nullability and (advanced) renaming to option sets and generics.

… and the story behind me “meeting” the Swift programming language.

Bas Broek

November 18, 2021
Tweet

More Decks by Bas Broek

Other Decks in Programming

Transcript

  1. DANCING WITH
    DINOSAURS
    1 — @basthomas, NSSpain, November 2021

    View Slide

  2. DANCING WITH DINOSAURS
    OBJECTIVE-C & SWIFT
    INTEROP
    2 — @basthomas, NSSpain, November 2021

    View Slide

  3. JUNE 2, 2014
    3 — @basthomas, NSSpain, November 2021

    View Slide

  4. OBJECTIVE-C
    4 — @basthomas, NSSpain, November 2021

    View Slide

  5. ... WITHOUT THE C
    5 — @basthomas, NSSpain, November 2021

    View Slide

  6. ▸ The Netherlands
    ▸ Germany
    ▸ The Netherlands (ish)
    ▸ Spain (ish)
    ▸ Ireland
    ▸ Rotterdam, the Netherlands
    6 — @basthomas, NSSpain, November 2021

    View Slide

  7. BAS
    ▸ XING Mobile Releases & Platform Team
    ▸ Apple macOS Accessibility
    ▸ WeTransfer
    7 — @basthomas, NSSpain, November 2021

    View Slide

  8. BAS
    ▸ XING Mobile Releases & Platform Team Objective-C & Swift
    ▸ Apple macOS Accessibility Objective-C
    ▸ WeTransfer Swift
    8 — @basthomas, NSSpain, November 2021

    View Slide

  9. OBJECTIVE-C &
    SWIFT INTEROP
    9 — @basthomas, NSSpain, November 2021

    View Slide

  10. I: AUDITING OBJECTIVE-C FOR
    SWIFT
    10 — @basthomas, NSSpain, November 2021

    View Slide

  11. GENERATED
    INTERFACES
    11 — @basthomas, NSSpain, November 2021

    View Slide

  12. 12 — @basthomas, NSSpain, November 2021

    View Slide

  13. NULLABILITY
    13 — @basthomas, NSSpain, November 2021

    View Slide

  14. #import
    @interface BTBWatch : NSObject
    - (NSInteger)currentHour;
    - (NSDate *)currentDate;
    - (BOOL)setTimeWithHours:(NSInteger)hours
    minutes:(NSInteger)minutes
    seconds:(NSInteger)seconds
    error:(NSError **)error;
    - (void)printCurrentTime;
    @property (nonatomic) NSString *strapType;
    @property (nonatomic, readonly) NSArray *functions;
    @end
    14 — @basthomas, NSSpain, November 2021

    View Slide

  15. open class BTBWatch : NSObject {
    open func currentHour() -> Int
    open func currentDate() -> Date!
    open func setTimeWithHours(_ hours: Int, minutes: Int, seconds: Int) throws
    open func printCurrentTime()
    open var strapType: String!
    open var functions: [Any]! { get }
    }
    15 — @basthomas, NSSpain, November 2021

    View Slide

  16. NS_ASSUME_NONNULL_BEGIN
    @interface BTBWatch : NSObject
    /// Interface here
    @end
    NS_ASSUME_NONNULL_END
    16 — @basthomas, NSSpain, November 2021

    View Slide

  17. open class BTBWatch : NSObject {
    open func currentHour() -> Int
    open func currentDate() -> Date
    open func setTimeWithHours(_ hours: Int, minutes: Int, seconds: Int) throws
    open func printCurrentTime()
    open var strapType: String
    open var functions: [Any] { get }
    }
    17 — @basthomas, NSSpain, November 2021

    View Slide

  18. @property (nonatomic, nullable) NSString *strapType;
    18 — @basthomas, NSSpain, November 2021

    View Slide

  19. open var strapType: String?
    19 — @basthomas, NSSpain, November 2021

    View Slide

  20. (RE)NAMING
    20 — @basthomas, NSSpain, November 2021

    View Slide

  21. - (NSInteger)currentHour;
    - (NSDate *)currentDate;
    @property (nonatomic, readonly) NSInteger currentHour;
    @property (nonatomic, readonly) NSDate *currentDate;
    21 — @basthomas, NSSpain, November 2021

    View Slide

  22. open func currentHour() -> Int
    open func currentDate() -> Date
    open var currentHour: Int { get }
    open var currentDate: Date { get }
    22 — @basthomas, NSSpain, November 2021

    View Slide

  23. open func setTimeWithHours(_ hours: Int, minutes: Int, seconds: Int)
    23 — @basthomas, NSSpain, November 2021

    View Slide

  24. - (void)setTimeWithHours:(NSInteger)hours
    minutes:(NSInteger)minutes
    seconds:(NSInteger)seconds
    error:(NSError **)error NS_SWIFT_NAME(setTime(hours:minutes:seconds:));
    24 — @basthomas, NSSpain, November 2021

    View Slide

  25. open func setTime(hours: Int, minutes: Int, seconds: Int) throws
    25 — @basthomas, NSSpain, November 2021

    View Slide

  26. ENUMS AND
    OPTION SETS
    26 — @basthomas, NSSpain, November 2021

    View Slide

  27. NS_ASSUME_NONNULL_BEGIN
    const NSString *patekPhilippe;
    const NSString *aLangeSoehne;
    const NSString *omega;
    const NSString *meisterSinger;
    NS_SWIFT_NAME(Watch)
    @interface BTBWatch : NSObject
    // ...
    27 — @basthomas, NSSpain, November 2021

    View Slide

  28. public let patekPhilippe: String
    public let aLangeSoehne: String
    public let omega: String
    public let meisterSinger: String
    28 — @basthomas, NSSpain, November 2021

    View Slide

  29. NS_ASSUME_NONNULL_BEGIN
    NS_SWIFT_NAME(Watch.Brand) typedef NSString * WatchBrand NS_TYPED_EXTENSIBLE_ENUM;
    const WatchBrand patekPhilippe;
    const WatchBrand aLangeSoehne;
    const WatchBrand omega;
    const WatchBrand meisterSinger;
    NS_SWIFT_NAME(Watch)
    @interface BTBWatch : NSObject
    // ...
    29 — @basthomas, NSSpain, November 2021

    View Slide

  30. extension Watch {
    public struct Brand : Hashable, Equatable, RawRepresentable {
    public init(_ rawValue: String)
    public init(rawValue: String)
    }
    }
    extension Watch.Brand {
    public static let patekPhilippe: Watch.Brand
    public static let aLangeSoehne: Watch.Brand
    public static let omega: Watch.Brand
    public static let meisterSinger: Watch.Brand
    }
    30 — @basthomas, NSSpain, November 2021

    View Slide

  31. extension Watch.Brand {
    public static let mondaine: Watch.Brand
    }
    31 — @basthomas, NSSpain, November 2021

    View Slide

  32. typedef NS_OPTIONS(NSInteger, BTBComplication) {
    BTBComplicationDateWindow = 1 << 0,
    BTBComplicationPowerReserveIndicator = 1 << 1,
    BTBComplicationChronograph = 1 << 2
    } NS_SWIFT_NAME(Watch.Complication);
    32 — @basthomas, NSSpain, November 2021

    View Slide

  33. extension Watch {
    public struct Complication : OptionSet {
    public init(rawValue: Int)
    public static var dateWindow: Watch.Complication { get }
    public static var powerReserveIndicator: Watch.Complication { get }
    public static var chronograph: Watch.Complication { get }
    }
    }
    33 — @basthomas, NSSpain, November 2021

    View Slide

  34. LIGHTWEIGHT
    GENERICS
    34 — @basthomas, NSSpain, November 2021

    View Slide

  35. @property (nonatomic, readonly) NSArray *functions;
    open var functions: [Any] { get }
    35 — @basthomas, NSSpain, November 2021

    View Slide

  36. @property (nonatomic, readonly) NSArray *functions;
    open var functions: [String] { get }
    36 — @basthomas, NSSpain, November 2021

    View Slide

  37. RECAP
    ▸ Generated Interfaces
    ▸ Nullability
    ▸ (Re)naming
    ▸ Enums and option sets
    ▸ Lightweight generics
    37 — @basthomas, NSSpain, November 2021

    View Slide

  38. II: EXPOSING SWIFT TYPES TO
    OBJECTIVE-C
    38 — @basthomas, NSSpain, November 2021

    View Slide

  39. POWERFUL
    ENUMS
    39 — @basthomas, NSSpain, November 2021

    View Slide

  40. enum Bookcase: String {
    case billy
    case hemnes
    case kallax
    }
    Bookcase.billy.rawValue // "billy"
    40 — @basthomas, NSSpain, November 2021

    View Slide

  41. // Bookcase.h
    // Nothing to be seeen here...
    41 — @basthomas, NSSpain, November 2021

    View Slide

  42. VALUE TYPE
    NULLABILITY
    42 — @basthomas, NSSpain, November 2021

    View Slide

  43. class Library {
    let bookcases: [Bookcase?]
    init(bookcases: [Bookcase?]) {
    self.bookcases = bookcases
    }
    }
    Library(bookcases: [.billy, nil, .kallax, .kallax])
    43 — @basthomas, NSSpain, November 2021

    View Slide

  44. A SWIFT-FIRST
    APPROACH
    44 — @basthomas, NSSpain, November 2021

    View Slide

  45. DON'T LIMIT
    YOURSELF
    45 — @basthomas, NSSpain, November 2021

    View Slide

  46. RECAP
    ▸ Powerful enums
    ▸ Value type nullability
    ▸ Swift-first
    46 — @basthomas, NSSpain, November 2021

    View Slide

  47. III: ADVANCED
    INTEROPABILITY
    47 — @basthomas, NSSpain, November 2021

    View Slide

  48. SWIFTIER
    OBJECTIVE-C
    48 — @basthomas, NSSpain, November 2021

    View Slide

  49. #if defined(__cplusplus)
    #define let auto const
    #else
    #define let const __auto_type
    #endif
    #if defined(__cplusplus)
    #define var auto
    #else
    #define var __auto_type
    #endif
    49 — @basthomas, NSSpain, November 2021

    View Slide

  50. map, forEach, flatMap, reduce
    50 — @basthomas, NSSpain, November 2021

    View Slide

  51. ASYNC-AWAIT
    51 — @basthomas, NSSpain, November 2021

    View Slide

  52. "IT JUST WORKS"
    SE-0297
    52 — @basthomas, NSSpain, November 2021

    View Slide

  53. - (void)fetchShareParticipantWithUserRecordID:(CKRecordID *)userRecordID
    completionHandler:(void (^)(CKShareParticipant * _Nullable, NSError * _Nullable))completionHandler;
    func fetchShareParticipant(
    withUserRecordID userRecordID: CKRecord.ID,
    completionHandler: @escaping (CKShare.Participant?, Error?) -> Void
    )
    func fetchShareParticipant(
    withUserRecordID userRecordID: CKRecord.ID
    ) async throws -> CKShare.Participant
    53 — @basthomas, NSSpain, November 2021

    View Slide

  54. @objc func perform(operation: String) async -> Int { ... }
    - (void)performWithOperation:(NSString * _Nonnull)operation
    completionHandler:(void (^ _Nullable)(NSInteger))completionHandler;
    54 — @basthomas, NSSpain, November 2021

    View Slide

  55. - (NSProgress *)takesALongTimeWithCompletionHandler:
    (void (^)(MyResult * _Nullable, NSError * _Nullable))completionHandler;
    55 — @basthomas, NSSpain, November 2021

    View Slide

  56. GENERICS
    56 — @basthomas, NSSpain, November 2021

    View Slide

  57. @interface Queue<__covariant ObjectType> : NSObject
    - (void)enqueue:(ObjectType)value;
    - (ObjectType)dequeue;
    @end
    57 — @basthomas, NSSpain, November 2021

    View Slide

  58. RECAP
    ▸ Swiftier Objective-C
    ▸ Async-await
    ▸ Generics
    58 — @basthomas, NSSpain, November 2021

    View Slide

  59. ¡GRACIAS!
    @BASTHOMAS
    59 — @basthomas, NSSpain, November 2021

    View Slide

  60. REFERENCES
    ▸ https://developer.apple.com/videos/play/wwdc2020/10680/
    ▸ https://basbroek.nl/objc-swift-interop-auditing
    ▸ https://basbroek.nl/there-and-back-again
    ▸ https://pspdfkit.com/blog/2017/even-swiftier-objective-c/
    ▸ https://github.com/apple/swift-evolution/blob/main/proposals/
    60 — @basthomas, NSSpain, November 2021

    View Slide