Workarounds for Generic Variable in Swift

Workarounds for generic variable in Swift

You could do something similar using a protocol for the array declaration and base methods that are not dependent on the data type of the key:

protocol KeyValueArray
{
associatedtype KeyType
var array:[(key:KeyType,value:String)] { get set }
}

extension KeyValueArray
{
var array:[(key: KeyType, value:String)] { get {return []} set { } }
}

class ViewController:UIViewController,KeyValueArray
{
// assuming this is like an "abstact" base class
// that won't actually be instantiated.
typealias KeyType = Any

// you can implement base class functions using the array variable
// as long as they're not dependent on a specific key type.
}

class SpecificVC:ViewController
{
typealias KeyType = Int
var array:[(key:Int,value:String)] = []
}

I'm assuming that, at some point the concrete instances of the view controller subclasses will have an actual type for the keys

Applying Generics to Variables and Functions - Swift

You can't add generics to variables in Swift as already shown in this answer, but you can add generics to a function like that:

func swapTwoValues<T>(inout a:T, inout b:T){
let temporaryA = a
a = b
b = temporaryA
}

Check the apple Documentation.

is it possible to create a generic closure in Swift?

No, because variables and expressions can't be generic. There are only generic functions and generic types.


To clarify: In some languages you can have types with a universal quantifier, like forall a. a -> a. But in Swift, types cannot have a universal quantifier. So expressions and values cannot be themselves generic. Function declarations and type declarations can be generic, but when you use such a generic function or an instance of such a generic type, some type (which could be a real type or a type variable) is chosen as the type argument, and thereafter the value you get is no longer itself generic.

Get the name (string) of a generic type in Swift

A pure swift way to achieve that is not possible.

A possible workaround is:

class MyClass<T: AnyObject> {
func genericName() -> String {
let fullName: String = NSStringFromClass(T.self)
let range = fullName.rangeOfString(".", options: .BackwardsSearch)
if let range = range {
return fullName.substringFromIndex(range.endIndex)
} else {
return fullName
}
}
}

The limitations relies on the fact that it works with classes only.

If this is the generic type:

class TestClass {}

NSStringFromClass() returns the full name (including namespace):

// Prints something like "__lldb_expr_186.TestClass" in playground
NSStringFromClass(TestClass.self)

That's why the func searches for the last occurrence of the . character.

Tested as follows:

var x = MyClass<TestClass>()
x.genericName() // Prints "TestClass"

UPDATE Swift 3.0

func genericName() -> String {
let fullName: String = NSStringFromClass(T.self)
let range = fullName.range(of: ".")
if let range = range {
return fullName.substring(from: range.upperBound)
}
return fullName
}

Swift array of mixed generic types

No need to explicitly type:

class Test<T, U> {

init(key: T, value: U) {
}

}

let array: [Test<String, Any>] = [

Test(key: "test", value: []),
Test(key: "test", value: 42)
]

Update:

typealias tuple = (Any,Any)

class TestBlock
{
let key: String
let block: (tuple) -> Void

init(key: String, block: @escaping (tuple) -> Void)
{
self.key = key
self.block = block
}
}

let block1: (tuple) -> Void = { (arg) in

let (_label, _size) = arg
let label = _label as! UILabel
label.font = label.font.withSize((_size as! CGFloat))
}

let block2: (tuple) -> Void = { (arg) in

let (_label, _color) = arg
let label = _label as! UILabel
let color = _color as! UIColor
label.textColor = color
}

let propertiesWithBlock: [TestBlock] = [

TestBlock(key: "fontSize", block: block1),
TestBlock(key: "textColor", block: block2)
]

How to call a generic function with parameter of associated type in Swift 3

It appears that Swift has a problem inferring T from the invocation by starting from MyClass.ID being T.ID and backing into MyClass being T.

If you change your function to take an additional parameter of type T, the code compiles and runs fine:

func process<T: B>(_ value: T.ID, _ workaround : T) {
// do something
}

let workaround = MyClass()
process(MyClass.ID.one, workaround)

Swift designers could approach this inference problem in two ways:

  • Fix inference engine to allow for this use case, or
  • Make it easier for the inference engine to figure out T by enforcing stricter rules on function signatures.

It appears that they decided on the second approach with Swift 4, because the original function fails to compile, issuing the following error:

generic parameter 'T' is not used in function signature

A better work-around is to pass the type instead of an instance, using Java-style approach:

func process<T: B>(_ value: T.ID, _ workaround: T.Type) {
// do something
}
...
process(MyClass.ID.one, MyClass.self)

Note: The edit is based on very insightful comments by tesch



Related Topics



Leave a reply



Submit