So sánh if let và guard let

Chào mừng bạn đến với Fx Studio. Chủ đề bài viết lần này sẽ nói về một từ khóa khá phổ biến trong Swift, đó là Guard. Nếu bạn là một iOS developer, thì đã rất quen thuộc với Guard rồi. Và mục đích bài viết này sẽ giúp bạn hệ thống lại khi nào sẽ sử dụng Guard cho hiệu quả.

Còn nếu mọi việc đã ổn rồi, thì …

Bắt đầu thôi!

Chuẩn bị

Về mặt kiến thức, bạn chỉ cần biết sơ qua về Swift là ổn rồi. Nhưng nếu bạn chưa biết gì về Swift, thì có thể đọc qua bài viết ở link sau:

  • Basic Swift trong 10 phút

Về tools, bạn không cần quá lo lắng. Các Xcode bây giờ đều chạy tốt và có sẵn Guard trong Core rồi.

Guard Keyword

Khi học về Swift, bạn sẽ bắt gặp kiểu cú pháp này khá là nhiều trong các project.

func hello(name: String?) {

guard let name = name else {  
    return  
}  
print("Hello, \(name)")  
}

Đây chính là ví dụ minh họa đơn giản nhất cho việc sử dụng từ khóa guard trong chương trình. Với Guard, bạn sẽ dùng kèm với các từ khóa khác, như là: else, throw, return . Và chúng ta có thể chia chúng ra với 3 dạng mục đích chính.

  • Exit Early
    • Đây là cách sử dụng phổ biết nhất. Thường được áp dụng trong các fucntion. Với mục đích kết thúc sớm function đó, khi dữ liệu đầu vào của bạn là các Optionals.
  • Validate Requirements
    • Tiếp theo là cách dùng tinh tế hơn. Khi bạn muốn kiểm tra điều kiện và kết hợp với xử lý lỗi, thay vì return một cách vô hồn.
  • Segment Logic
    • Bản chất của nó cũng tương tự như enum ValidationError: Error {
         case fullNameIsRequired  
         case lastNameIsRequired  
      
      } func validate(name: String) throws {
         guard name.count > 3 else {  
             throw ValidationError.fullNameIsRequired  
         }  
         guard name.contains(" ") else {  
             throw ValidationError.lastNameIsRequired  
         }  
      
      } 1, nên công dụng tiếp theo là điều hướng logic trong code. Bên cạnh đó, nó cũng bảo vệ các đoạn code của bạn, nhằm đảm bảo tính toàn vẹn của dữ liệu đầu vào.

Khá là hay phải không nào. Chúng ta sẽ tìm hiểu dần dần thông qua các ví dụ ở phần sau nhóe.

Else Throw

Để xóa đi ý niệm tồn tại lâu với return, thì chúng ta cần tạo ra kết hợp giữa các từ khóa sau:

guard + else + throw

Mục đích chính là bạn sẽ không xử lý lỗi phát sinh tại function đó. Bạn chỉ cần chuyển giao nó về function cao hơn để giải quyết. Tham khảo đoạn code sau nhóe.

enum ValidationError: Error {

case fullNameIsRequired  
case lastNameIsRequired  
} func validate(name: String) throws {
guard name.count > 3 else {  
    throw ValidationError.fullNameIsRequired  
}  
guard name.contains(" ") else {  
    throw ValidationError.lastNameIsRequired  
}  
}

Trong ví dụ trên, bạn sẽ nhận ra rằng là chúng ta bắt 2 điều kiện cho cùng một biến

enum ValidationError: Error {

case fullNameIsRequired  
case lastNameIsRequired  
} func validate(name: String) throws {
guard name.count > 3 else {  
    throw ValidationError.fullNameIsRequired  
}  
guard name.contains(" ") else {  
    throw ValidationError.lastNameIsRequired  
}  
}

6, mặc dù

enum ValidationError: Error {

case fullNameIsRequired  
case lastNameIsRequired  
} func validate(name: String) throws {
guard name.count > 3 else {  
    throw ValidationError.fullNameIsRequired  
}  
guard name.contains(" ") else {  
    throw ValidationError.lastNameIsRequired  
}  
}

6 không phải là Optional.

Ưu điểm của cách dùng này là:

  • Xác định cụ thể lỗi nào cho biến đầu vào nào
  • Vẫn đảm bảo tính toàn vẹn của các đoạn code ở dưới
  • Kết hợp được nhiều xử lý cho nhiều điều kiện đầu vào

Let Optional

Đây là cách phổ biết nhất với Guard trong Swift. Nhiệm vụ chính của nó sẽ là unwrap optionals, cũng khá tương tự với

enum ValidationError: Error {

case fullNameIsRequired  
case lastNameIsRequired  
} func validate(name: String) throws {
guard name.count > 3 else {  
    throw ValidationError.fullNameIsRequired  
}  
guard name.contains(" ") else {  
    throw ValidationError.lastNameIsRequired  
}  
}

8.

Bạn đã có ví dụ code về cách dùng này ở trên rồi. Ta sẽ xem lại ví dụ với cả 2 cách

enum ValidationError: Error {

case fullNameIsRequired  
case lastNameIsRequired  
} func validate(name: String) throws {
guard name.count > 3 else {  
    throw ValidationError.fullNameIsRequired  
}  
guard name.contains(" ") else {  
    throw ValidationError.lastNameIsRequired  
}  
}

8 &

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

0 nhóe.

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

