Những điểm cần lưu ý khi sử dụng enum trong Swift.
Bài đăng này đã không được cập nhật trong 4 năm
- 
Enumlà một trong nhữngAPImạnh mẽ được yêu thích nhất trongSwift. Thực tế là trongSwiftthìEnumđược phát triển cẩn thận để người dùng có thể sử dụng trong nhiều trường hợp với nhiềutypekhác nhau.
- 
Tuy nhiên vẫn có một số loại trường hợp chúng ta cần tránh khi sử dụng Enumvì rất có thể chúng ta sẽ tự làm chúng ta trở nên ngớ ngẩn cũng như việccodetrở nên khó đọc khó hnhau
1/ Liệt kê thiếu trường case:
- Lấy ví dụ cụ thể khi làm việc với app Podcastvới các danh mục khác nhau và haicaseđặc biệt sử dụng cho toàn bộpodcasthoặc không sử dụng cho bất kỳ mộtpodcastnào:
extension Podcast {
    enum Category: String, Codable {
        case none
        case all
        case entertainment
        case technology
        case news
        ...
    }
}
- Sau đó chúng ta sẽ triển khai một filterđể so sánhstring valuengười dùng đã chọn trong quá trình sử dụng appPodcast:
extension Podcast {
    func matches(filter: Filter) -> Bool {
        switch filter.category {
        case .all, category:
            return name.contains(filter.string)
        default:
            return false
        }
    }
}
- Mới nhìn thì triển khai trên trông có vẻ ổn. Tuy nhiên, nếu chúng ta dừng lại suy nghĩ kỹ 1 tẹo thì bản thân trong `Swift` cũng có một tính năng giải quyết cho trường hợp `value` của `variable` có thể có hoặc không, đó là `optronglđã
- Vì vậy chúng ta sẽ  sử dụng `optional` cho `category` trong `struct` `Podcast` để tận dụng tất cả các tính năng mà `Swift` đã hỗ trợ xử lý các giá trị `optional` (`if let`) :
```swift
struct Podcast {
    var name: String
    var category: Category?
    ...
}
- Sử dụng switchở đây là một điều thú vị khi chúng ta có thể áp dụngoptionalCategoryở bên trên màfunctionhoàn toàn không thay đổi cơ chế hoạt động. Cụ thể ở đây chúng ta sử dụngcase noneđể liệt kê cáccasechúng ta đãlack:
func title(forCategory category: Podcast.Category?) -> String {
    switch category {
    case .none:
    return "Uncategorized"
    case .all:
        return "All"
    case .entertainment:
        return "Entertainment"
    case .technology:
        return "Technology"
    case .news:
        return "News"
    ...
    }
}
2/ Sử dụng tên cụ thể cho từng case:
- 
Trên thực tế thì một podcastkhông thể thuộc tất cả cáccategorynên trường hợpallchỉ phát huy tác dụng khi sử dụng cùngfilter.
- 
Vì vậy, thay vì bao gồm trường hợp đó trong danh mục Danh mục chính của chúng tôi, thay vào đó, hãy tạo một loại chuyên dụng cụ thể cho miền lọc. Bằng cách đó, chúng tôi có thể phân tách các mối quan tâm khá gọn gàng và vì chúng tôi đang sử dụng các loại lồng nhau, chúng tôi có thể đặt địa chỉ mới của mình sử dụng cùng tên Danh mục, chỉ lần này nó sẽ được lồng trong mô hình Bộ lọc của chúng tôi - như thế này: 
- 
Thay vì chỉ sử dụng các casetrongenumCategorychúng ta sẽ tạo mộtenumriêng để sử dụng riêng choFilter. Bằng cách này chúng ta có thể tách biệt rõ ràng các danh mục khi chúng ta bắt đầu sử dụngnested-typenhư sau:
extension Filter {
    enum Category {
        case any
        case uncategorized
        case specific(Podcast.Category)
    }
}
- Bằng cách sử dụng wheretrongswitch-casechúng ta đã triển khai đượclogic filtermột cách cụ thể và ngắn gọn dễ hiểu hơn:
extension Podcast {
    func matches(filter: Filter) -> Bool {
        switch filter.category {
        case .any where category != nil,
             .uncategorized where category == nil,
             .specific(category):
            return name.contains(filter.string)
        default:
            return false
        }
  một
}
- Giờ đây chúng ta có thể tiếp tGc với việc xóa tất cả các casekhỏi danh sáchPodcast.Categoryđể có một danh sách đơn giản hơn nhiều về từng danh mục:
extension Podcast {
    enum Category: String, Codable {
        case entertainment
        case technology
        case news
        ...
    }
}
3/ Các trường hợp với các custom type:
- 
EnumPodcast.Categorycần được lưu ý về khả năng có thể mở rộng tron tương lai khi chúng ta bổ sung cáccase. Để làm được điều này chúng ta có thể xem việc tạo mộtcasetuỳ chình vớitypecụ thể.
- 
Như vây thì casecần cóassociated-valuenhư việc chúng ta córawValuedưới dạngStringđể đại diện chocategorycustom:
extension Podcast {
    enum Category: Codable {
        case all
        case entertainment
        case technology
        case news
        ...
        case custom(String)
    }
}
- 
Trong khi associated-valuecó thể hữu ích trong một số bối cảnh thì ở bối cảnh này nó lại không phát huy được sức mạnh vì trong tương lai với cáccustom-typeđặc biệt chúng ta sẽ cần có thể cácmethodđểdecodehoặcencodecác value pyhù hợp vớirawValue.
- 
Vì vậy chúng ta cần khám phá một hướng tiếp cận khác như chuyển đổi enumCategorthànhRawRepresentable. Chúng ta sẽ sử dụng tính năng có sẵn củaSwiftđể thực hiện việcencode/decodekhi làm việc vớistring valuecủarawValue.
extension Podcast {
    struct Category: RawRepresentable, Codable, Hashable {
        var rawValue: String
    }
}
- Chúng ta hiện tại có thể tự do khởi tạo Categorytừ bất kỳcustom stringmà chúng ta muốn để có thể phù hợp với các tính năng trong tương lai. Tuy nhiên để đảm bảo tính năng mới có thể tương thích ngược lại thì chúng ta cần sử dụng cácstaticcho cáctypemới:
extension Podcast.Category {
    static var entertainment: Self {
        Self(rawValue: "entertainment")
    }
    static var technology: Self {
        Self(rawValue: "technology")
    }
    static var news: Self {
        Self(rawValue: "news")
    }
    
