delegating initializers in structs are not marked with 'convenience' - swift

I keep getting this error and I don't understand why.
error: delegating initializers in structs are not marked with 'convenience'
This is what I have (as an example), a DeprecatedCurrency and a SupportedCurrency.
struct DeprecatedCurrency {
let code: String
}
struct SupportedCurrency {
let code: String
}
I then want to add a convenience init function for converting from the deprecated currency object to the new currency object. And this is what I have:
struct DeprecatedCurrency {
let code: String
}
struct SupportedCurrency {
let code: String
convenience init(_ currecny: DeprecatedCurrency) { // error!!
self.init(code: currecny.code)
}
init(code: String) {
self.code = code
}
}
What does this error even mean and how do I fix it?
I know that if we don't provide a default initializer, a initializer with signature init(code: String) will be automatically generated for us with struct in Swift. So by the end of the day, what I am really looking for is (if possible):
struct SupportedCurrency {
let code: String
convenience init(_ currecny: DeprecatedCurrency) { // error!!
self.init(code: currecny.code)
}
}

Just remove the convenience, it is not required for struct.
From Swift documentation.
Initializers can call other initializers to perform part of an instance’s initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.
They haven't mentioned using convenience. It is convenience in semantic but doesn't require the keyword.
struct DeprecatedCurrency {
let code: String
}
struct SupportedCurrency {
let code: String
init(_ currency: DeprecatedCurrency) { // error!!
self.init(code: currency.code)
}
init(code: String) {
self.code = code
}
}

structs don't need the word convenience
Try this:
struct SupportedCurrency {
let code: String
init(_ currency: DeprecatedCurrency) { // error!!
self.init(code: currency.code)
}
init(code: String) {
self.code = code
}
}
The question is not why don't we put convenience for structs but why do we put convenience for classes. The reason is that classes have inheritance. With a class you need to call the super class's designated constructor (not sure if that is the correct terminology, it comes from Objective-C's initialsers.. The word convenience marks the constructor as "not the designated constructor".

One option is to add the new init in an extension of your struct. This way, you wont loose the default auto generated memberwise initialiser.
struct SupportedCurrency {
let code: String
}
extension SupportedCurrency {
init(_ currency: DeprecatedCurrency) {
self.init(code: currency.code)
}
}
From Apple docs
NOTE
If you want your custom value type to be initializable with the
default initializer and memberwise initializer, and also with your own
custom initializers, write your custom initializers in an extension
rather than as part of the value type’s original implementation. For
more information, see Extensions.

Related

Swift Extension computed variable not read correctly until declared in protocol

I have a protocol extension which declares and assigns a static computed variable:
protocol DataType {
}
extension DataType {
static var mocks: [Self] { [] }
}
Then I have another protocol named Provider which has an associatedtype requirement of the DataType protocol and an extension:
protocol Provider {
associatedtype Data: DataType
}
extension Provider {
static var mock: [Data] {
Data.mocks
}
}
I then create the following types that conform to DataType and Provider:
struct Car: DataType {
var name: String
static var mocks: [Car] {
[.init(name: "Nissan"), .init(name: "Toyota")]
}
}
struct CarProvider: Provider {
typealias Data = Car
}
print(CarProvider.mock)
When I print this out (an empty array []), the CarProvider static variable mock prints out the default value of the mocks variable of DataType - even when Car has an assigned array value for mocks inside its struct definition
However, as soon as I declare the mocks property inside the DataType protocol as a requirement, then the mocks value of Car is correctly read (printing the correct values: [__lldb_expr_93.Car(name: "Nissan"), __lldb_expr_93.Car(name: "Toyota")]):
protocol DataType {
static var mocks: [Self] { get }
}
Why is the property definition required in the Protocol definition in the first place? Shouldn't the extension value be sufficient? And since the Car struct is assigning its own value to the mocks variable, shouldn't that be read instead of the default extension value?
Why is the property definition required in the Protocol definition in
the first place? Shouldn't the extension value be sufficient?
No. An implementation in a protocol extension can only be considered a true default implementation, if there is some way for it to be overridden in scope. Without having the requirement in the protocol, Swift has no reason or mechanism to look beyond an extension, because there's nothing else that will match, semantically.
protocol DataType { }
extension DataType {
static var mocks: [Self] { [] }
}
func mocks<Data: DataType>(_: Data.Type) -> [Data] {
Data.mocks // This *is* the extension. That is the only truth.
}
protocol DataType {
static var mocks: [Self] { get }
}
extension DataType {
static var mocks: [Self] { [] }
}
func mocks<Data: DataType>(_: Data.Type) -> [Data] {
Data.mocks // Now, we can dispatch to a concrete `Data` type!
}

How do I get a CustomStringConvertible description from this enum?

I have the following enum
enum Properties: CustomStringConvertible {
case binaryOperation(BinaryOperationProperties),
brackets(BracketsProperties),
chemicalElement(ChemicalElementProperties),
differential(DifferentialProperties),
function(FunctionProperties),
number(NumberProperties),
particle(ParticleProperties),
relation(RelationProperties),
stateSymbol(StateSymbolProperties),
symbol(SymbolProperties)
}
and the structs all look like this
struct BinaryOperationProperties: Decodable, CustomStringConvertible {
let operation: String
var description: String { return operation }
}
So how do I make that enum conform to CustomStringConvertible? I tried with a simple getter but obviously that calls itself and I'd like to call the specific struct's instead.
Bonus points: does an enum defined like that have a name?
Such an enum is called enum with associated values.
I'd implement description by manually switching over the cases:
extension Properties: CustomStringConvertible {
var description: String {
switch self {
case .binaryOperation(let props):
return "binaryOperation(\(props))"
case .brackets(let props):
return "brackets(\(props))"
...
}
}
}
Edit: an alternative is to use Swift's Mirror reflection API. The enum case of an instance is listed as the mirror's child and you can print its label and value like this:
extension Properties: CustomStringConvertible {
var description: String {
let mirror = Mirror(reflecting: self)
var result = ""
for child in mirror.children {
if let label = child.label {
result += "\(label): \(child.value)"
} else {
result += "\(child.value)"
}
}
return result
}
}
(This is a generic solution that should be usable for many types, not just enums. You'll probably have to add some line breaks for types that have more than a single child.)
Edit 2: Mirror is also what print and String(describing:) use for types that don't conform to Custom[Debug]StringConvertible. You can check out the source code here.

What is the proper way to reference a static variable on a Swift Protocol?

Assume a protocol defined below:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "Default Id" }
}
What is the best way to reference the static variable? The example below illustrates two ways to access the variable. What is the difference, and is the type(of:) better?
func work<I: Identifiable>(on identifiable: I) {
let identifier: String = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType: String = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
struct Thing: Identifiable {
static var identifier: String { return "Thing" }
}
work(on: Thing())
In the example you show, there is no difference. Because identifier is a protocol requirement, it will be dynamically dispatched to in both cases, therefore you don't need to worry about the wrong implementation being called.
However, one difference arises when you consider the value of self inside the static computed property when classes conform to your protocol.
self in a static method/computed property is the metatype value that it's is called on. Therefore when called on I, self will be I.self – which is the static type that the compiler infers the generic placeholder I to be. When called on type(of: identifiable), self will be the dynamic metatype value for the identifiable instance.
In order to illustrate this difference, consider the following example:
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String { return "\(self)" }
}
func work<I : Identifiable>(on identifiable: I) {
let identifier = I.identifier
print("from Protocol: \(identifier)")
let identiferFromType = type(of: identifiable).identifier
print("using type(of:): \(identiferFromType)")
}
class C : Identifiable {}
class D : C {}
let d: C = D()
// 'I' inferred to be 'C', 'type(of: d)' is 'D.self'.
work(on: d)
// from Protocol: C
// using type(of:): D
In this case, "which is better" completely depends on the behaviour you want – static or dynamic.

