I'll begin with small code examples, made as simple as possible to sum up the problem.
Let's say I've got a function defined as follow :
func doSomething<T>(using f: (String, (T) -> Void) -> Void, key: String) {
f("test", {
object in
// do something with object using key
//...
})
}
I've got a bunch of auto generated classes with class methods, for exemple :
class MyClass {
var prop1: String = ""
}
class MyClassAPI {
class func f1(param: String, completion: (MyClass) -> Void) {
let mc = MyClass()
mc.prop1 = "hello"
completion(mc)
}
}
Then, I got a whole set of key/functions couples in a dictionary :
// Any because of cast problem !
let keyFunctions: [String: Any] = [
"first" : MyClassAPI.f1,
"second" : MySecondClassAPI.f2,
"third" : MyThirdClassAPI.f3
//...
]
Finally, i'll iterate through all the keys/function to call doSomething:
for (k, f) in keyFunctions {
// This line doesn't work as the compiler complain about typing errors
doSomething(using: f, key: k)
}
The problem I'm facing is that I can't cast my functions to the right type to pass them to doSomething :
Xcode suppose to force cast using f as! (String, (_) -> ()) -> Void then gives me an error, _ is not a type.
I tried to be more permissive using if let uf = f as? (String, (Any) -> Void) -> Voidwith no chance.
I read the whole generics pages of Swift manual without any hint on how to achieve this.
Please let me know of any existing way to perform such things using genericity.
Instead of Any make a more specific type for a block
let keyFunctions: [String: (String) -> Any]
Then, at least, your will compile and doSomething will be called.
However, there is no point in making it generic, as T will always be Any . If doSomething relies on common behaviour between your classes, then it would make sense to define a protocol for all of them.
If you actually want to have information about your classes, then you can introduce a non generic class that would keep this info:
class Wrap {
var resultType: Any.Type
let f: (String) -> Any
init<T>(_ f: #escaping (String) -> T) {
self.f = f
resultType = T.self
}
}
let keyFunctions: [String: Wrap] = [
"first" : Wrap(MyClassAPI.f1),
"second" : Wrap(MySecondClassAPI.f2)
]
But resultType can not be used in casting later though.
I finally made it this way :
func doSomething<T>(using f: (String, (Any) -> Void, key: String, type: T.Type) -> Void, key: String) {
f("test", {
object in
guard let o as? type else { return }
// do something with o using key
//...
})
}
let keyFunctions: [String: (String, (Any) -> Void)] = [
"first" : MyClassAPI.f1,
"second" : MySecondClassAPI.f2,
"third" : MyThirdClassAPI.f3
//...
]
Related
I have a custom data struct that looks like this:
struct Entitlements {
var entitlements: [Entitlement]
struct Entitlement {
var type: String
var entitlement: Int
}
}
I have an initialiser that takes a dictionary of [String: Int] and maps it to [Entitlement]:
init(entitlements: [String: Int]) {
self.entitlements = entitlements.map({ (entitlement) -> Entitlement in
return Entitlement(type: entitlement.key, entitlement: entitlement.value)
})
}
I also have a function that maps [Entitlement] to [String: Int]. I have attempted to write it like this:
func simplify() -> [String: Int] {
self.entitlements.map { (entitlement) -> (key: String, value: Int) in
return (key: entitlement.type, value: entitlement.entitlement)
}
}
But I get an error that looks like this:
'Cannot convert return expression of type '[(key: String, value: Int)]' to return type '[String : Int]''
I have tried using flatmap to combine dictionaries into one but with no success:
func simplify() -> [String: Int] {
self.entitlements.flatMap { (entitlement) -> [String: Int] in
return [entitlement.type, entitlement.entitlement]
}
}
But I get an error:
'Unable to infer closure type in the current context'
Any help with this would be much appreciated!
You can use Dictionary(uniqueKeysWithValues:) and map the entitlements array to an array of tuples to get the correct input format for the init.
func simplify() -> [String: Int] {
Dictionary(uniqueKeysWithValues: entitlements.map{ ($0.type, $0.entitlement) })
}
If you need to handle duplicate keys there is also a variant that takes a closure for handling what to do. Here is an example based on Apples doc where the first key is always selected
func toDictionary() -> [String: Int] {
Dictionary(entitlements.map { ($0.type, $0.entitlement) },
uniquingKeysWith: { (first, _) in first })
}
I have a class implementing Mappable protocol and I want to pass the type as parameter to a function and get an instance of that object, but the type might also be a primitive type.
class MyMappable : Mappable {
required init?(map: Map) {
print("i am custom mappable")
}
}
func getInstance<T>() -> T{
if T.self == Int.self {
return 10 as T
}
if T.self is Mappable.self {
return T(Json : [:]) //this is the problem, T is not Mappable
}
}
What I tried is create 2 override for getInstance like this :
func getInstance<T>() -> T{}
func getInstance<T : Mappable>() -> T{}
but for more types it can be lots of override.
is there a way to let swift know T is mappable in first function?
You can conditionally cast it to a Mappable like this (untested):
if let MappableType = T.self as? Mappable.self {
return MappableType(Json : [:])
}
Usually variable names are lowercase but in this case I can imagine using uppercase since it is used as a type.
You could also do it differently depending on your usage/callsite.
How about constraining the getInstance<T>() -> T method:
getInstance<T: Mappable>() -> T
Given a class:
class First<T> {
}
And a method of class First:
func second<U>(closure: (value: T) -> U) {
}
How could I store the closure passed as an argument to second so that I can call it at a later date?
You would need to declare U in the class instead, so that you have a type for the storage:
class First<T,U> {
var f : ((T) -> U)! = nil
func second(closure: #escaping (T) -> U) {
self.f = closure
}
}
If having the function second work for only one kind of type is good enough for you, then Matt's answer is good.
class First<T, U> {
typealias ClosureType = (value: T) -> U
var savedClosure: ClosureType? = nil
func second(closure: ClosureType) {
savedClosure = closure
}
}
That doesn't actually answer your question as stated!
The thing is: You can't store a value of an unknown type.
But! if the type conforms to a known protocol, then you can save it.
protocol P {}
class First<T> {
typealias ClosureType = (value: T) -> P
var savedClosure: ClosureType? = nil
func second<U: P>(closure: (value: T) -> U) {
savedClosure = closure
}
}
The protocol could even be protocol<> "no protocol at all", which is typealiased to the keyword Any.
class First<T> {
typealias ClosureType = (value: T) -> Any
var savedClosure: ClosureType? = nil
func second<U>(closure: (value: T) -> U) {
savedClosure = closure
}
}
But we don't really know what you want to do, so there are multiple answers to your question… for example, maybe you wanted to store a separate closure for each type?
class First<T> {
typealias ClosureType = (value: T) -> Any
var savedClosures = [String: ClosureType]()
func second<U>(closure: (value: T) -> U) {
savedClosures[String(U)] = closure
}
}
Anyway, the real question is: "Do you really need to do this? Is there some simple change you can do that obviates this need?"
Initializer of class A takes an optional closure as argument:
class A {
var closure: ()?
init(closure: closure()?) {
self.closure = closure
self.closure()
}
}
I want to pass a function with an argument as the closure:
class B {
let a = A(closure: action(1)) // This throws the error: Cannot convert value of type '()' to expected argument type '(() -> Void)?'
func action(_ i: Int) {
//...
}
}
Class A should execute the closure action with argument i.
I am not sure about how to write this correctly, see error in code comment above. What has to be changed?
Please make your "what-you-have-now" code error free.
Assuming your class A like this:
class A {
typealias ClosureType = ()->Void
var closure: ClosureType?
init(closure: ClosureType?) {
self.closure = closure
//`closure` would be used later.
}
//To use the closure in class A
func someMethod() {
//call the closure
self.closure?()
}
}
With A given above, you need to rewrite your class B as:
class B {
private(set) var a: A!
init() {
//initialize all instance properties till here
a = A(closure: {[weak self] in self?.action(1)})
}
func action(i: Int) {
//...
}
}
The problem is that closure()? is not a type. And ()? is a type, but it is probably not the type you want.
If you want var closure to have as its value a certain kind of function, you need to use the type of that function in the declaration, e.g.
var closure: (Int) -> Void
Similarly, if you want init(closure:) to take as its parameter a certain kind of function, you need to use the type of that function in the declaration, e.g.
init(closure: (Int) -> Void) {
Types as Parameters
In Swift, every object has a type. For example, Int, String, etc. are likely all types you are extremely familiar with.
So when you declare a function, the explicit type (or sometimes protocols) of any parameters should be specified.
func swallowInt(number: Int) {}
Compound Types
Swift also has a concept of compound types. One example of this is Tuples. A Tuple is just a collection of other types.
let httpStatusCode: (Int, String) = (404, "Not Found")
A function could easily take a tuple as its argument:
func swallowStatusCode(statusCode: (Int, String)) {}
Another compound type is the function type. A function type consists of a tuple of parameters and a return type. So the swallowInt function from above would have the following function type: (Int) -> Void. Similarly, a function taking in an Int and a String and returning a Bool would have the following type: (Int, String) -> Bool.
Function Types As Parameters
So we can use these concepts to re-write function A:
class A {
var closure: (() -> Void)?
init(closure: (() -> Void)?) {
self.closure = closure
self.closure()
}
}
Passing an argument would then just be:
func foo(closure: (Int) -> Void) {
// Execute the closure
closure(1)
}
One way to do what I think you're attempting is with the following code:
class ViewController: UIViewController {
override func viewDidLoad() {
let _ = A.init(){Void in self.action(2)}
}
func action(i: Int) {
print(i)
}
}
class A: NSObject {
var closure : ()?
init(closure: (()->Void)? = nil) {
// Notice how this is executed before the closure
print("1")
// Make sure closure isn't nil
self.closure = closure?()
}
}
How can I store an array of functions to callback later in an array like in JavaScript? Any and AnyObject type cannot hold functions with different types of method signatures.
You can use an enum to put various functions into the Array and then extract the functions with a switch.
enum MyFuncs {
case Arity0 ( Void -> Void )
case Arity2 ( (Int, String) -> Void)
}
func someFunc(n:Int, S:String) { }
func boringFunc() {}
var funcs = Array<MyFuncs>()
funcs.append(MyFuncs.Arity0(boringFunc))
funcs.append( MyFuncs.Arity2(someFunc))
for f in funcs {
switch f {
case let .Arity0(f):
f() // call the function with no arguments
case let .Arity2(f):
f(2,"fred") // call the function with two args
}
}
Note: this answer is for Swift versions 1.0 and lower.
Functions that have different parameters and return types are of a different type so they can't be stored in an array together. They also don't conform to the Any or AnyObject protocols.
If you have functions with the same parameters though you can work around that. Even though the functions below return a tuple of Double and an Int, they can both be defined as () -> Any function types.
func func1 () -> Int {
return 1
}
func func2 () -> (Double, Double){
return (2, 3)
}
var a: () -> Int = func1
var b: () -> (Double, Double) = func2
var arr: Array< () -> Any> = [a, b]
Below is an example with both an array and a dictionary. Tested and working in Xcode 6.1 (6A1046a). Note that functions from dictionaries must first be unwrapped.
This technique does however fall apart when the functions have different parameter or return types, for the reasons explained by connor in his answer.
class MyViewController: UIViewController
{
let arrayOfFunctions = [function1, function2]
let dictionaryOfFunctions = [
"function1": function1,
"function2": function2
]
func function1() {
NSLog("function1")
}
func function2() {
NSLog("function2")
}
override func viewDidLoad()
{
let fn1 = arrayOfFunctions[0]
fn1(self)()
let fn2 = dictionaryOfFunctions["function2"]
fn2!(self)()
}
}
As of Swift 1.1, all function types conform to Any, so you can hold functions in an Any array.
func foo (str: String) -> Int {
return 1
}
func bar () -> (Double, Double){
return (2, 3)
}
var a: Any = foo
var b: Any = bar
var arr: Any = [a, b]
A simpler approach to call stored function in array on demand , to use parameters a simple workaround is to make dict args and use it inside the function.
var args = [ "a" : 1, "b" : 2 ]
var requestQueue : [() -> Void] = []
func a() -> Void {
let param = args["a"]
print(param!)
}
func b() -> Void {
let param = args["b"]
print(param!)
}
requestQueue.append(a)
requestQueue.append(b)
for item in requestQueue {
item() //calling the functions
}