Escaping closure cannot capture a mutating self(protocol/extension) parameter in swift5 - swift5

I have tried to update this link rotating UIView in swift 5.
But I have one issue in updating.
This is error line :
UIView.animate(withDuration: 0.2)
{
self.transform = CGAffineTransform(rotationAngle: radians)
}

The error you are seeing is because:
the protocol Rotatable can be implemented by value (struct) types; and
the function mutating rotate(radians:, animated:) implemented in the protocol extension contains an escaping closure which captures self.
Value types are copied (at least effectively) on assignment, so if a closure captures a mutable value type which copy is being mutated? Early Swift had a compromise semantics which did the expected thing most of the time, but produced confusing results at other times. Swift has since imposed greater restrictions to address this, the early Swift change proposal Limiting inout capture to #noescape contexts explains the issues and was implemented in Swift 3.
The Rotatable protocol can be implemented by value types, but in the code you are converting it is only used for the reference type UIView and the code would have worked fine in Swift 2.
In current Swift one fix is to limit Rotatable to reference types:
protocol Rotatable : class { ... }
which also requires dropping all the uses of mutable – these are for value type compatibility. With those simple changes the code works.
Note: If the original author had implemented the protocol within the UIView extension (extension UIView: Rotatable { func rotate(... }), rather than in a seperate protocol extension (extension Rotatable { ... }) then the code would still have worked today without change.
If you wish to keep the protocol implementable by value types you can do so by moving the implementation into the UIView extension (you only have to move the single function rotate(radians:, animated:) if you wish as the remaining ones work for both value and reference types).
HTH

Related

Error "Inout argument could be set to a value with a type other than ..." when passing in a protocol type

I am coming from C++ to Swift. I have this situation with respect to protocols and structs (I am working with random numbers):
(1) A protocol RandomPr that specifies there are methods such as randFloat() that returns a Float between 0 and 1.
(2) A struct RandomS that implements RandomPr using a "real" random number generator.
(3) A struct FakeRandomS that implements RandomPr, but has additional methods such as loadFloat() to load an array of Floats that are then regurgitated when I call randFloat() on a FakeRandomS instance. (For testing purposes.)
Now I have a function DoSomething(rng: inout RandomPr), which I want to use with both RandomS and FakeRandomS. The parameter has to be in-out as I need to update the RNG (whether it is real or fake). No problem calling DoSomething with an instance of RandomS.
But if I do
var fakeRng = FakeRandomS()
fakeRng.loadFloat([0.1, 0.2, 0.3])
DoSomething(rng: &fakeRng)
I get an error "Inout argument could be set to a value with a type other than 'FakeRandomS'. The suggested fix is to define fakeRng as
var fakeRng: RandomPr = FakeRandomS()
But now trying to call loadFloat on fakeRng fails because RandomPr doesn't have a method loadFloat, and the compiler doesn't see that fakeRng does have one.
I tried making another protocol FakeRandomPr that contains the extra methods that FakeRandomS has, and defining
var fakeRng: RandomPr & FakeRandomPr = FakeRandomS()
but, frustratingly, I now get the "Inout argument could be set to a value with a type other than ..." error back again.
I could do the equivalent of this in C++ without problems (a pure abstract base class and two child classes, passed in by reference), and it didn't matter that one of the things I wanted to pass in has extra methods. I want to translate this into protocols and structs. What is the Swift solution?
What is the Swift solution?
You may need to use generics:
func doSomething<RP: RandomPr>(rng: inout RP) {
//...
}
Swift is not just another syntax of C++, better think in Swifty way.

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.

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

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 :)

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)

Non-'#objc' method does not satisfy optional requirement of '#objc' protocol