How to define initializers in a protocol extension?

protocol Car {
var wheels : Int { get set}
init(wheels: Int)
}
extension Car {
init(wheels: Int) {
self.wheels = wheels
}
}
on self.wheels = wheels i get the error
Error: variable 'self' passed by reference before being initialized
How can I define the initializer in the protocol extension?
As you can see this doesn't work under these circumstances because when compiling, one has to make sure that all properties are initialized before using the struct/enum/class.
You can make another initializer a requirement so the compiler knows that all properties are initialized:
protocol Car {
var wheels : Int { get set }
// make another initializer
// (which you probably don't want to provide a default implementation)
// a protocol requirement. Care about recursive initializer calls :)
init()
init(wheels: Int)
}
extension Car {
// now you can provide a default implementation
init(wheels: Int) {
self.init()
self.wheels = wheels
}
}
// example usage
// mark as final
final class HoverCar: Car {
var wheels = 0
init() {}
}
let drivableHoverCar = HoverCar(wheels: 4)
drivableHoverCar.wheels // 4
As of Xcode 7.3 beta 1 it works with structs as expected but not with classes since if they are not final the init(wheels: Int) in the protocol is a required init and it can be overridden therefore it cannot be added through an extension. Workaround (as the complier suggests): Make the class final.
Another workaround (in depth; without final class)
To work with classes without making them final you can also drop the init(wheels: Int) requirement in the protocol. It seems that it behaves no different than before but consider this code:
protocol Car {
var wheels : Int { get set }
init()
// there is no init(wheels: Int)
}
extension Car {
init(wheels: Int) {
self.init()
print("Extension")
self.wheels = wheels
}
}
class HoverCar: Car {
var wheels = 0
required init() {}
init(wheels: Int) {
print("HoverCar")
self.wheels = wheels
}
}
// prints "HoverCar"
let drivableHoverCar = HoverCar(wheels: 4)
func makeNewCarFromCar<T: Car>(car: T) -> T {
return T(wheels: car.wheels)
}
// prints "Extension"
makeNewCarFromCar(drivableHoverCar)
So if you make a Car from a generic context where the type on which you call init is only to be known as Car the extension initializer is called even though an initializer is defined in HoverCar. This only occurs because there is no init(wheels: Int) requirement in the protocol.
If you add it you have the former problem with declaring the class as final but now it prints two times "HoverCar". Either way the second problem probably never occurs so it might be a better solution.
Sidenote: If I have made some mistakes (code, language, grammar,...) you're welcome to correct me :)
My understanding is that this isn't possible, because the protocol extension can't know which properties the conforming class or struct has - and therefore cannot guarantee they are correctly initialized.
If there are ways to get around this, I'm very interested to know! :)
#Qbyte is correct.
In addition, you can take a look at my Configurable
In that I have Initable protocol
public protocol Initable {
// To make init in protocol extension work
init()
}
public extension Initable {
public init(#noescape block: Self -> Void) {
self.init()
block(self)
}
}
Then in order to conform to it
extension Robot: Initable { }
I have 2 ways, using final or implement init
final class Robot {
var name: String?
var cute = false
}
class Robot {
var name: String?
var cute = false
required init() {
}
}
May not be the same but in my case instead of using init I used a static func to return the object of the class.
protocol Serializable {
static func object(fromJSON json:JSON) -> AnyObject?
}
class User {
let name:String
init(name:String) {
self.name = name
}
}
extension User:Serializable {
static func object(fromJSON json:JSON) -> AnyObject? {
guard let name = json["name"] else {
return nil
}
return User(name:name)
}
}
Then to create the object I do something like:
let user = User.object(fromJSON:json) as? User
I know its not the best thing ever but its the best solution I could find to not couple business model with the data layer.
NOTE: I'm lazy and I coded everything directly in the comment so if something doesn't work let me know.

