Genericize method to encode a `Codable` protocol - swift

import Foundation
protocol ProtocolA: Codable {
var var1: String { get }
}
struct Type1: ProtocolA {
var var1: String
var var2: Int
}
struct Type2: ProtocolA {
var var1: String
var var2: Bool
}
func encode<T: ProtocolA & Encodable>(object: T) throws -> Data {
return try JSONEncoder().encode(object as! T.Type)
}
Putting the above in a playground results in error: argument type 'T.Type' does not conform to expected type 'Encodable'
Why does this happen when I am saying that T has to conform to Encodable?

return try JSONEncoder().encode(object as! T.Type)
This means to convert object to the metatype of T. The type of the type. For example, 1 is an Int. But "Int" itself has a type, which is Int.Type, which is a metatype. Metatypes do not conform to Encodable.
You meant:
return try JSONEncoder().encode(object as! T)
But you really just meant:
return try JSONEncoder().encode(object)
since object is always of type T. That's its explicit type from the function signature. Since you also don't rely on ProtocolA for this algorithm, this all boils down to:
func encode<T: Encodable>(object: T) throws -> Data {
return try JSONEncoder().encode(object)
}

Your compiler is actually complaining about the type cast your doing at the end which is converting object to the metatype of T, in your example either Type1.Type or Type2.Type.
From the encoding perspective, what the compiler needs to know is the model confirms to Encodable which is implicit in the T: Codable statement.
import Foundation
protocol ProtocolA: Codable {
var var1: String { get }
}
struct Type1: ProtocolA {
var var1: String
var var2: Int
}
struct Type2: ProtocolA {
var var1: String
var var2: Bool
}
func encode<T: Codable>(object: T) throws -> Data {
return try JSONEncoder().encode(object)
}
let type2 = Type2(var1: "test1", var2: true)
print(type2)

Related

Swift - Why String doesn't conform to RawRepresentable?

I'm learning Swift and cannot realize why this code is correct:
enum Test1: String {
case value
}
let test1 = Test1.value.rawValue
but this one is incorrect and shows me errors
struct MyStruct {
}
extension MyStruct: Equatable {
static func == (lhs: MyStruct, rhs: MyStruct) -> Bool {
return true
}
}
enum Test2: MyStruct {
case value
}
I browsed thru Swift.String sources and didn't find rawValue declaration. How does it work in Swift? Is String a built-in type that "automatically" conforms to RawRepresentable, but all other types have to explicitly declare its conformance?
Notice that Test.value has type Test1, not String.
There is special treatment (implicit conformance to RawRepresentable), but it applies to string-valued enums, not String itself.
Raw values can be strings, characters, or any of the integer or
floating-point number types. Each raw value must be unique within its
enumeration declaration.
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html
But that's only for using the colon-based shortcut syntax. It's no problem to manually conform.
enum Test2 {
case value
}
extension Test2: RawRepresentable {
init?(rawValue: MyStruct) {
self = .value
}
var rawValue: MyStruct { .init() }
}

How to constrain a Swift generic type parameter to be any protocol and another parameter to conform to it?

I'm trying to create a property wrapper for dependency injection:
#propertyWrapper struct Dependency<T> {
private(set) var wrappedValue: T
init() {
wrappedValue = Dependencies.shared.resolve()
}
}
final class Dependencies {
static let shared = Dependencies()
private var dependencies = [String: AnyObject]()
func register<T, P>(_ dependency: T, as: P.Type) {
let key = String(describing: P.self)
dependencies[key] = dependency as AnyObject
}
func resolve<T>() -> T {
let key = String(describing: T.self)
let dependency = dependencies[key] as? T
precondition(dependency != nil, "No dependency found for \(key)! must register a dependency before resolve.")
return dependency!
}
}
Where the intention is to register an object that conforms to a protocol and then look that up based on the protocol type.
For example:
protocol Foo {
func foo() -> String
}
class Bar: Foo {
func foo() -> String {
"Hello World!"
}
}
Dependencies.shared.register(Bar(), as: Foo.self)
struct Test {
#Dependency var a: Foo
}
let t = Test()
print(t.a.foo())
This works as expected - printing "Hello World!"
However, I can also do this:
Dependencies.shared.register("not a Bar", as: Foo.self)
and the precondition blows up since String does not conform to Foo.
What I want to do is constrain func register<T, P>(_ dependency: T, as: P.Type) so that T must conform to P.
Something like register<T: P, P>(_ dependency: T, as: P.Type) - which obviously doesn't work.
Is this even possible?
I think this change must do what you expect:
func register<T>(_ dependency: T, as dependencyType: T.Type) {
let key = String(describing: dependencyType)
dependencies[key] = dependency as AnyObject
}
(The rest of your code stays unchanged.)
Then this line that bothers you doesn't compile (Cannot convert value of type 'String' to expected argument type 'Bar'):
Dependencies.shared.register("not a Bar", as: Bar.self)

Swift: Constrain a generic variable to be a class or a protocol

