# Swift Protocol Properties and Methods Explained Clearly

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

Swift protocols let you describe what a type must provide without deciding how that type stores data or performs work. In this article, you will learn how protocol properties and methods are declared, what conforming types must implement, how `get` and `set` requirements work, and how to write clear protocol-based code that is easy to extend and reuse.

> **Quick answer:** In Swift, a protocol can require properties and methods, but it does not store values itself. A conforming type must provide matching properties and methods with compatible names, types, and mutability rules.

> **Difficulty:** Beginner
>
> **Helpful to know first:** You'll understand this better if you know basic Swift syntax, how structs and classes define properties and methods, and the difference between `let` and `var`.

## 1. What Is Protocol Properties & Methods?

A protocol in Swift is a blueprint for related functionality. When we talk about protocol properties and methods, we mean the requirements a protocol can declare so that conforming types all expose the same interface.

For example, a protocol can say that every conforming type must have a `name` property and a `describe()` method. Each type then decides how to satisfy those requirements.

- A protocol can require instance properties, type properties, instance methods, and type methods.
- A protocol property declaration describes the property name, type, and whether it must be readable or writable.
- A protocol method declaration describes the method name, parameters, and return type.
- The protocol does not provide storage for required properties.
- Any struct, class, or enum that adopts the protocol must satisfy all required members unless default implementations cover some methods.

This topic is often confused with regular type definitions. A struct or class contains actual implementation, but a protocol only defines the contract. That difference matters because protocols help multiple unrelated types behave consistently.

## 2. Why Protocol Properties & Methods Matters

Protocol requirements are important because they let you write flexible code that depends on capabilities instead of concrete types. Instead of asking for a specific struct or class, you can ask for any type that has the required properties and methods.

That makes code easier to test, reuse, and extend. If several types can all provide a title and print a summary, a protocol lets you treat them in a common way without forcing them into a shared inheritance hierarchy.

- They make APIs more consistent because all conforming types expose the same required interface.
- They reduce tight coupling because your functions can accept a protocol instead of one exact type.
- They support composition because a type can conform to multiple protocols at once.
- They work especially well with structs, which cannot inherit from other structs.

Use protocol properties and methods when you want to define shared behavior across different types. Do not use a protocol just to wrap a single type with no real variation, because that can add unnecessary abstraction.

## 3. Basic Syntax

The core idea is simple: declare property and method requirements inside a protocol, then make a type conform by implementing them.

### Declaring required properties

This protocol requires a readable `title` property and a readable and writable `count` property.

```swift
protocol Trackable {
    var title: String { get }
    var count: Int { get set }
}
```

The `title` requirement means conforming types must let other code read the property. The `count` requirement means conforming types must allow both reading and writing.

### Declaring required methods

This protocol requires two methods: one that prints information and one that updates the value.

```swift
protocol Updatable {
    func displayStatus()
    mutating func increment(by amount: Int)
}
```

The `mutating` keyword is important for value types such as structs and enums. It tells Swift that the method may change the instance.

### Conforming to the protocol

Here is a struct that satisfies both property and method requirements.

```swift
struct Counter: Trackable, Updatable {
    let title: String
    var count: Int

    func displayStatus() {
        print("\(title): \(count)")
    }

    mutating func increment(by amount: Int) {
        count += amount
    }
}
```

This works because the property names, property types, and method signatures all match what the protocols require.

### Protocol property rules to remember

- `{ get }` means read-only from the protocol's point of view.
- `{ get set }` means the property must be readable and writable.
- A required property can be stored or computed in the conforming type.
- If a protocol method changes a struct or enum, declare it with `mutating`.
- Type-level requirements use `static` in the protocol.

## 4. Step-by-Step Examples

The best way to understand protocol properties and methods is to see them in realistic examples. The examples below build from simple property requirements to writable properties and mutating methods.

### Example 1: A read-only property requirement

This protocol requires each type to expose a username, but it does not require that other code be allowed to change it.

```swift
protocol UserRepresentable {
    var username: String { get }
}
```

Now a struct can conform by providing a stored property with the same name and type.

```swift
struct Member: UserRepresentable {
    let username: String
}

let member = Member(username: "sam_dev")
print(member.username)
```

