I got this functions:
public protocol ContentType {
associatedtype T
static var allTypes: [T] { get }
static func getContentType(contentTypeId: String) -> T
var contentTypeId: String? { get }
}
public protocol Copyable: class {
associatedtype T
static func copy(old: T, new: T)
}
public protocol CopyableContentType: class {
associatedtype T
init(existingContentType: T, newContentTypeId: String?)
}
I want to provide a default initializer for a class that:
ContentType
Copyable
With above code, I always do the same in the required init: calling the copy function of the implementing class. Is there any way to omit this duplicate code across classes while keeping this initializer? I thought of it adding to a protocol extension:
public extension CopyableContentType where Self: ContentType & Copyable {
typealias T = Self // So wrong but I have no idea what to put here
// T should be the same T as the T usud in ContentType and Copyable
init(existingContentType: T, newContentTypeId: String?) {
Self.copy(old: existingContentType, new: self)
}
}
But this results in a few errors. I do not know what to put in at the typealias and I can not use associatedtypes. Is there any way I can provide a default initializer that calls the copy function?
I want to provide a default initializer for a class that:
- Conforms to
ContentType
- Conforms to
Copyable
I'll attempt to answer this question assuming what you mean is that:
init(existingContentType: T, newContentTypeId: String?)
initializer, as blueprinted in ContentType
, if the type conforming to ContentType
also conforms to Copyable
.Let's first have a look at your Copyable
protocol. It might be due to omission of details/use case in your question, but I don't see the need of the associated type T
here, as the protocol basically blueprints/promises "the class
type conforming to Copyable
, say e.g. TheClassType
, will provide an implementation of the static
copy(from: TheClassType, to: TheClassType)
function" — where the conforming type (TheClassType
) is just Self
. I.e.:
protocol Copyable: class {
static func copy(from: Self, to: Self)
}
The same holds for ContentType
: is there a need for an associatedType
here, or is the ContentType
of a given concrete type simply the type itself; i.e. Self
? Before continuing, lets strip away the parts not relevant to your question:
protocol ContentType {
var contentTypeId: String? { get }
}
Now, prior for any initializer to copy (not assign - we are dealing with reference types) anything into self
(e.g. value type members of self
), self
must have been initialized (or assigned to). So to allow providing a default implementation for a init(existingContentType: Self, newContentTypeId: String?)
initializer of ContentType
(in case of conformance to Copyable
; Self
is a class
type)—where the implementation is intended to make use of the blueprinted copy(from: Self, to: Self)
of copyable
—the type conforming to ContentType
must know of a way to initializer itself prior to the copying step. I.e., the ContentType
needs to blueprint some initializer that can be used to initialize self
in the "copying initializer" prior to invoking the copy(from:to)
method. Let's simply blueprint init()
:
protocol ContentType {
var contentTypeId: String? { get set }
init()
}
Now, as the ContentType
blueprints a contentTypeId
member, and the copying initializer contains a newContentTypeId
parameter, it could be sensible to provide a default implemented initializer with contentTypeId
as its only parameter; namely init(contentTypeId: String?)
:
extension ContentType {
init(contentTypeId: String?) {
self.init()
self.contentTypeId = contentTypeId
}
}
With this in place, we can provide the default implemented init(existingContentType: Self, newContentTypeId: String?)
initializer as a constrained extension to ContentType
based on conformance of Self
to Copyable
:
extension ContentType where Self: Copyable {
init(existingContentType: Self, newContentTypeId: String?) {
self.init(contentTypeId: newContentTypeId)
Self.copy(from: existingContentType, to: self)
}
}
Putting the above together:
protocol Copyable: class {
static func copy(from: Self, to: Self)
}
protocol ContentType {
var contentTypeId: String? { get set }
init()
}
extension ContentType {
init(contentTypeId: String?) {
self.init()
self.contentTypeId = contentTypeId
}
}
extension ContentType where Self: Copyable {
init(existingContentType: Self, newContentTypeId: String?) {
self.init(contentTypeId: newContentTypeId)
Self.copy(from: existingContentType, to: self)
}
}
Example:
// Copyable content type
final class Foo: ContentType, Copyable {
// Note that since all stored properties have initial values,
// the compiler provides a synthesized initializer for init().
var contentTypeId: String?
var data = 0
static func copy(from: Foo, to: Foo) {
to.data = from.data
}
}
let foo1 = Foo(contentTypeId: "foo1")
foo1.data = 42
let foo2 = Foo(existingContentType: foo1, newContentTypeId: "foo2")
print(foo1.contentTypeId ?? "None", foo1.data) // foo1 42
print(foo2.contentTypeId ?? "None", foo2.data) // foo2 42
// Non-copyable content type
final class Bar: ContentType {
var contentTypeId: String?
} // Bar has no access to
// init(existingContentType: Self, newContentTypeId: String?)