Swift - Conform third-party type to my own protocol with conflicting requirement - swift

Here's the boiled down situation:
Let's say a third-party framework written by Alice Allman provides a very useful class:
public class AATrackpad {
public var cursorLocation: AAPoint = .zero
}
and another framework written by Bob Bell provides a different class:
public class BBMouse {
public var where_is_the_mouse: BBPoint = .zero
}
At runtime either one of these classes may be needed depending on which piece of hardware the user has decided to use. Therefore, in keeping with the Dependency Inversion Principle, I do not want my own types to depend on AATrackpad or BBMouse directly. Rather, I want to define a protocol which describes the behaviors I need:
protocol CursorInput {
var cursorLocation: CGPoint { get }
}
and then have my own types make use of that protocol instead:
class MyCursorDescriber {
var cursorInput: CursorInput?
func descriptionOfCursor () -> String {
return "Cursor Location: \(cursorInput?.cursorLocation.description ?? "nil")"
}
}
I want to be able to use an instance of BBMouse as the cursor input, like this:
let myCursorDescriber = MyCursorDescriber()
myCursorDescriber.cursorInput = BBMouse()
but in order for this to compile I have to retroactively conform BBMouse to my protocol:
extension BBMouse: CursorInput {
var cursorLocation: CGPoint {
return CGPoint(x: self.where_is_the_mouse.x, y: self.where_is_the_mouse.y)
}
}
Now that I've conformed BBMouse to my CursorInput protocol, my code compiles and my architecture is the way I want it. The reason I have no problem here is that I think that where_is_the_mouse is a terrible name for that property, and I'm quite happy to never use that name again. However, with AATrackpad its a different story. I happen to think that Alice named her cursorLocation property perfectly, and as you can see I want to be able to use the same name for my protocol requirement. My problem is that AATrackpad does not use CGPoint as the type of this property, but instead uses a proprietary point type called AAPoint. The fact that my protocol requirement (cursorLocation) has the same name as an existing property of AATrackpad but a different type means that I can't retroactively conform to CursorInput:
extension AATrackpad: CursorInput {
var cursorLocation: CGPoint { // -- Invalid redeclaration
return CGPoint(x: self.cursorLocation.x, y: self.cursorLocation.y) // -- Infinite recursion
}
}
As the comments in that snippet say, this code does not compile, and even if it did I'd be facing an infinite recursion at runtime because I have no way to specifically reference the AATrackpad version of cursorLocation. It would be great if something like this would work (self as? AATrackpad)?.cursorLocation, but I don't believe this makes sense in this context. Again though, the protocol conformance won't even compile in the first place, so disambiguating in order to solve the infinite recursion is secondary.
With all of that context in mind, my question is:
If I architect my app using protocols (which is widely recommended, for good reason), is it really true that my ability to use a certain third-party concrete type depends on the hope this third-party developer doesn't share my taste for naming conventions?
NOTE: The answer "Just pick a name that doesn't conflict with the types you want to use" won't be satisfactory. Maybe in the beginning I only had BBMouse and had no conflicts, and then a year later I decided that I wanted to add support for AATrackpad as well. I initially chose a great name and it's now used pervasively throughout my app - should I have to change it everywhere for the sake of one new concrete type? What happens later on when I want to add support for CCStylusTablet, which now conflicts with whatever new name I chose? Do I have to change the name of my protocol requirement again? I hope you see why I'm looking for a more sound answer than that.

