问题
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?
回答1:
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() {
}
}
来源:https://stackoverflow.com/questions/40028519/deprecating-protocols-for-swift-3-upgrade