Deprecating protocols for Swift 3 upgrade - swift

I have an iOS framework that I am upgradding to Swift 3. I would like the API's method signatures to follow the Swift 3 conventions of using a first named parameter for methods while maintaining backward compatibility. It's easy enough to add new API method signatures and deprecate the old ones. But what is the best way to handle this with protocols that are used in delegates?
API for Swift 2.x:
#objc(FooManager)
public class FooManager {
public var delegate: FooManagerDelegate?
public func saveFoo(foo: Foo) {
...
delegate?.didSaveFoo(foo)
}
...
}
#objc public protocol FooManagerDelegate {
#objc optional func didSaveFoo(foo: Foo)
}
New API for Swift 3.x:
#objc(FooManager)
public class FooManager {
public var delegate: FooManagerDelegate?
#available(*, deprecated, message: "use didSave(foo: foo)")
public func saveFoo(foo: Foo) {
...
delegate?.didSaveFoo(foo)
}
public func save(foo: Foo) {
...
delegate?.didSave(foo: foo)
}
...
}
#objc public protocol FooManagerDelegate {
#objc optional func didSaveFoo(foo: Foo)
#objc optional func didSave(foo: Foo)
}
The above solution will work, but it won't give any deprecation warnings to users for continuing to use the old delegate methods. I could create a new delegate and deprecate the old delegate class, but then I end up with having to have non-standard delegate class and property naming. I'd hate to have my FooManager look ugly like this:
public class FooManager {
#available(*, deprecated, message: "use swift3delegate")
public var delegate: FooDelegate?
public var swift3delegate: Swift3FooDelegate?
Are there any better solutions for migrating users to new protocol method signatures while maintaining backward compatibility?

Exactly what you are asking for is not possible in Swift (nor Objective-C?) to my knowledge. Quoting a response to a related question:
The primary problem with throwing a deprecation warning on any class which conforms to MyProtocol and implemented myOldFunction() is that there's nothing wrong with classes implementing functions and properties that are not part of your protocol.
That is, that the protocol's method is deprecated wouldn't necessarily mean that the method blueprint is universally to be avoided, it could just mean that for the purposes of conformance to that protocol, the method or property in question is now deprecated.
I totally see the point for this and I'd like this feature too, but to my knowledge Swift 3 at least does not offer it (neither does Objective-C to my knowledge).
One solution for this would be to deprecate the entire protocol, and produce a new protocol you need to declare conformance to in your Swift 3 code. So, this works:
#available(*, deprecated, message="use ModernX instead")
protocol X {}
class A: X {}
… and in your ModernX protocol simply include all the methods except for the deprecated method(s). Using a base protocol without the deprecated method could make this slightly less clunky, but it is a pretty boilerplate heavy workaround for sure:
protocol BaseX {
func foo()
func bar()
}
#available(*, deprecated, message: "Use ModernX instead")
protocol X: BaseX {
func theDeprecatedFunction()
}
protocol ModernX: BaseX {
func theModernFunction()
}
// you'll get a deprecation warning here.
class A: X {
func foo() {}
func bar() {}
func theDeprecatedFunction() {
}
}

Related

Emitting a warning for a deprecated Swift protocol method in implementing types

Suppose I have a protocol with a bar() method that has a default implementation — essentially the Swift way of making a protocol requirement optional for implementing types:
protocol Foo {
func bar()
}
extension Foo {
func bar() {
print("default bar() implementaion")
}
}
Now suppose that I decide to rename that method barrrr(), because more rs are better:
protocol Foo {
func barrrr()
}
extension Foo {
func barrrr() {
print("default barrrr() implementaion")
}
}
Existing code may still implement the method with the old name:
class Thinger: Foo {
func bar() {
print("custom bar() implementaion")
}
}
This code thinks it’s customizing a Foo method, but it isn’t. Callers will look for barrrr(), and get the default implementation. Nobody calls bar().
I would therefore like to generate a warning for types that implement Foo and have a bar() method. This does not work:
protocol Foo {
func barrrr()
#available(*, deprecated: 0.99, renamed: "barrrr()")
func bar()
}
extension Foo {
func barrrr() {
print("default barrrr() implementaion")
}
func bar() {
fatalError("renamed barrrr()")
}
}
class Thinger: Foo {
func bar() { // No warning here!
print("custom bar() implementaion")
}
}
Making the extension method final has no effect. Is there a way to do it? It needn’t be a friendly warning; any compiler error would suffice.
If I understand the question correctly, I don't believe there is a way to do this--nor should there be.
You have a protocol, let's call it MyProtocol...
protocol MyProtocol {
func myOldFunction()
}
extension MyProtocol {
func myOldFunction() {
print("doing my old things")
}
}
And in a later release, you would like to rename a function within your protocol.
protocol MyProtocol {
func myOldFunction() // now deprecated
func myNewFunction()
}
The primary problem with throwing a deprecation warning on any class which conforms to MyProtocol and implemented myOldFunction() is that there's nothing wrong with classes implementing functions and properties that are not part of your protocol.
These methods or properties might still be perfectly valid implementations used by other things that don't care about your protocol.
Consider that nothing would stop us from doing this:
protocol ViewControllerDeprecations {
func viewDidLoad() // mark deprecated
func viewWillAppear(Bool) // mark deprecated
// etc., for all the life cycle methods
}
extension UIViewController: ViewControllerDeprecations {}
Now every UIViewController subclass everywhere in any app that contains the file with the above code has these silly deprecation warnings all over it just because your particular protocol no longer uses these methods.
Without taking a position on whether it is a good thing, as suggested by another answer, exactly what you are asking for is not possible at least in Swift 3.
However, as I pointed out in response to a related question, it is possible to deprecate an entire protocol, and doing so would at least give you deprecation warnings at the implementing type level, if not precisely at the wanted property / method level.

Default Implementation of Objective-C Optional Protocol Methods

How can I provide default implementations for Objective-C optional protocol methods?
Ex.
extension AVSpeechSynthesizerDelegate {
func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
print(">>> did finish")
}
}
Expectation: Whatever class that conforms to AVSpeechSynthesizerDelegate should run the above function whenever a speech utterance finishes.
You do it just exactly as you've implemented it. The difference ends up being in how the method is actually called.
Let's take this very simplified example:
#objc protocol FooProtocol {
optional func bar() -> Int
}
class Omitted: NSObject, FooProtocol {}
class Implemented: NSObject, FooProtocol {
func bar() -> Int {
print("did custom bar")
return 1
}
}
By adding no other code, I'd expect to have to use this code as such:
let o: FooProtocol = Omitted()
let oN = o.bar?()
let i: FooProtocol = Implemented()
let iN = i.bar?()
Where oN and iN both end up having type Int?, oN is nil, iN is 1 and we see the text "did custom bar" print.
Importantly, not the optionally chained method call: bar?(), that question mark between the method name in the parenthesis. This is how we must call optional protocol methods from Swift.
Now let's add an extension for our protocol:
extension FooProtocol {
func bar() -> Int {
print("did bar")
return 0
}
}
If we stick to our original code, where we optionally chain the method calls, there is no change in behavior:
However, with the protocol extension, we no longer have to optionally unwrap. We can take the optional unwrapping out, and the extension is called:
The unfortunate problem here is that this isn't necessarily particularly useful, is it? Now we're just calling the method implemented in the extension every time.
So there's one slightly better option if you're in control of the class making use of the protocol and calling the methods. You can check whether or not the class responds to the selector:
let i: FooProtocol = Implemented()
if i.respondsToSelector("bar") {
i.bar?()
}
else {
i.bar()
}
This also means you have to modify your protocol declaration:
#objc protocol FooProtocol: NSObjectProtocol
Adding NSObjectProtocol allows us to call respondsToSelector, and doesn't really change our protocol at all. We'd already have to be inheriting from NSObject in order to implement a protocol marked as #objc.
Of course, with all this said, any Objective-C code isn't going to be able to perform this logic on your Swift types and presumably won't be able to actually call methods implemented in these protocol extensions it seems. So if you're trying to get something out of Apple's frameworks to call the extension method, it seems you're out of luck. It also seems that even if you're trying to call one or the other in Swift, if it's a protocol method mark as optional, there's not a very great solution.

