Swift protocol default values are not changable - swift

I have such a protocol properties with default values. But with the current implementation if I create an instance of AssetViewAttributes with some other values for avgPrice, precision they still have the default values. How can I change them?
struct Crypto: AssetViewAttribures {
let name: String
let logo: URL
let symbol: String
let avgPrice: String
let precision: Int
}
struct Commodity: AssetViewAttribures {
let name: String
let logo: URL
let symbol: String
let avgPrice: String
let precision: Int
}
struct Fiat: AssetViewAttribures {
let name: String
let logo: URL
let symbol: String
}
protocol AssetViewAttribures {
var name: String { get }
var logo: URL { get }
var symbol: String { get }
}
extension AssetViewAttribures {
var avgPrice: String { get { return "" } set {} }
var precision: Int { get{ return 0 } set{} }
}
var type1: AssetViewAttribures = Crypto(name: "name", logo: URL(string: "https://pixabay.com/de/illustrations/online-maus-web-internet-weltweit-523234/")!, symbol: "symbol", avgPrice: "123", precision: 2)
type1.avgPrice // "" instead of "123"

var type1: AssetViewAttribures = Crypto(name: "name", logo: URL(string: "https://pixabay.com/de/illustrations/online-maus-web-internet-weltweit-523234/")!, symbol: "symbol", avgPrice: "123", precision: 2)
type1.avgPrice
This would call the getter declared in the protocol extension, which just returns "". This is because Crypto.avgPrice has no relation to the avgPrice declared in the protocol extension. You can't "override" a member in an extension, because extensions are dispatched statically. The compiler sees that test is of type AssetViewAttributes, finds the default getter you have declared in the extension, and that's what it will call.
To fix this, you need to add avgPrice as a requirement of the protocol:
protocol AssetViewAttributes {
...
var avgPrice: String { get }
}
This causes Swift to find avgPrice declared in the protocol, and dispatches it dynamically. If the implementing class happens to implement avgPrice, that implementation will be called. If not, then the default implementation is called.

Related

How to disable default nil-initialization of Optional

Let's say I have these two structures:
struct DataOne {
let id: String
// ...
}
struct DataTwo {
let id: String
// ...
}
And there is a separate conversion function:
extension DataOne {
func convertToDataTwo() -> DataTwo {
.init(
id: self.id,
// ...
)
}
}
At some point, the var name: String? field is added to both structures:
struct DataOne {
let id: String
var name: String?
// ...
}
struct DataTwo {
let id: String
var name: String?
// ...
}
But the assembly does not swear because the field is optional.
And when converting, the name field is lost.
Is it possible to disable the autofill of the option or to call the warnings?
I tried to find such a rule in SwiftLint, but I didn't find it.
If your var's type is written T? with no default value, and Swift synthesizes a memberwise init for your type, then the synthesized init uses a default value of nil for that var.
However, if your var's type is written Optional<T> with no default value, then the synthesized init does not use a default value.
So write this instead:
struct DataOne {
let id: String
var name: Optional<String>
// ...
}
struct DataTwo {
let id: String
var name: Optional<String>
// ...
}
Or write out your init instead of letting the compiler synthesize it:
struct DataOne {
let id: String
var name: String?
init(id: String, name: String?) {
self.id = id
self.name = name
}
}
struct DataTwo {
let id: String
var name: String?
init(id: String, name: String?) {
self.id = id
self.name = name
}
}
You can use Xcode's Editor > Refactor > Generate Memberwise Initializer command to write most of the init for you, then delete the = nil default:

What is the difference between { } and = when assigning an enum to a variable in Swift?

What is the difference between { } and = when assigning an enum to a variable in Swift?
Why would you say var type: ItemType { .recipe } over var type: ItemType = .video.
Does the {} indicate that it is a computed property?
Also, is the { get } needed after the type in the protocol?
enum ItemType: String, Decodable {
case video
case recipe
}
protocol Item: Decodable {
var type: ItemType { get }
var title: String { get }
var imageURL: URL { get }
}
struct Video: Item {
var type: ItemType = .video
var title: String
var imageURL: URL
var url: URL
var duration: String
var resolution: String
}
struct Recipe: Item {
var type: ItemType { .recipe }
var title: String
var imageURL: URL
var text: String
var ingredients: [String]
}
Does the {} indicate that it is a computed property?
Yes.
Also, is the { get } needed after the type in the protocol?
To specify that the property, whether it be computed or stored, needs to support at least a getter. The setter is optional.

How to create a protocol conform Comparable in Swift

