Declare an array of a generic struct specialized with different types - swift

I know that Swift does not have wildcard types to specialize generic types. I have a problem I would know how to solve using them in a language like Java.
In the following example I'm trying to define a struct Property which encapsulates access to a property of a type T to perform two operations, getting the hash value of the value of that property on an instance of T and comparing the values of the property on two instances of T. To allow this, the struct contains a property getter holding a function which returns the value of the specific property on an instance. Because of this, the struct needs to be generic not only in T but also the type E of the property.
In the struct Test, I would like to define a static property properties holding an array of Property instances, one for each property of Test. I cannot see a way to do that because I don't know what type to use for properties or how to "hide" the type parameter E so that it does not need to be specified in the declaration of properties.
// Struct which represents a property of type T with a value of type E.
// Aside from initialization only T is visible from outside.
public struct Property<T, E: Hashable> {
let getter: (T) -> E
func getMemberHashValue(instance: T) -> Int {
return getter(instance).hashValue
}
func equalsMembers(instance1: T, instance2: T) -> Bool {
return getter(instance1) == getter(instance2)
}
}
struct Test {
// Some properties with different types
let a: Int
let b: Double
// Array of Property instances for all properties of Test.
// Not valid syntax.
let properties: [Property<Test, ?>] = [
Property(getter: { (instance: Test) in instance.a }),
Property(getter: { (instance: Test) in instance.b })]
}
I know that I could in this case substitute AnyHashable for the second type parameter in the declaration of properties as it has no associated types, but I would like to find a general solution I could apply in cases not involving Hashable.
Is there a way to change this example to allow such a property properties to be defined holding multiple Property instances for properties of different types?

Overview:
I am not sure I understood your question correctly.
Might be worth stating why you would like a heterogeneous array, would lose all type safety in the process.
Might be revisiting the design
Option 1:
If it is absolutely required, if so just use an NSArray / NSMutableArray
Option 2:
If you want to access properties of a class / struct / enum dynamically, you could do so with KeyPath.
Code:
struct Test : Hashable {
// Some properties with different types
let a: Int
let b: Double
let c: String
//This function will return any property of Test instance
func property<T>(forKeyPath keyPath: KeyPath<Test, T>) -> T {
return self[keyPath: keyPath]
}
//MARK: Hashable
var hashValue: Int {
return a.hashValue ^ b.hashValue ^ c.hashValue
}
//MARK: Equatable
static func ==(lhs: Test, rhs: Test) -> Bool {
return lhs.a == rhs.a &&
lhs.b == rhs.b &&
lhs.c == rhs.c
}
}
let test1 = Test(a: 10, b: 22.22, c: "aaaa")
let cValue = test1.property(forKeyPath: \.c) //aaaa
let cHashValue = test1.property(forKeyPath: \.c.hashValue) //Hash value of aaaa
let someInt = 10 //This is also Hashable
var mixedArray = [AnyHashable]()
mixedArray.append(test1)
mixedArray.append(someInt)
Refer:
If this matches your requirement, then please read about KeyPath, PartialKeyPath.
Version:
Swift 4

Related

Usage of Generics