How to call static methods on a protocol if they are defined in a protocol extension?

protocol Car {
static func foo()
}
struct Truck : Car {
}
extension Car {
static func foo() {
print("bar")
}
}
Car.foo() // Does not work
// Error: Car does not have a member named foo
Truck.foo() // Works
Xcode autocompletes the Car.foo() correctly, so what i'm asking is if its a bug that it doesn't compile (says it does not have a member named foo()). Could you call static methods directly on the protocol if they are defined in a protocol extension?
Apple doc
Protocols do not actually implement any functionality themselves.
Nonetheless, any protocol you create will become a fully-fledged type
for use in your code.
Therefore, you cannot call static methods directly of protocol.
No, the error message isn't good, but it's telling you the correct thing.
Think of it this way, you can't have
protocol Car {
static func foo() {
print("bar")
}
}
This compiles with the error "Protocol methods may not have bodies".
Protocol extensions don't add abilities to protocols which don't exist.

Final self class for generics, in method signature, in Swift

I have a BaseObject model that defines common behaviour I want to share across all my data entities. It has a method like this:
class BaseObject {
static func fetch(block: ((Results<BaseObject>) -> Void)) {
// networking code here
}
}
Naturally, I need the signature of this method be dynamic enough so that a model of class Products
class Products: BaseObject { //...
yields a Results<Product> list instead of Results<BaseObject>. I don't want to re-implement the fetch method in every subclass, because, save for the exact final subclass name used in the body and in the signature, it would be identical.
I cannot use Self:
Do I have any options at all?
You can now do this as of Swift 2.0 as it allows default implementations of methods in protocols. To do so, you would make your base class a protocol, and use Self, as you tried in your example.
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID521
Edit:
This compiles in Swift 2.0 / Xcode 7.0 Beta:
class Results<T> {
}
protocol BaseObject {
static func fetch(block: ((Results<Self>) -> Void))
}
extension BaseObject {
static func fetch(block: ((Results<Self>) -> Void)) {
// networking code here
}
}
This feature is only available in Swift 2.0, to my knowledge, there is no solution in Swift 1.2 or previous.

What's the difference between a protocol extended from AnyObject and a class-only protocol?

Both this declaration
protocol SomeProtocol : AnyObject {
}
and this declaration
protocol SomeProtocol : class {
}
seem to make it so that only classes can conform to this protocol (i.e. that the instances of the protocol are references to objects), and have no other effects.
Is there any difference between them? Should one be preferred over the other? If not, why is there two ways to do the same thing?
I am using the latest released Xcode 6.3.1.
This was answered by an official Swift developer (Slava_Pestov) on the Swift forums. Here is the summary:
You should use AnyObject (protocol SomeProtocol: AnyObject).
AnyObject and class are equivalent. There is no difference.
class will eventually be deprecated.
Regarding the answer https://forums.swift.org/t/class-only-protocols-class-vs-anyobject/11507/4, this answer is deprecated. These words are the same now.
DEPRECATED
Update: After consulting with the powers that be, the two definitions are supposed to be equivalent, with AnyObject being used as a stand-in while class was being finished. In the future the latter will obviate the former but, for now, they do present a few minor differences.
The difference lies in the semantics of #objc declarations. With AnyObject, the expectation is that conforming classes may or may not be proper Objective-C objects, but the language treats them as such anyway (in that you lose static dispatch sometimes). The takeaway from this is that you can treat an AnyObject et al. protocol constraint as a way to ask for #objc member functions as shown in the example in documentation for AnyObject in the STL:
import Foundation
class C {
#objc func getCValue() -> Int { return 42 }
}
// If x has a method #objc getValue()->Int, call it and
// return the result. Otherwise, return nil.
func getCValue1(x: AnyObject) -> Int? {
if let f: ()->Int = x.getCValue { // <===
return f()
}
return nil
}
// A more idiomatic implementation using "optional chaining"
func getCValue2(x: AnyObject) -> Int? {
return x.getCValue?() // <===
}
// An implementation that assumes the required method is present
func getCValue3(x: AnyObject) -> Int { // <===
return x.getCValue() // x.getCValue is implicitly unwrapped. // <===
}
The same example falls over immediately if you change that to a class-deriving protocol:
import Foundation
protocol SomeClass : class {}
class C : SomeClass {
#objc func getCValue() -> Int { return 42 }
}
// If x has a method #objc getValue()->Int, call it and
// return the result. Otherwise, return nil.
func getCValue1(x: SomeClass) -> Int? {
if let f: ()->Int = x.getCValue { // <=== SomeClass has no member 'getCValue'
return f()
}
return nil
}
// A more idiomatic implementation using "optional chaining"
func getCValue2(x: SomeClass) -> Int? {
return x.getCValue?() // <=== SomeClass has no member 'getCValue'
}
// An implementation that assumes the required method is present
func getCValue3(x: SomeClass) -> Int { // <===
return x.getCValue() // <=== SomeClass has no member 'getCValue'
}
So it seems class is a more conservative version of AnyObject that should be used when you only care about reference semantics and not about dynamic member lookups or Objective-C bridging.
In the Swift programming language guide for protocols, under the Class-Only Protocols section. It only mentioned AnyObject, but not class.
You can limit protocol adoption to class types (and not structures or enumerations) by adding the AnyObject protocol to a protocol’s inheritance list.
protocol SomeClassOnlyProtocol: AnyObject, SomeInheritedProtocol {
// class-only protocol definition goes here
}
For that reason, I will suggest using AnyObject over class for new code or new project. Other than that, I don't see any obvious difference between them.
From 2021, Xcode 12.5, Big Sur OS:
Usage of class is deprecated by apple.
Use AnyObject instead.
Happy Coding.
AnyObject is a protocol to which all classes implicitly conform (source). So I would say there is no difference: you can use either to require class constraint.
If you open the help (alt-click) in Xcode 9 for class in a line such as protocol P: class {}, you will get typealias AnyObject.
Thus, the code compiled (in Swift 4) will be the same whether you constrain the protocol to class or AnyObject.
That said, there is also the question of style and future options — a future Swift version might want to treat class and AnyObject differently in some subtle way, even if that is not the case right now.
(Edit: This has finally happened in Swift 5.4/Xcode 12.5.)
I misspoke before. #MartinR should really answer this, since he's the one who corrected me and provided the correct information.
The real difference is that a protocol with the class qualifier can only be applied to a class, not a struct or enum.
Martin, why don't you answer and the OP can accept your answer?