Enum là gì?

An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.

=> Enum định nghĩa 1 nhóm các giá trị có liên quan đến nhau và cho phép bạn làm việc với những giá trị đó 1 cách an toàn trong code của bạn

Nhắc lại 1 chút về định nghĩa cho vui thôi 😄 bài viết này mình sẽ tập trung nói về cú pháp, cách sử dụng và cùng sử dụng nó trong các bài toán thực tế.

Enum cơ bản

Nếu bạn làm việc với scroll direction, bài toán đặt ra là bạn cần phải xác định được chiều scroll. Chắc hẳn khi các bạn tập tọe code thì điều đầu tiên các bạn nghĩ đến là là tạo 1 biến thế này đúng không? (trước mình toàn thế thôi á 😄)

var isScrollUp = true

Nhưng thế này là không ổn nhé. Vì không may scrollView của bạn scroll được cả 4 hướng thì sao? (khóc-ing)

Trong tình huống đó thì enum sẽ là sự lựa chọn hoàn hảo, cùng xem ví dụ nhé:

enum ScrollDirection {
    case left
    case right
    case up
    case down
}

Và sau đó bạn hoàn toàn có thể control việc scroll nhé: (cười)

let scrollDirection = ScrollDirection.down

switch scrollDirection {
case .down:
    print("scroll down")
default:
    break
}

if case .down == scrollDirection {
    print("scroll down")
}

if scrollDirection == .down {
    print("scroll down")
}

Enum values

Trong nhiều trường hợp bạn cần set giá trị cho từng case của enum để tiện sử dụng. Hiểu được điều này Swift cung cấp nhiều cách để bạn có thể set giá trị cho nó

// Mapping to Integer
enum Movement: Int {
    case left = 0
    case right = 1
    case top = 2
    case bottom = 3
}

// You can also map to strings
enum House: String {
    case baratheon = "Ours is the Fury"
    case greyjoy = "We Do Not Sow"
    case martell = "Unbowed, Unbent, Unbroken"
    case stark = "Winter is Coming"
    case tully = "Family, Duty, Honor"
    case tyrell = "Growing Strong"
}

// Or to floating point (also note the fancy unicode in enum cases)
enum Constants: Double {
    case π = 3.14159
    case e = 2.71828
    case φ = 1.61803398874
    case λ = 1.30357
}

Trong trường hợp giá trị của case trong Enum là Int hoặc String. Bạn có thể bỏ qua giá trị của nó mà Swift vẫn tự hiểu và chạy đúng:

// Mercury = 1, Venus = 2, ... Neptune = 8
enum Planet: Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

// North = "North", ... West = "West"
enum CompassPoint: String {
    case North, South, East, West
}

Nesting Enums

Hãy tưởng tượng một nhân vật trong một trò chơi. Mỗi nhân vật có thể có một vũ khí, tất cả các nhân vật đều có quyền truy cập cùng một bộ vũ khí. Tất cả các nhân vật khác trong trò chơi không có quyền truy cập vào những vũ khí (họ đang có nhiệm vụ khác...)

enum Character {
  enum Weapon {
    case Bow
    case Sword
    case Lance
    case Dagger
  }
  enum Helmet {
    case Wooden
    case Iron
    case Diamond
  }
  case Thief
  case Warrior
  case Knight
}

Bây giờ bạn có một hệ thống có thứ bậc để mô tả các mục khác nhau mà nhân vật của bạn có quyền truy cập rồi:

let character = Character.Thief
let weapon = Character.Weapon.Bow
let helmet = Character.Helmet.Iron

Containing Enums

Để dễ hiểu: Containing Enums có nghĩa là bạn có thể để enum nằm trong struct hay class khác. Vẫn với ví dụ về vũ khí bên trên nhé:

struct Character {
   enum CharacterType {
    case Thief
    case Warrior
    case Knight
  }
  enum Weapon {
    case Bow
    case Sword
    case Lance
    case Dagger
  }
  let type: CharacterType
  let weapon: Weapon
}

let warrior = Character(type: .Warrior, weapon: .Sword)

Nó vẫn giúp giữ thông tin liên hệ gữa các đối tuơng đúng không? 😃

Associated values