I have the following structs:
struct A<T> {
var val: T
}
struct B<T> {
var setOfA: Set<A<T>>
}
Is this incorrect usage of generics, if the set of As can have values of both A<Int> and A<Float> type (or any other type)?
If yes, is using Any the correct way to do this?
Would generics have been the right way to go, if the set of As was constrained to A<>s of a single type?
Swift is a moving target, all code tested using Xcode 9.2 & Swift 4.0.
First if you wish to insert A<T> items into a set then the type must implement the Hashable protocol. You can implement this protocol as needed but for illustrative purposes a simple approach is to require the type parameter T to be Hashable and to implement the protocol by indirecting to val:
struct A<T> : Hashable where T : Hashable
{
var val : T
var hashValue: Int { return val.hashValue }
static func ==(lhs: A<T>, rhs: A<T>) -> Bool
{
return lhs.val == rhs.val
}
}
The rest of the answer does not require the above implementation, just that A<T> is Hashable.
Constraining your Set to only containing instances of A<T> for any and all T is more of a challenge. One approach to this problem is to use an existential type, which in this context essentially allows the wrapping of differently typed value inside another non-generic wrapper. Swift has some pre-defined existential types, in particular AnyHashable which you could use to define B's var:
struct B
{
var setOfA : Set<AnyHashable> = Set()
...
}
Unfortunately this does not constrain the set to only hold values of type A<T> for any T.
The type of setOfA cannot be Set<A<AnyHashable>> as that would require a value of type A<T> for some T to be cast-able to A<AnyHashable> and Swift does not support that (researching variance might help understand why).
You might wonder why the type cannot be Set<Hashable>. Unfortunately (in this context!) Hashable is a protocol with a Self requirement and Swift does not support using it as a generic type parameter in this context. AnyHashable is a non-generic struct which type erases (the existential type bit) the value it wraps.
So back to solving the problem of constraining the set to only contains A's. One solution is to hide (private) the set inside B and provide a generic insertion function which only accepts A<T> values, for any T. The set value itself can then be made public using a read-only computed property. For example:
struct B
{
// private property so only B's functions can modify the set
private var _setOfA : Set<AnyHashable> = Set()
// public read-only property so _setOfA cannot be changed directly
var setOfA : Set<AnyHashable> { return _setOfA }
// public insert function which only accepts A<T> values, for any T
mutating func insert<T>(_ element : A<T>) -> (Bool, A<T>)
{
return _setOfA.insert(element)
}
}
Here is dome sample code using the above:
let x = A(val: 4) // x : A<Int>
let y = A(val: "hello") // y : A<String>
var z = B()
print(z.insert(x)) // insert being generic accepts any A<T>
print(z.insert(y))
print(z.insert(x)) // should return (false, x) as element already added
print("\(z)")
for member in z.setOfA
{
print("member = \(member) : \(type(of:member))")
if let intMember = member as? A<Int>
{
print(" intMember = \(intMember) : \(type(of:intMember))")
}
else if let stringMember = member as? A<String>
{
print(" stringMember = \(stringMember) : \(type(of:stringMember))")
}
}
This outputs:
(true, __lldb_expr_501.A<Swift.Int>(val: 4))
(true, __lldb_expr_501.A<Swift.String>(val: "hello"))
(false, __lldb_expr_501.A<Swift.Int>(val: 4))
B(_setOfA: Set([AnyHashable(__lldb_expr_501.A<Swift.String>(val: "hello")), AnyHashable(__lldb_expr_501.A<Swift.Int>(val: 4))]))
member = A<String>(val: "hello") : AnyHashable
stringMember = A<String>(val: "hello") : A<String>
member = A<Int>(val: 4) : AnyHashable
intMember = A<Int>(val: 4) : A<Int>
HTH
If you plan on letting your struct contain val of a specific type, use <T: DesiredProtocol>. If the type doesn't matter, and your val can be of any type, just implement the structure as struct A<T> {}. A<T> is equal to A<T: Any>.
Note that your example won't compile if you intend to have setOfA contain A with different types.
The elements in a Set must be hashable.
In B<T>, T represents only one type. Therefore, you cannot store different types in Set<A<T>>.
Because of this requirement, struct A<T> must conform to the Hashable protocol, and the implementation of B must be changed.
struct A<T>: Hashable {
var val: T
// Implementation of Hashable
}
/// This struct should not be generic. It would constrain its setOfA to only contain the same type.
/// By removing the generics, and use Any instead, you let each element have its own type.
struct B {
var setOfA: Set<A<Any>>
}
var set: Set<A<Any>> = Set()
set.insert(A(val: 0 as Int))
set.insert(A(val: 1 as Float))
let b = B(setOfA: set)
Edit:
Due to the fact that you're looking for a way to insert a generic A<T> into A<Any> without specifying its type as A<Any> on creation, I've come up with two different ways:
let v1 = A(val: 5)
var v2 = A(val: 3)
aSet.insert(A(val: v2.val))
aSet.insert(withUnsafePointer(to: &v3, { return UnsafeRawPointer($0) }).assumingMemoryBound(to: A<Any>.self).pointee)
Edit
If it is as you write, that B's setOfA would only have A<> of a single type, it's the choice of the context whether you should use generics or not. The question is rather if struct B needs to be generic. struct A can be a value type for many different tasks, while struct B doesn't need to. If B is more specific, you could rather just write:
struct B {
var setOfA: Set<A<Int>>
}
As above-mentioned, if you intend to have the same set contain A<Int> and A<Float>, then var setOfA: Set<A<Any>> is the correct way.
If struct A has a very specific use-case, and you don't actually need it to be generic, you should change the type of val. Your code should be understandable and comprehensive. I would recommend one of these solutions:
struct A: Hashable {
var val: Any
// or
var val: Numeric
// Implementation of Hashable
}

