Protocol in Swiftは、採用する型が満たさなければならない一連の要件を定義する契約です。プロトコルは、プロトコル指向プログラミング(POP)の基礎です — AppleがSwiftのために従来のOOPと並んで推奨するパラダイムです。Apple Documentation, 2026によると、構造体、クラス、列挙型はプロトコルを採用でき、アーキテクチャを柔軟でテスト可能にします。
重要なポイント
Protocolは、型が何をできるべきかを定義する抽象インターフェースであり、どのようにそれを行うかは定義しません。Swiftでは、プロトコルはJavaやGoのインターフェースに類似していますが、追加の機能を備えています。
プロトコルは以下を要求できます:
{ get }または{ get set }指定子付き)WWDC 2015によると、Appleはプロトコル指向プログラミングをSwiftアプリケーションアーキテクチャの基本的なアプローチとして導入しました。クラスの継承が堅い階層構造を作るOOPとは異なり、POPはプロトコルの合成を提供します。これにより柔軟性が生まれます:単一の型が複数のプロトコルに準拠でき、拡張を通じてそれらの要件と実装を獲得できます。
Protocolはキーワードprotocol、名前、本体で宣言されます。
protocol Drawable {
func draw(context: CGContext)
var boundingBox: CGRect { get }
}
型は名前の後にコロンを使用してプロトコルを採用します:
struct Circle: Drawable {
let center: CGPoint
let radius: CGFloat
func draw(context: CGContext) {
context.addArc(center: center, radius: radius, ...)
}
var boundingBox: CGRect {
CGRect(x: center.x - radius, y: center.y - radius,
width: radius * 2, height: radius * 2)
}
}
単一の型はカンマ区切りで複数のプロトコルを採用できます:struct MyType: ProtocolA, ProtocolB。コンパイラは各プロトコルのすべての要件が満たされているか検証します。
プロパティ要件はキーワードvarとアクセス指定子{ get }または{ get set }で宣言されます。
protocol UserProtocol {
var name: String { get }
var age: Int { get set }
static var maxAge: Int { get }
mutating func updateName(_ newName: String)
}
プロトコル内のキーワードmutatingは、メソッドがselfを変更できることを示します。構造体はそのようなメソッドをmutatingとして実装する必要がありますが、クラスは省略できます。メソッド要件はパラメータラベルを含む完全なシグネチャで宣言されます。
Protocolは別のプロトコルを継承でき、新しい要件を追加できます。これにより、クラスへの堅い結合なしに契約の階層構造が作成されます。
protocol Vehicle {
var speed: Double { get set }
func move()
}
protocol Flyable: Vehicle {
var altitude: Double { get set }
func takeOff()
}
Flyableを採用する型は、両方のプロトコル — VehicleとFlyable — の要件を満たさなければなりません。Swiftはプロトコルの多重継承をサポートしています:protocol A: B, C。
Protocol Compositionは、新しい結合プロトコルを作成せずに、型が同時に複数のプロトコルに準拠する必要があることを指定できるメカニズムです。
func render(_ item: Drawable & Animatable) {
item.draw(context: ...)
item.animate(duration: 0.3)
}
合成(&)は関数パラメータ、変数、ジェネリック制約で機能します。コンパイラは渡された型が同時にすべてのプロトコルを満たすか検証します。合成はSwiftUIで広く使用されています:some Viewは合成を伴うジェネリクスです。
Protocol Compositionは継承階層を作成するよりも望ましいです:protocol A: B, Cの代わりに、直接B & Cを採用できます。これにより柔軟性が向上し、結合が減少します。合成は特に、中間プロトコルを作成せずに一時的に要件を組み合わせる必要がある関数パラメータやジェネリック制約で役立ちます。これによりコード内の補助型の数が減ります。
Protocolは変数、関数パラメータ、コレクション要素の型として使用できます。これは existential type と呼ばれます。
var drawableItem: Drawable
let items: [Drawable] = [Circle(...), Rectangle(...)]
func process(drawables: [any Drawable]) {
for item in drawables {
item.draw(context: ...)
}
}
キーワードany(Swift 5.6+)は明示的に existential を示します。anyがない場合、コンパイラは警告を発します。Swiftはプロトコルの背後に具体的な型を隠すためのsome(opaque types)もサポートしています — これはSwiftUIでの標準的なアプローチです。
プロトコルを扱う際、開発者はしばしばいくつかの繰り返し発生する間違いを犯します。
<T: Drawable>の代わりにDrawableを使用するAnyObjectを使用し、構造体と列挙型を除外するよくある質問
Protocolは実装なしで要件のみを定義します(protocol extensionsの登場まで)。抽象クラスは実装と状態を含むことができます。Swiftには抽象クラスはありません — その役割は拡張を伴うプロトコルが果たします。
はい、構造体、クラス、列挙型はすべてプロトコルを採用できます。これがPOPの基礎です:アーキテクチャを組織化するためにクラスを使う必要はありません。プロトコルを持つ構造体は値セマンティクスと不変性を提供します。
Associated type(associatedtype)は、実装する型が自分で決定する型のプレースホルダーです。例:Collectionにはassociatedtype Elementがあります。これはプロトコルレベルのジェネリクスです。
はい、AnyObjectからの継承を追加します:protocol MyProtocol: AnyObject。そうするとクラスのみがこのプロトコルを採用できます。これは弱参照やデリゲートに便利です。
確認にはisを、キャストにはas?を使用します:if let drawable = item as? Drawable。これはassociated typeのないプロトコルでのみ機能します。
まとめ
&)は継承なしでプロトコルを組み合わせるany)とopaque types(some) — プロトコルを扱う2つの方法ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。