Objective-C calling parameterized Swift method crashes Swift compiler - objective-c

I have a simple Swift extension on NSManagedObject, in which I have a parametrized method for finding a single object - the signature looks like:
public class func findFirst<T:NSManagedObject>(inContext context : NSManagedObjectContext? = .None) -> T?
I'm trying to call this from Objective-C, but it seems like it cannot be seen. If I create a non-parameterized version I can see and call it just fine from Objective-C:
public class func findFirstUntypedWithPredicate(predicate:NSPredicate?, inContext context : NSManagedObjectContext? = .None) -> NSManagedObject?
Is there any way for ObjectiveC to be able to reach the parameterized version of the call?
I would use Self like so:
public class func findFirst(inContext context : NSManagedObjectContext? = .None) -> Self?
using the technique found here:
How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
However, that causes the Swift compiler to segfault when compiling the code (Xcode 6.3.1, or Xcode 6.4 beta 2).
Edit: Here's a link with the full source of the framework I'm trying to build, including bonus Swift compiler crashes caused by templated methods:
https://www.dropbox.com/s/fixaj9ygdoi4arp/KiGiCoreData.zip?dl=0

Generic methods are not visible from Objective-C. However you can use
the ideas from How to use generic types to get object with same type to define a findFirst() class method
which returns Self? (the Swift equivalent of instancetype) without
being generic:
// Used to cast `AnyObject?` to `Self?`, `T` is inferred from the context.
func objcast<T>(obj: AnyObject?) -> T? {
return obj as! T?
}
extension NSManagedObject
{
class func entityName() -> String {
let classString = NSStringFromClass(self)
// The entity is the last component of dot-separated class name:
let components = split(classString) { $0 == "." }
return components.last ?? classString
}
// Return any matching object, or `nil` if none exists or an error occurred
class func findFirst(context : NSManagedObjectContext, withPredicate pred : NSPredicate?) -> Self? {
let name = entityName()
let request = NSFetchRequest(entityName: name)
request.predicate = pred
var error : NSError?
let result = context.executeFetchRequest(request, error: &error)
if let objects = result {
return objcast(objects.first)
} else {
println("Fetch failed: \(error?.localizedDescription)")
return nil
}
}
}
This can be used from Swift
if let obj = YourEntity.findFirst(context, withPredicate: nil) {
// found
} else {
// not found
}
and from Objective-C:
YourEntity *obj = [YourEntity findFirst:context withPredicate:nil];

Related

Extension can't access generic parameters at runtime

I´m using AWS Cognito, in the doc it says I have to add this function.
But I´m getting this error:
Extension of a generic Objective-C class cannot access the class's generic parameters at runtime
extension AWSTask {
public func continueWithExceptionCheckingBlock(completionBlock:#escaping (_ result: Any?, _ error: Error?) -> Void) {
self.continue({(task: AWSTask) -> Any? in
if let exception = task.exception {
print("Fatal exception: \(exception)")
kill(getpid(), SIGKILL);
}
let result: AnyObject? = task.result
let error: NSError? = task.error as NSError?
completionBlock(result, error)
return nil
})
}
}
For those who have this issue in Swift 5, try adding #objc modifier to the method. Please find the example below:
extension NSLayoutAnchor {
#objc func constrainEqual(_ anchor: NSLayoutAnchor<AnchorType>, constant: CGFloat = 0) {
let constraint = self.constraint(equalTo: anchor, constant: constant)
constraint.isActive = true
}
}
Without context I can't say if you are actually doing what the error message prompts you with, but there is an open bug report describing this issue (where no offending code is actually used):
SR-2708: Extending ObjC generics in Swift 3 does not compile
ObjC:
#interface MySet<T : id<NSCopying>> : NSObject
#end
Swift:
class Foo { }
struct Bar { }
extension MySet {
func foo() -> Foo { return Foo() }
func bar() -> Bar { return Bar() }
}
Both of the extension methods result in "Extension of a generic Objective-C class cannot access the class's generic parameters at
runtime". However, neither really does anything like that (at least
not explicitly).
If you read the comments to the bug report, you'll see that a user named 'Vasili Silin' describes having this issue when attempting to extend AWSTask, so you might have to consider alternative approaches until this bug is resolved.
I just got the same error and solve it this way :
extension yourClass where T == yourType {}

SWIFT 3.0 migration error - Extension of a generic Obj-C class cannot access the class' generic parameter at runtime

