How to define optional methods in Swift protocol?
1. Using default implementations (preferred).
protocol MyProtocol {
func doSomething()
}
extension MyProtocol {
func doSomething() {
/* return a default value or just leave empty */
}
}
struct MyStruct: MyProtocol {
/* no compile error */
}
Advantages
No Objective-C runtime is involved (well, no explicitly at least). This means you can conform structs, enums and non-
NSObject
classes to it. Also, this means you can take advantage of powerful generics system.You can always be sure that all requirements are met when encountering types that conform to such protocol. It's always either concrete implementation or default one. This is how "interfaces" or "contracts" behave in other languages.
Disadvantages
For non-
Void
requirements, you need to have a reasonable default value, which is not always possible. However, when you encounter this problem, it means that either such requirement should really have no default implementation, or that your you made a mistake during API design.You can't distinguish between a default implementation and no implementation at all, at least without addressing that problem with special return values. Consider the following example:
protocol SomeParserDelegate {
func validate(value: Any) -> Bool
}If you provide a default implementation which just returns
true
— it's fine at the first glance. Now, consider the following pseudo code:final class SomeParser {
func parse(data: Data) -> [Any] {
if /* delegate.validate(value:) is not implemented */ {
/* parse very fast without validating */
} else {
/* parse and validate every value */
}
}
}There's no way to implement such an optimization — you can't know if your delegate implements a method or not.
Although there's a number of different ways to overcome this problem (using optional closures, different delegate objects for different operations to name a few), that example presents the problem clearly.
2. Using @objc optional
.
@objc protocol MyProtocol {
@objc optional func doSomething()
}
class MyClass: NSObject, MyProtocol {
/* no compile error */
}
Advantages
- No default implementation is needed. You just declare an optional method or a variable and you're ready to go.
Disadvantages
It severely limits your protocol's capabilities by requiring all conforming types to be Objective-C compatible. This means, only classes that inherit from
NSObject
can conform to such protocol. No structs, no enums, no associated types.You must always check if an optional method is implemented by either optionally calling or checking if the conforming type implements it. This might introduce a lot of boilerplate if you're calling optional methods often.
Can I have a Swift protocol without functions
Person
s are not the only not the only things that you may want to name. Pets have names, roads have names, heck, some people name their cars.
What if we want to name each object in a collection of different objects? If we store those objects in a collection of Any
, we don't have any way to guarentee that all objects have names.
This is where protocols come in. By creating a Nameable
protocol, we can create a collection of Nameable
objects, and be certain that all the objects within are guaranteed to have a name.
Here's an example:
protocol Nameable {
var name: String {get}
}
struct Person : Nameable {
let name: String
let age: Int
// other properties of a Person
}
struct Pet : Nameable {
let name: String
let species: String
// other properties of a Pet
}
struct Car : Nameable {
let name: String
let horsepower: Double
// other properties of a Car
}
let namableItems: [Nameable] = [
Person(name: "Steve", age: 21),
Pet(name: "Mittens", species: "Cat"),
Car(name: "My Pride and Joy", horsepower: 9000)
]
for nameableItem in namableItems {
print("\(nameableItem.name) is a \(nameableItem.dynamicType).")
}
Which prints:
Steve is a Person.
Mittens is a Pet.
My Pride and Joy is a Car.
You can try it here.
Optional Protocol methods in swift without using @objc
You can define default func implementation by:
protocol Opt {
func requiredFunc()
func optionalFunc()
}
extension Opt {
func optionalFunc() {}
}
With this you don't have to implement optionalFunc() in classes conforming to Opt, because they already have their default implementation.
Add a function to existing protocol without function declaration using extension
You cannot extend an existing protocol by adding new requirements (aka methods) to it. However you can derive the existing protocol, and add the new method to that protocol:
protocol MyTableViewDelegate: UITableViewDelegate {
optional func tableView(_ tableView: UITableView, didUpdateContentSize contentSize: CGSize)
}
, and call this new method from the table view:
class MyTableView: UITableView {
override open var contentSize: CGSize {
didSet {
(self.delegate as? MyTableViewDelegate)?.tableView?(self, didUpdateContentSize: contentSize)
}
}
}
Note that the above code subclasses UITableView
instead of extending it, as overriding methods in extensions is not recommendable (though if you really want you can add the code in the extension over UITableView
). This should not cause too many headaches, as the table view class can be easily changed in storyboards/xibs/code.
You can then have your view controller conform to the extended delegate, and you should be good to go:
extension MyViewController: MyTableViewDelegate {
func tableView(_ tableView: UITableView, didUpdateContentSize contentSize: CGSize) {
// do your stuff
}
}
Swift Protocol Optional conformance via Non-Optional
If the protocol provides a default implementation that returns an optional:
protocol SomeProtocol {
var foo: Int? { get }
}
extension SomeProtocol {
var foo: Int? { return nil }
}
protocol-conforming types can then provide an overriding non-optional version of the variable/function:
struct StructB: SomeProtocol {
let foo: Int
}
I found this discussed on the Swift Evolution forum:
At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.
https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448
This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:
Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)
Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns
which is tracked on Stack Overflow here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
why do I have to mark both the protocol & contained optional functions in a swift protocol as @objc?
UICollectionViewDataSource is imported from ObjC. The auto-generated Swift header doesn't insert @objc
on every element. It is common for these headers to be invalid Swift (for example, they define structs and classes without implementations, which isn't valid Swift).
When you're writing Swift (rather than looking at auto-generated headers), you need to tell the compiler that it needs to bridge certain things to ObjC, and you do that with @objc
. Imported ObjC doesn't have to be bridged.
Protocol function implementation without actually conforming to a protocol
You must conform to CustomStringConvertible
if you want string interpolation to use your description
property.
You use string interpolation in Swift like this:
"Here's my linked list: \(linkedList)"
The compiler basically turns that into this:
String(stringInterpolation:
String(stringInterpolationSegment: "Here's my linked list: "),
String(stringInterpolationSegment: linkedList),
String(stringInterpolationSegment: ""))
There's a generic version of String(stringInterpolationSegment:)
defined like this:
public init<T>(stringInterpolationSegment expr: T) {
self = String(describing: expr)
}
String(describing: )
is defined like this:
public init<Subject>(describing instance: Subject) {
self.init()
_print_unlocked(instance, &self)
}
_print_unlocked
is defined like this:
internal func _print_unlocked<T, TargetStream : TextOutputStream>(
_ value: T, _ target: inout TargetStream
) {
// Optional has no representation suitable for display; therefore,
// values of optional type should be printed as a debug
// string. Check for Optional first, before checking protocol
// conformance below, because an Optional value is convertible to a
// protocol if its wrapped type conforms to that protocol.
if _isOptional(type(of: value)) {
let debugPrintable = value as! CustomDebugStringConvertible
debugPrintable.debugDescription.write(to: &target)
return
}
if case let streamableObject as TextOutputStreamable = value {
streamableObject.write(to: &target)
return
}
if case let printableObject as CustomStringConvertible = value {
printableObject.description.write(to: &target)
return
}
if case let debugPrintableObject as CustomDebugStringConvertible = value {
debugPrintableObject.debugDescription.write(to: &target)
return
}
let mirror = Mirror(reflecting: value)
_adHocPrint_unlocked(value, mirror, &target, isDebugPrint: false)
}
Notice that _print_unlocked
only calls the object's description
method if the object conforms to CustomStringConvertible
.
If your object doesn't conform to CustomStringConvertible
or one of the other protocols used in _print_unlocked
, then _print_unlocked
creates a Mirror
for your object, which ends up just printing the object's type (e.g. MyProject.LinkedList
) and nothing else.
Accessing optional method in protocol returns scope error
class MyClass: NSObject, MyProtocol { ... }
This says that MyClass implements MyProtocol, which allows, but does not require myFunction. The compiler can clearly see that MyClass does not implement myFunction, so self.myFunction()
isn't a thing. It could be a thing, but it's clearly not a thing. The protocol doesn't play into this.
You could "work around" this by saying "I don't mean MyClass; I mean MyProtocol":
func doSomething() {
(self as MyProtocol).myFunction?()
}
But this is kind of silly IMO. self
doesn't implement myFunction and this is known at compile time.
If the point is to implement this via subclassing, the correct way to do that is to just implement it as empty in the superclass:
class MyClass: NSObject, MyProtocol {
func myFunction() {}
func doSomething() {
myFunction()
}
}
This is precisely equivalent to "do nothing" while allowing overrides in subclasses.
Why does adding a Swift protocol in some situations not require all the normally required methods?
Why does my code compile without fulfilling all the protocol requirements?
What you are seeing here are optional protocol requirements. They are here because Swift code needs to interact with Objective-C, and Objective-C has them.
All methods except centralManagerDidUpdateState
declared in CBCentralManagerDelegate
are optional, from here:
The only required method is
centralManagerDidUpdateState(_:)
; the central manager calls this when its state updates, thereby indicating the availability of the central manager.
And SFSpeechRecognizerDelegate
, only contains one optional method, from here:
Use this protocol's optional method to track those changes and provide an appropriate response.
why does the code not compile if you remove the superclasses then?
This is because both of those protocols in your question also inherit from NSObjectProtocol
, so they actually also have the additional requirements of NSObjectProtocol
. UIViewController
and CBCentralManager
both inherits from NSObject
, so they satisfy the NSObjectProtocol
requirements, but your Swift class without a superclass doesn't.
Protocols with optional requirements don't have to inherit from NSObjectProtocol
though. It's just that most of them are that way in the framework. You can for example do:
@objc protocol Foo {
@objc optional func foo()
}
class FooClass : Foo {
// compiles fine!
}
Swift optional method in protocol without objc
What I do in this situation is return a class (compatible with Objective-C) which wraps the Swift struct. In other words, just make it so that everything Objective-C sees is compatible with Objective-C, and inside that you can do anything you like.
Related Topics
Views Compressed by Other Views in Swiftui VStack and List
Swift 3 Get Start Index (As Int) of Substring
Insertion-Order Dictionary (Like Java's Linkedhashmap) in Swift
Swiftui .Rotationeffect() Framing and Offsetting
Does Kotlin Has Extension Class to Interface Like Swift
Uisplitviewcontroller Displaymodebuttonitem()
How to Connect Localhost (With Invalid Certificate) Using Alamofire
Subclass Nsapplication in Swift
Is There Any Way of Locking an Object in Swift Like in C#
Swift Error: Missing Argument Label 'Name:' in Call
Reversing the Order of a String Value
How to Detect a 'Click' Gesture in Swiftui Tvos
Swift Programmatically Set Segues
How to Make Protocol Associated Type Require Protocol Inheritance and Not Protocol Adoption
Testing an Executable with Swift
Iterating with for .. in on a Changing Collection
How to Programmatically Change the Alpha of a Uivisualeffectview in a Navigationbar
Nsbundle.Mainbundle().Urlforresource("Bach1", Withextension: "Jpg") Returning Null