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
Using an Uiview as a Mask in Another Uiview on Swift
Unsafemutablepointer<Void> to Concrete Object Type
Why Do I Get "Static Member '...' Cannot Be Used on Instance of Type '...'" Error
How to Initialise a New Nsdocument Instance in Swift
Require Associatedtype to Be Representable in a @Convention(C) Block
Uilabel Subclass Appearance in Storyboard
Avspeechsynthesizer Errors in iOS 10
Safari App Extension Crashes After a Few Seconds for Hello World Project
How to Detect If a Observable Has Not Emitted Any Events for Specific Time in Rxswift
Ios/Tvos Playground Fails with "Unable to Find Execution Service for Selected Run Destination"
Using @Fetchrequest(Entity: ) for Swiftui MACos App Crashes
Module Compiled with Swift 4.0 Cannot Be Imported in Swift 3.0.2
In Swift, Can You Find All Types in a Module That Adhere to a Specific Protocol