Inspired by Jonas Maier's comment, I found what I believe to be an architecturally adequate solution to this problem. As Jonas said, function overloading exhibits the behavior that I'm looking for. I'm starting to think that maybe protocol requirements should only ever be functions, and not properties. Following this line of thinking, my protocol will now be:
protocol CursorInput {
func getCursorLocation () -> CGPoint
func setCursorLocation (_ newValue: CGPoint)
}
(Note that in this answer I'm making it settable as well, unlike in the original post.)
I can now retroactively conform AATrackpad to this protocol without conflict:
extension AATrackpad: CursorInput {
func getCursorLocation () -> CGPoint {
return CGPoint(x: self.cursorLocation.x, y: self.cursorLocation.y)
}
func setCursorLocation (_ newValue: CGPoint) {
self.cursorLocation = AAPoint(newValue)
}
}
Important - This will still compile even if AATrackpad already has a function func getCursorLocation () -> AAPoint, which has the same name but a different type. This behavior is exactly what I was wanting from my property in the original post. Thus:
The major problem with including a property in a protocol is that it can render certain concrete types literally incapable of conforming to that protocol due to namespace collisions.
After solving this in this way, I have a new problem to solve: there was a reason I wanted cursorLocation to be a property and not a function. I definitely do not want to be forced to use the getPropertyName() syntax all across my app. Thankfully, this can be solved, like this:
extension CursorInput {
var cursorLocation: CGPoint {
get { return self.getCursorLocation() }
set { self.setCursorLocation(newValue) }
}
}
This is what is so cool about protocol extensions. Anything declared in a protocol extension behaves analogously to a default argument for a function - only used if nothing else takes precedence. Because of this different mode of behavior, this property does not cause a conflict when I conform AATrackpad to CursorInput. I can now use the property semantics that I originally wanted and I don't have to worry about namespace conflicts. I'm satisfied.
"Wait a second - now that AATrackpad conforms to CursorInput, doesn't it have two versions of cursorLocation? If I were to use trackpad.cursorLocation, would it be a CGPoint or an AAPoint?
The way this works is this - if within this scope the object is known to be an AATrackpad then Alice's original property is used:
let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint
However, if the type is known only to be a CursorInput then the default property that I defined gets used:
let cursorInput: CursorInput = AATrackpad()
type(of: cursorInput.cursorLocation) // This is CGPoint
This means that if I do happen to know that the type is AATrackpad then I can access either version of the property like this:
let trackpad = AATrackpad()
type(of: trackpad.cursorLocation) // This is AAPoint
type(of: (trackpad as CursorInput).cursorLocation) // This is CGPoint
and it also means that my use case is exactly solved, because I specifically wanted not to know whether my cursorInput happens to be an AATrackpad or a BBMouse - only that it is some kind of CursorInput. Therefore, wherever I am using my cursorInput: CursorInput?, its properties will be of the types which I defined in the protocol extension, not the original types defined in the class.
There is one possibility that a protocol with only functions as requirements could cause a namespace conflict - Jonas pointed this out in his comment. If one of the protocol requirements is a function with no arguments and the conforming type already has a property with that name then the type will not be able to conform to the protocol. This is why I made sure to name my functions including verbs, not just nouns (func getCursorLocation () -> CGPoint) - if any third-party type is using a verb in a property name then I probably don't want to be using it anyway :)

Related

What's the mechanics behind extension's methods overriding with '#objc' attribute?

Kind of nerd question. It's unclear to me, what exactly makes this code works:
class Shape { }
extension Shape {
#objc func redraw() {
print("from ext")
}
}
class Circle: Shape { }
class Line: Shape {
override func redraw() { // Compiler error: Declarations from extensions cannot be overridden yet
print("from subclass")
}
}
let line = Line()
let shape:Shape = line
let circle = Circle()
line.redraw() //from subclass
circle.redraw() //from ext
shape.redraw() //from subclass
If I omit #objc keyword in extension, the code won't compile - it's expected behaviour since methods in extension use static method dispatch -> cannot be overridden.
But why adding #objc makes it work? According to documentation and most articles, all is #objc doing is making things visible to Objective-c runtime. To change method dispatch type there is a special keyword - dynamic. But seems it is not necessary here!
Help me figure out, why is adding #objc (and omitting dynamic) makes such things possible.
Extensions,
as the name already says, are supposed to extend/add/include methods
to an existing implementation, making them one of the most beautiful
things about Objective-C, and now Swift, since you can add code to a
class or framework you do not own. Therefore, it makes sense that
you’re not supposed to “replace” code in extensions, conceptually
speaking.
That’s why the compiler complains when you try to do it.
Also Check out this answer.
however this seems to be a support issue too, as swift compiler simply throw this error:
overriding non-#objc declarations from extensions is not supported.
According to Apple,
Extensions can add new functionality to a type, but they cannot
override existing functionality.
But that is not the case, as we are overriding from the extension not vice versa,
which takes us back to the declaration of extension.
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling). Extensions are similar to categories in Objective-C. (Unlike Objective-C categories, Swift extensions do not have names.) Here.
Going back to the legacy topic swift compiler vs Objc compiler,
Dynamic dispatch vs. Static dispatch .
And there is no official documentation from apple on why this is not supported from the swift compiler or if they have any future plans to fix this or consider it an issue at all.
However, there’s no such thing as Swift dynamic dispatch; we only have
the Objective-C runtime’s dynamic dispatch. That means you can’t have
just dynamic and you must write #objc dynamic. So this is effectively
the same situation as before, just made explicit.
And here is a great article talking about this topic deeply.