I have this code working fine in Swift 2.
extension PHFetchResult: Sequence {
public func makeIterator() -> NSFastEnumerationIterator {
return NSFastEnumerationIterator(self)
}
}
Since I upgraded to Swift 3
Extension of a generic Objective-C class cannot access the class's generic parameters at runtime
I have no idea on how to fix this. Any help is much appreciated!
Problem was reported here: https://bugs.swift.org/browse/SR-1576
But in the end you can't use for in with PHFetchResult in Swift 3.0. Let's see some examples:
let collections = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)
let collectionLists = PHCollectionList.fetchCollectionLists(with: .momentList, subtype: .momentListYear, options: nil)
let assets = PHAsset.fetchAssets(with: .image, options: nil)
You can either use the built-in enumeration of PHFetchResult (my recommended solution):
collections.enumerateObjects(_:) { (collection, count, stop) in
//...
}
collectionLists.enumerateObjects(_:) { (collectionList, count, stop) in
//...
}
assets.enumerateObjects(_:) { (asset, count, stop) in
//...
}
Or access each object by its index:
for idx in 0 ..< collections.count {
let collection = collections[idx]
// ...
}
for idx in 0 ..< collectionLists.count {
let collectionList = collectionLists[idx]
// ...
}
for idx in 0 ..< assets.count {
let asset = assets[idx]
// ...
}
Pulling from Realm, you may be able to get around this by extending a subclass of what you want to conform to Sequence and put the makeIerator function there.
// Sequence conformance for ClassA is provided by ProtocolX's `makeIterator()` implementation.
extension ClassA: Sequence {}
extension ProtocolX {
// Support Sequence-style enumeration
public func makeIterator() -> RLMIterator {
return RLMIterator(collection: self)
}
}
You can see the full code at https://github.com/realm/realm-cocoa/blob/master/Realm/Swift/RLMSupport.swift
You can use a wrapper type as suggested by Swift engineer Jordan Rose on the bug report:
import Photos
struct ResultSequence<Element: AnyObject>: Sequence {
var result: PHFetchResult<Element>
init(_ result: PHFetchResult<Element>) {
self.result = result
}
func makeIterator() -> NSFastEnumerationIterator {
return NSFastEnumerationIterator(self.result)
}
}
func test(_ request: PHFetchResult<PHCollection>) {
for elem in ResultSequence(request) {
print(elem)
}
}
There is no way to fix that, you might need to refactor your code or use some other techniques.
you can refer to this:
How to use a swift class with a generic type in objective c
docs:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-XID_78

swift 2.1 use of unresolved identifier

I am developing an app which uses a sqlite database..
So I wrote this Singleton class as a model manager :
class ModelManager {
static let sharedInstance = ModelManager()
var database = FMDatabase(path: Utility.getPath("myDB.sqlite"))
class func getInstance() -> ModelManager {
if(sharedInstance.database == nil) {
sharedInstance.database = FMDatabase(path: Utility.getPath("myDB.sqlite"))
}
return sharedInstance
}
}
The problem is that I get compiler error when I use the sharedInstance that says use of unresolved identifier sharedInstance
For example in this class where I add data to the database
func addSData(favBuddy: favorites) -> Bool {
sharedInstance.database!.open()
let isInserted = sharedInstance.database!.executeUpdate("INSERT INTO favorties (eName, eEmail) VALUES (test, test)", withArgumentsInArray: [favorties.eName, favorties.eMail])
sharedInstance.database!.close()
return isInserted
}
Any idea how to solve this?
I'm inferring from your question that you are trying to access your shared database instance from a separate class. In that case, you need to do: ModelManager.sharedInstance

How to access private members of an Objective-C class from a Swift extension?

I'm trying to extend an Objective-C class in Swift and make it conform to the Equatable protocol. This requires to access some private members of the extended class, which the compiler doesn't let me do. What is the correct way to do it without making the private members public?
My Swift code:
import Foundation
extension ShortDate : Equatable { }
public func == (lhs: ShortDate, rhs: ShortDate) -> Bool {
if (lhs.components.year == rhs.components.year)
&& (lhs.components.month == rhs.components.month)
&& (lhs.components.day == rhs.components.day) {
return true;
}
return false;
}
Objective-C:
#interface ShortDate : NSObject<NSCopying, NSCoding> {
NSDate *inner;
NSDateComponents *components; // The date split into components.
}
...
#end
The error I'm getting:
ShortDate.swift:26:9: 'ShortDate' does not have a member named 'components'
I came across this question while trying to find a way to access a private variable of a class from one of the SDKs we use. Since we don't have or control the source code we can't change the variables to properties. I did find that the following solution works for this case:
extension ObjcClass {
func getPrivateVariable() -> String? {
return value(forKey: "privateVariable") as? String
}
open override func value(forUndefinedKey key: String) -> Any? {
if key == "privateVariable" {
return nil
}
return super.value(forUndefinedKey: key)
}
}
Overriding value(forUndefinedKey:) is optional. value(forKey:) will crash if the private variable doesn't exist on the class unless you override value(forUndefinedKey:) and provide a default value.
I believe that there is no way to access Objective-C instance variables from Swift. Only Objective-C properties get mapped to Swift properties.

