How to access swift public function in objective c files?

前端 未结 2 1703
青春惊慌失措
青春惊慌失措 2021-01-18 16:32

I am using the public method as below to convert the API data type into Int.

public func convertToInt( value : Any) -> Int{
    if let v = value as? Strin         


        
相关标签:
2条回答
  • 2021-01-18 17:00

    The problem is both that global Swift functions are unavailable to Objective-C, and the protocol Any cannot be represented in Objective-C.

    Therefore, one potential solution is to encapsulate your function in a helper class, and to use AnyObject for your value argument – as Int and String can be freely bridged to NSNumber and NSString respectively, and thus can be treated as objects when Foundation is imported.

    @objc final class HelperFunctions : NSObject {
    
        static func convertToInt(value : AnyObject) -> Int {
            if let v = value as? String {
                if let myNumber = NSNumberFormatter().numberFromString(v) {
                    return myNumber.integerValue
                } else {
                    print ("Cannot convert to Int...")
                }
            } else  if let v = value as? Int {
                print("int")
                return v
            } 
    
            // note that this check is completely redundant as NSNumber is freely bridgable
            // to Int – therefore the 'as? Int' check will always get triggered instead
            else if let v = value as? NSNumber { 
                print("nsnum")
                return v.integerValue
            }
            return 0
        }
    }
    

    Although that being said, this really isn't very Swifty code. NSNumber is bridgeable to Int, so you can already convert it directly:

    let n = NSNumber(integer: 5)
    let i = n as Int // 5
    

    And Int already has an initialiser that can take a string:

    let i = Int("55") // Optional(55)
    

    So, I don't really see what Swift problem this function is solving. Although I do see the Objective-C problem that it's solving, and for that reason I would simply implement this function as a C function in an Objective-C file.

    extern NSInteger convertToInt(id _Nonnull value);
    

    inline NSInteger convertToInt(id _Nonnull value) {
    
        if ([value isKindOfClass:[NSString class]]) {
            NSNumberFormatter* f = [[NSNumberFormatter alloc] init];
            return [f numberFromString:(NSString*)value].integerValue;
        } else if ([value isKindOfClass:[NSNumber class]]) {
            return ((NSNumber*)value).integerValue;
        }
    
        return 0;
    }
    

    You can always import this back into Swift if you really want – and then it'll be available as a global function. But I would recommend that you don't import this back to Swift, and instead find a more Swifty solution to your problem.


    For example, I would recommend that you implement this in Swift using protocols and extensions. You can define an IntRepresentable protocol in order to represent types that can be converted to Int. The advantage of doing this is that you're being explicit about the types that you can convert to Int, rather than letting it be an implementation detail of your convenience function.

    protocol IntRepresentable {
        func _asInt() -> Int?
    }
    

    Now you can extend the types that your dictionary could contain and make them conform to IntRepresentable.

    extension NSString : IntRepresentable {
        func _asInt() -> Int? {return Int(self as String)}
    }
    
    extension NSNumber : IntRepresentable {
        func _asInt() -> Int? {return self.integerValue}
    }
    
    // not really necessary, as your dict will be [Whatever:AnyObject],
    // so the NSString extension will be used – here for completeness
    extension String : IntRepresentable {
        func _asInt() -> Int? {return Int(self)}
    }
    
    extension Int : IntRepresentable {
    
        func _asInt() -> Int? {return self}
    
        init?(representable:IntRepresentable) {
            guard let i = representable._asInt() else {return nil}
            self = i
        }
    }
    

    Now you can convert your dictionary from [Whatever:AnyObject] to [Whatever:Int] by doing the following:

    let inputDict = ["foo":"5", "bar":6, "baz":NSNumber(int:5)]
    
    var outputDict = [String:Int]()
    for (key, value) in inputDict {
        if let value = value as? IntRepresentable {
            outputDict[key] = Int(representable: value)
        }
    }
    
    print(outputDict) // ["baz": 5, "foo": 5, "bar": 6]
    

    You can always stick this in a convenience function if it makes you feel better, although I would still advocate against global functions. You can use a caseless enum in order to avoid polluting the global namespace:

    enum DictionaryConversion {
    
        static func toIntValues<Key>(dict:[Key:NSObject]) -> [Key:Int] {
            var outputDict = [Key:Int]()
            for (key, value) in dict {
                if let value = value as? IntRepresentable {
                    outputDict[key] = Int(representable: value)
                }
            }
            return outputDict
        }
    }
    

    Or just extend Dictionary itself:

    extension Dictionary {
    
        func convertValuesToInt() -> [Key:Int] {
            var outputDict = [Key:Int]()
            for (key, value) in self {
                if let value = value as? IntRepresentable {
                    outputDict[key] = Int(representable: value)
                }
            }
            return outputDict
        }
    }
    

    let outputDict = DictionaryConversion.toIntValues(inputDict)
    

    let outputDict = inputDict.convertValuesToInt()
    
    0 讨论(0)
  • 2021-01-18 17:13

    Setup your project to use both swift and objective c. Here is the link for that Then you need to import the Swift class in the similar way you import Objective C class and use it in the same way you would have used in Objective C Class.

    0 讨论(0)
提交回复
热议问题