Đây là 1 cách tuyệt vời để bạn có thể gắn thêm thông tin vào 1 enum case. Ví dụ bạn cần xây định nghĩa barcode của sản phẩm. Có 2 loaị barcode là UPC gồm 4 chữ số, và QRCode là 1 đoạn String. Bạn có thể định nghĩa nó như sau:

enum Barcode {
    case upc(Int, Int, Int, Int)
    case qrCode(String)
}

Khi đó bạn có thể tạo 1 biến barcode như sau:

var productBarcodeUPC = Barcode.upc(8, 85909, 51226, 3)
var productBarcodeQr = Barcode.qrCode("ABCDEFGHIJKLMNOP")

Để so sánh 1 enum có Associated values bạn có thể làm như sau:

switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
    print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
    print("QR code: \(productCode).")
}

bạn có thể bỏ "let" (hoặc var) đi và dùng như sau:

switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
    print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
    print("QR code: \(productCode).")
}

Methods

Bạn có thể thêm các hàm cho enum như sau:

enum Device: String {
    case iphone
    case ipad
    case tv
    case watch

    func getOS() -> String {
        switch self {
        case .ipad, .iphone:
            return "iOS"
        case .watch:
            return "WatchOS"
        case .tv:
            return "tvOS"
        }
    }
}

Properties

Bạn không thể thêm Stored Properties cho enum. Nhưng bạn vẫn có thể tạo computed properties như sau:

enum Device: String {
    case iphone
    case ipad
    case tv
    case watch

    var description: String {
        switch self {
        case .ipad, .iphone:
            return "iOS"
        case .watch:
            return "WatchOS"
        case .tv:
            return "tvOS"
        }
    }
}

Static Methods

Bạn cũng có thể thêm 1 static methods cho enum như sau:

enum Device: String {
    case iphone
    case ipad
    case tv
    case watch

    static func biggestDevice() -> Device {
        return Device.tv
    }
}
print(Device.biggestDevice())

Mutating Methods

Các methods có thể viết dưới dang mutating, và cho phép bạn thay đổi case của chính self:

enum DeviceMode {
    case metting
    case outdoor
    case silent
    case normal

    mutating func setSilent() {
        self = .silent
    }
}
var deviceMode =  DeviceMode.metting
deviceMode.setSilent()
print(deviceMode)

Advanced Enum Usage

Protocols

Swift cho phép bạn sử dụng Protocols và Protocol Extensions với Enum:

protocol CustomStringConvertible {
    var description: String { get }
}

enum DeviceMode: String, CustomStringConvertible {
    case metting
    case outdoor
    case silent
    case normal

    var description: String {
        return self.rawValue
    }
}

Extensions

Bạn có thể sử dụng extension để bổ sung các properties và methods:

enum DeviceMode: String {
    case metting
    case outdoor
    case silent
    case normal
}

extension DeviceMode {
    var volume: Int {
        switch self {
        case .metting:
            return 10
        case .outdoor:
            return 100
        case .silent:
            return 0
        case .normal:
            return 50
        }
    }
}

Use Enum for Error Handling

Bạn có thể sử dụng Enum để định nghĩa tập hợp các lỗi có thể gặp phải. Khi định nghĩa lỗi Decode Data:

enum DecodeError: Error {
  case typeMismatch(expected: String, actual: String)
  case missingKey(String)
  case custom(String)
}

Khi định nghĩa lỗi API:

enum APIError : Error {
    // Can't connect to the server (maybe offline?)
    case connectionError(error: NSError)
    // The server responded with a non 200 status code
    case serverError(statusCode: Int, error: NSError)
    // We got no data (0 bytes) back from the server
    case noDataError
    // The server response can't be converted from JSON to a Dictionary
    case jsonSerializationError(error: Error)
    // The Argo decoding Failed
    case jsonMappingError(converstionError: DecodeError)
}

Dùng để định nghĩa HTTP status error:

enum HttpError: String {
  case Code400 = "Bad Request"
  case Code401 = "Unauthorized"
  case Code402 = "Payment Required"
  case Code403 = "Forbidden"
  case Code404 = "Not Found"
}

Trên đây là những gì tìm hiểu và sưu tầm được, nếu có gì sai sót mong các bạn bổ sung giúp mình 😃

Nguồn: