Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access swift public function in objective c files?

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? String {
        if let myNumber = NSNumberFormatter().numberFromString(v) {
            return myNumber.integerValue
        } else {
            print ("Cannot convert to Int...")
        }
    } else  if let v = value as? Int {
        return v
    } else  if let v = value as? NSNumber {
        return v.integerValue
    }
    return 0
}

I am using both swift and objective c files in my project. I Want to access this function into objective c file also. If anyone know the solution please help me.

like image 811
karthik Avatar asked Oct 18 '22 08:10

karthik


1 Answers

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()
like image 148
Hamish Avatar answered Nov 15 '22 04:11

Hamish