Overview:
I have a protocol P1 which provides a default implementation of one of the Objective-C optional functions.
When I provide a default implementation of the optional function there is a warning
Compiler Warning:
Non-'#objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '#objc' protocol 'UIAdaptivePresentationControllerDelegate'
Version:
Swift: 3
Xcode: 8 (public release)
Attempts made:
Tried adding #objc but doesn't help
Question:
How do I resolved this ?
Is there a work around ?
Code:
#objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
}
While I think I can answer your question, it's not an answer you will like.
TL;DR: #objc functions may not currently be in protocol extensions. You could create a base class instead, though that's not an ideal solution.
Protocol Extensions and Objective-C
First, this question/answer (Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c) seems to suggest that because of the way protocol extensions are dispatched under the hood, methods declared in protocol extensions are not visible to the objc_msgSend() function, and therefore are not visible to Objective-C code. Since the method you are trying to define in your extension needs to be visible to Objective-C (so UIKit can use it), it yells at you for not including #objc, but once you do include it, it yells at you because #objc is not allowed in protocol extensions. This is probably because protocol extensions are not currently able to be visible to Objective-C.
We can also see that the error message once we add #objc states "#objc can only be used with members of classes, #objc protocols, and concrete extensions of classes." This is not a class; an extension to an #objc protocol is not the same as being in the protocol definition itself (i.e. in requirements), and the word "concrete" would suggest that a protocol extension does not count as a concrete class extension.
Workaround
Unfortunately, this pretty much completely prevents you from using protocol extensions when the default implementations must be visible to Objective-C frameworks. At first, I thought perhaps #objc was not allowed in your protocol extension because the Swift Compiler could not guarantee that conforming types would be classes (even though you have specifically specified UIViewController). So I put a class requirement on P1. This did not work.
Perhaps the only workaround is to simply use a base class instead of a protocol here, but this is obviously not completely ideal because a class may only have a single base class but conform to multiple protocols.
If you choose to go this route, please take this question (Swift 3 ObjC Optional Protocol Method Not Called in Subclass) into account. It appears that another current issue in Swift 3 is that subclasses do not automatically inherit the optional protocol requirement implementations of their superclass. The answer to that questions uses a special adaption of #objc to get around it.
Reporting the Issue
I think this is being discussed already among those working on the Swift open source projects, but you could be sure they are aware by either using Apple's Bug Reporter, which would likely eventually make its way to the Swift Core Team, or Swift's bug reporter. Either of these may find your bug too broad or already known, however. The Swift team may also consider what you are looking for to be a new language feature, in which case you should first check out the mailing lists.
Update
In December 2016, this issue was reported to the Swift community. The issue is still marked as open with a medium priority, but the following comment was added:
This is intended. There is no way to add the implementation of the method to every adopter, since the extension could be added after the conformance to the protocol. I suppose we could allow it if the extension is in the same module as the protocol, though.
Since your protocol is in the same module as your extension, however, you may be able to do this in a future version of Swift.
Update 2
In February 2017, this issue was officially closed as "Won't Do" by one of the Swift Core Team members with the following message:
This is intentional: protocol extensions cannot introduce #objc entry points due to limitations of the Objective-C runtime. If you want to add #objc entry points to NSObject, extend NSObject.
Extending NSObject or even UIViewController will not accomplish exactly what you want, but it unfortunately does not look like it will become possible.
In the (very) long-term future, we may be able to eliminate reliance on #objc methods entirely, but that time will likely not come anytime soon since Cocoa frameworks are not currently written in Swift (and cannot be until it has a stable ABI).
Update 3
As of Fall 2019, this is becoming less of a problem because more and more Apple frameworks are being written in Swift. For example, if you use SwiftUI instead of UIKit, you sidestep the problem entirely because #objc would never be necessary when referring to a SwiftUI method.
Apple frameworks written in Swift include:
SwiftUI
RealityKit
Combine
CryptoKit
One would expect this pattern to continue over time now that Swift is officially ABI and module stable as of Swift 5.0 and 5.1, respectively.
I just ran into this after enabling 'module stability' (turning on 'Build libraries for distribution') in a swift framework I use.
What I had was something like this:
class AwesomeClass: LessAwesomeClass {
...
}
extension AwesomeClass: GreatDelegate {
func niceDelegateFunc() {
}
}
The function in the extension had these errors:
'#objc' instance method in extension of subclass of 'LessAwesomeClass' requires iOS 13.0.0
Non-'#objc' method 'niceDelegateFunc' does not satisfy requirement of '#objc' protocol 'GreatDelegate'
Moving the functions into the class rather than in an extension resolved the issue.
Here's another workaround. I ran into this issue as well, and cannot switch from UIKit to SwiftUI yet. Moving the default implementations into a common base class was not an option for me either. My default implementations were quite extensive so I really did not want to have all that code duplicated. The workaround I ended up using was to use wrapper functions in the protocol, and then simply call those functions from each class. Not pretty, but may be better than the alternative, depending on the situation. Your code would then look something like this:
#objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}
extension P1 where Self : UIViewController {
func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return UIViewController()
}
}
class A : UIViewController, P1 {
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
}
}