SwiftライブラリのObjC対応における落とし穴と回避策

 SwiftライブラリのObjC対応における落とし穴と回避策

0e726052fdb28ba1c004aa90eccbe261?s=128

Shin Yamamoto

November 11, 2019
Tweet

Transcript

  1. SwiftϥΠϒϥϦͷObjCରԠ ʹ͓͚Δམͱ݀͠ͱճආࡦ 2019/11/11 potatotips #66 Shin Yamamoto @scenee

  2. എܠ Swift 5.1: ABI/Module Stability • SwiftʹΑΔBinary Frameworkͷ։ൃɾϝϯς ϯε͕༰қʹ •

    ঎༻ϥΠϒϥϦͷObjC -> Swift΁ͷҠߦ΁
  3. ͳͥObjCରԠ͕ඞཁͳͷ͔ʁ • SwiftϥΠϒϥϦͰ΋ɺObjCରԠ͢ΔͱҎԼͷϓϥο τϑΥʔϜͰར༻Ͱ͖ɺϢʔβʔϕʔε͕޿͕Δ • ReactNative • Kotlin/Native • Xamarin

    • C++/Objective-CͷΈͷΞϓϦ
  4. @objcΛ෇༩͢Δ͚ͩͰ͸ʁ • @objcଐੑΛPublic APIʹ෇༩ • @objcͰ໋໊มߋ • ObjCͷ໋໊ن໿ʹԊ͏Α͏ʹ • BooleanͳͲ͸getter/setter͝ͱʹ

  5. @objc open class FloatingPanelController: UIViewController { @objc public var isInteractionEnabled:

    Bool { @objc(setInteractionEnabled:) set { } @objc(isInteractionEnabled) get { } } @objc(trackScrollView:) public func track(scrollView: UIScrollView?) { } }
  6. ઃܭ࣍ୈͰ @objcଐੑͷΈͰ͸ෆे෼

  7. 3ͭͷམͱ݀͠ 1. String Enum͕ఆٛͰ͖ͳ͍ 2. SubclassԽ͕Ͱ͖ͳ͍ 3. Protocol Extensionͷҙਤ͠ͳ͍ಈ࡞(᠘)

  8. 1. String Enum͕ఆٛͰ͖ͳ͍ • @objc͸ɺInt EnumͰ͔͠෇༩Ͱ͖ͳ͍ • Publicͷ@objc APIʹ͓͍ͯDictionary Keyʹ

    Int Enum͸࢖͑ͳ͍ • Int -> NSInteger (not NSNumber) • Swift͚ͩͰѻ͏ͱ͖͸໰୊ͳ͍
  9. ղܾࡦ ObjCͰString EnumΛఆٛ 1. ObjCͰString EnumΛఆٛ 2. Umbrella headerͰimport 3.

    SwiftͰར༻ɾ֦ு
  10. https://developer.apple.com/videos/play/wwdc2018/408/

  11. Umbrella header

  12. #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN typedef NSString * FloatingPanelState NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(FloatingPanelState); FOUNDATION_EXPORT

    FloatingPanelState FloatingPanelStateFull; FOUNDATION_EXPORT FloatingPanelState FloatingPanelStateHalf; FOUNDATION_EXPORT FloatingPanelState FloatingPanelStateTip; FOUNDATION_EXPORT FloatingPanelState FloatingPanelStateHidden; NS_ASSUME_NONNULL_END #import "FloatingPanelState.h" FloatingPanelState FloatingPanelStateFull = @"Full"; FloatingPanelState FloatingPanelStateHalf = @"Half"; FloatingPanelState FloatingPanelStateTip = @"Tip"; FloatingPanelState FloatingPanelStateHidden = @"Hidden"; FloatingPanelState.m FloatingPanelState.h
  13. #ifndef FloatingPanel_h #define FloatingPanel_h #import <UIKit/UIKit.h> #import <FloatingPanel/FloatingPanelState.h> FOUNDATION_EXPORT double

    FloatingPanelVersionNumber; FOUNDATION_EXPORT const unsigned char FloatingPanelVersionString[]; #endif /* FloatingPanel_h */ FloatingPanel.h
  14. extension FloatingPanelState: CaseIterable { public static var allCases: [FloatingPanelState] {

    return [.full, .half, .tip, .hidden] } } FloatingPanel.swift
  15. open var layoutAnchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] { return [ .full: FloatingPanelLayoutAnchor(absoluteInset:

    18.0, edge: .top), .half: FloatingPanelLayoutAnchor(fractionalInset: 0.5, edge: .bottom), .tip: FloatingPanelLayoutAnchor(absoluteInset: 69.0, edge: .bottom), ] }
  16. - (NSDictionary<FloatingPanelState, id<FloatingPanelLayoutAnchoring>> *)layoutAnchors { return @{ FloatingPanelStateFull: [[FloatingPanelLayoutAnchor alloc]

    initWithAbsoluteInset:0.0], FloatingPanelStateTip: [[FloatingPanelLayoutAnchor alloc] initWithAbsoluteInset:44.f], }; }
  17. #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN typedef NSString * FloatingPanelState NS_TYPED_EXTENSIBLE_ENUM NS_SWIFT_NAME(FloatingPanelState); FOUNDATION_EXPORT

    FloatingPanelState FloatingPanelStateFull; FOUNDATION_EXPORT FloatingPanelState FloatingPanelStateHalf; FOUNDATION_EXPORT FloatingPanelState FloatingPanelStateTip; FOUNDATION_EXPORT FloatingPanelState FloatingPanelStateHidden; NS_ASSUME_NONNULL_END #import "FloatingPanelState.h" FloatingPanelState FloatingPanelStateFull = @"Full"; FloatingPanelState FloatingPanelStateHalf = @"Half"; FloatingPanelState FloatingPanelStateTip = @"Tip"; FloatingPanelState FloatingPanelStateHidden = @"Hidden"; FloatingPanelState.m FloatingPanelState.h ඞͣPublic headerʹ͢Δ
  18. Umbrella headerͷԠ༻ • C/ObjCͷ࣮૷ΛϥΠϒϥϦʹऔΓࠐΈ΍͍͢ • ґଘؔ܎͸Ұํ޲: • Swift -use-> ObjC

    • ͨͩ͠ɺܕ͚ͩͰ͋Ε͹@class/@protocol એݴͯ͠ར༻Մೳ
  19. 2. SubclassԽͰ͖ͳ͍ • SwiftͰఆٛ͞ΕͨClass͸ɺObjCͰSubclassԽͰ͖ ͳ͍ • എܠͱཧ༝͸ʁ • “Cannot subclass

    a class that was declared with the ‘objc_subclassing_restricted’ attribute”
  20. ճආࡦ • @objc protocolͰΠϯλϑΣʔεΛެ։͢Δ • ઃఆΦϒδΣΫτΛఆٛ͢Δ • ObjCͰ࣮૷͢Δ (Umbrella headerͰެ։)

  21. 3. Protocol Extensionͷ᠘ • Protocol Extensionͷ࣮૷͕ObjC͔Βݟ͑ͳ͍ • @objc protocolͷoptional methodʹProtocol

    ExtensionͰσϑΥ ϧτ࣮૷෇༩ • Warningൃੜ: non-‘@objc’ method does not satisfy optional requirement of ‘@objc’ protocol • optional methodͷ࣮૷͕ඞཁʹͳΔ -> optionalͰ͸ͳ͘ͳΔ
  22. Protocol Extensionͷ᠘ • SwiftͰར༻͢Δͱ͖΋classܧঝͰҎԼͷ໰୊͕ൃੜ͢Δ • SR-103 Protocol Extension: function’s implementation

    cannot be overridden by a subclass - Swift — https:// bugs.swift.org/browse/SR-103 • SwiftͷϓϩτίϧΤΫεςϯγϣϯͷ᠘ - Qiita — https://qiita.com/omochimetaru/items/ 17cdbb5a77972c82a781
  23. ճආࡦ Protocol ExtensionΛ࢖Θͳ͍ • Public ProtocolͷσϑΥϧτ࣮૷༻్Ͱ࢖Θͳ͍ • ϢʔβʔαΠτʹӨڹΛ༩͑ͳ͍ϩδοΫͷ࣮૷ ͸໰୊ͳ͍

  24. ·ͱΊ • ObjCରԠͰϥΠϒϥϦͷϢʔεέʔε͕޿͕Δ (ಛʹReactNative, Kotlin/NativeͳͲ) • ObjCରԠ͸@objc෇༩͚ͩͰ͸ෆे෼ • ͍͔ͭ͘ͷ੍໿͸APIઃܭʹΑͬͯରԠՄೳ

  25. References • Binary Frameworks in Swift - WWDC 2019 -

    https:// developer.apple.com/videos/play/wwdc2019/416/ • Migrating Your Objective-C Code to Swift - https:// developer.apple.com/documentation/swift/migrating_your_objective- c_code_to_swift • Swift and Objective-C Interoperability - WWDC 2015 - https:// developer.apple.com/videos/play/wwdc2015/401/ • https://github.com/apple/swift-evolution/blob/master/proposals/0160- objc-inference.md
  26. Thank you