I have a use case to compare the enployee's rank. Here is what I want to do:
protocol Enployee: Comparable {
var id: String { get }
var rank: Int { get }
var name: String { get }
var type: String { get }
}
extension Enployee {
static func <(lhs: Enployee, rhs: Enployee) -> Bool {
return lhs.rank < rhs.rank
}
}
But I got the following error:
Protocol 'Enployee' can only be used as a generic constraint because it has Self or associated type requirements
Then I changed my code:
extension Enployee {
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.rank < rhs.rank
}
}
I can compile it. But when I continue working on my user case:
struct Engineer: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Engineer"
}
struct Manager: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Manager"
}
let staff1 = Engineer(id: "123", rank: 2, name: "Joe")
let staff2 = Engineer(id: "124", rank: 2, name: "Frank")
let staff3 = Manager(id: "101", rank: 10, name: "John")
public struct Department<T: Comparable> {
}
let queue = Department<Enployee>()
I got another error message:
Protocol 'Enployee' as a type cannot conform to 'Comparable'
Any idea?
The error message tells you what the problem is. Having declared Department<T: Comparable> you cannot resolve T as Enployee; it is a protocol, not a type conforming to Comparable (such as Engineer).
A generic has to be resolved to one type. It would be legal to declare Department<T: Enployee> if that is what you really mean. But then you are setting yourself up for multiple Department types, a department of Manager and a department of Engineer. That is probably not what you want, so this is a bad use of a generic.
Moreover, type strings are a horrifically bad smell:
struct Engineer: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Engineer" // uck
}
It seems much more likely that you want a class and subclasses here. Why fight against OOP?

Cannot convert return expression of type 'Int' to return type 'Property<Int>

I'm trying to implement the MVVM pattern using Bond in a test project.
The idea is simple:
Define an abstraction which the viewModel then uses.
Make a concrete type from this abstraction.
Inject this concrete type in the viewModel.
This is my code so far:
// 1.
protocol Commentable {
var id: Int { get }
var name: String { get }
var body: String { get }
}
// 2.
struct Comment: Commentable {
var id: Int
var name: String
var body: String
}
// 3.
struct CommentViewModel {
private let comment: Commentable
init(comment: Commentable) {
self.comment = comment
}
public var id: Observable<Int> {
return self.comment.id
}
}
Xcode shows the following error when I try to return self.comment.id:
Cannot convert return expression of type 'Int' to return type
'Property
This makes sense - comment.id is an Int and self.id is an Observable<Int>. How do make this work though, since I don't want to define the properties in my Comment type as Observable.
Fixed it - just had to change the syntax:
struct CommentViewModel {
private let comment: Observable<Commentable>
init(comment: Commentable) {
self.comment = Observable(comment)
}
public var id: Observable<Int> {
return Observable(comment.value.id)
}
}

Custom structure using jSONEncoder

Want to encode an object into a custom structure using JSONEncoder+Encodable.
struct Foo: Encodable {
var name: String?
var bars: [Bar]?
}
struct Bar: Encodable {
var name: String?
var value: String?
}
let bar1 = Bar(name: "bar1", value: "barvalue1")
let bar2 = Bar(name: "bar2", value: "barvalue2")
let foo = Foo(name: "foovalue", bars: [bar1, bar2])
Default approach of encoding foo gives:
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(foo)
print(String(data: data, encoding: .utf8)!)
Output:
{
"name": "foovalue",
"bars": [
{
"name": "bar1",
"value": "barvalue1"
},
{
"name": "bar2",
"value": "barvalue2"
}
]
}
In the custom output I'd like to use the value of property name as the key, and the values of rest as the value for the mentioned key. The same will be applicable for nested objects. So I'd expect the output to be:
{
"foovalue": [
{
"bar1": "barvalue1"
},
{
"bar2": "barvalue2"
}
]
}
Question is whether Encodable/JSONEncoder supports this. Right now I just process the the first output dictionary and restructure it by iterating the keys.
If you’d like to keep Foo and Bar Encodable, you can achieve this by providing a custom encode(to:) that uses a specific coding key whose value is name:
private struct StringKey: CodingKey {
let stringValue: String
var intValue: Int? { return nil }
init(_ string: String) { stringValue = string }
init?(stringValue: String) { self.init(stringValue) }
init?(intValue: Int) { return nil }
}
struct Foo: Encodable {
var name: String
var bars: [Bar]
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringKey.self)
try container.encode(bars, forKey: StringKey(name))
}
}
struct Bar : Encodable {
var name: String
var value: String
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: StringKey.self)
try container.encode(value, forKey: StringKey(name))
}
}
StringKey can take on any String value, allowing you to encode arbitrarily as needed.