Output (tested with Swift 6.4):

```text
sam_dev
```

This is valid because a stored constant property can satisfy a `{ get }` requirement. The protocol only promises that the value can be read.

### Example 2: A read-write property requirement

If the protocol says a property must be both readable and writable, the conforming type must allow setting it.

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

This struct conforms because `volume` is declared with `var`.

```swift
struct Speaker: Adjustable {
    var volume: Int
}

var speaker = Speaker(volume: 5)
speaker.volume = 8
print(speaker.volume)
```

Output (tested with Swift 6.4):

```text
8
```

This example shows the practical difference between `{ get }` and `{ get set }`. If the protocol requires setting, a constant property is not enough.

### Example 3: A computed property can satisfy a protocol

A conforming type does not need to store the property directly. It can also compute the value.

```swift
protocol ShapeInfo {
    var areaDescription: String { get }
}

struct Rectangle: ShapeInfo {
    var width: Double
    var height: Double

    var areaDescription: String {
        let area = width * height
        return "Area: \(area)"
    }
}
```

This works because the protocol only cares that the property exists and has the correct type and access pattern. It does not care whether the value is stored or computed.

### Example 4: A method requirement with parameters

Methods in protocols work much like properties: the conforming type must provide a matching signature.

```swift
protocol Greeting {
    func greet(person: String) -> String
}

struct FriendlyGreeter: Greeting {
    func greet(person: String) -> String {
        return "Hello, \(person)!"
    }
}

let greeter = FriendlyGreeter()
print(greeter.greet(person: "Taylor"))
```

Output (tested with Swift 6.4):

```text
Hello, Taylor!
```

The method signature includes the parameter label, parameter type, and return type. All of them must match the protocol requirement.

### Example 5: A mutating method in a struct

When a protocol method is meant to change a value type, the protocol and the implementation must both reflect that.

```swift
protocol Resettable {
    mutating func reset()
}

struct Score: Resettable {
    var points: Int

    mutating func reset() {
        points = 0
    }
}

var score = Score(points: 42)
score.reset()
print(score.points)
```

Output (tested with Swift 6.4):

```text
0
```

This shows how protocols support behavior that changes a struct's stored values. Without `mutating`, the method could not reassign `points`.

## 5. Practical Use Cases

Protocol properties and methods are most useful when several types need to follow the same contract but use different implementations internally.

- Modeling displayable items, where different types all provide a `title` property and a `renderSummary()` method.
- Building reusable data-processing code that accepts any type with an `identifier` property and a validation method.
- Defining app services such as loggers or storage providers, where each implementation exposes the same methods but behaves differently.
- Creating game entities that all have `health` and methods like `takeDamage()` or `heal()`.
- Writing testable code by depending on a protocol instead of a concrete class, so a mock type can provide the same properties and methods in tests.
- Sharing behavior across structs and classes without forcing inheritance, which is especially useful in modern Swift design.

> A helpful way to think about protocols is this: if several types need to answer the same questions or perform the same actions, protocol properties and methods give you a clean shared contract.

## 6. Common Mistakes

Protocol requirements are simple once you understand the rules, but beginners often run into a few specific problems. Most of these issues happen because a protocol describes capabilities, not stored data or exact implementation details.

### Mistake 1: Treating a protocol property like stored storage

A property declared in a protocol is a requirement, not a stored property definition. The conforming type must provide that property, but the protocol itself does not store the value.

**Problem:** This code tries to put a stored property directly inside a protocol, which Swift does not allow.

```swift
protocol UserInfo {
    var name: String = "Guest"
}
```

**Fix:** Declare only the property requirement in the protocol, then provide the actual stored or computed property in the conforming type.

```swift
protocol UserInfo {
    var name: String { get }
}

struct GuestUser: UserInfo {
    var name: String = "Guest"
}
```

The corrected version works because the protocol only declares what must exist, while the struct supplies the real storage.

### Mistake 2: Using `get` when the property needs to be writable

If your code needs to update a property through the protocol, the requirement must include `set`. A read-only requirement cannot promise mutation.

**Problem:** The protocol says the property is read-only, so code using the protocol type cannot assign a new value. This often leads to a message like `Cannot assign to property: 'score' is a get-only property`.

