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.
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.
Comments