Using Class Type as Dictionary Key [duplicate]

I'm trying to do this sort of thing ..
static var recycle: [Type: [CellThing]] = []
but - I can't :)
Undeclared type 'Type'
In the example, CellThing is my base class, so A:CellThing, B:CellThing, C:CellThing and so on. The idea is I would store various A A A, B B, C C C C in the dictionary arrays.
How to make a "Type" (ideally I guess, constrained to CellThing) be the key in a Swift dictionary?
I appreciate I could (perhaps?) use String(describing: T.self), but that would make me lose sleep.
Here's a use case, envisaged code would look something like this ...
#discardableResult class func make(...)->Self {
return makeHelper(...)
}
private class func makeHelper<T: CellThing>(...)->T {
let c = instantiateViewController(...) as! T
return c
}
So then something like ...
static var recycle: [Type: [CellThing]] = []
private class func makeHelper<T: CellThing>(...)->T {
let c = instantiateViewController(...) as! T
let t = type whatever of c (so, maybe "A" or "B")
recycle[t].append( c )
let k = recycle[t].count
print wow, you have k of those already!
return c
}
Unfortunately, it's currently not possible for metatype types to conform to protocols (see this related question on the matter) – so CellThing.Type does not, and cannot, currently conform to Hashable. This therefore means that it cannot be used directly as the Key of a Dictionary.
However, you can create a wrapper for a metatype, using ObjectIdentifier in order to provide the Hashable implementation. For example:
/// Hashable wrapper for a metatype value.
struct HashableType<T> : Hashable {
static func == (lhs: HashableType, rhs: HashableType) -> Bool {
return lhs.base == rhs.base
}
let base: T.Type
init(_ base: T.Type) {
self.base = base
}
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(base))
}
// Pre Swift 4.2:
// var hashValue: Int { return ObjectIdentifier(base).hashValue }
}
You can then also provide a convenience subscript on Dictionary that takes a metatype and wraps it in a HashableType for you:
extension Dictionary {
subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
get { return self[HashableType(key)] }
set { self[HashableType(key)] = newValue }
}
}
which could then use like so:
class CellThing {}
class A : CellThing {}
class B : CellThing {}
var recycle: [HashableType<CellThing>: [CellThing]] = [:]
recycle[A.self] = [A(), A(), A()]
recycle[B.self] = [B(), B()]
print(recycle[A.self]!) // [A, A, A]
print(recycle[B.self]!) // [B, B]
This should also work fine for generics, you would simply subscript your dictionary with T.self instead.
Unfortunately one disadvantage of using a subscript with a get and set here is that you'll incur a performance hit when working with dictionary values that are copy-on-write types such as Array (such as in your example). I talk about this issue more in this Q&A.
A simple operation like:
recycle[A.self]?.append(A())
will trigger an O(N) copy of the array stored within the dictionary.
This is a problem that is aimed to be solved with generalised accessors, which have been implemented as an unofficial language feature in Swift 5. If you are comfortable using an unofficial language feature that could break in a future version (not really recommended for production code), then you could implement the subscript as:
extension Dictionary {
subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
get { return self[HashableType(key)] }
_modify {
yield &self[HashableType(key)]
}
}
}
which solves the performance problem, allowing an array value to be mutated in-place within the dictionary.
Otherwise, a simple alternative is to not define a custom subscript, and instead just add a convenience computed property on your type to let you use it as a key:
class CellThing {
// Convenience static computed property to get the wrapped metatype value.
static var hashable: HashableType<CellThing> { return HashableType(self) }
}
class A : CellThing {}
class B : CellThing {}
var recycle: [HashableType<CellThing>: [CellThing]] = [:]
recycle[A.hashable] = [A(), A(), A()]
recycle[B.hashable] = [B(), B()]
print(recycle[A.hashable]!) // [A, A, A]
print(recycle[B.hashable]!) // [B, B]
If you extend the Dictionary type you can use the already defined generic Key directly.
extension Dictionary {
// Key and Value are already defined by type dictionary, so it's available here
func getSomething(key: Key) -> Value {
return self[key]
}
}
This works because Dictionary already has generics Key and Value defined for it's own use.
Hope AnyHashable helps. But It appeared in Xcode 8.0
You can do something like:
var info: [AnyHashable : Any]? = nil

Make a Swift dictionary where the key is "Type"?

I'm trying to do this sort of thing ..
static var recycle: [Type: [CellThing]] = []
but - I can't :)
Undeclared type 'Type'
In the example, CellThing is my base class, so A:CellThing, B:CellThing, C:CellThing and so on. The idea is I would store various A A A, B B, C C C C in the dictionary arrays.
How to make a "Type" (ideally I guess, constrained to CellThing) be the key in a Swift dictionary?
I appreciate I could (perhaps?) use String(describing: T.self), but that would make me lose sleep.
Here's a use case, envisaged code would look something like this ...
#discardableResult class func make(...)->Self {
return makeHelper(...)
}
private class func makeHelper<T: CellThing>(...)->T {
let c = instantiateViewController(...) as! T
return c
}
So then something like ...
static var recycle: [Type: [CellThing]] = []
private class func makeHelper<T: CellThing>(...)->T {
let c = instantiateViewController(...) as! T
let t = type whatever of c (so, maybe "A" or "B")
recycle[t].append( c )
let k = recycle[t].count
print wow, you have k of those already!
return c
}
Unfortunately, it's currently not possible for metatype types to conform to protocols (see this related question on the matter) – so CellThing.Type does not, and cannot, currently conform to Hashable. This therefore means that it cannot be used directly as the Key of a Dictionary.
However, you can create a wrapper for a metatype, using ObjectIdentifier in order to provide the Hashable implementation. For example:
/// Hashable wrapper for a metatype value.
struct HashableType<T> : Hashable {
static func == (lhs: HashableType, rhs: HashableType) -> Bool {
return lhs.base == rhs.base
}
let base: T.Type
init(_ base: T.Type) {
self.base = base
}
func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(base))
}
// Pre Swift 4.2:
// var hashValue: Int { return ObjectIdentifier(base).hashValue }
}
You can then also provide a convenience subscript on Dictionary that takes a metatype and wraps it in a HashableType for you:
extension Dictionary {
subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
get { return self[HashableType(key)] }
set { self[HashableType(key)] = newValue }
}
}
which could then use like so:
class CellThing {}
class A : CellThing {}
class B : CellThing {}
var recycle: [HashableType<CellThing>: [CellThing]] = [:]
recycle[A.self] = [A(), A(), A()]
recycle[B.self] = [B(), B()]
print(recycle[A.self]!) // [A, A, A]
print(recycle[B.self]!) // [B, B]
This should also work fine for generics, you would simply subscript your dictionary with T.self instead.
Unfortunately one disadvantage of using a subscript with a get and set here is that you'll incur a performance hit when working with dictionary values that are copy-on-write types such as Array (such as in your example). I talk about this issue more in this Q&A.
A simple operation like:
recycle[A.self]?.append(A())
will trigger an O(N) copy of the array stored within the dictionary.
This is a problem that is aimed to be solved with generalised accessors, which have been implemented as an unofficial language feature in Swift 5. If you are comfortable using an unofficial language feature that could break in a future version (not really recommended for production code), then you could implement the subscript as:
extension Dictionary {
subscript<T>(key: T.Type) -> Value? where Key == HashableType<T> {
get { return self[HashableType(key)] }
_modify {
yield &self[HashableType(key)]
}
}
}
which solves the performance problem, allowing an array value to be mutated in-place within the dictionary.
Otherwise, a simple alternative is to not define a custom subscript, and instead just add a convenience computed property on your type to let you use it as a key:
class CellThing {
// Convenience static computed property to get the wrapped metatype value.
static var hashable: HashableType<CellThing> { return HashableType(self) }
}
class A : CellThing {}
class B : CellThing {}
var recycle: [HashableType<CellThing>: [CellThing]] = [:]
recycle[A.hashable] = [A(), A(), A()]
recycle[B.hashable] = [B(), B()]
print(recycle[A.hashable]!) // [A, A, A]
print(recycle[B.hashable]!) // [B, B]
If you extend the Dictionary type you can use the already defined generic Key directly.
extension Dictionary {
// Key and Value are already defined by type dictionary, so it's available here
func getSomething(key: Key) -> Value {
return self[key]
}
}
This works because Dictionary already has generics Key and Value defined for it's own use.
Hope AnyHashable helps. But It appeared in Xcode 8.0
You can do something like:
var info: [AnyHashable : Any]? = nil

Swift 2: How to refer to a type of an element in default implementation of a function in a protocol

I need to get some objects from REST API and map them to local objects using ObjectMapper.
These objects contain a number of enumerations. All of them are coming as integers and I want to map them to locally described enums.
To do that I have to describe standard transform function that is used by ObjectMapper.
enum Types: Int {
case Uno = 1
case Dos = 2
case Tres = 3
static var transform = TransformOf<Types,Int>(
fromJSON: {
$0 != nil
? Types(rawValue:$0!)
: nil
},
toJSON: { $0?.rawValue})
}
The problem is that I have a number of these enumerations and the function is totally same in all of them except that first argument in TransformOf<..> list is specific for each enum.
What I want to do is to create a common protocol with default implementation of that function, something like
protocol Transformable {
var transform: TransformOf<self.Type,Int> {
get {
return TransformOf<self.Type,Int>(
fromJSON: {
$0 != nil
? Types(rawValue:$0!)
: nil
},
toJSON: { $0?.rawValue})
}
}
}
...and then to apply the protocol with the implementation to all of the enumerations I have.
Obviously reference of self.Type is not working there and I just can't get how to generally refer to type of specific instance that will finally use the function? Probably I'm thinking wrong way of solving that problem.
I think what you're missing is the Self identifier. When implementing Generics, the Self keyword acts as a placeholder for the type that implements your protocol. (For more information)
In other words:
protocol Transformable {
var rawValue: Int { get }
init?(rawValue: Int)
func toJSON() -> Int
static func fromJSON(rawValue: Int) -> Self?
}
Each enum that conforms to the protocol Transformable will then have a static method that returns its own type.
Secondly, since this is Swift 2, you can implement a protocol extension:
extension Transformable {
func toJSON() -> Int {
return self.rawValue
}
static func fromJSON(rawValue: Int) -> Self? {
return Self(rawValue: rawValue)
}
}
Now all enums that conform to the protocol will convert themselves to and from Int:
enum Types: Int, Transformable {
case Uno = 1
case Dos = 2
case Tres = 3
//No extra implementation
}
enum OtherTypes: Int, Transformable {
case Cuatro = 4
case Cinco = 5
case Seis = 6
//No extra implementation
}
print(Types.fromJSON(1)!) //prints Uno
print(OtherTypes.fromJSON(4)!) //prints Cuatro
print(Types.fromJSON(4)!) /* throws an error, because the Types enum
does not recognise 4 as a raw value */

