Swift - Protocol extensions - Property default values
It seems you want to add a stored property
to a type via protocol extension. However this is not possible because with extensions you cannot add a stored property.
I can show you a couple of alternatives.
Subclassing (Object Oriented Programming)
The easiest way (as probably you already imagine) is using classes instead of structs.
class IdentifiableBase {
var id = 0
var name = "default"
}
class A: IdentifiableBase { }
let a = A()
a.name = "test"
print(a.name) // test
Cons: In this case your A class needs to inherit from
IdentifiableBase
and since in Swift theres is not multiple inheritance this will be the only class A will be able to inherit from.
Components (Protocol Oriented Programming)
This technique is pretty popular in game development
struct IdentifiableComponent {
var id = 0
var name = "default"
}
protocol HasIdentifiableComponent {
var identifiableComponent: IdentifiableComponent { get set }
}
protocol Identifiable: HasIdentifiableComponent { }
extension Identifiable {
var id: Int {
get { return identifiableComponent.id }
set { identifiableComponent.id = newValue }
}
var name: String {
get { return identifiableComponent.name }
set { identifiableComponent.name = newValue }
}
}
Now you can make your type conform to Identifiable
simply writing
struct A: Identifiable {
var identifiableComponent = IdentifiableComponent()
}
Test
var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
Swift: Default value for properties in Protocol
protocol Test {
var aValue: CGFloat { get set }
}
extension Test {
var aValue: CGFloat {
get {
return 0.3
}
set {
print("the new value is \(newValue)")
}
}
}
class Default: Test {
init() {
print("value \(aValue)")
}
}
class ViewController: Test {
var aValue: CGFloat {
get {
return 0.4
}
set {
print("i am overriding the setter")
}
}
init() {
print("value \(aValue)")
}
}
var d = Default() // value 0.3
d.aValue = 1 // the new value is 1.0
var vc = ViewController() // value 0.4
vc.aValue = 1 // i am overriding the setter
Since you have a protocol extension, you don't have to implement neither the getter nor the setter if you don't want to.
https://docs.swift.org/swift-book/LanguageGuide/Properties.html#//apple_ref/doc/uid/TP40014097-CH14-ID259
In addition to stored properties, classes, structures, and
enumerations can define computed properties, which do not actually
store a value. Instead, they provide a getter and an optional setter
to retrieve and set other properties and values indirectly.
You can't set the value of the same variable in the setter itself.
Swift protocol default values are not changable
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.
Extending a Protocol property to provide a default implementation in Swift
A possible solution is to locate the current value in the allCases
collection,
and the return the next element (or wrap around to the first element):
public protocol CycleValue: CaseIterable, Equatable {
var nextValue: Self { get }
}
public extension CycleValue {
var nextValue: Self {
var idx = Self.allCases.index(of: self)!
Self.allCases.formIndex(after: &idx)
return idx == Self.allCases.endIndex ? Self.allCases.first! : Self.allCases[idx]
}
}
(Note that both forced unwraps are safe!)
Example:
public enum AppThemeAttributes: CycleValue {
case classic, darkMode // etc.
}
let a = AppThemeAttributes.classic
print(a) // classic
let b = a.nextValue
print(b) // darkMode
let c = b.nextValue
print(c) // classic
The protocol must conform to Equatable
for this to compile, but that
is not a real restriction: A CaseIterable
protocol cannot have
associated values, so that the compiler can always synthesize theEquatable
conformance.
Is default implementation of a get-set variable possible in a protocol extension?
You can provide default implementations of get/set properties for computed values, but you can’t add storage to a type from an extension.
Why does the protocol default value passed to the function not change, even though the function does when subclassing?
You appear to be trying to use protocols to create multiple-inheritance. They're not designed for that, and even if you get this working, you're going to get bitten several times. Protocols are not a replacement for inheritance, multiple or otherwise. (As a rule, Swift favors composition rather than inheritance in any form.)
The problem here is that HigherClass conforms to HigherProtocol and so now has implementations for level
and doSomething
. LowerClass inherits from that, and wants to override those implementations. But the overrides are in a protocol extension, which is undefined behavior. See Extensions from The Swift Programming Language:
Extensions can add new functionality to a type, but they cannot override existing functionality.
Undefined behavior doesn't mean "it doesn't override." It means "anything could happen" including this weird case where it sometimes is overridden and sometimes isn't.
(As a side note, the situation is similar in Objective-C. Implementing a method in two different categories makes it undefined which one is called, and there's no warning or error to let you when this happens. Swift's optimizations can make the behavior even more surprising.)
I wish the compiler could detect these kinds of mistakes and raise an error, but it doesn't. You'll need to redesign your system to not do this.
How to encode protocol property default implementation to dictionary
The synthesized encoder considers only the members in the struct, not any properties in a protocol extension nor computed properties.
You have to write a custom initializer. And I'd prefer to make the struct adopt Encodable
rather than the protocol.
struct MyStruct: MyStructProtocol, Encodable {
var value: String
private enum CodingKeys: String, CodingKey { case value, defaultValue }
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(value, forKey: .value)
try container.encode(defaultValue, forKey: .defaultValue)
}
}
protocol MyStructProtocol { ...
Related Topics
Fcm Push Notifications Do Not Work on iOS 11
How to Only Disable Scroll in Scrollview But Not Content View
How to Load Icarousel View from Storyboard or Xib
iOS Web Page Errors Over Cellular Data But Not Over Wifi? Recent Change to At&T Cellular Network
How to Make Generics in Collection Type Constraint
Nsurlsessiondatatask Datataskwithurl Completion Handler Not Getting Called
Replace a Particular Color Inside an Image with Another Color
How to Access an Xcassets Directory on the Filesystem
Alamofire Fire Variable Type Has No Subscript Members
Why Force Unwrapping Is Required in Case of Enum and Switch
Saving and Loading Up a Highscore in Swift Using Nsuserdefaults
Cashapelayer with Different Colors
React-Native iOS Podfile Issue with "Use_Native_Modules!"
iOS Scatter Core Plot with a Gap
How to Add an Identifier to Auto Layout Constraints in Interface Builder