Reputation: 5729
This is a follow-up to this question: Can I have a Swift protocol without functions
Suppose I want to add more properties to my protocol:
protocol Nameable {
var name: String {get}
var fullName: String: {get}
var nickName: String: {get}
}
However, not every struct that conforms to this protocol may have a fullName and/or nickName. How do I go about this? Can I make these two properties somehow optional? Or maybe I need three separate protocols? Or do I just add them to each struct, but leave them empty, like this:
struct Person : Nameable {
let name: String
let fullName: String
let nickName: String
let age: Int
// other properties of a Person
}
let person = Person(name: "Steve", fullName: "", nickName: "Stevie", age: 21)
That compiles and works, but I don't know if this is the 'correct' approach?
Upvotes: 4
Views: 2995
Reputation: 80781
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?
}
Upvotes: 4
Reputation: 11250
You can give a default implementation to these property using protocol extension
and override the property in classes/structs
where actually you needed
extension Nameable{
var fullName: String{
return "NoFullName"
}
var nickName: String{
return "NoNickName"
}
}
struct Foo : Nameable{
var name: String
}
Upvotes: 1