What is the Swift equivalent of respondsToSelector?

I've googled but not been able to find out what the swift equivalent to respondsToSelector: is.
This is the only thing I could find (Swift alternative to respondsToSelector:) but isn't too relevant in my case as its checking the existence of the delegate, I don't have a delegate I just want to check if a new API exists or not when running on the device and if not fall back to a previous version of the api.
As mentioned, in Swift most of the time you can achieve what you need with the ? optional unwrapper operator. This allows you to call a method on an object if and only if the object exists (not nil) and the method is implemented.
In the case where you still need respondsToSelector:, it is still there as part of the NSObject protocol.
If you are calling respondsToSelector: on an Obj-C type in Swift, then it works the same as you would expect. If you are using it on your own Swift class, you will need to ensure your class derives from NSObject.
Here's an example of a Swift class that you can check if it responds to a selector:
class Worker : NSObject
{
func work() { }
func eat(food: AnyObject) { }
func sleep(hours: Int, minutes: Int) { }
}
let worker = Worker()
let canWork = worker.respondsToSelector(Selector("work")) // true
let canEat = worker.respondsToSelector(Selector("eat:")) // true
let canSleep = worker.respondsToSelector(Selector("sleep:minutes:")) // true
let canQuit = worker.respondsToSelector(Selector("quit")) // false
It is important that you do not leave out the parameter names. In this example, Selector("sleep::") is not the same as Selector("sleep:minutes:").
There is no real Swift replacement.
You can check in the following way:
someObject.someMethod?()
This calls the method someMethod only if it's defined on object someObject but you can use it only for #objc protocols which have declared the method as optional.
Swift is inherently a safe language so everytime you call a method Swift has to know the method is there. No runtime checking is possible. You can't just call random methods on random objects.
Even in Obj-C you should avoid such things when possible because it doesn't play well with ARC (ARC then triggers warnings for performSelector:).
However, when checking for available APIs, you can still use respondsToSelector:, even if Swift, if you are dealing with NSObject instances:
#interface TestA : NSObject
- (void)someMethod;
#end
#implementation TestA
//this triggers a warning
#end
var a = TestA()
if a.respondsToSelector("someMethod") {
a.someMethod()
}
Update Mar 20, 2017 for Swift 3 syntax:
If you don't care whether the optional method exists, just call delegate?.optionalMethod?()
Otherwise, using guard is probably the best approach:
weak var delegate: SomeDelegateWithOptionals?
func someMethod() {
guard let method = delegate?.optionalMethod else {
// optional not implemented
alternativeMethod()
return
}
method()
}
Original answer:
You can use the "if let" approach to test an optional protocol like this:
weak var delegate: SomeDelegateWithOptionals?
func someMethod() {
if let delegate = delegate {
if let theMethod = delegate.theOptionalProtocolMethod? {
theMethod()
return
}
}
// Reaching here means the delegate doesn't exist or doesn't respond to the optional method
alternativeMethod()
}
If the method you are testing for is defined as an optional method in a #objc protocol (which sounds like your case), then use the optional chaining pattern as:
if let result = object.method?(args) {
/* method exists, result assigned, use result */
}
else { ... }
When the method is declare as returning Void, simply use:
if object.method?(args) { ... }
See:
“Calling Methods Through Optional Chaining”
Excerpt From: Apple Inc. “The Swift Programming Language.”
iBooks. https://itun.es/us/jEUH0.l
It seems you need to define your protocol as as subprotocol of NSObjectProtocol ... then you'll get respondsToSelector method
#objc protocol YourDelegate : NSObjectProtocol
{
func yourDelegateMethod(passObject: SomeObject)
}
note that only specifying #objc was not enough. You should be also careful that the actual delegate is a subclass of NSObject - which in Swift might not be.
For swift3
If you just want to call the method, run the code below.
self.delegate?.method?()
Functions are first-class types in Swift, so you can check whether an optional function defined in a protocol has been implemented by comparing it to nil:
if (someObject.someMethod != nil) {
someObject.someMethod!(someArgument)
} else {
// do something else
}
In Swift 2,Apple introduced a new feature called API availability checking, which might be a replacement for respondsToSelector: method.The following code snippet comparison is copied from the WWDC2015 Session 106 What's New in Swift which I thought might help you,please check it out if you need to know more.
The Old Approach:
#IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
if dropButton.respondsToSelector("setSpringLoaded:") {
dropButton.springLoaded = true
}
}
The Better Approach:
#IBOutlet var dropButton: NSButton!
override func awakeFromNib() {
if #available(OSX 10.10.3, *) {
dropButton.springLoaded = true
}
}
For swift 3.0
import UIKit
#objc protocol ADelegate : NSObjectProtocol {
#objc optional func hi1()
#objc optional func hi2(message1:String, message2:String)
}
class SomeObject : NSObject {
weak var delegate:ADelegate?
func run() {
// single method
if let methodHi1 = delegate?.hi1 {
methodHi1()
} else {
print("fail h1")
}
// multiple parameters
if let methodHi2 = delegate?.hi2 {
methodHi2("superman", "batman")
} else {
print("fail h2")
}
}
}
class ViewController: UIViewController, ADelegate {
let someObject = SomeObject()
override func viewDidLoad() {
super.viewDidLoad()
someObject.delegate = self
someObject.run()
}
// MARK: ADelegate
func hi1() {
print("Hi")
}
func hi2(message1: String, message2: String) {
print("Hi \(message1) \(message2)")
}
}
Currently (Swift 2.1) you can check it using 3 ways:
Using respondsToSelector answered by #Erik_at_Digit
Using '?' answered by #Sulthan
And using as? operator:
if let delegateMe = self.delegate as? YourCustomViewController
{
delegateMe.onSuccess()
}
Basically it depends on what you are trying to achieve:
If for example your app logic need to perform some action and the delegate isn't set or the pointed delegate didn't implement the onSuccess() method (protocol method) so option 1 and 3 are the best choice, though I'd use option 3 which is Swift way.
If you don't want to do anything when delegate is nil or method isn't implemented then use option 2.
As I started to update my old project to Swift 3.2, I just needed to change the method from
respondsToSelector(selector)
to:
responds(to: selector)
I just implement this myself in a project, see code below. As mentions by #Christopher Pickslay it is important to remember that functions are first class citizens and can therefore be treated like optional variables.
#objc protocol ContactDetailsDelegate: class {
optional func deleteContact(contact: Contact) -> NSError?
}
...
weak var delegate:ContactDetailsDelegate!
if let deleteContact = delegate.deleteContact {
deleteContact(contact)
}
another possible syntax by swift..
if let delegate = self.delegate, method = delegate.somemethod{
method()
}
I use guard let else, so that can do some default stuff if the delegate func is not implemented.
#objc protocol ViewController2Delegate: NSObjectProtocol {
optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnVoid string: String)
optional func viewController2(controller: ViewController2, didSomethingWithStringAndReturnString string: String) -> String
}
class ViewController2: UIViewController {
weak var delegate: ViewController2Delegate?
#IBAction func onVoidButtonClicked(sender: AnyObject){
if (delegate != nil && delegate!.respondsToSelector(Selector("viewController2:didSomethingWithStringAndReturnVoid:"))) {
NSLog("ReturnVoid is implemented")
delegate!.viewController2!(self, didSomethingWithStringAndReturnVoid: "dummy")
}
else{
NSLog("ReturnVoid is not implemented")
// Do something by default
}
}
#IBAction func onStringButtonClicked(sender: AnyObject){
guard let result = delegate?.viewController2?(self, didSomethingWithStringAndReturnString: "dummy") else {
NSLog("ReturnString is not implemented")
// Do something by default
return
}
NSLog("ReturnString is implemented with result: \(result)")
}
}
I guess you want to make a default implementation for delegate. You can do this:
let defaultHandler = {}
(delegate?.method ?? defaultHandler)()
Swift 3:
protocol
#objc protocol SomeDelegate {
#objc optional func method()
}
Object
class SomeObject : NSObject {
weak var delegate:SomeObject?
func delegateMethod() {
if let delegateMethod = delegate?.method{
delegateMethod()
}else {
//Failed
}
}
}
The equivalent is the ? operator:
var value: NSNumber? = myQuestionableObject?.importantMethod()
importantMethod will only be called if myQuestionableObject exists and implements it.