When a protocol declares a property as optional and a concrete type declares it as non-optional, how can I make the concrete type conform to the protocol?
Here's the problem:
protocol Track {
var trackNumber: Int? { get } // not all tracks have a track number
}
struct SpotifyTrack {
let trackNumber: Int // all SpotifyTrack are guaranteed to have a track number
}
extension SpotifyTrack: Track {
var trackNumber: Int? {
return self.trackNumber // WARNING: All paths through this function will call itself
}
}
I don't want to make trackNumber optional in SpotifyTrack because I know the value will always be there for SpotifyTracks. Are there are any solutions more elegant than just renaming the properties?
There is no elegant solution to your problem other than renaming the conflicting property on the conforming type.
Swift doesn't allow 2 properties of the same name to exist on a type even if their types are different. On the other hand, Int? and Int are completely different types, so you cannot have trackNumber: Int fulfil the protocol requirement of trackNumber: Int?.
The only solution (other than changing the type in either the protocol or the struct) is to rename the non-Optional property in SpotifyTrack and make an optional computed property of the same name returning the non-optional one.
protocol Track {
var trackNumber: Int? { get }
}
struct SpotifyTrack {
private let _trackNumber: Int
}
extension SpotifyTrack: Track {
var trackNumber: Int? { _trackNumber }
}
One way could be that you have a base track protocol and then have a second protocol which extend the base protocol. For example
protocol BaseTrackProtocol {
var title:String
var artist:String
}
protocol SpotifyTrackProtocol : BaseTrackProtocol {
var trackNumber:Int
}
struct SoptifyTrack: SpotifyTrackProtocol {
var title:String
var artist:String
var trackNumber:Int
}
other type of tracks could then follow different extensions of the base track protocol
hope it helps :)
You can have a second property that acts as backing for the first:
protocol Track {
var trackNumber: Int? { get }
}
struct SpotifyTrack {
var realTrackNumber: Int
}
extension SpotifyTrack: Track {
var trackNumber: Int? { self.realTrackNumber }
}
However, I have a feeling that that is what you mean by "renaming": you would prefer not to do that. Nevertheless, the "instructions" of a protocol are firm: you cannot adopt Track without declaring a trackNumber that is explicitly typed as Int?.
Objective-C has #optional protocol members, meaning you could have Track declare trackNumber Int but #optional, and then you could adopt Track without having a trackNumber at all. But if you wanted to take advantage of that, this could not be a struct.
Related
I have the following protocol:
protocol Device: AnyObject, Identifiable where ID == String {
var id: String { get }
var name: String { get }
var isConnected: Bool { get }
func connect()
func disconnect()
}
When I use it like this:
class ConnectionManager: ObservableObject {
#Published private (set) var devices = [Device]()
}
I get this error:
Protocol 'Device' can only be used as a generic constraint because it has Self or associated type requirements
Removing the Identifiable definition gets rid of this warning, but I need that definition to satisfy usage in SwiftUI.
Aren't the associated type requirements already satisfied by explicitly defining ID as String?
I'm using Xcode 13.1 / Swift 5.5.
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!
}
I come from Java world. Now I am programming in Swift 4.
I would like to implement abstract class in Swift, I know in Swift there is no such concept of abstract class. But I know we could mimic this concept in Swift by using protocol. For example this is what I tried:
// With protocol, I can define functions that concrete class have to implement
protocol ProductProvider {
func getProductNumber() -> Int
}
// with protocol extension, I can define shared (computed) properties and functions among concrete classes that comply with this protocol
extension ProductProvider {
var maxProductCount: Int {
return 10
}
}
But now, I would like to have a shared variable that could be set & get ("shared" means to be shared with classes that comply with this protocol):
extension ProductProvider {
var maxProductCount: Int {
set(newValue) {
// set to what if I couldn't define private stored variable in extension to hold the most recent set value ?
}
get{
// how to return the most recent value set?
}
}
}
My question is in the comment of above code. How can I do that set and get for a variable in extension of protocol in Swift 4? If it is impossible, what are the workarounds possible?
The simplest way i think is define the variable in the protocol with the getter and setter. Then in your conform object you should declare the variable to conformance.
An Example:
protocol AbstractObject {
var maxProductCount: Int { get set }
}
struct ConformObject: AbstractObject {
var maxProductCount: Int
}
So now you can use your variable in your default implementations
extension AbstractObject {
mutating func addOne() -> Int {
self.maxProductCount += 1
return self.maxProductCount
}
}
Aside from the discussion if its a right way to achieve what you want, you can use object association.
public final class ObjectAssociation<T: AnyObject> {
private let policy: objc_AssociationPolicy
/// - Parameter policy: An association policy that will be used when linking objects.
public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
self.policy = policy
}
/// Accesses associated object.
/// - Parameter index: An object whose associated object is to be accessed.
public subscript(index: AnyObject) -> T? {
get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
}
}
And in your extension
extension SomeType {
private static let association = ObjectAssociation<NSObject>()
var simulatedProperty: NSObject? {
get { return SomeType.association[self] }
set { SomeType.association[self] = newValue }
}
}
It is not possible to store Swift types via object association directly. You can store e.g. NSNumber instead of Int.
Source: https://stackoverflow.com/a/43056053/1811810
Say I have a protocol Item, and a struct ConcreteItem that conforms to it.
protocol Item {
var name: String { get }
}
struct ConcreteItem: Item {
let name: String
}
At some point I want to have two sets of ConcreteItem.
let set1 = Set([ConcreteItem(name: "item1")])
let set2 = Set([ConcreteItem(name: "item2"), ConcreteItem(name: "item1")])
Which I'd expect to return the item with name "item1".
I can make ConcreteItem conform to Hashable and the Set code will work. However, lets say I also had the following:
struct AnotherConcreteItem: Item {
let name: String
}
I'd like AnotherConcreteItem to also conform to Hashable simply for having conformed to Item.
However, when I try to implement that idea:
extension Item: Hashable {
var hashValue: Int {
return name.characters.count
}
}
I get the following error: Extension of protocol 'Item' cannot have an inheritance clause.
Extension of protocol 'Item' cannot have an inheritance clause
Here item is the protocol so conforming Hashable protocol will not work. For more details Refer here
What you are trying to do is possible with some protocols, but not all. If the protocol you are trying to conform to does not have any associated types or Self, this is possible.
Example:
protocol A {
func foo()
}
protocol Item: A {
var name: String { get }
}
struct ConcreteItem: Item {
let name: String
}
extension Item {
func foo() {
}
}
Everything that conforms to Item will also conform to A.
However, Hashable has Self constraints. To conform to Hashable, you must also conform to Equatable. To conform to Equatable, the implementation of == must be in a concrete class, not another protocol because Equatable can't be used as a parameter type. The most you can do is something like this:
protocol Item: Hashable {
var name: String { get }
}
struct ConcreteItem: Item {
let name: String
// you need to implement == in every concrete class
static func ==(lhs: ConcreteItem, rhs: ConcreteItem) -> Bool {
return ...
}
}
extension Item {
var hashValue: Int {
return name.characters.count
}
}
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.