```swift
protocol Scorable {
    var score: Int { get }
}

func resetScore(item: inout Scorable) {
    item.score = 0
}
```

**Fix:** Use `{ get set }` when writing through the protocol should be allowed.

```swift
protocol Scorable {
    var score: Int { get set }
}

struct Player: Scorable {
    var score: Int
}

func resetScore(item: inout Player) {
    item.score = 0
}
```

The corrected version works because the protocol now promises a writable property and the conforming type matches that contract.

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

When a protocol method is meant to change a struct or enum, the requirement usually needs the `mutating` keyword. Without it, conforming value types cannot implement mutation correctly.

**Problem:** This method changes the struct's stored property, but the protocol requirement does not allow mutation for value types.

```swift
protocol Counter {
    func increment()
}

struct StepCounter: Counter {
    var count = 0

    func increment() {
        count += 1
    }
}
```

**Fix:** Mark the protocol method as `mutating`, and also mark the struct implementation as `mutating`.

```swift
protocol Counter {
    mutating func increment()
}

struct StepCounter: Counter {
    var count = 0

    mutating func increment() {
        count += 1
    }
}
```

The corrected version works because Swift now knows that calling the method may change the value type itself.

### Mistake 4: Trying to satisfy a writable requirement with a read-only computed property

If a protocol requires `get set`, the conforming type must provide both reading and writing. A computed property with only a getter is not enough.

**Problem:** The protocol requires a settable property, but the implementation only returns a value. This can produce a conformance error such as `Type 'Book' does not conform to protocol` or a note that the candidate is not settable.

```swift
protocol Nameable {
    var title: String { get set }
}

struct Book: Nameable {
    var title: String {
        return "Swift Guide"
    }
}
```

**Fix:** Use a stored property or a computed property with both `get` and `set`.

```swift
struct Book: Nameable {
    var title: String
}
```

The corrected version works because the property now matches the protocol's writable requirement.

## 7. Best Practices

Good protocol design keeps APIs flexible without making them vague. These practices help your protocols stay useful as projects grow.

### Practice 1: Require the least permission necessary

If callers only need to read a value, prefer a read-only requirement. This keeps conforming types more flexible and reduces accidental mutation.

```swift
// Less preferred when writing is not needed
protocol Profile {
    var username: String { get set }
}

// Preferred when callers only read the value
protocol Profile {
    var username: String { get }
}
```

This is better because it expresses the smallest useful contract and avoids forcing every conforming type to expose mutation.

### Practice 2: Use meaningful method names that describe behavior

A protocol method should clearly communicate what an adopting type must do. Names like `process()` can be too vague if the role of the type is broad.

```swift
// Less clear
protocol FileAction {
    func doThing()
}

// Preferred
protocol FileAction {
    func saveToDisk()
}
```

This is better because anyone reading the protocol immediately understands the required behavior.

### Practice 3: Put shared default behavior in protocol extensions carefully

Protocol extensions are a great way to avoid repeated code, especially for methods that can have a sensible default. But defaults should support the contract, not hide important differences.

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

extension Describable {
    func describe() {
        print("Item: \(name)")
    }
}
```

This is better because conforming types get useful shared behavior while still being free to provide their own implementation if needed.

### Practice 4: Keep protocols focused

Small protocols are easier to understand, easier to conform to, and easier to reuse. Instead of one huge protocol with many unrelated requirements, split behavior into clear pieces.

```swift
// Less focused
protocol AppItem {
    var title: String { get }
    func save()
    func delete()
    func share()
    func archive()
}

// Preferred
protocol Titled {
    var title: String { get }
}

