Giới thiệu

Trong lập trình Swift, chắc hẳn chúng ta sử dụng chắc hẳn sử dụng rất nhiều kiểu dữ liệu Enum. Enum trong swift dùng để định nghĩa một tập hợp có số phần tử cố định và liệt kê sẵn, chúng ta không thể không thể thêm hoặc bớt được số phần tử. Trong bài viết này tôi muốn giới thiệu đến các bạn cách dùng Enum một trường hợp của enum có chứa giá trị liên kết (Associated Values ) mà chúng ta không thể gán cho chúng được RawValue cho chúng được.

Raw Values

Với Enums, bạn có thể định nghĩa mỗi Enum cho một giá trị Inter, Double, String hay Character …:

enum Airport: Int {
    case munich = 0
    case sanFrancisco = 1
    case singapore = 2
}
enum Airport: String {
    case munich = "MUC"
    case sanFrancisco = "SFO"
    case singapore = "SIN"
}

Ở đây, các giá trị được được gắn cho mỗi Enum được gọi là Rawvalue.

Associated Values

Bây giờ bạn có một list các sân bay được gán với tên của thành phố nơi chúng được đặt. Nhưng sau đó bạn nhận ra rằng có một số thành phố có nhiều sân bay: ví dụ như London. Trong trường hợp này, bạn phải đổi tên tất cả các trường hợp enum để đạt được tên phù hợp, ví dụ như:

enum Airport: String {
    case munichFranzJosephStrauss = "MUC"
    case sanFranciscoInternational = "SFO"
    case singaporeChangi = "SIN"
    case londonStansted = "STN"
    case londonHeathrow = "LHR"
    case londonGatwick = "LGW"
}

Nhưng chúng ta thấy rằng điều này là không phù hợp với cách liệt kê như chúng ta dự định ban đầu, bài toán của chúng ta là có một Enum chứa các sân bay với tên thành phố. Nếu sử dụng Enum như trên thì chúng ta đang bị trộn lẫn hai loại thông tin khác nhau: thành phố nơi có sân bay (như ban đầu) + sân bay của thành phố đó, đây rốt cuộc chỉ là một danh sách.
May mắn thay, Swift đưa ra cách giải quyết vấn đề này: Nó cho phép bạn ràng buộc một (hoặc nhiều) các giá trị bổ sung vào một trường hợp enum. Các giá trị này được gọi là các giá trị liên kết (associated values). Ví dụ: Chúng tôi có thể sử dụng String để xác định sân bay cụ thể và vì nó chỉ cần cho thành phố London trong danh sách hiện tại của chúng tôi, chúng tôi chỉ thêm giá trị liên quan này vào trường hợp ở Lodon:

enum Airport {
    case munich
    case sanFrancisco
    case singapore
    case london(airportName: String)
}

Bây giờ chúng a trở lại với enum clear hơn và chỉ sử dụng một giá trị bổ sung để xác định các sân bay cụ thể khi cần thiết.
Nhưng một String không phải là an toàn và dễ bị lỗi, chúng ta có thể đi thêm một bước nữa và thay thế bằng một enum khác:

enum LondonAirportName {
    case stansted
    case heathrow
    case gatwick
}

Bây giờ enum Airport của chúng tôi sẽ như sau:

enum Airport {
    case munich
    case sanFrancisco
    case singapore
    case london(airportName: LondonAirportName)
}

Và khi định nghĩa giá trị của Enum chúng ta có thể chỉ định giá trị cho Enum đó như sau

var airport: Airport

airport = .munich
airport = .london(airportName: .heathrow)

hoặc

var airport: Airport

airport = .london(.heathrow)

The Problem with Associated Values

Bạn có thể nhận ra rằng với Enum có chứa Associated Values như ví dụ bên trên chúng ta không hề có giá trị Rawvalue. Tại vì trong Swift không cho phép chúng ta có cả hai: rawValueAssociated Values trong cùng một enum.
Một Swift enum có thể có cả raw values hoặc associated values.
Tại sao lại như vậy?
Đó là vì định nghĩa của raw values: raw values là một cái gì đó nhận dạng duy nhất một giá trị của một loại cụ thể. “Duy nhất” có nghĩa là bạn không mất bất kỳ thông tin nào bằng cách sử dụng giá trị nguyên thay vì giá trị ban đầu. Nó được chính thức hoá trong Swift với giao thức RawRepresentable mà tài liệu của nó nêu rõ:

Với kiểu RawRepresentable, bạn có thể chuyển qua lại giữa một kiểu tùy chỉnh và một kiểu RawValue có liên quan mà không làm mất giá trị của kiểu RawRepresentable ban đầu.
Để hiểu rõ hơn, chúng ta cùng nhau đi vào các ví dụ cụ thể.

Enums Without Associated Values

Enum ban đầu của chúng ta không có bất kỳ Associated Values nào, chỉ có Raw Value:

enum Airport: String {
    case munich = "MUC"
    case sanFrancisco = "SFO"
    case singapore = "SIN"
}

Chúng ta dễ dàng chuyển đổi giữa rawvalue và kiểu Enum của nó từ Raw Value

let airport = .sanFrancisco

let airportRawValue = airport.rawValue // "SFO"

let reconstructedAirport = Airport(rawValue: airportRawValue)! // .sanFrancisco

Enums With Associated Values

Bây giờ chúng ta thử làm tương tự như trên với trường hợp enum với Associated Values


enum Airport: String {
    case munich = "MUC"
    case sanFrancisco = "SFO"
    case singapore = "SIN"
    case london(airportName: LondonAirportName) = "LON"
}

⚠️ This is not possible in Swift.
Chúng ta vẫn có thể đặt biến cho trường hợp này:

let airport = .london(airportName: .stansted)

let airportRawValue = airport.rawValue // “LON”

Tuy nhiên, chúng ta không thể lấy lại giá trị Enum ban đầu từ rawvalue đó bởi vì không có cách nào để cho biết giá trị liên quan nào được lựa chọn:

let reconstructedAirport = Airport(rawValue: airportRawValue)! // .london(💥❓)

How to Get Your Raw Values Back

Khi chúng ta không thể lấy giá trị Rawvalue ban đầu theo cách trên, chúng ta cần phải định nghĩa một giá trị thay thế tương tự:

enum Airport {
    case munich
    case sanFrancisco
    case singapore
    case london(airportName: LondonAirportName)
    
    var cityIdentifier: String {
        switch self {
        case .munich:
            return "MUC"
        case .sanFrancisco:
            return "SFO"
        case .singapore:
            return "SIN"
        case .london:
            return "LON"
        }
    }
}

Và thực hiện khai báo:

var airport: Airport = .london(airportName: .heathrow)

Lúc này bạn muốn lấy Rawvalue thay vì bạn gọi airport.rawValue thì bạn gọi* airport.cityIdentifier*.

Tổng kết

Trên đây tôi đã giới thiệu với các bạn về cách sử dụng Enum với trường hợp Enum đó có Associated Values (giá trị liên kết), nếu có bất kì câu hỏi nào hoặc góp ý gì, vui lòng comment bên dưới.
Cám ơn các bạn!
Nguồn: Medium