Satisfying ExpressibleByArrayLiteral Protocol
The reason is inheritance: any init
inside a protocol must be marked as required
if adopted by a class. A struct
cannot be inherited.
For a class, An init
must initialize an instance of that same class or return nil
. When that class adopts a protocol, itself and all its subclasses must provide their own implementations so the compiler can verify that fact:
class ClassA : ExpressibleByArrayLiteral {
required init(arrayLiteral elements: Self.Element...) {
// this implicitly returns an instance of ClassA
}
}
class ClassB : ClassA {
// without its own init(arrayLitteral:), it will fallback on ClassA's init and return
// an instance of ClassA, which Swift does not allow.
}
This is due to Swift's static typing system. Unlike in Objective-C where you think you create an NSString
but actually get an _NSContiguousString
instead (the so-called class cluster design pattern)
NSString * str = [[NSString alloc] initWithString:@"Hello world"];
// surprise: you may get an _NSContiguousString instead
Just get rid of the extension and implement it in the class's definition:
public class Stack<T> : ExpressibleByArrayLiteral {
private var buffer: [T]
init() {
self.buffer = []
}
public func push(_ value: T) {
self.buffer.append(value)
}
public func pop() -> T? {
return self.buffer.popLast()
}
var size: Int {
return self.buffer.count
}
var isEmpty: Bool {
return self.size == 0
}
// MARK: -
required public init(arrayLiteral: T...) {
self.buffer = []
for item in arrayLiteral {
self.push(item)
}
}
}
`ExpressibleByArrayLiteral` conformed to by class and its superclass = superclass is not convertible to subclass
I've ended up with a solution using a postfix operator.
Since I need to be able to instatiate the UIKits UILabel
using ExpressibleByArrayLiteral
I cannot use murphguys proposed solution with Label
and SecondLabel
.
My original code works by adding this postfix operator:
postfix operator ^
postfix func ^<I: Instantiatable>(attributes: [I.Element]) -> I {
return I(values: attributes)
}
Which makes the code compile and work. Although it feels a bit "hacky"...
let custom: MyLabel = [1, 2, 3, 4]^ // note use of caret operator. Now compiles
print(custom.text!) // prints "Sum 10"
If you are interested in why and how I use this you can take a look at my framework ViewComposer which enables this syntax:
let label: UILabel = [.text("Hello World"), .textColor(.red)]
But I also wanted to be able to create my own Composable
subclass called MyLabel
(or just Label
..)
let myLabel: MyLabel = [.text("Hello World"), .textColor(.red)] // does not compile
Which did not work earlier, but now works using the caret postfix operator ^
, like this:
let myLabel: MyLabel = [.text("Hello World"), .textColor(.red)]^ // works!
Which for now is the most elegant solution I think.
How to satisfy a protocol which includes an initializer?
Ok, my bad.
To guarantee that all subclasses conform to MyProtocol
new initializer has to be marked as required
as well.
Furthermore Swift requires to declare all required initializers directly within the class and does not allow to declare them in extensions.
extension MyClass : MyProtocol {
required convenience init(name: String) {
self.init()
self.name = name
}
}
How a generic class to conform a protocol (that have init in it) with constraint?
I don't know of a way to achieve this without making Graph final
. If you do make it final
, however, it will work fine as long as you remove required
(required
doesn't mean anything if there can't be subclasses):
extension Graph: ExpressibleByArrayLiteral where D == Int {
convenience init(arrayLiteral elements: Node...) {
self.init(Array(zip(0..., elements)))
}
}
Swift: Implementing Protocol Initializer in a Class
Surely the designated superclass initializer would be inherited?
No, not always. If the subclass defines its own designated initialisers, then it won't automatically inherit the superclass' designated initialisers. Consider the following example:
class Foo {
init() {}
}
class Bar : Foo {
var str: String
init(str: String) {
self.str = str
}
}
let b = Bar() // illegal – what value would the 'str' property have?
As Bar
defines its own init(str:)
designated initialiser, it doesn't automatically inherit Foo
's designated initialiser init()
. This prevents unsafe initialisation in cases where the subclass declares its own stored properties.
Marking init()
as required
enforces Bar
has an init()
, be it through providing its own implementation:
class Foo {
required init() {}
}
class Bar : Foo {
var str: String
init(str: String) {
self.str = str
}
// implement required init(), as Bar defines its own designated initialiser.
required init() {
self.str = "foo" // now str is correctly initialised when calling init()
}
}
let b = Bar() // now legal
Or by inheriting Foo
's implementation (when Bar
doesn't define its own designated initialisers):
class Foo {
required init() {}
}
class Bar : Foo {
// inherits init() from Foo, as Bar doesn't define its own designed initialisers.
}
let b = Bar() // legal
ArrayLiteralConvertible: Just a normal protocol?
Generally, literals are a purely compile-time artifact. They can be used to produce an object initialized from that literal, but once the compilation phase is over, nobody knows that something was a literal.
This suggests that any support for the protocols below needs to be built into the compiler itself:
ArrayLiteralConvertible
BooleanLiteralConvertible
DictionaryLiteralConvertible
ExtendedGraphemeClusterLiteralConvertible
FloatLiteralConvertible
NilLiteralConvertible
IntegerLiteralConvertible
StringLiteralConvertible
StringInterpolationConvertible
UnicodeScalarLiteralConvertible
The compiler would not take your own protocol as a replacement of any of the above.
Create a copyable protocol default initializer
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:
- You want to provide a default implementation of a
init(existingContentType: T, newContentTypeId: String?)
initializer, as blueprinted inContentType
, if the type conforming toContentType
also conforms toCopyable
.
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)
}
}
Example
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?)
Related Topics
How to Make Player Move to Opposite Side While Is in a Path
Realitykit - How to Edit or Add a Lighting
Swift Find Superview of Given Class with Generics
Swiftui Tabbar: Action for Tapping Tabitem of Currently Selected Tab to Reset View
How to Draw Two Polylines in Different Colors in Mapkit
How to Convert Text to Speech for Osx in Swift Playground
Swift How to Assign a String to a Uitextfield
Create JSON Object from Class in Swift
Why iOS13 Login with Facebook Does Not Work
Parsing Fetched JSON to Dictionary in Swift 3
How to Migrate Core Data's Data to App Group's Data
Swift: Copy Information Selected by User in Abpersonviewcontroller to Dictionary
Icloud Drive Issue: "[Documentmanager] Failed to Associate Thumbnails for Picked Url"
Swiftlint Overriding Project Settings Related to Spm
Realmswift + Multiple Predicate
Compactmap on Sequence() Not Lazy