Swift protocols define an interface or type that other structures can conform to. This includes structures such as: classes, structs, enums, and other protocols. Just like enums classes in Swift protocols are more advanced than their counterparts in Objective-C. Here is everything you need to know about protocols in Swift. (Note: Swift 1.2 Xcode 6.3)
Protocol Basics
Protocol Definition:
Here is the simplest protocol you can define in Swift:
protocol AnInterface {
}
A class or a struct can conform to or adopt a protocol like this:
class User: Person, AnInterface {
}
struct SomeDataStructure: AnInterface {
}
enum SomeEnum: AnInterface {
}
Note that you define a class
by giving it a super class type after the colon and then list all the protocols it conforms to. If you want to inherit your class from a base class (and its protocols), you must type the base class first and then type all the protocols.
When using struct
or enum
you don't need a super class, you just start listing protocols instead.
Properties:
To define a property requirement you add a property just like any other var
in Swift type but you need to specify whether the property is going to be gettable
or gettable
and settable
.
protocol SomeProtocol {
var firstName: String { get }
var lastName: String { get set }
}
class Person: SomeProtocol {
var firstName: String
var lastName: String
}
In the case of static class properties you do the same but prepend your property definition with the static
keyword.
protocol SomeProtocol {
static var firstName: String { get }
static var lastName: String { get set }
}
class Person: SomeProtocol {
static var firstName: String
static var lastName: String
}
Methods:
Similarly, you can define protocol methods:
protocol SomeProtocol {
func method1()
static func method2()
}
class Person: SomeProtocol {
func method1() {
// method implementation
}
static func method2() {
// method implementation
}
}
Notice how the same rule of a static
prefix applies for class or type level methods. Protocols use the same syntax as normal methods, but are not allowed to specify default values for method parameters.
Optionals:
By default all the properties and methods declared in your protocol will be required by the conforming type. You can have optional methods and properties (just like in Objective-C protocols). The trick is that a protocol with optionals has to be Objective-C compatible and can be adopted only by class
types (no enum
or struct
).
@objc protocol SomeProtocol {
var name: String { get }
optional static func test()
}
@objc class Person: SomeProtocol {
var name: String
init() {
name = "Name"
}
static func test() {
}
}
Mutation:
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
Swift protocols can define methods that mutate underlying value type (enum
or struct
) using keyword mutating
. (See the Apple Doc Modifying Value Types from Within Instance Methods)
The idea is that if a method has the mutating
keyword prepended then a value type that adopts that protocol is allowed to mutate (change) its underlying values.
protocol Changeable {
mutating func changeMethod()
}
struct DataStructureOfSomeKind: Changeable {
var someName: String
mutating func changeMethod() {
someName = "some other value"
}
}
var structInstance: DataStructureOfSomeKind = DataStructureOfSomeKind(someName: "some string value")
print(structInstance.someName)
structInstance.changeMethod()
print(structInstance.someName)
Extensions:
Another way to have a type conform to a protocol is through extensions. The syntax is similar to the basic protocol conformance shown above but you need to use the extension
keyword and provide the implementation.
protocol AnInterface {
func method()
}
class User: Person {
}
extension User: AnInterface {
func method() {
}
}
Protocol Conformance
You can check to see if a type conforms to a protocol or you can cast it to one using is
and as
operators as described in Swift Type Casting. But you can only check for protocol conformance if you use the @objc
attribute, just like with optionals.
@objc protocol SomeProtocol {
var name: String { get }
}
@objc class Person: SomeProtocol {
var name: String
init() {
name = "Name"
}
}
let person: AnyObject = Person()
if person is SomeProtocol {
print("person: \(person.name)");
} else {
print("nope")
}
let people: [AnyObject] = [
Person(),
NSObject(),
Person()
]
for object in people {
if let person = object as? SomeProtocol {
print("\(person.name)")
} else {
print("this is something else")
}
}
Inheritance
Protocols can inherit from one or many protocols. When you adopt a protocol that has inherited from other protocols you need it to conform not only to that protocol’s requirements but also to the requirements imposed by that protocol’s super protocols. In the example below the Car
class conforms to SomeProtocol3
which effectively makes it conform to SomeProtocol1
and SomeProtocol2
because SomeProtocol3
inherits from them.
protocol SomeProtocol1 {
func method1()
func method2()
}
protocol SomeProtocol2 {
func method3()
func method4()
}
protocol SomeProtocol3: SomeProtocol1, SomeProtocol2 {
func method5()
func method6()
}
class Car: SomeProtocol3 {
func method5() {
}
func method6() {
}
func method3() {
}
func method4() {
}
func method2() {
}
func method1() {
}
}
Composition
In Swift when you define a method it is possible to require a method parameter that conforms to multiple protocols at the same time. Let's expand on the example above:
func someMethod(firstMethodParam: String, secondMethodParam: protocol<SomeProtocol1, SomeProtocol2>) {
secondMethodParam.method1()
secondMethodParam.method2()
secondMethodParam.method3()
secondMethodParam.method4()
}
let car = Car()
someMethod("a string", car)
Here we define a function someMethod
that takes two params and the second param is of a protocol composition type protocol<SomeProtocol1, SomeProtocol2>
. That means that any object that conforms to SomeProtocol1
and SomeProtocol2
at the same time, through direct conformance or through inheritance, will satisfy the composition requirement. That is why the car
object can be passed to that function. If the Car
class implements SomeProtocol1
and SomeProtocol2
directly like this: class Car: SomeProtocol1, SomeProtocol1
it will satisfy the requirements as well.
Note:
Protocol compositions do not define a new, permanent, protocol type. Rather, they define a temporary local protocol that has the combined requirements of all protocols in the composition.
(reference Apple Doc)
How to make it work with Objective-C
In order to make Swift protocols work with Objective-C you need to prefix them with the @objc
attribute. That will make your protocol work with Objective-C but you'll only be able to use it with class
type, enum
or struct
are excluded.
protocol SomeProtocol {
func someMethod()
}
@interface Car : NSObject<SomeProtocol>
@end
@implementation Car
- (void)someMethod {
}
@end
Conclusion
For the most part, Swift protocols (except optionals) work the same way as Objective-C protocols. They have many additional features that make them even more useful.
If you have any questions about protocols or Swift in general feel free to leave a comment below or shoot us an email at info@smartcloud.io. We'd be happy to help you.
__
Alex Bush @alexvbush.
Editor: Tim Baron.