protocol Savable {
    func save()
}
```

This is better because each protocol represents one idea clearly, and types can adopt only what they actually need.

## 8. Limitations and Edge Cases

Protocol properties and methods are powerful, but they do not solve every design problem. These limitations and edge cases are useful to know before you rely on them heavily.

- A protocol can require a property, but it cannot provide stored property storage by itself.
- A `{ get }` requirement can be satisfied by either a stored property, a computed property, or even a read-write property, but callers using the protocol still only know it is readable.
- A `{ get set }` property requirement is stricter and can cause conformance errors if the implementation is read-only.
- Value types such as structs and enums need `mutating` for methods that change `self` or stored properties.
- Default implementations in protocol extensions can reduce boilerplate, but method dispatch rules can sometimes surprise beginners when a value is used through a protocol type.
- If something seems to be “not working,” check whether your code is calling a method declared in the protocol or only declared in an extension. That difference affects what implementation is chosen.
- Protocols describe shared capability, not inheritance-based shared state. If you need stored shared data, a base class or another design may be more suitable.

> A common mental model is: protocols define required shape and behavior, while concrete types decide how that shape and behavior are actually implemented.

## 9. Practical Mini Project

Let’s build a small example that uses protocol properties and methods in a realistic way. This mini project models tasks in a to-do app. Each task must have a title, a completion state, and methods for marking and describing itself.

The protocol defines the contract, and the struct provides the concrete implementation.

```swift
protocol TaskItem {
    var title: String { get }
    var isCompleted: Bool { get set }
    mutating func markCompleted()
    func summary()
}

struct DailyTask: TaskItem {
    let title: String
    var isCompleted: Bool

    mutating func markCompleted() {
        isCompleted = true
    }

    func summary() {
        let status = isCompleted ? "Done" : "Pending"
        print("\(title): \(status)")
    }
}

var task = DailyTask(title: "Write Swift notes", isCompleted: false)
task.summary()
task.markCompleted()
task.summary()
```

Output (tested with Swift 6.4):

```text
Write Swift notes: Pending
Write Swift notes: Done
```

This example shows several important ideas together:

- `title` is read-only in the protocol, so the struct can safely use `let`.
- `isCompleted` is writable, so the protocol uses `get set`.
- `markCompleted()` is marked `mutating` because it changes a struct property.
- `summary()` is a shared behavior requirement that each conforming type can implement its own way.

If you run the code, the output is:

```swift
Write Swift notes: Pending
Write Swift notes: Done
```

This is a small example, but it matches the same design pattern used in larger apps: define what a type must provide, then let each type supply its own implementation.

## 10. Key Points

- Protocol properties define requirements, not stored values.
- `{ get }` means readable; `{ get set }` means readable and writable.
- Protocol methods describe behavior that conforming types must implement.
- Use `mutating` for protocol methods that change structs or enums.
- A conforming type can satisfy property requirements with stored or computed properties, as long as the access rules match.
- Protocol extensions can provide default method implementations to reduce repeated code.
- Small, focused protocols are usually easier to reuse than large all-in-one protocols.

## 11. Practice Exercise

Try building your own protocol for a simple media player item.

- Create a protocol named `Playable`.
- Require a read-only `name` property of type `String`.
- Require a writable `isPlaying` property of type `Bool`.
- Require a `mutating` method named `play()`.
- Create a struct named `Song` that conforms to the protocol.
- Print the state before and after calling `play()`.

**Expected output:**

```swift
Calm Waves false
Calm Waves true
```

**Hint:** The `play()` method should set `isPlaying` to `true`, so both the protocol requirement and the struct implementation need to support mutation.

```swift
protocol Playable {
    var name: String { get }
    var isPlaying: Bool { get set }
    mutating func play()
}

struct Song: Playable {
    let name: String
    var isPlaying: Bool

    mutating func play() {
        isPlaying = true
    }
}

var song = Song(name: "Calm Waves", isPlaying: false)
print(song.name, song.isPlaying)
song.play()
print(song.name, song.isPlaying)
```

Output (tested with Swift 6.4):

```text
Calm Waves false
Calm Waves true
```

This solution works because the property access levels and the method requirement all match what the protocol promises.

## 12. Final Summary

Swift protocol properties and methods let you define a clean contract that multiple types can follow. A protocol can require readable or writable properties, instance or type methods, and mutating behavior when value types need to change themselves. That makes protocols one of the most important tools for writing flexible, reusable Swift code.

As you saw in the examples, the most important rules are matching the requirement correctly, choosing `get` versus `get set` carefully, and using `mutating` when structs or enums need to update their data. Once those ideas are clear, protocol conformance becomes much easier to read and debug.

A strong next step is to learn more about Swift protocol extensions and default implementations, because they build directly on the property and method rules you covered here and make protocol-oriented design even more powerful.