Default equality operator for objects in Swift

When using find to search the index of an object in a array:
var index = find(Store.allItems, myItem)
The compiler says that the Item class does not conform to protocol Equatable.
My Item class is an object, so in my opinion, coming from C#, the default equality comparer should be reference equality.
In Swift, It seems that you need to specify Equatable on each reference-type in order for find to work.
Also, you need to imlement the == operator so that it uses === to compare references:
func ==(a:Item, b:Item) -> Bool {
return a === b
}
Moreover, this last declaration must be top-level code, which disgust me...
I tried writing:
func ==(a:AnyObject, b:AnyObject) -> Bool {
return a === b
}
but it seems not to work.
Is there some faster and more elegant way to achieve standard equality comparing for objects?
Just try to make Item class conforming to Equatable:
extension Item:Equatable { }
func ==(lhs:Item, rhs:Item) -> Bool {
return lhs === rhs
}
You can get this to work by using a generic function for your == overload; you'll still have to make your class implement the Equatable protocol, of course.
The implementation would look something like this:
func ==<T: AnyObject>(a: T, b: T) -> Bool {
return a === b
}
class AClass: Equatable {}
Testing it out:
let a = AClass()
let b = AClass()
let c = AClass()
let arr = [a, b, c]
let idxA = find(arr, a)
let idxB = find(arr, b)
let idxC = find(arr, c)
idxA, idxB, and idxC are 0, 1, and 2 respectively.
If you aren't crazy about implementing a custom operator, you could implement another version of find() that does what you want:
func find<C : CollectionType where C.Generator.Element : AnyObject>(domain: C, value: C.Generator.Element) -> C.Index? {
var index = domain.startIndex
while index != domain.endIndex {
if domain[index] === value {
return index
}
index = index.successor()
}
return nil
}
If you only want a reference equality, it's actually easy.
Just make your Item class inherit from NSObject.
class Item: NSObject {
var name: String
init(name: String) {
self.name = name
}
}
let item1 = Item(name: "item1")
let item2 = Item(name: "item2")
let item3 = Item(name: "item3")
let items = [item1, item2, item3]
for item in items {
if let index = find(items, item) {
println(items[index].name)
}
}
// Console Output:
// item1
// item2
// item3
Update
Object Comparison
“There are two distinct types of comparison when you compare two
objects in Swift. The first, equality (==), compares the contents of
the objects. The second, identity (===), determines whether or not the
constants or variables refer to the same object instance.
Swift and Objective-C objects are typically compared in Swift using
the == and === operators. Swift provides a default implementation of
the == operator for objects that derive from the NSObject class. In
the implementation of this operator, Swift invokes the isEqual: method
defined on the NSObject class. The NSObject class only performs an
identity comparison, so you should implement your own isEqual: method
in classes that derive from the NSObject class. Because you can pass
Swift objects (including ones not derived from NSObject) to
Objective-C APIs, you should implement the isEqual: method for these
classes if you want the Objective-C APIs to compare the contents of
the objects rather than their identities.
As part of implementing equality for your class, be sure to implement
the hash property according to the rules in Object comparison in Cocoa
Core Competencies. Further, if you want to use your class as keys in a
dictionary, also conform to the Hashable protocol and implement the hashValue property.
Excerpt From: Apple Inc. “Using Swift with Cocoa and Objective-C.”
iBooks. https://itun.es/tw/1u3-0.l