Why does the generic type not participate in method overloading via constraint further down the call chain?

Swift allows generic methods to be overloaded by the constraints placed upon the generic types passed in. If you use this with concrete types, then the type passed in will participate in this overloading and constraints will be inferred from that type.
As soon as a generic method delegates to another with overload resolution, the constraints can no longer be inferred and will instead utilize the constraints already placed on the type from above.
protocol Conformance {}
extension String : Conformance {}
// #1
func baseMethod<T>(_ value: T) {
let isConforming = T.self is Conformance.Type
}
// #2
func baseMethod<T>(_ value: T) where T : Conformance {
let isConforming = T.self is Conformance.Type
}
func delegatingMethod<T>(_ value: T) {
baseMethod(value)
}
func run() {
// Calls #2, isConforming = true
baseMethod(String())
// Calls #1, isConforming = true
delegatingMethod(String())
}
I assume this is there so that you have sufficient type information from the call site about what constraints are applicable no matter where the generic type is used, but it seems to severely and artificially limit the utility of overloading by constraint.
Are there any known workarounds to this oddity? Something that emulates this would be extremely useful.
Swift allows generic methods to be overloaded by the constraints placed upon the generic types passed in.
Yes...but be very clear that this is a static overload, not a dynamic override. It is based on types that can be proven at compile-time.
func delegatingMethod<T>(_ value: T) {
baseMethod(value)
}
We're compiling this now, and we need to write it as a concrete, static function call, possibly inlined, into the binary. What do we know about T? We know nothing about T, so any where clause will fail.
We don't even know about how this function is called, because the call may come from another compile unit or module. While in principle, it could have different semantics based on access level, such that one version were used when it is private and all calls can be evaluated, and another used when it's public, that would be a really horrible source of bugs.
What you're asking for is that delegatingMethod defer its decision about what function call to make until runtime. That's not how generics work. Moreover, you're asking that all the where clauses be encoded somewhere in the binary so that they can be evaluated at runtime. Also not how generics work. That would require a much more dynamic dispatch system than Swift wants to implement. It's not impossible; it's just a completely different animal, and prevents lots of optimizations.
This feels like you're trying to reinvent class inheritance with protocols and generics. You can't. They're different solutions and have different features. Class inheritance is fundamentally dynamic. Protocols and generics are fundamentally static. If you want dynamic dispatch based on specific types, use classes.

In swift, why can't I instantiate a protocol when it has an initialiser?

