Bulk Fix Hundreds of "#Selector Not Exposed to Objective-C" Errors in Xcode 9 or 9.1

Bulk fix hundreds of #selector not exposed to Objective-C errors in Xcode 9 or 9.1

You'll want to run the migrator – go "Edit > Convert > To Current Swift Syntax..." in Xcode, and select "Minimize Inference" for the "Swift 4 @objc inference" option (I actually didn't realise until recently that the most of what the migrator does is just automatically applying compiler fix-its).

If you're already in a Swift 4 target, you won't be able to run migration on it. You can fix this by just changing the "Swift Language Version" build setting to "Swift 3.2" for that target, and then running the migrator on it (which will switch the version back to 4).

After the migrator has run, you'll notice that the "Swift 3 @objc inference" build setting has been set to "On" – you'll want to test your program with this build setting to ensure you don't get any runtime warnings about Obj-C entrypoints that aren't marked as @objc, and then change the build setting to "Default" when done, as discussed in this Q&A.

Additionally, after migration, feel free to look over the places where @objc has been added – you may be able to neaten code up by using a single @objc extension of the given class to define a group of methods that you want to expose to Obj-C. If you're looking at a class that requires Obj-C exposure for all its compatible members, then you can mark the class @objcMembers. See this Q&A for more info on that.

Bridging Swift Initialisers to Objective C

You may choose to add @objcMembers to your class declaration:

public class @objcMembers MapsAPI : NSObject {

let delegate: MapsAPIResponseDelegate

public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}

Alternatively (or additionally... who am I to judge) you can mark your initializer as being exposed to Objective-C

public class MapsAPI : NSObject {

let delegate: MapsAPIResponseDelegate

@objc public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}

And if you want to, you can also explicitly define the Objective-C selector used:

public class MapsAPI : NSObject {

let delegate: MapsAPIResponseDelegate

@objc(initManagerWithDelegate:)
public init(managerWithDelegate delegate: MapsAPIResponseDelegate) {
self.delegate = delegate
}
}

Proper way to use selectors in Swift

  1. why can't I no longer pass simply the function name String to the action?

Using strings for selectors has been deprecated, and you should now write #selector(methodName)instead of "methodName". If the methodName() method doesn't exist, you'll get a compile error – another whole class of bugs eliminated at compile time. This was not possible with strings.


  1. how is the proper way to implement this following the Swift Way? Using the Selector class?

You did it the right way:

button.addTarget(self, action: #selector(ClassName.methodName(_:)), forControlEvents: UIControlEvents.TouchUpInside)


  1. why do we need to pass the @objc keyword and how it affects the function?

In Swift the normal approach is to bind method's calls and method's bodies at compile time (like C and C++ do). Objective C does it at run time. So in Objective C you can do some things that are not possible in Swift - for example it is possible to exchange method's implementation at run time (it is called method swizzling). Cocoa was designed to work with Objective C approach and this is why you have to inform the compiler that your Swift method should be compiled in Objective-C-like style. If your class inherits NSObject it will be compiled ObjC-like style even without @objc keyword.

Share Extension to open containing app

Currently there's no way to do this. A share extension cannot open the containing app.

The intended approach for share extensions is that they handle all of the necessary work themselves. Extensions can share code with their containing apps by using custom frameworks, so in most cases that's no problem.

If you want to make data available to your app, you can set up an app group so that you have a shared directory. The extension can write data there, and the app can read it. That won't happen until the next time the user launches the app, though.

Delete/Reset all entries in Core Data?

You can still delete the file programmatically, using the NSFileManager:removeItemAtPath:: method.

NSPersistentStore *store = ...;
NSError *error;
NSURL *storeURL = store.URL;
NSPersistentStoreCoordinator *storeCoordinator = ...;
[storeCoordinator removePersistentStore:store error:&error];
[[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error];

Then, just add the persistent store back to ensure it is recreated properly.

The programmatic way for iterating through each entity is both slower and prone to error. The use for doing it that way is if you want to delete some entities and not others. However you still need to make sure you retain referential integrity or you won't be able to persist your changes.

Just removing the store and recreating it is both fast and safe, and can certainly be done programatically at runtime.

Update for iOS5+

With the introduction of external binary storage (allowsExternalBinaryDataStorage or Store in External Record File) in iOS 5 and OS X 10.7, simply deleting files pointed by storeURLs is not enough. You'll leave the external record files behind. Since the naming scheme of these external record files is not public, I don't have a universal solution yet. – an0 May 8 '12 at 23:00



Related Topics



Leave a reply



Submit