Swift Protocol requiring a #Published Bool be defined - swift

I would like to declare a Swift protocol that requires conforming classes define a #Published Bool
class MyModel: ObservableObject, MyProtocol {
#Published var myBool: Bool = false
.
.
}
My best shot at this is,
protocol MyProtocol {
var _myBool: Binding<Bool> { get set }
}
...but the compiler complains, "Cannot find type 'Binding' in scope"
In my case, the class implementing the protocol will define the Bool, so I don't want a shared implementation (extension), I just need to require the class to have its own.
How can I achieve this?

Related

How to use class protocol as a generic? [duplicate]

I'd like to have a generic weak reference to an object and parametrize it by a protocol that is class-bound.
Here is the code example that does not work in my case:
protocol Protocol: class { // also written as `protocol Protocol: AnyObject {`
func function()
}
final class A: Protocol {
func function() {}
}
final class Weak<Type> where Type: AnyObject {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
let a = A()
let something = Weak<Protocol>(property: a) // error: 'Weak' requires that 'Protocol' be a class type
I get an error on last line: 'Weak' requires that 'Protocol' be a class type.
As Protocol will always be of class type (which is the same as AnyObject) shouldn't that be allowed by the compiler?
Is it possible to resolve that issue with swift 4?
If not, is it a limitation that can be resolved in a future version of swift or is it something impossible that the type system can not allow to happen?
A not accepted solution is to use #objc to the protocol declaration as in:
#obc protocol Protocol: class {
func function()
}
as this leads to limitations.
You are saying:
final class Weak<Type> where Type: AnyObject {
So you yourself are requiring that Type be a class (because that is exactly what AnyObject means).
A protocol is not a class. It is a protocol.
This is currently impossible because protocols do not conform to themselves. A protocol is not a type; it is a recipe for a type. As the simplest example of why this is hard, consider this situation:
protocol X {
init()
}
func f<T: X>(type: T.Type) -> T {
return type.init()
}
f(type: X.self)
This would create a value of type X, but would that be? This isn't allowed, and the mechanism that prevents it is that protocols do not conform to themselves. This crops up in a lots of other ways, and your example is one of them.
Some of this will be addressed when more of the Generics Manifesto is implemented, particularly the section Generalized Existentials, but currently what you want isn't possible. You can't have a "weak reference to some unknown thing." You need to know what that thing is.
The standard solution to this problem is a box, not a protocol. See Weak Arrays for an example.
If I understand your problem I design this solution
First I created a protocol called WeakProtocol, this will be our base of protocols
protocol WeakProtocol : AnyObject {}
After change your protocol to extends this new WeakProtocol
protocol Protocol: WeakProtocol {
func function()
}
after your Weak class you need apply a WeakProtocol type example
final class Weak<Type> where Type: WeakProtocol {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
Now to implement you don't need say it Protocol because the constructor receive the protocol.
Complete code is
protocol WeakProtocol : AnyObject {}
protocol Protocol: WeakProtocol {
func function()
}
final class A: Protocol {
func function() {}
}
final class Weak<Type> where Type: WeakProtocol {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
let a = A()
let something = Weak(property: a)
You can too only change Protocol to extends AnyObject and Weak class to Type:Protocol, like this
protocol Protocol: AnyObject {
func function()
}
final class A: Protocol {
func function() {}
}
final class Weak<Type> where Type: Protocol {
final private weak var property: Type?
init(property: Type) {
self.property = property
}
}
let a = A()
let something = Weak(property: a)

swift protocol conformance when same property name is optional

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.

PropertyWrappers and protocol declaration?

Using Xcode 11 beta 6, I am trying to declare a protocol for a type with properties using #Published (but this question can be generalized to any PropertyWrapper I guess).
final class DefaultWelcomeViewModel: WelcomeViewModel & ObservableObject {
#Published var hasAgreedToTermsAndConditions = false
}
For which I try to declare:
protocol WelcomeViewModel {
#Published var hasAgreedToTermsAndConditions: Bool { get }
}
Which results in a compilation error: Property 'hasAgreedToTermsAndConditions' declared inside a protocol cannot have a wrapper
So I try to change it into:
protocol WelcomeViewModel {
var hasAgreedToTermsAndConditions: Published<Bool> { get }
}
And trying
Which does not compile, DefaultWelcomeViewModel does not conform to protocol, okay, so hmm, I cannot using Published<Bool> then, let's try it!
struct WelcomeScreen<ViewModel> where ViewModel: WelcomeViewModel & ObservableObject {
#EnvironmentObject private var viewModel: ViewModel
var body: some View {
// Compilation error: `Cannot convert value of type 'Published<Bool>' to expected argument type 'Binding<Bool>'`
Toggle(isOn: viewModel.hasAgreedToTermsAndConditions) {
Text("I agree to the terms and conditions")
}
}
}
// MARK: - ViewModel
protocol WelcomeViewModel {
var hasAgreedToTermsAndConditions: Published<Bool> { get }
}
final class DefaultWelcomeViewModel: WelcomeViewModel & ObservableObject {
var hasAgreedToTermsAndConditions = Published<Bool>(initialValue: false)
}
Which results in the compilation error on the Toggle: Cannot convert value of type 'Published<Bool>' to expected argument type 'Binding<Bool>'.
Question: How can I make a protocol property for properties in concrete types using PropertyWrappers?
I think the explicit question you're asking is different from the problem you are trying to solve, but I'll try to help with both.
First, you've already realized you cannot declare a property wrapper inside a protocol. This is because property wrapper declarations get synthesized into three separate properties at compile-time, and this would not be appropriate for an abstract type.
So to answer your question, you cannot explicitly declare a property wrapper inside of a protocol, but you can create individual property requirements for each of the synthesized properties of a property wrapper, for example:
protocol WelcomeViewModel {
var hasAgreed: Bool { get }
var hasAgreedPublished: Published<Bool> { get }
var hasAgreedPublisher: Published<Bool>.Publisher { get }
}
final class DefaultWelcomeViewModel: ObservableObject, WelcomeViewModel {
#Published var hasAgreed: Bool = false
var hasAgreedPublished: Published<Bool> { _hasAgreed }
var hasAgreedPublisher: Published<Bool>.Publisher { $hasAgreed }
}
As you can see, two properties (_hasAgreed and $hasAgreed) have been synthesized by the property wrapper on the concrete type, and we can simply return these from computed properties required by our protocol.
Now I believe we have a different problem entirely with our Toggle which the compiler is happily alerting us to:
Cannot convert value of type 'Published' to expected argument type 'Binding'
This error is straightforward as well. Toggle expects a Binding<Bool>, but we are trying to provide a Published<Bool> which is not the same type. Fortunately, we have chosen to use an #EnvironmentObject, and this enables us to use the "projected value" on our viewModel to obtain a Binding to a property of the view model. These values are accessed using the $ prefix on an eligible property wrapper. Indeed, we have already done this above with the hasAgreedPublisher property.
So let's update our Toggle to use a Binding:
struct WelcomeView: View {
#EnvironmentObject var viewModel: DefaultWelcomeViewModel
var body: some View {
Toggle(isOn: $viewModel.hasAgreed) {
Text("I agree to the terms and conditions")
}
}
}
By prefixing viewModel with $, we get access to an object that supports "dynamic member lookup" on our view model in order to obtain a Binding to a member of the view model.
I would consider another solution - have the protocol define a class property with all your state information:
protocol WelcomeViewModel {
var state: WelcomeState { get }
}
And the WelcomeState has the #Published property:
class WelcomeState: ObservableObject {
#Published var hasAgreedToTermsAndConditions = false
}
Now you can still publish changes from within the viewmodel implementation, but you can directly observe it from the view:
struct WelcomeView: View {
#ObservedObject var welcomeState: WelcomeState
//....
}

How do you implement protocol methods that return covariant Selfs?

error: protocol 'Protocol' requirement 'instance' cannot be satisfied by a non-final class ('Class') because it uses 'Self' in a non-parameter, non-result type position
protocol Protocol {
var instance: Self {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Here is how I would express what I want, in C#. (C# does not, to my knowledge, have a way to enforce that the generic parameter "Self" is actually the Self we know from Swift, but it functions well enough as documentation that should make me do the right thing.)
interface Protocol<Self> where Self: Protocol<Self> {
Self instance {get;}
}
class Class: Protocol<Class> {
public Class instance {get {return new Subclass();}}
}
class Subclass: Class {}
…how that might look in a future version of Swift:
protocol Protocol {
typealias FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf
var instance: FinalSelf {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
How I'm emulating the portion of that which is relevant to my problem:
protocol Protocol: ProtocolInstance {
static var instance: ProtocolInstance {get}
}
protocol ProtocolInstance {}
class Class: Protocol {
static var instance: ProtocolInstance {return Subclass()}
}
class Subclass: Class {}
And, here is what I believe to be the relevant portion of my code:
protocol Protocol {
static var 🎁: Self? {get} // an existing instance?
static var 🐥: Self {get} // a new instance
func instanceFunc()
}
extension Protocol {
static func staticFunc() {
(🎁 ?? 🐥).instanceFunc()
}
}
As it says, you can't do this, and for good reason. You can't prove you'll keep your promise. Consider this:
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
So x should be AnotherSubclass according to your protocol (that's Self). But it'll actually be Subclass, which is a completely different type. You can't resolve this paradox unless the class is final. This isn't a Swift limitation. This limitation would exist in any correct type system because it allows an type contradiction.
On the other hand, something you can do is promise that instance returns some consistent type across all subclasses (i.e. the superclass). You do that with an associated type:
protocol Protocol {
typealias InstanceType
var instance: InstanceType {get}
}
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
class AnotherSubclass: Class {}
let x = AnotherSubclass().instance
Now x is unambiguously of type Class. (It also happens to be random other subclass, which is kind of weird, but that's what the code says.)
BTW, all of this usually suggests that you're using subclassing when you really shouldn't be. Composition and protocols would probably solve this problem better in Swift. Ask yourself if there's any reason that Subclass needs to actually be a subclass of Class. Could it be an independent type that conforms to the same protocol? All kinds of problems go away when you get rid of subclasses and focus on protocols.
I've been thinking about this more, and there may be a way to get what you're looking for. Rather than saying that all subclasses implement instance, attach instance as an extension. You can still override that if you want to return something else.
protocol Protocol {
init()
}
class Class: Protocol {
required init() {}
var instance: Class { return Subclass() }
}
extension Protocol {
var instance: Self { return self.dynamicType.init() }
}
class Subclass: Class {}
This dodges the inheritance problem (you can't create the same "AnotherClass returning the wrong type" this way).
This would actually make sense and work if you don't want to actually return Self for every subclass like this:
protocol Protocol : class {
typealias Sub : Self
var instance: Sub {get}
}
Which means that your protocol defines a typealias that has to be a subclass of itself. The following code would just work:
class Class: Protocol {
var instance: Class {return Subclass()}
}
class Subclass: Class {}
Class().instance // Returns SubClass()
However the code above doesn't compile with the error
error: inheritance from non-protocol, non-class type '`Self`'
which I think is a bug, because Self is declared as a class type. You can however make it somehow work like this:
protocol Protocol : class {
typealias Sub : Class
var instance: Sub {get}
}
but then you don't have much of the protocol itself because only the class itself should ever conform to it.

Swift protocol forcing the Equatable protocol

I have define 2 protocols.
I need the first one (NameProtocol) to enforce the Equatable protocol.
While the other class (BuilderProtocol) have a method that return the first one (NameProtocol).
public protocol NameProtocol : Equatable {
var name: String { get }
}
public protocol BuilderProtocol {
func build() -> NameProtocol? // Compiler error
init()
}
The compiler error :
"Protocol 'NameProtocol' can only be used as a generic constraint because it has Self or associated type requirements"
I need the object return by build() to return an object conforming to the NameProtocol and on which I can define ==
Is there a way I can make this work?
Thanks
If using a typealias in BuilderProtocol how can I make the array declaration work?
public protocol OtherRelatedProtocol {
var allNames : Array<NameProtocol> { get }
}
Conclusion
I will remove the Equatable and implement an isEqual method.
public protocol NameProtocol {
func isEqual(nameable: NameProtocol) -> Bool
var name: String { get }
}
If you're familiar with Java or C#, Swift protocols are about halfway between generics and interfaces. One thing that you can do in a protocol, for instance, is this:
protocol Foo {
func compareWith(foo: Self)
}
Classes that implement this protocol will have a method compareWith that accept an object of their own type (and not an object of type Foo).
This is what the compiler calls "Self or associated type requirements", and this is how Equatable is defined (it needs an operator== that accepts two Self operands). The downside of these protocols, though, is that you can only use them as generic constrains: you can't use them as an expression type.
The solution is to use generics. In this case, you'd make your ProtocolBuilder protocol generic, with a constraint that the type implements NameProtocol.
public protocol NameProtocol : Equatable {
var name: String { get }
}
public protocol BuilderProtocol {
typealias T: NameProtocol
func build() -> T?
init()
}