I understand that generally I cannot instantiate a protocol.
But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later, it will have an init which it can use?
My code is as below and line:
protocol Solution {
var answer: String { get }
}
protocol Problem {
var pose: String { get }
}
protocol SolvableProblem: Problem {
func solve() -> Solution?
}
protocol ProblemGenerator {
func next() -> SolvableProblem
}
protocol Puzzle {
var problem: Problem { get }
var solution: Solution { get }
init(problem: Problem, solution: Solution)
}
protocol PuzzleGenerator {
func next() -> Puzzle
}
protocol FindBySolvePuzzleGenerator: PuzzleGenerator {
var problemGenerator: ProblemGenerator { get }
}
extension FindBySolvePuzzleGenerator {
func next() -> Puzzle {
while true {
let problem = problemGenerator.next()
if let solution = problem.solve() {
return Puzzle(problem: problem, solution: solution)
}
}
}
}
The line:
return Puzzle(problem: problem, solution: solution)
gives error: Protocol type 'Puzzle' cannot be instantiated
Imagine protocols are adjectives. Movable says you can move it, Red says it has color = "red"... but they don't say what it is. You need a noun. A Red, Movable Car. You can instantiate a Car, even when low on details. You cannot instantiate a Red.
But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later, it will have an init which it can use?
Protocols must be adopted by classes, and there might be a dozen different classes that all adopt your Puzzle protocol. The compiler has no idea which of those classes to instantiate.
Protocols give us the power to compose interfaces without the complexity of multiple inheritance. In a multiple inheritance language like C++, you have to deal with the fact that a single class D might inherit from two other classes, B and C, and those two classes might happen to have methods or instance variables with the same name. If they both have a methodA(), and B::methodA() and C::methodA() are different, which one do you use when someone call's D's inherited methodA()? Worse, what if B and C are both derived from a common base class A? Protocols avoid a lot of that by not being directly instantiable, while still providing the interface polymorphism that makes multiple inheritance attractive.
I understand that I can't do it - I just want to understand why the
compiler can't do it?
Because protocols in Swift represent abstraction mechanism. When it comes to abstraction, you could think about it as a template, we don't have to care about the details of how it behaves or what's its properties; Thus it makes no sense to be able to create an object from it.
As a real world example, consider that I just said "Table" (as an abstracted level), I would be pretty sure that you would understand what I am talking about! nevertheless we are not mentioning details about it (such as its material or how many legs it has...); At some point if I said "create a table for me" (instantiate an object) you have the ask me about specs! and that's why the complier won't let you create object directly from a protocol. That's the point of making things to be abstracted.
Also, checking: Why can't an object of abstract class be created? might be helpful.
Unfortunately swift does not allow that even with such "hack"
You would need to use a class that confirms to that protocol as an object you refer to.
When you instantiate an object, the Operating System has to know how to allocate and deal with that kind of object in the memory: Is it a reference type (Classes)? Strong, weak or unowned reference? Or is it a value type (Structs, Strings, Int, etc)?
Reference types are stored in the Heap, while value types live in the Stack. Here is a thorough explanation of the difference between the two.
Only Reference and Value types (objects) can be instantiated. So, only the objects that conform to that protocol can then be instantiated, not the protocol itself. A protocol is not an object, it is a general description or schema of a certain behavior of objects.
As to Initialization, here what the Apple docs say:
Initialization is the process of preparing an instance of a class,
structure, or enumeration for use. This process involves setting an
initial value for each stored property on that instance and performing
any other setup or initialization that is required before the new
instance is ready for use.

Why must Protocol defaults be implemented via Extensions in Swift?

