Optional Variables in protocol is possible?
protocol TestProtocol {
var name : String {set get}
var age : Int {set get}
}
Provide a default extension for the protocol. Provide the default implementation for all the variables set and get which u want them to be optional.
In below protocol, name and age are optional.
extension TestProtocol {
var name: String {
get { return "Any default Name" } set {}
}
var age : Int { get{ return 23 } set{} }
}
Now if I am conforming above protocol to any other class, like
class TestViewController: UIViewController, TestProtocol{
var itemName: String = ""
**I can implement the name only, and my objective is achieved here, that the controller will not give a warning that "TestViewController does not conform to protocol TestProtocol"**
var name: String {
get {
return itemName ?? ""
} set {}
}
}
Swift: setting an optional property of a protocol
It's impossible in Swift (yet?). Referenced from an ADF thread:
Optional property requirements, and optional method requirements that return a value, will always return an optional value of the appropriate type when they are accessed or called, to reflect the fact that the optional requirement may not have been implemented.
So it's no surprise to get optional values easily. However, setting a property requires implementation to be guaranteed.
Override swift protocol property to optional
The existence of this property in XMLBaseMappable
protocol is crucial in order to function correctly the whole library.
Having said that, you can't omit the implementation of this property in your structs and classes but you can "hide" it in a super class. Using this:
class BasicXMLMappable: XMLMappable {
var nodeName: String!
required init(map: XMLMap) {
}
func mapping(map: XMLMap) {
}
}
You can have XMLMappable
objects that extends BasicXMLMappable
and they don't have to implement nodeName
property:
class TestBasicXMLMappable: BasicXMLMappable {
// Your custom properties
required init(map: XMLMap) {
super.init(map: map)
}
override func mapping(map: XMLMap) {
// Map your custom properties
}
}
Edit:
As of version 1.5.1 you can use XMLStaticMappable
for implementing XMLMapper
in an extension. For example:
class CustomClass {
var property: String?
}
extension CustomClass: XMLStaticMappable {
var nodeName: String! {
get {
return "default"
}
set(newValue) {
}
}
static func objectForMapping(map: XMLMap) -> XMLBaseMappable? {
// Initialize CustomClass somehow
return CustomClass()
}
func mapping(map: XMLMap) {
property <- map["property"]
}
}
Hope this helps.
Swift protocol with properties that are not always used
Unlike in Objective-C, you cannot define optional protocol requirements in pure Swift. Types that conform to protocols must adopt all the requirements specified.
One potential way of allowing for optional property requirements is defining them as optionals, with a default implementation of a computed property that just returns nil
.
protocol Nameable {
var name : String? { get }
var fullName : String? { get }
var nickname : String? { get }
}
extension Nameable {
var name : String? { return nil }
var fullName : String? { return nil }
var nickname : String? { return nil }
}
struct Person : Nameable {
// Person now has the option not to implement the protocol requirements,
// as they have default implementations that return nil
// What's cool is you can implement the optional typed property requirements with
// non-optional properties – as this doesn't break the contract with the protocol.
var name : String
}
let p = Person(name: "Allan")
print(p.name) // Allan
However the downside to this approach is that you potentially pollute conforming types with properties that they don't implement (fullName
& nickName
in this case).
Therefore if it makes no logical sense for a type to have these properties (say you wanted to conform City
to Nameable
– but cities don't (really) have nicknames), you shouldn't be conforming it to Nameable
.
A much more flexible solution, as you say, would be to define multiple protocols in order to define these requirements. That way, types can choose exactly what requirements they want to implement.
protocol Nameable {
var name : String { get }
}
protocol FullNameable {
var fullName : String { get }
}
protocol NickNameable {
// Even types that conform to NickNameable may have instances without nicknames.
var nickname : String? { get }
}
// A City only needs a name, not a fullname or nickname
struct City : Nameable {
var name : String
}
let london = City(name: "London")
// Person can have a name, full-name or nickname
struct Person : Nameable, FullNameable, NickNameable {
var name : String
var fullName: String
var nickname: String?
}
let allan = Person(name: "Allan", fullName: "Allan Doe", nickname: "Al")
You could even use protocol composition in order to define a typealias
to represent all three of these protocols for convenience, for example:
typealias CombinedNameable = Nameable & FullNameable & NickNameable
struct Person : CombinedNameable {
var name : String
var fullName: String
var nickname: String?
}
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?
swift protocol conformance when same property name is optional
There is no elegant solution to your problem other than renaming the conflicting property on the conforming type.
Swift doesn't allow 2 properties of the same name to exist on a type even if their types are different. On the other hand, Int?
and Int
are completely different types, so you cannot have trackNumber: Int
fulfil the protocol requirement of trackNumber: Int?
.
The only solution (other than changing the type in either the protocol
or the struct
) is to rename the non-Optional property in SpotifyTrack
and make an optional computed property of the same name returning the non-optional one.
protocol Track {
var trackNumber: Int? { get }
}
struct SpotifyTrack {
private let _trackNumber: Int
}
extension SpotifyTrack: Track {
var trackNumber: Int? { _trackNumber }
}
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.
Class With Non-Optional Property Conforming To Protocol With Optional Property
Unfortunately, you can't redeclare the same variable in MyClass
with a different type.
What Dennis suggests will work, but if you want to keep your variable in MyClass
non-Optional, then you could use a computed property to wrap around your stored variable:
protocol MyProtocol {
var a: String? { get set }
}
class MyClass {
// Must use a different identifier than 'a'
// Otherwise, "Invalid redeclaration of 'a'" error
var b: String
}
extension MyClass: MyProtocol {
var a: String? {
get {
return b
}
set {
if let newValue = newValue {
b = newValue
}
}
}
}
Why do properties modified by @optional become immutable?
From Jordan Rose (who worked on Swift at the time that SE-0070 was implemented) on the forums:
Normally optional requirements add an extra level of optionality:
- Methods become optional themselves (
f.bar?()
)- Property getters wrap the value in an extra level of
Optional
(if let bar = f.bar
)But there's nowhere to put that extra level of Optional for a property setter. That's really the entire story: we never figured out how to expose optional property setters in a safe way, and didn't want to pick any particular unsafe solution. If someone can think of something that'd be great!
So the answer appears to be: at the time that optional
protocol requirements were intentionally limited to Objective-C protocols in Swift (SE-0070), no spelling for an explicit implementation of this was decided on, and it appears that this functionality is uncommon enough that this hasn't really come up since.
Until (and if) this is supported, there are two potential workarounds:
Introduce an explicit method to
Playback
which assigns a value toassetURL
- Sadly, this method cannot be named
-setAssetURL:
because it will be imported into Swift as if it were the property setter instead of a method, and you still won't be able to call it. (This is still true if you markassetURL
asreadonly
) - Also sadly, this method won't be able to have a default implementation, since Objective-C doesn't support default protocol implementations, and you can't give the method an implementation in a Swift
extension
because you still can't assign to the protocol
- Sadly, this method cannot be named
Do like you would in Swift and introduce a protocol hierarchy, where, for example, an
AssetBackedPlayback
protocol inherits fromPlayback
and offersassetURL
as a non-@optional
-property instead:@protocol Playback <NSObject>
// Playback methods
@end
@protocol AssetBackedPlayback: Playback
@property (nonatomic, nonnull) NSURL *assetURL;
@endYou would then need to find a way to expose
PlayerController.currentPlayerManager
as anAssetBackedPlayback
in order to assign theassetURL
.
Some additional alternatives from Jordan:
I think the original recommended workaround was "write a
static inline
function in Objective-C to do it for you", but that's not wonderful either.setValue(_:forKey:)
can also be good enough in practice if it's not in a hot path.
The static inline
function recommendation can function similarly to a default protocol implementation, but you do need to remember to call that function instead of accessing the property directly.
setValue(_:forKey:)
will also work, but incurs a noticeable performance penalty because it supports a lot of dynamism through the Objective-C runtime, and is significantly more complicated than a simple assignment. Depending on your use-case, the cost may be acceptable in order to avoid complexity!
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.
Related Topics
Command Compileswiftsources Failed With a Nonzero Exit Code Xcode 10
App Tracking Transparency Dialog Does Not Appear on iOS
How to Create a Fixed-Size Array of Objects
Override Func Error in Swift 2
Swift Nspredicate Throwing Exc_Bad_Access(Code=1, Address=0X1) When Compounding Statements
Ambiguous Reference to Member 'Tableview'
How to Use Array.Filter to Filter a Class Object Based on a Property
Round Up Double to 2 Decimal Places
How to Specify That a Non-Generic Swift Type Should Comply to a Protocol
Swift 3: Set Finder Label Color
Why Is the Shorthand Argument Name $0 Returning a Tuple of All Parameters
Function with Datatask Returning a Value
How to Make Class Methods/Properties in Swift
Can the Height of the Uisearchbar Textfield Be Modified
Swift 3: the Difference Between Public and Internal Access Modifiers
Implicitly Unwrapped Optional Made Immutable