Using protocol with typealias as a property

Dániel Nagy

I have a protocol with a typealias:

protocol Archivable {
    typealias DataType

    func save(data: DataType, withNewName newName: String) throws
    func load(fromFileName fileName: String) throws -> DataType
}

and a class that conforms to that protocol:

class Archiver: Archivable {
    typealias DataType = Int

    func save(data: DataType, withNewName newName: String) throws {
        //saving
    }

    func load(fromFileName fileName: String) throws -> DataType {
        //loading
    }
}

and I would like to use Archivable as a property in another class like:

class TestClass {

    let arciver: Archivable = Archiver() //error here: Protocol 'Archivable' can only be used as a generic constraint because it has Self or associated type requiments
}

but it fails with

Protocol 'Archivable' can only be used as a generic constraint because it has Self or associated type requiments

My goal is that TestClass should only see Archiver as Archiveable, so if I want to change the saving/loading mechanism, I just have to create a new class that conforms to Archivable as set it as the property in TestClass, but I don't know if this is poosible, and if so, then how.

And I would like to avoid using AnyObject instead of DataType.

Hector Matos

Depending on what you are actually trying to do, this can work using type erasure. If you follow the instructions in the link R Menke posted in the comments, you can achieve what you are trying to do. Since your property in TestClass seems to be a let, I'm going to assume you already know the type of DataType at compile time. First you need to setup a type erased Archivable class like so:

class AnyArchiver<T>: Archivable {
    private let _save: ((T, String) throws -> Void)
    private let _load: (String throws -> T)

    init<U: Archivable where U.DataType == T>(_ archiver: U) {
        _save = archiver.save
        _load = archiver.load
    }

    func save(data: T, withNewName newName: String) throws {
        try _save(data, newName)
    }

    func load(fromFileName fileName: String) throws -> T {
        return try _load(fileName)
    }
}

Much like Swift's AnySequence, you'll be able to wrap your Archiver in this class in your TestClass like so:

class TestClass {
    let archiver = AnyArchiver(Archiver())
}

Through type inference, Swift will type TestClass' archiver let constant as an AnyArchiver<Int>. Doing it this way will make sure you don't have to create a dozen protocols to define what DataType is like StringArchiver, ArrayArchiver, IntArchiver, etc. Instead, you can opt in to defining your variables with generics like this:

let intArchiver: AnyArchiver<Int>
let stringArchiver: AnyArchiver<String>
let modelArchiver: AnyArchiver<Model>

rather than duplicating code like this:

protocol IntArchivable: Archivable {
    func save(data: Int, withNewName newName: String) throws
    func load(fromFileName fileName: String) throws -> Int
}
protocol StringArchivable: Archivable {
    func save(data: String, withNewName newName: String) throws
    func load(fromFileName fileName: String) throws -> String
}
protocol ModelArchivable: Archivable {
    func save(data: Model, withNewName newName: String) throws
    func load(fromFileName fileName: String) throws -> Model
}

let intArchiver: IntArchivable
let stringArchiver: StringArchivable
let modelArchiver: ModelArchivable

I wrote a post on this that goes into even more detail in case you run into any problems with this approach. I hope this helps!

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

AssociatedType in a protocol with typealias

Test if instance is a protocol with typealias in Swift?

Swift typealias in protocol and generic types

Type does not conform to protocol - typealias to another protocol

Using weak property not like a protocol

Swift - Typealias dictionary with value that implements a generic protocol

Cast a Swift generic class to a protocol with a typealias

Abort trap: 6 in protocol extension with typealias definitions

Using a typealias when declaring an IBOutlet

How to reference a generic class with a constraint from a Protocol with a typealias in Swift?

How does the statement "public typealias AnyObject" make AnyObject a protocol?

Typealias must be declared public because it matches a requirement in public protocol

Why am I allowed to set a read only property of a protocol using a struct that inherits said protocol?

Using typealias in place of typedef defined in class in definition

Protocol with associated type as property

Swift Protocol with Variadic property

Shared Instance property in protocol

Swift protocol extension with property conforming to protocol

Objective-c on the protocol defined a property protocol

How to expose existing property on Obj-C class using an extension protocol in Swift

A codable structure contains a protocol property

Cannot read property 'protocol' of undefined

Is there a way to declare protocol property as private?

Add class property to protocol in Swift

Override UIButton backgroundColor property with protocol

Specify a settable property/variable in a protocol

Override swift protocol property to optional

Swift Property that conforms to a Protocol and Class

Swift: setting an optional property of a protocol