# Swift Protocol Conformance: Rules, Syntax, and Best Practices

Source: https://devdocs10.com/articles/swift/swift-protocol-conformance  
By Murat Bo · Published 2026-05-27 · DevDocs10 (https://devdocs10.com)

Swift protocol conformance is the process of making a type such as a struct, class, or enum promise that it provides specific properties, methods, or other behavior defined by a protocol. Understanding conformance is essential because protocols are one of Swift’s main tools for building flexible, reusable, and type-safe code.

> **Quick answer:** A type conforms to a protocol by listing the protocol after its name and then implementing every required member. If any requirement is missing, has the wrong type, or has the wrong mutability, Swift reports that the type does not conform to the protocol.

> **Difficulty:** Beginner
>
> **Helpful to know first:** You’ll understand this better if you know basic Swift syntax, how structs and classes are declared, and how functions and properties work.

## 1. What Is Protocol Conformance?

In Swift, a protocol describes a set of rules or requirements. Protocol conformance means a type agrees to follow those rules.

For example, a protocol might require a read-only property called `id` and a method called `displayName()`. Any type that conforms to that protocol must provide both of them.

- A protocol defines requirements, not stored data.
- Conformance is the type’s promise to satisfy those requirements.
- Structs, classes, and enums can all conform to protocols.
- A type can conform to more than one protocol.
- Conformance lets different types be used through a shared interface.

> Protocol conformance is different from inheritance. Inheritance is mainly for classes and reuses implementation from a superclass. Protocol conformance focuses on capabilities a type provides, regardless of its concrete type.

## 2. Why Protocol Conformance Matters

Protocol conformance matters because it helps you write code that depends on behavior instead of a specific concrete type. That makes your programs easier to extend, test, and maintain.

Here are some real reasons it matters:

- You can write functions that accept any type conforming to a protocol instead of one exact type.
- You can model shared behavior across structs, classes, and enums.
- Many Swift standard library features rely on protocol conformance, such as `Equatable`, `Comparable`, `Hashable`, and `CustomStringConvertible`.
- Protocols make APIs more expressive by describing what a type can do.
- They support cleaner architecture because you can separate interface from implementation.

You should use protocol conformance when multiple types share the same required behavior. You should not use a protocol just to wrap a single type with no real abstraction benefit.

## 3. Basic Syntax

The basic syntax has two parts: defining a protocol and making a type conform to it.

### Declaring a protocol

This protocol requires a read-only `name` property and a method called `speak()`.

```swift
protocol Describable {
    var name: String { get }
    func speak()
}
```

The protocol does not store a value for `name` and does not implement `speak()`. It only describes what conforming types must provide.

### Making a type conform

Here, a struct adopts the protocol by listing it after the type name and then implementing every requirement.

```swift
struct Dog: Describable {
    let name: String

    func speak() {
        print("Woof! My name is \(name).")
    }
}
```

This struct conforms because it provides both the required property and the required method with compatible types and signatures.

### Using the conforming type

Once a type conforms, you can use it anywhere that protocol is expected.

```swift
let pet = Dog(name: "Milo")
pet.speak()
```

Output (tested with Swift 6.4):

```text
Woof! My name is Milo.
```

This works because `Dog` satisfies the requirements of `Describable`.

## 4. Step-by-Step Examples

### Example 1: Conforming a struct to a simple protocol

This example shows the most basic kind of conformance.

```swift
protocol IdentifiableItem {
    var id: Int { get }
}

struct Book: IdentifiableItem {
    let id: Int
    let title: String
}
```

`Book` conforms because it includes the required `id` property. It may also contain extra properties like `title`.

### Example 2: Conforming a class to a protocol with a mutable property

A protocol can require a property to support both reading and writing by using `get set`.

```swift
protocol Resettable {
    var value: Int { get set }
    func reset()
}

final class Counter: Resettable {
    var value: Int = 0

    func reset() {
        value = 0
    }
}
```

This class conforms because `value` is mutable and `reset()` is implemented.

### Example 3: Conforming an enum to a protocol

Enums can conform too, which is useful when each case represents a known set of values.

```swift
protocol Labelable {
    func label() -> String
}

enum Priority: Labelable {
    case low
    case medium
    case high

    func label() -> String {
        switch self {
        case .low:
            return "Low"
        case .medium:
            return "Medium"
        case .high:
            return "High"
        }
    }
}
```

The enum conforms by providing the required method. The protocol does not care that the conforming type is an enum instead of a struct or class.

### Example 4: Using standard library protocol conformance

Many built-in Swift features depend on protocol conformance. Here is a custom type conforming to `CustomStringConvertible`.

```swift
struct User: CustomStringConvertible {
    let username: String
    let isAdmin: Bool

    var description: String {
        "User(username: \(username), isAdmin: \(isAdmin))"
    }
}

let user = User(username: "sara", isAdmin: true)
print(user)
```

Output (tested with Swift 6.4):

```text
User(username: sara, isAdmin: true)
```

Because `User` conforms to `CustomStringConvertible`, printing it uses the custom `description` value.

## 5. Practical Use Cases

- Defining a shared API for multiple models, such as products, users, and orders that all expose an identifier.
- Allowing different types to be passed into the same function, such as anything that can be displayed, sorted, or encoded.
- Using standard protocols like `Equatable` so your custom types can be compared with `==`.
- Using `Hashable` so custom types can be stored in sets or used as dictionary keys.
- Creating testable code by depending on protocols instead of concrete implementations.
- Separating behavior from data modeling when several unrelated types need the same capability.

## 6. Common Mistakes

### Mistake 1: Missing a required property or method

A type must implement every required member of the protocol unless a default implementation fully satisfies the requirement. If one requirement is missing, Swift reports a conformance error.

**Problem:** This struct declares conformance to the protocol but does not implement the required method, so Swift reports that the type does not conform to the protocol.

```swift
protocol Runnable {
    func run()
}

struct Robot: Runnable {
    let name: String
}
```

**Fix:** Add the missing method with the correct signature.

```swift
protocol Runnable {
    func run()
}

struct Robot: Runnable {
    let name: String

    func run() {
        print("\(name) is running.")
    }
}
```

The corrected version works because the type now satisfies every requirement in the protocol.

### Mistake 2: Using the wrong property mutability

Protocol property requirements are strict about whether a property is read-only or read-write. A protocol that says `get set` requires a mutable property.

**Problem:** The protocol requires a property that can be both read and changed, but this struct uses `let`, which creates an immutable property and does not satisfy the setter requirement.

```swift
protocol Adjustable {
    var level: Int { get set }
}

struct Volume: Adjustable {
    let level: Int
}
```

**Fix:** Change the property to `var` so it can be updated.

```swift
protocol Adjustable {
    var level: Int { get set }
}

struct Volume: Adjustable {
    var level: Int
}
```

The corrected version works because the property now matches the protocol’s read-write requirement.

### Mistake 3: Implementing a method with the wrong signature

The method name alone is not enough. Parameter labels, parameter types, return type, and mutating behavior must all match what the protocol requires.

**Problem:** This code defines a method with a different parameter type from the protocol requirement, so Swift does not count it as satisfying the protocol.

```swift
protocol Greeter {
    func greet(person: String)
}

struct Host: Greeter {
    func greet(person: Int) {
        print("Hello, guest #\(person)")
    }
}
```

**Fix:** Match the protocol requirement exactly.

```swift
protocol Greeter {
    func greet(person: String)
}

struct Host: Greeter {
    func greet(person: String) {
        print("Hello, \(person)")
    }
}
```

The corrected version works because the method signature exactly matches the protocol definition.

### Mistake 4: Forgetting `mutating` for value types

When a protocol requires a method that changes a struct or enum, the protocol method must be marked `mutating`, and the conforming value type must also use `mutating`.

**Problem:** This method changes a property inside a struct without being marked `mutating`, which causes an error such as “Cannot assign to property: 'self' is immutable”.

```swift
protocol Switchable {
    mutating func toggle()
}

struct Light: Switchable {
    var isOn = false

    func toggle() {
        isOn.toggle()
    }
}
```

**Fix:** Mark the conforming method as `mutating` when the value type changes its own state.

```swift
protocol Switchable {
    mutating func toggle()
}

struct Light: Switchable {
    var isOn = false

    mutating func toggle() {
        isOn.toggle()
    }
}
```

The corrected version works because value types need `mutating` when a method changes the instance.

## 7. Best Practices

### Practice 1: Conform to protocols that express real behavior

Choose protocols that describe meaningful capabilities. This makes your types easier to understand and your APIs easier to use.

A less helpful approach uses vague protocols with unclear purpose.

```swift
protocol DataThing {
    func process()
}
```

A better approach uses a protocol name that clearly describes the capability.

```swift
protocol Payable {
    func calculateTotal() -> Double
}
```

This matters because protocol names and requirements communicate design intent.

### Practice 2: Prefer standard library protocols when they fit

You usually do not need a custom protocol if Swift already provides one. Built-in protocols often integrate with operators and library APIs automatically.

```swift
struct Point: Equatable {
    let x: Int
    let y: Int
}

let a = Point(x: 1, y: 2)
let b = Point(x: 1, y: 2)

print(a == b)
```

Output (tested with Swift 6.4):

```text
true
```

This is better than inventing a custom equality protocol because `Equatable` is already understood throughout Swift.

### Practice 3: Keep protocol requirements focused

Small, focused protocols are usually easier to implement and combine than large protocols with unrelated requirements.

```swift
protocol Readable {
    func read() -> String
}

protocol Writable {
    func write(_ text: String)
}
```

This design is often better than one oversized protocol that forces conforming types to implement behavior they do not actually need.

## 8. Limitations and Edge Cases

- Conformance must fully satisfy all requirements. A similar method or property is not enough if the signature does not match exactly.
- A type cannot declare the same protocol conformance more than once in the same inheritance hierarchy. Swift may report a redundant conformance error.
- Protocol conformance to standard protocols like `Equatable` or `Hashable` can sometimes be synthesized automatically, but only when all stored properties also support that synthesis.
- If a protocol requirement is `static`, the conforming type must provide a type-level implementation, not an instance member.
- Value types need `mutating` for methods that change their own state, while classes do not use `mutating`.
- Protocol extensions can add default implementations, but that is not the same thing as automatic conformance. The type still has to declare that it conforms to the protocol.
- If a property requirement is read-only with `get`, a conforming type may satisfy it with either a stored property, a computed property, or a read-write property.

> A common point of confusion is protocol extension vs protocol conformance. A protocol extension can provide default behavior, but the type still needs to adopt the protocol explicitly if you want Swift to treat it as conforming.

## 9. Practical Mini Project

Let’s build a small example that uses protocol conformance to model billable items. This shows how different types can share one interface while keeping their own implementation details.

```swift
protocol Billable {
    var name: String { get }
    func totalPrice() -> Double
}

struct Product: Billable {
    let name: String
    let price: Double

    func totalPrice() -> Double {
        price
    }
}

struct Subscription: Billable {
    let name: String
    let monthlyPrice: Double
    let months: Int

    func totalPrice() -> Double {
        monthlyPrice * Double(months)
    }
}

func printInvoice(for items: [Billable]) {
    var grandTotal: Double = 0

    for item in items {
        let amount = item.totalPrice()
        print("\(item.name): $\(amount)")
        grandTotal += amount
    }

    print("Grand total: $\(grandTotal)")
}

let items: [Billable] = [
    Product(name: "Keyboard", price: 99.0),
    Subscription(name: "Cloud Backup", monthlyPrice: 5.0, months: 12)
]

printInvoice(for: items)
```

Output (tested with Swift 6.4):

```text
Keyboard: $99.0
Cloud Backup: $60.0
Grand total: $159.0
```

This mini project works because both `Product` and `Subscription` conform to `Billable`. The `printInvoice` function does not need to know the exact concrete type of each item. It only depends on the shared protocol requirements.

## 10. Key Points

- Protocol conformance means a type implements every requirement of a protocol.
- Structs, classes, and enums can all conform to protocols in Swift.
- Conformance requires exact matches for method signatures and property requirements.
- `get set` requires a mutable property, while `get` only requires readability.
- Value types use `mutating` when protocol-required methods change their own state.
- Standard protocols like `Equatable` and `CustomStringConvertible` provide immediate practical value.
- Protocol extensions can provide default implementations, but they do not replace explicit conformance declarations.

## 11. Practice Exercise

Create a protocol named `Playable` that requires a `title` property and a `play()` method. Then create a struct named `Song` that conforms to it.

- The protocol should require `title` as a read-only `String`.
- The struct should store both `title` and `artist`.
- The `play()` method should print a message using both values.
- Create one `Song` instance and call `play()`.

**Expected output:** A message like `Now playing: Midnight City by M83`.

**Hint:** Define the protocol first, then list it after the struct name and implement all required members.

```swift
protocol Playable {
    var title: String { get }
    func play()
}

struct Song: Playable {
    let title: String
    let artist: String

    func play() {
        print("Now playing: \(title) by \(artist)")
    }
}

let song = Song(title: "Midnight City", artist: "M83")
song.play()
```

Output (tested with Swift 6.4):

```text
Now playing: Midnight City by M83
```

## 12. Final Summary

Swift protocol conformance is one of the language’s core design tools. It lets you define behavior once in a protocol and then apply that shared contract to many different types. As you saw, conformance is not just about writing a colon and a protocol name. The type must satisfy every requirement exactly, including property mutability, method signatures, and `mutating` behavior for value types.

In practice, protocol conformance helps you write cleaner APIs, reuse logic across unrelated types, and take advantage of standard library protocols such as `Equatable`, `Hashable`, and `CustomStringConvertible`. If you want to go further, the next useful topic is protocol extensions, because they show how to add shared default behavior on top of protocol conformance.
