3

Problem:

The declared static let sharedInstance is not accessible from Objective-C code when building the main app target.

Code:

    @objc public protocol ManagedObjectContextProvider {
          var context: NSManagedObjectContext { get }
    }

@objc public final class ManagedObjectContextProviderImpl: NSObject, ManagedObjectContextProvider {
    public static let sharedInstance: ManagedObjectContextProvider = ManagedObjectContextProviderImpl()
    
    override private init() {}
    
    public var context: NSManagedObjectContext {
        return NSManagedObjectContext.mr_default()
    }
}

Context:

  • This file is contained in 2 targets [main iOS App and Share extension]

  • In the swift to objc generated header of the main app the interface of the swift class is generated as follows:

      @class NSManagedObjectContext;
    
      SWIFT_PROTOCOL("_TtP4<#MyAppName#>28ManagedObjectContextProvider_")
      @protocol ManagedObjectContextProvider
      @property (nonatomic, readonly, strong) NSManagedObjectContext * _Nonnull context;
      @end
    
    
      SWIFT_CLASS("_TtC4<#MyAppName>32ManagedObjectContextProviderImpl")
      @interface ManagedObjectContextProviderImpl : NSObject <ManagedObjectContextProvider>
      - (nonnull instancetype)init SWIFT_UNAVAILABLE;
      @property (nonatomic, readonly, strong) NSManagedObjectContext * _Nonnull context;
      @end
    
  • In the swift to objc generated header of the share extension the interface of the swift class is generated correctly and is:

      @class NSManagedObjectContext;
    
      SWIFT_PROTOCOL("_TtP4<MyAppName>28ManagedObjectContextProvider_")
      @protocol ManagedObjectContextProvider
      @property (nonatomic, readonly, strong) NSManagedObjectContext * _Nonnull context;
      @end
    
    
      SWIFT_CLASS("_TtC4<MyAppName>32ManagedObjectContextProviderImpl")
      @interface ManagedObjectContextProviderImpl : NSObject <ManagedObjectContextProvider>
      SWIFT_CLASS_PROPERTY(@property (nonatomic, class, readonly, strong)       id <ManagedObjectContextProvider> _Nonnull sharedInstance;)
      + (id <ManagedObjectContextProvider> _Nonnull)sharedInstance SWIFT_WARN_UNUSED_RESULT;
      - (nonnull instancetype)init SWIFT_UNAVAILABLE;
      @property (nonatomic, readonly, strong) NSManagedObjectContext * _Nonnull context;
       @end
    
  • MyAppName specified above is the module name used when generating the Swift to Obj-C header and is the same for both main target and share extension

What I've tried:

  1. Copy the same class declaration in both headers [tricks the compiler but fails at runtime with "unrecognized selector"]
  2. Modify the module name of the extension so that it would have a diff module name than the target

What not to propose as a solution:

  1. To use a different module instead of files belonging to 2 targets
Cœur
  • 37,241
  • 25
  • 195
  • 267
Fawkes
  • 3,831
  • 3
  • 22
  • 37
  • 1
    did you try to use `@objcMembers` on the class? – Milan Nosáľ Feb 27 '18 at 10:20
  • or `@objc` directly on the `sharedInstance`? – Milan Nosáľ Feb 27 '18 at 10:21
  • https://stackoverflow.com/questions/24489075/call-a-swift-singleton-from-objective-c – PPL Feb 27 '18 at 10:23
  • @PPL, I don't think the scope of that answer is valid anymore with latest swift. I can reach the shared instance when working with my shared extension target even if it is declared as a static let. My problem is I cannot reach it when building the main target. – Fawkes Feb 27 '18 at 10:37
  • @MilanNosáľ, works with objcMembers. Please propose it as a solution and would be nice if you include a short explanation or direct link to Apple docs regarding usage of objcMembers. Thank you! – Fawkes Feb 27 '18 at 10:43
  • @MilanNosáľ, it's still strange that only the main target complains. – Fawkes Feb 27 '18 at 10:43
  • good article on the solution https://medium.com/@WadeSellers/swift4-and-xcode9-will-be-greeting-you-with-a-new-warning-in-your-existing-code-popping-up-near-56cf7d978cd5 – Fawkes Feb 27 '18 at 10:46

1 Answers1

6

To make members (also static) accessible to ObjC code, you need to add @objc modifier to them, or @objcMembers modifier to class (which is a syntactic sugar to marking all the members with @objc). Read more in documentation.

Milan Nosáľ
  • 19,169
  • 4
  • 55
  • 90
  • 1
    I guess the extension module swift version is "3" thus this works for the extension while the main target's swift version is "4" and it no longer generates the interface for all the members of the class if objcMembers is not added. Thank you for the answer. – Fawkes Feb 27 '18 at 10:50