プロトコルを使うメリット
- 複数適用できる
- 柔軟性が高い
- 抽象化できる
基本文法
- 多言語でいうインターフェース
- classやstructやenumなどで使える。
- 複数適用可能
- @objc optional なメソッドを定義できる
- デリゲートで使う
ドキュメント
Extensionで読みやすく
protocol SomeProtocol {
var computedA: String { get }
func methodA(_ str: String)
func methodB(a: Int, b: Int) -> Int
}
class someClass {}
extension someClass: SomeProtocol {
var computedA: String {
return "a"
}
func methodA(_ str: String) {
print(str)
}
func methodB(a: Int, b: Int) -> Int {
return a+b
}
}
let s = someClass()
s.computedA // a
s.methodA("hello world") // hello world
s.methodB(a: 2, b: 5) // 7
こんな感じで書けるので凄く見やすい、てかほとんどこれがしたいだけです。
Xcodeだと補完がサクサク出てくるので楽チンです。
tableViewのDelegateとかもこうやって分けて書きます。
プロトコルは型なので、配列やメソッドの引数に使うことができます
protocol SomeProtocol {}
extension SomeProtocol {
func className() {
print(String(describing: type(of: self)))
}
}
class someClassA: SomeProtocol {}
class someClassB: SomeProtocol {}
class someClassC: SomeProtocol {}
let a = someClassA()
let b = someClassB()
let c = someClassC()
let someArray: [SomeProtocol] = [a,b,c]
someArray.forEach { $0.className() }
someClassA
someClassB
someClassC
Protocol Extension
プロトコルはそれに準拠する型に対して、特定のメソッド、 initializer, subscript, and computed property を実装することができる。
に準拠する各型でそれぞれ実装せずに、プロトコル自身に実装することができる。
protocol SomeProtocol { }
extension SomeProtocol {
func sayHoge() {
print("hoge")
}
}
class someClass: SomeProtocol {}
let s = someClass()
s.sayHoge() // hoge
someClassはsomeProtocolをに準拠しており、SomeProtocolがsayHogeメソッドを実装しているので、someClassのインスタンスは"hoge"って言ってくれます。
Providing Default Implementations
protocol SomeProtocol { }
extension SomeProtocol {
var someString: String {
return "some"
}
}
class someClass: SomeProtocol {}
let s = someClass()
s.someString // some
SomeProtocolでsomeStringをデフォルト実装しました、
SomeProtocolに準拠する型でsomeStringを実装した場合、デフォルト実装の代わりに、その型で実装したものが実行されます。
class someClass: SomeProtocol {
var someString: String {
return "conforming type implementation"
}
}
let s = someClass()
s.someString // conforming type implementation
Adding Constraints to Protocol Extensions 型制約
where句特定の型に対して、Extensionを定義できます
protocol SomeProtocol {}
extension SomeProtocol {
func sayHello() {
print("hello")
}
}
extension SomeProtocol where Self: SomeClass {
func someMethod(){
print("instance of SomeClass can use this method")
}
}
class SomeClass: SomeProtocol {}
class OtherClass: SomeProtocol {}
let s = SomeClass()
s.sayHello() //hello
s.someMethod() //instance of SomeClass can use this method
let o = OtherClass()
o.sayHello() //hello
o.someMethod() // error: 'OtherClass' is not a subtype of 'SomeClass'
SomeClassのインスタンスもOhterクラスのインスタンスも、SomeProtocolに準拠しているので、sayHelloメソッドを使えますが、someMethodは、SomeClassにのみ使うことができます。
特定の型が準拠することができるProtocol
Swift5から以下の二つは同じである。
protocol MyView: UIView { /*...*/ }
protocol MyView where Self: UIView { /*...*/ }
下の書き方はSwift4.2だとランタイムエラーになったりした。
ので仕方なく下のように型制約をつけて書いてた。
protocol MyView {}
extension MyView where Self: UIView {
}
Swift5から型制約をつけずに以下のように書けるようになった。
protocol MyView: UIView { /*...*/ }
extension MyView {
func setWhiteBackgroundColor {
backgroundColor = UIColor.white
}
}
associatedtype
標準ライブラリのプロトコル
Equatable
Hashable
CustomStringConvertible
プロトコルの実践的な使い方
プロトコルってどうゆう時に使うの?何が便利なのという疑問が
Reusable
ReusableプロトコルはreuseIdentifierをもち、デフォルト実装でクラス名の文字列返すようになっています。
protocol Reusable {
static var reuseIdentifier: String { get }
}
extension Reusable {
static var reuseIdentifier: String {
return String(describing: self)
}
}
extension UITableViewCell: Reusable {}
extension UITableView {
func register<T: UITableViewCell>(_ cell: T.Type) {
register(cell, forCellReuseIdentifier: cell.reuseIdentifier)
}
func dequeueReusableCell<T: UITableViewCell>(_ cell: T.Type, for indexPath: IndexPath) -> T {
guard let cell = dequeueReusableCell(withIdentifier: cell.reuseIdentifier, for: indexPath) as? T else {
fatalError("missing")
}
return cell
}
}
tableView.register(CustomeCell.self)
tableView.dequeueReusableCell(CustomeCell.self, for indexPath)
APIクライアント
NSObjectを拡張してみる
protocol Extensible {
associatedtype ExtensibleType
static var ex: Extension<ExtensibleType>.Type { get }
var ex: Extension<ExtensibleType> { get }
}
extension Extensible {
public static var ex: Extension<Self>.Type {
return Extension<Self>.self
}
public var ex: Extension<Self> {
return Extension(self)
}
}
public struct Extension<Base> {
public let base: Base
public init(_ base: Base) {
self.base = base
}
}
extension NSObject: Extensible {}
NSObjectを継承する全てのクラスはexプロパティを持つようになりました。
型制約をつけて拡張することで、特定のクラスに対してexで区切ってメソッドなどを実装できるようになります。
extension Extension where Base: UIImageView {
func load(from urlString: String) {
// load image from urlString
}
}
let imageView = UIImageView()
imageView.ex.load(from: "url")
疎結合
テスト
クラスをプロトコルを使った抽象に依存させることでテストがしやすくなります。
protocol DataStoreProtocol {
func getItem() -> Item
}
final class ViewModel {
private var dataStore: DataStoreProtocol
init(dataStore: DataStoreProtocol) {
self.dataStore = dataStore
}
func getItem() -> Item {
return dataStore.getItem()
}
}
struct DataStoreStub: DataStoreProtocol {
func getItem() -> Item {
return Item()
}
}
let viewModel = ViewModel(dataStore: DataStore())
swiftが柔軟なためほぼ共通処理はBaseClassのようなものを作らなくてもプロトコルで十分対応できますので気が向いたら使って見てください。
個人的にはクラスに武器を装備させるような感じで楽しいです。
授業中暇なので書いてみました。
(追記)
自分への戒めとして適当な投稿をしたのを消さずに残していたのですが、
やっぱり時間のある時にコンテンツを改善していこうと思います。