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

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.

Related

How to implement custom implementation of method?

I'm drawing a blank for some reason.. If I want to make a bunch of objects from a class, but I want each instance to have its own unique implementation of a certain method, how would I do this?
For example:
class MyClass {
var name: String
func doSomething() {
// Each object would have custom implementation of this method, here.
}
}
Do I provide each object with its own closure during initialization, and then call that closure in the doSomething() method? I'm trying to figure out the correct or "Swiftly" way to do this. I'm also thinking along the lines of something with protocols, but I can't seem to figure out how to go about this.
I think there're many ways to do it.
In case of Base class + some sub-classes (e.g. Animal, subclassed by Dog, Cat, etc), you can do this:
First of all it's a good idea to define a protocol:
protocol MyProtocol {
func doSomething()
}
Also provide a default implementation, which throws a fatal error if a class doesn't override that method:
extension MyProtocol {
func doSomething() {
fatalError("You must override me")
}
}
Now your base class confirms the protocol thanks to default implementation. But it will throw a fatal error at runtime:
class MyClass: MyProtocol {
// conformant
}
Child class, however, will run correctly as long as it overrides this function:
class MyOtherClass: MyClass {
func doSomething() {
print("Doing it!")
}
}
You could also move fatal error into base class, and not do any extension implementation.
In case of many instances of the same Class, that solution makes no sense. You can use a very simple callback design:
typealias MyDelegate = () -> Void
class MyClass {
var delegate: MyDelegate?
func doSomething() {
delegate?()
}
}
let x = MyClass()
x.delegate = {
print("do it!")
}
x.doSomething()
// Or you can use a defined function
func doIt() {
print("also doing it")
}
x.delegate = doIt
x.doSomething()
It can also be that you re looking for Strategy pattern, or Template pattern. Depends on your usage details.
Do I provide each object with its own closure during initialization, and then call that closure in the doSomething() method
Yes. That is extremely common and eminently Swifty. Incredibly miminalistic example:
struct S {
let f:()->()
func doYourThing() { f() }
}
let s = S { print("hello") }
let s2 = S { print("goodbye" )}
s.doYourThing() // hello
s2.doYourThing() // goodbye
Giving an object a settable method instance property is very, very easy and common. It doesn't have to be provided during initialization — you might set this property later on, and a lot of built-in objects work that way too.
That, after all, is all you're doing when you create a data task with dataTask(with:completionHandler:). You are creating a data task and handing it a function which it stores, and which it will call when it has performed the actual networking.

Difference when declaring swift protocol using inheritance from another protocol or using where Self

I still don't understand what is the difference when declaring a Swift protocol using inheritance:
protocol SubProtocol: SuperProtocol { ... }
or using where Self
protocol SubProtocol where Self: SuperProtocol { ... }
By doing this on these two ways the results are exactly the same, both options compile fine, and it works, SubProtocol will have the same stuff than SuperProtocol has. So what is the difference?
The only difference I can see is the semantic, one is more clear than the other (see example below). But this is my point of view and I would like to know if someone else thinks the same, or perhaps I am miss-understanding the whole thing.
Example:
protocol Progressable {
var isInProgress: Bool { get }
}
protocol Downloadable: Progressable {
func download()
}
protocol ProgressReporting where Self: Progressable {
func reportProgress() -> Bool
}
For me, Downloadable makes sense it inherits from Progressable, every download is progressable, so that is fine.
But ProgressReporting not necessary needs to inherit from Progressable, for me it would make more sense to constraint it by using where, this way the reader can know that whoever implements it, will need to conform to Progressable too (see the comments on the code below), here is when I think the semantic is different.
class MyClassA: Downloadable {
var isInProgress: Bool { return true }
func download() {}
func foo() {
/*
I have access to `self.isInProgress` because this class conforms `Downloadable`
which inherits from `Progressable`, so this makes sense
*/
_ = self.isInProgress
}
}
class MyClassB: ProgressReporting {
var isInProgress: Bool { return true }
func reportProgress() {}
func foo() {
/*
I have access to `self.isInProgress` but according to `ProgressReporting` definition,
this class should be `Progressable` which is not, at least not explicitely
*/
_ = self.isInProgress
}
}
I would apreciate if someone can explain me what are the differences 🙂
Thanks in advance.
Speaking about Swift5, there is no difference between the two forms, see Swift 5 Release notes:
Protocols can now constrain their conforming types to those that subclass a given class. Two equivalent forms are supported:
protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ }
Swift 4.2 accepted the second form, but it wasn’t fully implemented and could sometimes crash at compile time or runtime. (SR-5581) (38077232)

Inheritence in swift protocols

How to resolve in swift (2.0) that case: we have two protocols:
protocol SuperDelegate{
func handleSuccess(rsp: BaseRsp)
}
protocol ChildOfSuperDelegate: SuperDelegate {
func handleSuccess(rsp: ChildOfBaseRsp)
}
and some place in code where we want do:
class A{
var delegate:SuperDelegate?
func foo(){
delegate?.handleSuccess(childOfBaseRspObj)
}
}
class Xyz:ChildOfSuperDelegate{
func handleSuccess(rsp: ChildOfBaseRsp){
print("Great!")
}
}
but compiler not founding inheritence in protocols (handleSuccess have function with base and child parameters) there are compile error:
Type 'Xyz' does not conform to protocol 'SuperDelegate'
. How to resolve them? What is the best way to only implements ChildOfSuperDelegate methods but class "A" use SuperDelegate variable.
Meybe I must use generic types to have somethink like inheritance in protocols.
Here your protocol ChildOfSuperDelegate is inherited from SuperDelegate and though it is a protocol it doesn't implement its methods. But when you inherit from ChildOfSuperDelegate you have to implement method of SuperDelegate as well ChildOfSuperDelegate.
So do like this:
class Xyz:ChildOfSuperDelegate{
func handleSuccess(rsp: BaseRsp) {
//do stuff
}
func handleSuccess(rsp: ChildOfBaseRsp){
print("Great!")
}
}

Deprecating protocols for Swift 3 upgrade

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() {
}
}

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.