Swift cannot assign to self in a class init method

I have a class called Letter
class Letter
{
init() {}
}
And I have an extension for this class:
extension Letter
{
convenience init(file_path:String) {
self = Letter.loadFromFile(file_path)
}
class func loadFromFile(file_path:String)->Letter {...}
}
I need to create and init with path to file and when i call Letter(file_path) I need a new object that returned by a func loadFromFile. How to assign in an init method or to return a new object?
It gives the error:
Cannot assign to value: 'self' is immutable
Class functions that return instances of that class seems to be an anti-pattern in Swift. You'll notice that the "with" Objective-C class methods like [NSString stringWithString:#"some other string"] made the transition as "with"-less convenience initializers: NSString(string: "some other string").
Furthermore, you'll need to delegate to a designated initializer from within a convenience initializer.
Also, since you're 1) defining the original class and 2) don't need the convenience initializer scoped differently than the designated initializer, I don't see any reason to place it in an extension.
Putting those together:
class Letter {
init() { … }
convenience init(filePath: String) {
self.init()
loadFromFile(filePath)
}
func loadFromFile(filePath: String) { … }
}
let letter1 = Letter()
letter1.loadFromFile("path1")
let letter2 = Letter(filePath: "path2")
In summary, the analogy for assigning to self in Swift is calling an initializer.
Let me know if this works for you!
Convenience initializer must delegate up to designated initializer
It says that convenience init(file_path:String) should call other initialiser
convenience init(file_path:String) {
self.init()
//here can set other properties
}
Convenience initialiser usually provide some default parameters
Convenience initialiser are designed to make creation of class instance less complicated. It means that you don't need to pass all arguments to constructor. In your example the class should look like this
Designated initializer takess all possible arguments.
Convenience provide default value
Code example
// Create instance of a Letter
Letter()
Letter(file_path: "path.txt")
Letter(file_path: "path.txt", option: 0, other: 0)
//Class Implementation
class Letter
{
init(file_path: String , option: Int, other: Int) {
// Instansiate class
}
}
extension Letter {
convenience init() {
self.init(file_path:"a")
}
convenience init(file_path:String) {
self.init(file_path: file_path , option: 0, other: 0)
}
class func loadFromFile(file_path:String) -> Letter {
return Letter()
}
}
Now you can create instance of Letter this way -
You can't assign to self. What about something like this:
class Letter {
}
extension Letter {
convenience init(filePath: String) {
self.init()
// code to load a Letter from a file goes here.
}
class func loadFromFile(filePath: String) -> Letter {
return Letter(filePath: filePath)
}
}