In Swift, you cannot define default implementations of functions or properties in the Protocol definition itself, i.e.:
protocol Container {
//These are fine
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get set }
subscript(i: Int) -> Item { get }
//These are not fine
var defaultValue: Int = 10
mutating func messWithCount(){
self.count *= 10
}
}
extension Container {
//This is fine though
mutating func messWithCount(){
self.count *= 10
}
}
But you could do so via the Extensions (although Extensions do not support stored properties, they support functions and computed ones - although the stored property issue can be worked around).
What is the explanation behind this? As an add along, what is the explanation for optional func being only implementable if we mark both the Protocol and the func as #objc and hence rendering it unusable for Structs/Enums (which are Value not Reference based)?
EDIT: Added in Extension example
The #optional directive is an Objective-C only directive and has not been translated to Swift. This doesn't mean that you can't use it in Swift, but that you have to expose your Swift code to Objective-C first with he #objc attribute.
Note that exposing to Obj-C will only make the protocol available to types that are in both Swift and Obj-C, this excludes for example Structs as they are only available in Swift!
To answer your first question, Protocols are here to define and not implement:
A protocol defines a blueprint of methods, properties, and other requirements [...]
And the implementation should thus be supplied by the class/stuct/enum that conforms to it:
The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements
This definition really applies to Protocols we use in our daily lives as well. Take for example the protocol to write a Paper:
The PaperProtocol defines a paper as a document with the following non-nil variables:
Introduction
Chapters
Conclusion
What the introduction, chapters and conclusion contain are up to the one implementing them (the writer) and not the protocol.
When we look at the definition of Extensions, we can see that they are here to add (implement) new functionalities:
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code.
So extending a protocol (which is allowed) gives you the possibility to add new functionality and hereby give a default implementation of a defined method. Doing so will work as a Swift only alternative to the #optional directive discussed above.
UPDATE:
While giving a default implementation to a protocol function in Switch does make it "optional", it is fundamentally not the same as using the #optional directive in Objective-C.
In Objective-C, an optional method that is not implemented has no implementation at all so calling it would result in a crash as it does not exist. One would thus have to check if it exists before calling it, in opposition to Swift with an extension default where you can call it safely as a default implementation exists.
An optional in Obj-C would be used like this:
NSString *thisSegmentTitle;
// Verify that the optional method has been implemented
if ([self.dataSource respondsToSelector:#selector(titleForSegmentAtIndex:)]) {
// Call it
thisSegmentTitle = [self.dataSource titleForSegmentAtIndex:index];
} else {
// Do something as fallback
}
Where it's Swift counterpart with extension default would be:
let thisSegmentTitle = self.dataSource.titleForSegmentAtIndex(index)

Design pattern for Swift using a protocol as a delegate

updated Clarifying question to make clear this is an issue with a protocol that has a typealias, causing the general error of can only be used as a generic constraint.
I have the following class/protocol pattern:
protocol Storage { /* ... */ }
protocol StorageView {
typealias StorageType: Storage
/* ... */
}
class StorageColumnView<StorageType:Storage>: StorageView { /* ... */ }
class SomeStorage: Storage { /* ... */ }
and I want to define a class that combines my Storage class with View class. Ideally, it would look something like:
class MyClass<S:StorageType> {
var view:ViewType<S>
}
This won't compile because you can't specify a variable's type based on a protocol. After searching around, the general answer I found was to use type-erasure and make a AnyView class. However, such an approach seems cumbersome for a single variable (in theory this is the only place I'll use it) and difficult because StorageView has enough functionality to make wrapping each variable time consuming. Additionally, the methods of the view may get called a decent amount (yes, premature optimization is the root of all evil, but its subscripts will be called in loops), so I'm worried about the overhead.
Three alternative methods I'm currently investigating are:
(1) Declaring view as AnyObject, and then casting it to the correct type:
var view:AnyObject
// ...
view = StorageColumnView(/*...*/)
// ...
if let v = view as? StorageView {
// operate on v
}
(2) Treating view as a function, and letting the type be defined using a closure:
var view: () -> StorageView
// ...
view = { return StorageColumnView(self) }
/// ...
view().doX()
(3) Parameterizing MyClass by the ViewType rather than Storage:
class MyClass<V:ViewType> {
typealias StorageType = ViewType.StorageType
}
I'm trying to evaluate which of the 3 options is best (in terms of Swift style as well as speed), or if there is another alternative I'm not aware of (or I really should just use type-erasure to solve this problem -- though it seems like AnyObject is essentially just that).
So:
Are there any major penalties for the first approach? Is this closer to c++'s static_cast or dynamic_cast?
Is there a way to make the closure approach a little more user-friendly (i.e. I rather not require the user to actually pass in a closure, but rather the type). Maybe create a helper function that is a generic that then returns the type?
While the last solution is potentially the cleanest in amount of extra code required, it also requires a design that is against what I'm trying to do. The ViewType is really supposed to act like a delegate, and be fungible. Instead, I'm not creating a specific type of MyClass based on the ViewType.
Any and all opinions welcome on the best design pattern! I'm a little surprised that making a delegate-type pattern is so difficult (assuming I'm doing things correctly), considering that is primarily how Objective-C is used in Cocoa.
Also, does anyone know the rationale for not letting a variable to be defined as a protocol type that has a typealias? What's the underlying difference between a protocol with and without a Self?