Swift compile time errors when using generics and accessing dictionary's values - ios

Below I have a class, B, with a generic type and the generic type has a subclass type constraint. In a separate class, A, I create a dictionary property, values, with the key as String and value as B. Class A then has methods to return or set values in the dictionary such that the values are not constrained to a single type (they maintain their generic SomeType type which is a subclass of NSObject). However, this produces the following two errors noted inline below:
class A: NSObject {
var values = [String : B]()
func get<SomeType: NSObject>(key: String) -> B<SomeType>? {
// #1 Error on line below: cannot convert return expression of type 'B<NSObject>?' to return type 'B<SomeType>?'
return values[key]
}
func set<SomeType: NSObject>(key: String, value: B<SomeType>) {
// #2 Error on line below: cannot assign value of type 'B<SomeType>' to type 'B<NSObject>?'
values[key] = value
}
}
class B<SomeType: NSObject>: NSObject {
}
I've attempted various forms of declaring the values dictionary to tell the compiler that SomeType is a subclass of NSObject and everything is going to be ok, but have been unsuccessful. Similarly to this question, I'm a bit stumped because the methods define SomeType as a subclass of NSObject and therefore things appear to be type safe when setting and getting from values.
I could remove the generic types from the methods and instead force the type to be <NSObject>, but then I'd run into the same problem as noted here.