    ...
    static func custom(_ id: String) -> Self {
        Self(rawValue: id)
    }
}
- Chúng ta có functioncó têntitietrả vềStringứng với từngcasecủaCategorykhi sử dụngswitchmethod. Tuy nhiên với những thay đổi bên trên chúng ta cần có điều chỉnh thích hợp để trả về các giá trị chotitlethích hợp. Chúng ta sẽ di chuyển các giá trịstringđó sangLocalizable.stringvà thực hiện các xử lý cần thiết trước khi trả về giá trịStringcuối cùng chotitle:
func title(forCategory category: Podcast.Category?) -> String {
    guard let id = category?.rawValue else {
        return NSLocalizedString("category-uncategorized", comment: "")
    }
    let key = "category-\(id)"
    let string = NSLocalizedString(key, comment: "")
    // Handling unknown cases by returning a capitalized version
    // of their key as a fallback title:
    guard string != key else {
        return key.capitalized
    }
    return string
}
4/ Đặt tên tự động cho static property:
function
- Đây thực chất là một ịnh mà chúng ta cần bổ sung thêm khi một nhược điểm của phương pháp dựa trên code-baseở trên là giờ đây chúng ta phải thủ công xác định cácrawValuecho mỗistatic propertysong đó là điều mà chúng ta có thể dễ dàng giải quyết với việc sử dụngkeyword#functionđể tự động thay thế tên củafunction(hoặcproperty) như sau:
extension Podcast.Category {
    static func autoNamed(_ rawValue: StaticString = #function) -> Self {
        Self(rawValue: "\(rawValue)")
    }
ion
- Với extensionở trên thì chỉ cần gọi đếnautoNamed()tronvàmỗiAPIcategoryđược tích hợp sẵn vàSwiftsẽ tự động điền cácrawValueđó cho chúng ta:
extension Podcast.Category {
    static var entertainment: Self { autoNamed() }
static var technology: Self { autoNamed() }
static var news: Self { autoNamed() }
    ...
    static func custom(_ id: String) -> Self {
        Self(rawValue: id)
    }
}
All rights reserved
 
  
 