Is there any syntax can make this work? I need a property can determine its type in the compile time.
protocol P {}
struct A: P {
var onlyAHas: String
}
struct B: P {
var onlyBHas: String
}
var ins1: any P = A()
var ins2: any P = B()
ins1.onlyAHas = "a only"
ins2.onlyBHas = "b only"
Before getting to the solution, let's break down what any means, and while we're at it, we'll include some as well:
When you write:
var ins1: any P = A()
You are telling the compiler that you want to use ins1 as P. It's the protocol oriented equivalent of this OOP code:
class Base {
var baseProperty: String? = nil
}
class Concrete: Base {
var concreteProperty: String? = nil
}
let obj: Base = Concrete();
obj.baseProperty = "Some value" // <-- This is fine
obj.concreteProperty = "Some value" // <-- This is an error
This code tells the compiler that obj is a Base. You can assign it from a Concrete, but because that's a subclass of Base, but obj is still known locally as a Base not as a Concrete, so it can't access the properties of Concrete that weren't inherited from Base.
It's the same in your example. ins1 is known locally as a P not as an A, and P doesn't have an onlyAHas property.
You'd get similar behavior with some instead of any. There are a few differences between the two, but let's just talk about the main one:
some tells the compiler that it will be a type that it can resolve to one specific concrete type, but that it should enforce the abstraction to the protocol in source code. This allows it to generate more efficient code internally, because knowing the concrete type allows the compiler to call the concrete's implementation directly instead of going through its protocol witness table, which is the protocol-oriented analog of a "vtable" in OOP, so the effect is like in OOP when the compiler devirtualizes a method call because despite the syntax, it knows the actual concrete type. This avoids the runtime overhead of dynamic dispatch while still letting you use the abstraction of the existential type... well it's more like it requires you to use the abstraction of the existential type than lets you, because from a source code point of view, the abstraction is enforced.
any also enforces the abstraction, but it goes the other way in terms of the kind of optimizations the compiler can do. It says that the compiler must go through the protocol witness table, because, as the keyword suggests, its value could be any concrete type that conforms to the protocol, even if the compiler could determine that it's actually just one specific type locally. It also allows relaxation of some rules regarding using the protocol as a type when it has Self and associatedtype constraints.
But either way, you are telling the compiler that you want to use ins1 as a P and not as an A.
The solutions
There are a few solutions, actually:
Downcasting
The first is to downcast to the concrete type, as was suggested in comments by Joakim Danielson:
if var ins1 = ins1 as? A {
ins1.onlyAHas = "a only"
}
Downcasting is a code smell, but sometimes is actually the clearest or simplest solution. As long as it's contained locally, and doesn't become a wide-spread practice for using instances of type, P, it might be fine.
However, that example does have one problem: A is a value type, so the ins1 whose onlyAHas property is being set is a copy of the original ins1 you explicitly created. Having the same name confuses it slightly. If you only need the change to be in effect in the body of the if, that works just fine. If you need it to persist outside, you'd have to assign back to the original. Using the same name prevents that, so you'd need to use different names.
Execute concrete-specific code only at initialization
This only applies if the concrete type just configures some things for the protocol up-front, and thereafter protocol-only code can be used:
var ins1: any P = A(onlyAHas: "a only")
// From here on code can only do stuff with `ins1` that is defined in `P`
Or your could delegate the initialization to a function that internally knows the concrete type, but returns any P.
func makeA(_ s: String) -> any P
{
var a = A()
a.onlyAHas = s
return a
}
var ins1 = makeA("a only");
// From here on code can only do stuff with `ins1` that is defined in `P`
Declare protocol methods/computed properties that do the work.
This is the usual way to use protocols. Declaring a method in the protocol is similar to declaring a method in a base class. Implementing the method in a conforming concrete type is like overriding the method in a subclass. If you don't also provide a default implementation in a protocol extension, the protocol will enforce that conforming types implement the protocol - which is a big advantage over the OOP approach.
protocol P {
mutating func setString(_ s: String)
}
struct A: P
{
var onlyAHas: String
mutating func setString(_ s: String) {
onlyAHas = s
}
}
struct B: P
{
var onlyBHas: String
mutating func setString(_ s: String) {
onlyBHas = s
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
I'm doing this with a setString method, but you could certainly use a computed variable in the protocol to do the same thing, and that would be more "Swifty." I didn't do that just to emphasize the more general idea of putting functionality in the protocol, and not get hung up on the fact that the functionality in question happens to be setting a property.
If you don't need all conforming types to be able to set a String, one solution is to provide a do-nothing default implmentation in an extension on P:
protocol P {
mutating func setString(_ s: String)
}
extension P
{
mutating func setString(_ s: String) { /* do nothing */ }
}
// Same A and B definitions go here
struct C: P { }
var ins3: any P = C();
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
ins3.setString("c only") // <- Calls setString from extension of P
Most often though, setting/getting some concrete property is an implementation detail of doing some task that varies with the concrete type. So instead, you'd declare a method in the protocol to do that task:
protocol P
{
mutating func frobnicate()
}
struct A
{
var onlyAHas: String
mutating func frobnicate()
{
// Do some stuff
onlyAHas = "a only"
// Do some other stuff that uses onlyAHas
}
}
B would be defined similarly doing whatever is specific to it. If the stuff in comments is common code, you could break it down into prologue, main action, and epilogue.
protocol P
{
mutating func prepareToFrobnicate()
mutating func actuallyFrobnicate() -> String
mutating func finishFrobnication(result: String)
}
extension P
{
/*
This method isn't in protocol, so this exact method will be called;
however, it calls methods that *are* in the protocol, we provide
default implementations, so if conforming types, don't implement them,
the versions in this extension are called, but if they do implement
them, their versions will be called.
*/
mutating func frobnicate()
{
prepareToFrobnicate()
finishFrobnication(result: actuallyFrobnicate());
}
mutating func prepareToFrobnicate() {
// do stuff general stuff to prepare to frobnicate
}
mutating func actuallyFrobnicate() -> String {
return "" // just some default value
}
mutating func finishFrobnication(result: String) {
// define some default behavior
}
}
struct A
{
var onlyAHas: String
mutating func actuallyFrobnicate() -> String
{
// Maybe do some A-specific stuff
onlyAHas = "a only"
// Do some more A-specific stuff
return onlyAHas
}
}
struct B
{
var onlyBHas: String
mutating func actuallyFrobnicate() -> String {
"b only"
}
mutating func finishFrobnication(result: String)
{
// Maybe do some B-specific stuff
onlyBHas = result
// Do some more B-specific stuff
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.frobnicate();
ins2.frobnicate();
In this example, the frobnicate in the protocol extension is called, because it's defined only in the protocol extension.
For ins1, frobnicate then calls the extension's prepareToFrobnicate, because even though it's declared directly in the protocol, A doesn't implement that and a default implementation is provided in the extension.
Then it calls A's actuallyFrobnicate because it's defined directly in the protocol, and A does implement it, so the default implementation isn't used. As a result the onlyAHas property is set.
Then it passes the result from A's actuallyFrobnicate to the finishFrobnication in the extension, because it's defined directly in the protocol, but A doesn't implement it, and the extension provides a default implementation.
For ins2, frobnicate still calls the default prepareToFrobnicate, and then call's B's implementation of actuallyFrobnicate, but B's implementation doesn't set its onlyBHas property there. Instead, it just returns a string, which frobnicate passes to finishFrobnication, which calls B's implementation, because unlike A, B provides its own implementation, and that's where B sets it.
Using this approach, you can simultaneously standardize the general algorithm of a task like frobnicate, while allowing for dramatically different implementation behavior. Of course, in this case, both A and B just set a property in their respective concrete types, but they do it at different phases of the algorithm, and you can imagine adding other code, so that the two effects really would be very different.
The point of this approach is that when we call inst1.frobnicate(), it doesn't know or care about exactly what inst1 is doing internally do accomplish it. The fact that it internally sets the onlyAHas property in the concrete type is an implementation detail the calling code doesn't need to be concerned with.
Just use the concrete type
In your code example, you are creating and using ins1, and ins2 in the same context. So they could just as easily be defined like this:
var ins1 = A()
var ins2 = B()
ins1.onlyAHas = "a only" // <- This is fine because ins1 is an A
ins2.onlyBHas = "b only" // <- This is fine because ins2 is a B
If you have some function, munge that you want to do on both A and B, you can define it terms of the protocol.
func munge(_ p: any P)
{
// In here you can only use `p` as defined by the protocol, `P`
}
If munge needs to do things that depend on concrete-specific properties or methods, you can use one of the previously described approaches...
OR...
If you know for sure that you only will ever have a small number of concrete types conforming to P, which admittedly is sometimes impossible to really know, but occasionally you do, then you can just write specialized overloaded versions of munge for each concrete type:
func munge(_ a: A) {
// Do `A`-specific stuff with `a`
}
func munge(_ b: B) {
// Do `B`-specific stuff with `b`
}
This kind of regresses to older solutions to problems like this. When I say it's an old solution, I'm referring to the fact that even back when the C++ compiler was just a preprocessor that converted C++ source code to C source code which would then be compiled, didn't have templates, and standardization wasn't even on the horizon, it would let you overload functions. You can do that with Swift too, and it's a perfectly valid solution. Sometimes it's even the best solution. More often it leads to code duplication, but it's in your toolbox to use when it's appropriate.
While reading swift forum about exceptions I found interesting issue. One of the examples about exceptions was something like this:
protocol Base {
func foo() throws -> Int
}
protocol Refined: Base {
func foo() -> Int
}
struct Test: Refined {
func foo() -> Int {
0
}
}
It's interesting, I thought it was typo that it would not compile, but it does. I am not sure how this does work behind the scenes. I mean when protocol adopts another protocol it adopts also its requirements. But in this case declaring same method without throws somehow satisfies also first protocol Base.
At the very least I expected Test to need to have 2 implementations of foo. What am I missing here?
This is because a non-throwing function is by definition a sub-type of a throwing function
From the Swift Programming Language book
The throws keyword is part of a function’s type, and nonthrowing functions are subtypes of throwing functions. As a result, you can use a nonthrowing function in the same places as a throwing one.
But you can't do it the other way around so the below code will generate an error
protocol Base {
func foo() -> Int
}
protocol Refined: Base {
func foo() throws -> Int //error: Cannot override non-throwing method with throwing method
}
Also note that this is not only for protocols, if you remove the func declaration from Refined you can still implement the function in Test as non throwing.
Let's assume I have protocol FooProtocol in Swift with one method with its default implementation:
protocol FooProtocol {
func foo()
}
extension FooProtocol {
func foo() {
print("protocol")
}
}
and class FooClass with cast instance:
class FooClass : FooProtocol {
func foo() {
print("class")
}
}
let A = FooClass() as FooProtocol
A.foo()
As expected, "class" will be printed in console.
However, if I make foo() method of my protocol being optional - "protocol" will be printed instead.
#objc protocol FooProtocol {
#objc optional func foo()
}
But if I call A.foo?() rather than A.foo() - "class" will be printed again.
I wonder, just for theoretical purposes, what the hell is happening here? Any explanations are appreciated.
You cannot call optional protocol method without optional chaining. I.e. if you comment out
extension FooProtocol {
func foo() {
...
and you try to call A.foo(), you will get a compilation error:
value of optional type '(() -> ())?' must be unwrapped to a value of type '() -> ()'
As you see the signature of optional is not even the same as non-optional. So by calling A.foo(), you are not calling method you implemented, you are calling default protocol implementation directly.
It also makes sense since primary reason for #objc optional is interpolation with Objective C, where default protocol implementation won't be visible. So relying on protocol function for objc function would produce different result on swift and objective c, which is undesired.
I cannot say if there's any loophole to use both optional and default protocol implementation on the same function. But question is: do you really need it?
If you are concerned with interpolation, you need equal solution for both Swift and Objective c, and that means you rather need basic protocol implementation, which both Swift and objc can inherit
If you need no interpolation, you don't really need optional, and can rely on default protocol implementation alone.
Assume the below playground:
protocol MyProtocol
{
func MyFunc()
}
class MyBase : MyProtocol
{
func MyFunc()
{
}
}
class MyClass : MyBase, MyProtocol
{
}
I was expecting this to not compile, and nag about not implementing my protocol. But unlike other languages, Swift seems to think its fine if the protocol conforms to the base class.
How can I get MyClass to force my method to be overridden?
I'm using Swift 1.1, tia.
Your code is definitely valid. MyClass inherits from MyBase which already conforms to MyProtocol, so having the subclass also conform to it is completely redundant. That being said, you are basically looking for an abstract function which does not really exist in Swift, the closest I think you can get to achieving what you want is recommended in this answer. So in your base class just give a warning if not overriden:
class MyBase : MyProtocol {
func MyFunc() {
fatalError("This method must be overridden")
}
}
As recommended in the question comments, a different approach may work better for what you are attempting to do (e.g. composition or using a struct), but it is a bit hard to recommend a different solution without knowing more about what you are trying to achieve (rather than just the expected solution). If you do provide additional details you may receive a more suitable answer.
I have to pass an interface as a parameter to a function. Interface is generic a.k.a. has a associated type. I couldn't find a good way to do that. Here is my code:
protocol IObserver : class {
typealias DelegateT
...
}
class Observer: IObserver {
typealias DelegateT = IGeneralEventsDelegate // IGeneralEventsDelegate is a protocol
...
}
func notify(observer: IObserver) { ... } // here I need a type for observer param
I found that this will work:
func notify<T: IObserver where T.DelegateT == IGeneralEventsDelegate>(observer: T) { ... }
, but come on that is too complicated. What if I want to save this param in class variable, should I make the whole class generic, just because of this function.
It is true that I'm C++ developer and I'm new to the Swift language, but the way the things are done are far too complicated and user unfriendly ... or I'm too stupid :)
If you use typealias in a protocol to make it generic-like, then you cannot use it as a variable type until the associated type is resolved. As you have probably experienced, using a protocol with associated type to define a variable (or function parameter) results in a compilation error:
Protocol 'MyProtocol' can only be used as a generic constraint because it has Self os associated type requirements
That means you cannot use it as a concrete type.
So the only 2 ways I am aware of to use a protocol with associated type as a concrete type are:
indirectly, by creating a class that implements it. Probably not what you have planned to do
making explicit the associated type like you did in your func
See also related answer https://stackoverflow.com/a/26271483/148357