Tùy vào ý nghĩa mà bạn muốn thể hiện, kết quả nhận được sẽ khác nhau. Với:

  • enum ValidationError: Error {
    case fullNameIsRequired  
    case lastNameIsRequired  
    
    } func validate(name: String) throws {
    guard name.count > 3 else {  
        throw ValidationError.fullNameIsRequired  
    }  
    guard name.contains(" ") else {  
        throw ValidationError.lastNameIsRequired  
    }  
    
    } 8 : bạn đảm bảo được giá trị của biến trong phạm vi câu lệnh // guard let guard let name = name else {
    return  
    
    } print("Hello, (name)") // if let if let name = name {
    print("Hello, \(name)")  
    
    } 2. Và sau khi thoát khỏi câu lệnh // guard let guard let name = name else {
    return  
    
    } print("Hello, (name)") // if let if let name = name {
    print("Hello, \(name)")  
    
    } 2 thì mọi thứ sẽ quay về lại như ban đầu.
  • // guard let guard let name = name else {
    return  
    
    } print("Hello, (name)") // if let if let name = name {
    print("Hello, \(name)")  
    
    } 0 : bạn đảm bảo được giá trị của biến cho toàn bộ các phần phái dưới, sau khi thoát khỏi câu lệnh điều kiện. Biến mới sẽ đảm bảo giá trị và an toàn.

Multiple Conditions

Với các chương trình lớn, độ phức tạp về mặt logic cũng tăng theo. Nên đôi lúc bạn cần phải sử dụng nhiều điều kiện trong cùng 1 câu lệnh. Và khi bạn không cần quan tâm tới cụ thể từng lỗi do mỗi điều kiện gây ra, thì việc kết hợp chúng lại với nhau là giải pháp hợp lý nhất.

Công việc trên cũng khá là quen thuộc với bạn, nhưng khi bạn sử dụng thêm guard thì bạn đảm bảo dữ liệu của mình với nhiều điều kiện một lúc. Xem ví dụ code sau nhóe!

class FormViewController: UIViewController {

var hasEdits = true  
var forceSave = false
// other functions
func saveData(data: Data?) {  
    // A guard statement with two conditions  
    guard let data = data, hasEdits || forceSave else {  
        return  
    }  
    // Handle data saving  
}  
}

Bạn sẽ sử dụng keyword

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

6 để tách biệt các điều kiện với nhau. Trong ví dụ, function

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

7 đã đảm bảo dữ liệu

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

8 khi nó là một Optional. Và cũng đảm bảo với các điều kiện khác của class quy định.

Guard Available

Đôi lúc, function của bạn chỉ hoạt động trên một số version OS cụ thể. Thì bạn vẫn có thể áp dụng cú pháp

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

9, vì bản chất guard cũng tương tự

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

2 mà thôi.

Xem ví dụ code nhóe!

func showTelephotoCamera() throws {

guard 
# available(iOS 13, *) else {
    throw CameraError.deviceNotSupported  
}  
let cameraController = MyCameraViewController()  
cameraController.camera = .telephoto
present(  
    cameraController,   
    animated: true,   
    completion: nil  
)  
}

Ý nghĩa to lớn nhất của cách dùng này là:

  • Bạn không cần viết lại function hay bọc chúng trong // guard let guard let name = name else {
    return  
    
    } print("Hello, (name)") // if let if let name = name {
    print("Hello, \(name)")  
    
    } 2
  • Vẫn bảo vệ được các đoạn code phía dưới câu lệnh guard

Case Enum

Đây là cách ít người sử dụng guard. Bạn sẽ kết hợp với các

class FormViewController: UIViewController {

var hasEdits = true  
var forceSave = false
// other functions
func saveData(data: Data?) {  
    // A guard statement with two conditions  
    guard let data = data, hasEdits || forceSave else {  
        return  
    }  
    // Handle data saving  
}  
}

5 của Enum, nhằm kiểm tra nó trong một số điều kiện nhất định. Xem qua ví dụ code tiếp nhóe!

enum State {

case initial  
case active  
case done  
} var state = State.active guard case state = State.initial else {
// Handle unexpected state  
} // Continue execution

Với ví dụ trên, nếu

class FormViewController: UIViewController {

var hasEdits = true  
var forceSave = false
// other functions
func saveData(data: Data?) {  
    // A guard statement with two conditions  
    guard let data = data, hasEdits || forceSave else {  
        return  
    }  
    // Handle data saving  
}  
}

6 khác với

class FormViewController: UIViewController {

var hasEdits = true  
var forceSave = false
// other functions
func saveData(data: Data?) {  
    // A guard statement with two conditions  
    guard let data = data, hasEdits || forceSave else {  
        return  
    }  
    // Handle data saving  
}  
}

7 thì các đoạn code ở dưới sẽ được thực thi. Bạn chỉ cần thực hiện câu lệnh

class FormViewController: UIViewController {

var hasEdits = true  
var forceSave = false
// other functions
func saveData(data: Data?) {  
    // A guard statement with two conditions  
    guard let data = data, hasEdits || forceSave else {  
        return  
    }  
    // Handle data saving  
}  
}

8 thay vì phải

class FormViewController: UIViewController {

var hasEdits = true  
var forceSave = false
// other functions
func saveData(data: Data?) {  
    // A guard statement with two conditions  
    guard let data = data, hasEdits || forceSave else {  
        return  
    }  
    // Handle data saving  
}  
}

9 nhóe. Cũng khá là tiện lợi phải không nào!

Tạm kết

Qua bài viết trên, bạn sẽ áp dụng được guard trong các trường hợp throw errors, unwrap optionals, verify … hay kiểm tra các API … Khi bạn hiểu được ý nghĩa của mỗi trường hợp thì logic của bạn sẽ hiệu quả hơn, thay vì khô khan với

// guard let guard let name = name else {

return  
} print("Hello, (name)") // if let if let name = name {
print("Hello, \(name)")  
}

2 nhóe!

Okay! Tới đây, mình xin kết thúc bài viết về Guard keyword trong iOS . Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.