This may not be doing what you think it's doing:
var values = [String : B]()
This isn't really [String : B], it's [String : B<NSObject>]. (I'm actually kind of surprised that this is legal syntax; I'd be tempted to open a bugreport about that; B isn't a proper type in Swift.) You may realize this already, but it's the first important note.
The second important note is that generic types are not covariant in Swift. A Thing<Cat> is not a subtype of Thing<Animal>. There are some type-theory reasons for this, and there are some technical implementation reasons for this, but the important fact is that it's not possible in Swift.
If you need to hold a variety of B types, then you'll need to build a type eraser. In your case, the type eraser could possibly be B<NSObject> with something like this:
class B<SomeType: NSObject>: NSObject {
let value: SomeType
init(value: SomeType) {
self.value = value
}
func liftGeneric() -> B<NSObject> {
return B<NSObject>(value: value)
}
}
If you need to hold just one kind of B type, then make A generic as well.

Related

Can #dynamicMemberLookup be used to call methods?

In the documentation for #dynamicMemberLookup it says,
Apply this attribute to a class, structure, enumeration, or protocol to enable members to be looked up by name at runtime.
If I'm not mistaken, instance methods are considered members of a struct / class. However, when I try to call a function dynamically I get an error saying:
Dynamic key path member lookup cannot refer to instance method foo()
To reproduce the problem:
struct Person {
var name: String
var age: Int
func greet() {
print("hello, my name is \(name)")
}
}
#dynamicMemberLookup
struct Wrapper {
var value: Person
subscript<T>(dynamicMember keypath: KeyPath<Person, T>) -> T {
value[keyPath: keypath]
}
}
let person = Person(name: "John Doe", age: 21)
let wrapper = Wrapper(value: person)
wrapper.greet() // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
// Or
let function = wrapper.greet // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
function()
How can I dynamically call greet() using #dynamicMemberLookup? Is there any way to achieve what I'm trying to do?
Thanks in advance!
No, dynamicMemberLookup does not work for methods. As the signature of the subscript suggests, it only works for things that can be represented as a KeyPath. Method calls cannot be part of a key path. :(
Key-Path Expression
A key-path expression refers to a property or subscript of a type.
The path consists of property names, subscripts, optional-chaining
expressions, and forced unwrapping expressions. Each of these key-path
components can be repeated as many times as needed, in any order.
At compile time, a key-path expression is replaced by an instance of
the KeyPath class.
I suspect the reason why it is called "dynamic member lookup" is because it also works with subscripts. The alternative of dynamicPropertyOrSubscriptLookup is rather a mouthful isn't it?
One rather hacky fix would be to change greet into a computed property:
var greet: () -> Void { {
print("hello, my name is \(name)")
} }
If greet has had parameters, you could also change it into a subscript, but I think that is an even uglier solution.

.self after struct type in Swift

I’m confused by a line of code found in the Metal example where the memory pointer is bound to a type.
uniforms = UnsafeMutableRawPointer(uniformBuffer.contents()).bindMemory(to: Uniforms.self, capacity: 1)
My confusion is the .self after the Uniforms type. Uniforms is a struct defined in an Objective-C file and the code wont run without .self being in the call. Why is that necessary?
The .self returns the metatype instance for the corresponding type. Think of it as a typesafe type identifier (e.g., way safer than using a string for that). You can then safely call the available initializers, static methods, static properties on such metatype instance.
For instance, you could store it in a variable as well:
let metatype: Uniforms.Type = Uniforms.self
and Uniforms.Type is the actual metatype (i.e., the type's type).
Metatype crash course. A very quick example to get a feel of how this meta stuff might be actually useful:
class Super {
let id: Int
required init(id: Int) { self.id = id }
}
class SubA: Super { ... }
class SubB: Super { ... }
let subclass: Super.Type = SubA.self
and then, later on, use subclass to create an instance without hardcoding the actual subclass type name:
let obj = subclass.init(id: 123) // new SubA instance.
In Swift, .self could be used on a type to extract its meta type or on an instance of a type. Example, use .self to get the meta type and pass it to the API:
self.tableView.registerClass(
UITableViewCell.self, forCellReuseIdentifier: "myUIViewCell")

Using .map to map an array of one class to an array of another

I'm new to Swift, and am trying to get my head around using .map. My understanding is that the behaviour is similar to that of Javascript, but maybe I'm not nailing the Swift syntax correctly?
I've created a public Array of my custom class CustomItem (which is a subclass of the type coming back in the response):
public var availableItems: [CustomItem] = []
public static func getAvailableItems(id: String, completion: (items: [CustomItem]) -> ()) -> Void {
DataConnector.getRelated(type: "users", id: id, relationship: "available") { (response) -> Void in
availableItems = (response.data?.map { return $0 as! CustomItem })!
completion(items: availableItems)
}
}
When I do a po response.data.map { return $0 } in the console with a breakpoint after the response is received, I get:
(lldb) po response.data.map { return $0 }
▿ Optional([<AnSDK.RemoteDataObject: 0x7f8a0b9c16b0>])
▿ Some : 1 elements
▿ [0] : < AnSDK.RemoteDataObject: 0x7f8a0b9c16b0>
So it definitely seems that part works, but when I try to cast the data object to CustomItem class, I get:
Could not cast value of type 'AnSDK.RemoteDataObject' (0x100abbda0) to 'Project.CustomItem' (0x100882c60).
Here's my CustomItem class just in case:
import AnSDK
public class CustomItem: RemoteDataObject {
var displayName: String = ""
var value: Float = 0.0
var owner: User?
}
If I don't use the ! to force downcast, I get:
RemoteDataObject is not convertible to CustomItem [...]
... in the compiler.
(I'm really just restating Ben Gottlieb's answer here, but hopefully a bit clearer since I believe some readers were confused by his attempt.)
The message seems fairly clear. You've received an array of AnSDK.RemoteDataObject. As best I can tell from your output, that is the actual class of the objects. You can't just say "it's really this subclass of RDO" unless it really is that subclass. Looking at your code, that seems unlikely. Somewhere in AnSDK it would have to construct a CustomItem and then just happen to return it as a RemoteDataObject. That doesn't appear to be what's happening inside of getRelated. Given your code, I doubt AnSDK knows anything about CustomItem, so how would it have constructed one?
There are numerous ways to fix this depending on what the types really are and how they interact. Ben's solution is one, which basically creates a copy of the object (though in that case, there's no particular reason for CustomItem to be a subclass of RDO, and probably shouldn't be.)
If you just want to add methods to RemoteDataObject, you can do that with extensions. You don't need to create a subclass.

How to create an enumeration that accepts a generic type as associated type

I'm trying to create an enumeration that accepts a generic type as associated value.
The compiler complains:
Reference to generic type 'GenericItem' requires arguments in <...>
The scheme is pretty simple:
struct GenericItem <Item:FormattableAsStringWithPrecision> {
let value: Item
}
enum Enumeration {
case Generic(values: [GenericItem])
}
I can't understand how to make this possible.
You need to add the Generic type to the enum too, the types can be inferred from the initialiser so you do not need to pass it as a generic type argument.
Below is an example of how you might do it.
struct GenericItem<T: CustomDebugStringConvertible> {
let value: T
}
enum Enumeration<T: CustomDebugStringConvertible> {
case Generic(value: [GenericItem<T>])
}
let someValue = Enumeration.Generic(value: [ GenericItem(value: "") ])
edit: I changed the FormattableAsStringWithPrecision to CustomDebugStringConvertible as I assumed it was one of your own custom protocols which can be easily swapped out, but the same logic would still apply for any protocol.

swift how to compare generics

I have a protocol called SomeProtocol
I want to create a function that get an object that confirms to this protocol, and add it to an array.
then I have another function that remove an object from this array.
var allObjs = [SomeProtocol]()
func addObj<T: AnyObject where T: SomeProtocol>(obj: T) {
allObjs.append(obj)
}
func removeObj<T: AnyObject where T: SomeProtocol>(obj: T) {
for someObj in allObjs {
if someObj == obj { // compile time error -> Binary operator '==' cannot be applied to operands of type 'SomeProtocol' and 'T'
}
}
}
This code will cause a compile time error "Binary operator '==' cannot be applied to operands of type 'SomeProtocol' and 'T'"
not sure how can i fix that, both object where defined as AnyObject who confirm to the SomeProtocol protocol, so what is the problem here?
For comparing two generics, you can declare the generics such that, types, that are capable to be in the place of your generic type, should conform to Comparable protocol.
struct Heap<T: Comparable>{
var heap = [T]()
}}
Now we will be able to do:-
if heap[parentIndex] < heap[childIndex] {
//Your code
}
How this works?
As we know, conforming to a protocol means implementing all the required methods in that protocol. Comparable protocol has got all the comparison methods as required parameters, and any type that is implementing Comparable will be able to do a comparison.
Happy coding using Generics.
If you want reference equality, then you need to ensure SomeProtocol only applies to classes (since you can’t use reference equality on structs, as they’re value types):
protocol SomeProtocol: class { }
Now, only classes can implement SomeProtocol.
You don’t need generics to use reference equality now, just regular run-time polymorphism:
func removeObj(obj: SomeProtocol) {
// since any SomeProtocol-conforming object
// must be a class, you can now use ===
if let idx = allObjs.indexOf({ $0 === obj}) {
allObjs.removeAtIndex(idx)
}
}
The generic function must also conform to the Equatable protocol

Resources