As KeyPath can't be casted to super types in Swift, I want to write a type-erased version, which represents any KeyPath whose value could be casted to a specific protocol or super type:
public struct PropertyGetter<Root, ValueType> {
private let keyPath: PartialKeyPath<Root>
public init<T: ValueType>(_ keyPath: KeyPath<Root, T>) {
self.keyPath = keyPath
}
public func get(_ instance: Root) -> ValueType {
return instance[keyPath: self.keyPath] as! ValueType
}
}
The compiler rightfully complains that
type 'T' constrained to non-protocol, non-class type 'ValueType.Type'
as ValueType could potentially be a struct type.
So how do we properly constrain this? That is enforcing that
ValueType is either a class type or a protocol, i.e subclassable / implementable
T is constrained to conform to ValueType, i.e x as! ValueType must be guranteed to work, where x: T
Notice that it is indeed possible to write such a type-erasing struct, when the Protocol type is fixed. E.g a class which only accepts KeyPaths pointing to CustomStringConvertible members:
public struct CustomStringConvertibleGetter<Root> {
private let keyPath: PartialKeyPath<Root>
public init<T: CustomStringConvertible>(_ keyPath: KeyPath<Root, T>) {
self.keyPath = keyPath
}
public func get(_ instance: Root) -> CustomStringConvertible {
return instance[keyPath: self.keyPath] as! CustomStringConvertible
}
}
let getter1 = CustomStringConvertibleGetter(\SomeClass.someString) // works
let getter2 = CustomStringConvertibleGetter(\SomeClass.nonConformingMember) // will throw an error at compile time

How to handle multiple generic protocols in Swift?

I'm trying to use two generic protocols that relate to each other as:
protocol PersistableData {}
protocol DataStore: class {
associatedtype DataType: PersistableData
func save(data: DataType, with key: String)
func retreive(from key: String) -> DataType?
}
protocol PersistentDataModel {
// Swift infers that DataType: PersistableData as DataType == DataStoreType.DataType: PersistableData
// Setting it explicitly makes the compiler fail
associatedtype DataType
associatedtype DataStoreType: DataStore where DataStoreType.DataType == DataType
}
extension String: PersistableData {}
protocol StringDataStore: DataStore {
associatedtype DataType = String
}
class Test: PersistentDataModel {
typealias DataType = String
typealias DataStoreType = StringDataStore
}
However Xcode fails to compile saying that Type 'Test' does not conform to protocol 'PersistentDataModel' and suggesting that Possibly intended match 'DataStoreType' (aka 'StringDataStore') does not conform to 'DataStore' while StringDataStore is defined as conforming to DataStore
I've read a few good resources about generic protocols including SO and this Medium post, but I could not find where the issue is.
This happening because your typealias for associatedtype should have concretion, not abstraction.
Thus, for your case, StringDataStore should be a class, not protocol.
protocol PersistableData {}
protocol DataStore: class {
associatedtype DataType: PersistableData
func save(data: DataType, with key: String)
func retreive(from key: String) -> DataType?
}
protocol PersistentDataModel {
// Swift infers that DataType: PersistableData as DataType == DataStoreType.DataType: PersistableData
// Setting it explicitly makes the compiler fail
associatedtype DataType
associatedtype DataStoreType: DataStore where DataStoreType.DataType == DataType
}
extension String: PersistableData {}
class StringDataStore: DataStore {
typealias DataType = String
func save(data: String, with key: String) {
//
}
func retreive(from key: String) -> String? {
return nil
}
}
class Test: PersistentDataModel {
typealias DataType = String
typealias DataStoreType = StringDataStore
}
However, you can keep using protocols and solve it by using additional generics conditions in your Test class:
class Test<T: StringDataStore>: PersistentDataModel where T.DataType == String {
typealias DataStoreType = T
typealias DataType = T.DataType
}
Using this you can tell to compiler that concrete type will be passed to Test somewhere else.
Like this:
class ConcreteStringDataStore: StringDataStore {
func save(data: String, with key: String) {
//
}
func retreive(from key: String) -> String? {
return nil
}
}
let test = Test<ConcreteStringDataStore>()

Return Generic Type in a function with Swift (Cannot convert return expression of type...)

I have a problem with generics in swift. Let's expose my code.
Parsable protocol:
protocol Parsable {
associatedtype T
var value: String {get set}
init(value: String)
func parseString() -> T
}
Generic class:
class ParsableGeneric<T: Parsable> {
var value: String
init(v: String) {
value = v
}
func parse() -> T{
return T(value: self.value)
}
}
Implementation of Int Type:
class ParsableIntNew: ParsableGeneric<IntParse> {}
struct IntParse: Parsable {
func parseString() -> Int {
return Int(value)!
}
var value: String
typealias T = Int
}
Then I have a function like this, that I want to return a ParsableGeneric Type:
func test<T: Parsable>() -> ParsableGeneric<T> {
let intclass = ParsableIntNew(v: "54")
let number: Int = intclass.parse().parseString()
return intclass
}
But I've got an error in the return intclass (Cannot convert return expression of type 'ParsableIntNew' to return type 'ParsableGeneric'
Why is this happening. I'm returning the correct value.
Thanks, I hope I find a good solution to this.
Your test() function basically promises "I will return a ParsableGeneric<T> object for any T that is a Parsable". However, the implementation only returns a ParsableIntNew, i.e. it only works when T is IntParse.
Imagine what happens when you also have a BoolParse: Parsable and the compiler concludes that when you call test() that T is BoolParse. The function would still return a ParsableGeneric<IntParse> even though the function return type in this case is ParsableGeneric<BoolParse>.