DEV Community

Cover image for KeyPath in Swift: Syntax.
Sergey Leschev
Sergey Leschev

Posted on • Edited on

KeyPath in Swift: Syntax.

KeyPath is a type that represent a references to properties and subscripts. Since it is a type, you can store it in a variable, pass it around, or even perform an operation on a key path. We can use a key path to get/set their underlying values at a later time.

Let's create a few structs, which we will use as an example.


// 1
struct User {
    let name: String
    let email: String
    let address: Address?
    let role: Role
}

// 2
struct Address {
    let street: String
}

// 3
enum Role {
    case admin
    case member
    case guest

    var permissions: [Permission] {
        switch self {
        case .admin:
            return [.create, .read, .update, .delete]
        case .member:
            return [.create, .read]
        case .guest:
            return [.read]
        }
    }
}

// 4
enum Permission {
    case create
    case read
    case update
    case delete
}
Enter fullscreen mode Exit fullscreen mode

1 We declare a User struct which stores name, email, address, and role.
2 Address is a struct that keeps the information about the address. For brevity, we only have a street name.
3 We have all possible roles as an enum, Role. It also holds a computed property that returns an array of Permission that particular have.
4 Permission is an enum of all possible actions a user can make.

Key Path Expression

Key Path expressions have the following form:

\type name.path
Enter fullscreen mode Exit fullscreen mode

Type Name

The type name is the name of a concrete type, including any generic parameters if the types have one.

// 1
let stringDebugDescription = \String.debugDescription 
// KeyPath

// 2
let userRole = \User.role 
// KeyPath

// 3
let firstIndexInteger = \[Int][0] 
// WritableKeyPath<[Int], Int>

// 4
let firstInteger = \Array.first 
// KeyPath<[Int], Int?>
Enter fullscreen mode Exit fullscreen mode

1 Key path which type is a String.
2 Key path which type is our custom struct, User.
3, 4 We can reference array by the full type name (Array) or shortened form ([]), but you have to also specify its generic type, [Int] and Array<Int>.

Like most things in Swift, type names can be omitted in contexts where type can be inferred.

In the following example, type name can be inferred from the explicit variable type, so we can leave the type name blank. Notice that we still need \ and .

// \String.debugDescription => \.debugDescription
let stringDebugDescription: KeyPath<String, String> = \.debugDescription
Enter fullscreen mode Exit fullscreen mode

Path

The path can be property names, subscripts, optional-chaining expressions, and forced unwrapping expressions. Basically, everything that we usually use when reference value on an instance object/struct.

// 1 
let userName = \User.name
//  KeyPath

// 2
let firstIndexInteger = \[Int][0]
// WritableKeyPath<[Int], Int>

// 3
let streetAddress = \User.address?.street
// KeyPath

// 4
let forceStreetAddress = \User.address!.street
// KeyPath
Enter fullscreen mode Exit fullscreen mode

1 Key path which path is a property name.
2 Key path which path is a subscript.
3 Key path which path is an optional-chaining to a property.
4 Key path which path is forced unwrapping to a property name.

Path can be repeated as many times as needed, and it can also refer to a computed property name.

let userRolePermissions = \User.role.permissions
// KeyPath<User, [Permission]>

let firstUserRolePermissions = \User.role.permissions[0]
// KeyPath<User, Permission>
Enter fullscreen mode Exit fullscreen mode

Syntax

I think the syntax of a key path should become clearer to you at this point. Since KeyPath is a way to define a reference to properties and subscripts, that means it can use any expression we use to reference properties and subscripts from an instance.

To convert a normal reference to a key path, we replace any class/struct instance with a backslash ( \ ) followed by that instance type.

let streetValue = user.address?.street
// KeyPath version referencing the same value.
let streetKeyPath = \User.address?.street
Enter fullscreen mode Exit fullscreen mode

Next Articles:


Contacts
I have a clear focus on time-to-market and don't prioritize technical debt. And I took part in the Pre-Sale/RFX activity as a System Architect, assessment efforts for Mobile (iOS-Swift, Android-Kotlin), Frontend (React-TypeScript) and Backend (NodeJS-.NET-PHP-Kafka-SQL-NoSQL). And I also formed the work of Pre-Sale as a CTO from Opportunity to Proposal via knowledge transfer to Successful Delivery.

🛩ī¸ #startups #management #cto #swift #typescript #database
📧 Email: sergey.leschev@gmail.com
👋 LinkedIn: https://linkedin.com/in/sergeyleschev/
👋 LeetCode: https://leetcode.com/sergeyleschev/
👋 Twitter: https://twitter.com/sergeyleschev
👋 Github: https://github.com/sergeyleschev
🌎 Website: https://sergeyleschev.github.io
🌎 Reddit: https://reddit.com/user/sergeyleschev
🌎 Quora: https://quora.com/sergey-leschev
🌎 Medium: https://medium.com/@sergeyleschev
🖨ī¸ PDF Design Patterns: Download

Top comments (0)