+1

Có gì mới trong Swift 4.1?

Conditional Conformance

Đây là một tính năng mạnh mẽ giúp mã của bạn linh hoạt hơn. Bạn có thể xem cách nó hoạt động với một vài ví dụ.

Conditional Conformance trong thư viện chuẩn

Trong Swift 4, bạn có thể so sánh các Mảng, Dictionary và các tùy chọn miễn là các phần tử của chúng là Equatable. Điều này hoàn toàn tốt cho các tình huống cơ bản như:

// Arrays of Int
let firstArray = [1, 2, 3]
let secondArray = [1, 2, 3]
let sameArray = firstArray == secondArray

// Dictionaries with Int values
let firstDictionary = ["Cosmin": 10, "George": 9]
let secondDictionary = ["Cosmin": 10, "George": 9]
let sameDictionary = firstDictionary == secondDictionary

// Comparing Int?
let firstOptional = firstDictionary["Cosmin"]
let secondOptional = secondDictionary["Cosmin"]ng
let sameOptional = firstOptional == secondOptional

Sử dụng toán tử == để kiểm tra giống nhau trong các ví dụ này, vì IntEquatable trong Swift 4. Tuy nhiên, so sánh các phần tử optional là một tình huống phổ biến mà bạn có thể gặp phải với Swift 4 vì các phần tử optional không phù hợp với Equatable. Swift 4.1 khắc phục vấn đề này bằng cách sử dụng Conditional Conformance, cho phép các kiểu optional với các kiểu Equatable cơ bản được so sánh:


// Array of Int?
let firstArray = [1, nil, 2, nil, 3, nil]
let secondArray = [1, nil, 2, nil, 3, nil]
let sameArray = firstArray == secondArray

// Dictionary with Int? values
let firstDictionary = ["Cosmin": 10, "George": nil]
let secondDictionary = ["Cosmin": 10, "George": nil]
let sameDictionary = firstDictionary == secondDictionary

// Comparing Int?? (Optional of Optional)
let firstOptional = firstDictionary["Cosmin"]
let secondOptional = secondDictionary["Cosmin"]
let sameOptional = firstOptional == secondOptional

Int? is Equatable in Swift 4.1, so the == operator works for [Int?], [String: Int?] and Int??. Một vấn đề tương tự đã được giải quyết khi so sánh các mảng của mảng (ví dụ [[Int]]). Trong Swift 4, bạn chỉ có thể so sánh các mảng của các Set (ví dụ: [Set <Int>]), vì các Set phù hợp với Equatable. Swift 4.1 giải quyết điều này, vì các mảng (và Dictionary) là Equatable miễn là các giá trị bên trong của chúng cũng vậy.

let firstArrayOfSets = [Set([1, 2, 3]), Set([1, 2, 3])]
let secondArrayOfSets = [Set([1, 2, 3]), Set([1, 2, 3])]

// Will work in Swift 4 and Swift 4.1
// since Set<Int> is Equatable
firstArrayOfSets == secondArrayOfSets

let firstArrayOfArrays = [[1, 2, 3], [3, 4, 5]]
let secondArrayOfArrays = [[1, 2, 3], [3, 4, 5]]

// Caused an error in Swift 4, but works in Swift 4.1
// since Arrays are Equatable in Swift 4.1
firstArrayOfArrays == secondArrayOfArrays

Nói chung, Optional, ArrayDictionary của Swift 4.1 phù hợp với EquatableHashable bất cứ khi nào các giá trị hoặc phần tử cơ bản của chúng phù hợp với các giao thức này. Đây là cách conditional conformance hoạt động trong thư viện chuẩn. Tiếp theo, bạn sẽ thực hiện nó trong code.

Conditional conformance trong code

Bạn sẽ sử dụng Conditional conformance để tạo ra các nhạc cụ của riêng bạn. Thêm khối mã sau vào cuối playground để bắt đầu:

// 1 
class LeadInstrument: Equatable {
  let brand: String
  
  init(brand: String) {
    self.brand = brand
  }
  
  func tune() -> String {
    return "Standard tuning."
  }
  
  static func ==(lhs: LeadInstrument, rhs: LeadInstrument) -> Bool {
    return lhs.brand == rhs.brand
  }
}

// 2
class Keyboard: LeadInstrument {
  override func tune() -> String {
    return "Keyboard standard tuning."
  }
}

// 3
class Guitar: LeadInstrument {
  override func tune() -> String {
    return "Guitar standard tuning."
  }
}

Bạn thực hiện theo những bước sau: 1. LeadInstrument phù hợp với Equatable. Nó có một brand nhất định và một method có tên là tune() mà cuối cùng bạn sẽ sử dụng tune để điều chỉnh nhạc cụ. 2. Bạn override tune() trong Keyboard để trả về điều chỉnh chuẩn của Keyboard. 3. Bạn làm điều tương tự với Guitar.

Tiếp theo, bạn khai báo band:

// 1  
class Band<LeadInstrument> {
  let name: String
  let lead: LeadInstrument
  
  init(name: String, lead: LeadInstrument) {
    self.name = name
    self.lead = lead
  }
}

// 2
extension Band: Equatable where LeadInstrument: Equatable {
  static func ==(lhs: Band<LeadInstrument>, rhs: Band<LeadInstrument>) -> Bool {
    return lhs.name == rhs.name && lhs.lead == rhs.lead
  }
}

Dưới đây, bạn thực hiện từng bước: 1. Bạn tạo một lớp có tên là Band với kiểu generic - LeadInstrument. Mỗi ban nhạc có một tên duy nhất và công cụ chính. 2. Bạn sử dụng where để hạn chế Band để phù hợp với Equatable miễn là LeadInstrument làm.

Tiếp theo, xác định các ban nhạc yêu thích của bạn và so sánh chúng:

// 1
let rolandKeyboard = Keyboard(brand: "Roland")
let rolandBand = Band(name: "Keys", lead: rolandKeyboard)
let yamahaKeyboard = Keyboard(brand: "Yamaha")
let yamahaBand = Band(name: "Keys", lead: yamahaKeyboard)
let sameBand = rolandBand == yamahaBand

// 2
let fenderGuitar = Guitar(brand: "Fender")
let fenderBand = Band(name: "Strings", lead: fenderGuitar)
let ibanezGuitar = Guitar(brand: "Ibanez")
let ibanezBand = Band(name: "Strings", lead: ibanezGuitar)
let sameBands = fenderBand == ibanezBand

Trong đoạn mã này, bạn tạo hai Keyboards và Guitars cùng với các Bands thích hợp của chúng. Sau đó bạn so sánh trực tiếp các ban nhạc, nhờ vào sự phù hợp có điều kiện mà bạn đã xác định trước đó.

Conditional conformance trong JSON parsing

Array, Dictionary, Set và các optional phù hợp với Codable nếu các phần tử của chúng tuân theo Codable trong Swift 4.1. Thêm đoạn mã sau vào playground của bạn để thử điều này:

struct Student: Codable, Hashable {
  let firstName: String
  let averageGrade: Int
}

let cosmin = Student(firstName: "Cosmin", averageGrade: 10)
let george = Student(firstName: "George", averageGrade: 9)
let encoder = JSONEncoder()

// Encode an Array of students
let students = [cosmin, george]
do {
  try encoder.encode(students)
} catch {
  print("Failed encoding students array: \(error)")
}

// Encode a Dictionary with student values
let studentsDictionary = ["Cosmin": cosmin, "George": george]
do {
  try encoder.encode(studentsDictionary)
} catch {
  print("Failed encoding students dictionary: \(error)")
}

// Encode a Set of students
let studentsSet: Set = [cosmin, george]
do {
  try encoder.encode(studentsSet)
} catch {
  print("Failed encoding students set: \(error)")
}

// Encode an Optional Student
let optionalStudent: Student? = cosmin
do {
  try encoder.encode(optionalStudent)
} catch {
  print("Failed encoding optional student: \(error)")
}

Bạn sử dụng mã này để mã hóa [Student], [String: Student], Set<Student>Student?. Điều này hoạt động trơn tru trong Swift 4.1 vì StudentCodable, điều này làm cho các loại bộ sưu tập này phù hợp với nó.

Chuyển đổi giữa trường hợp Camel và Snake Case trong quá trình mã hóa JSON

Swift 4.1 cho phép bạn chuyển đổi các thuộc tính CamelCase thành các khóa snake_case trong khi mã hóa JSON

var jsonData = Data()
encoder.keyEncodingStrategy = .convertToSnakeCase
encoder.outputFormatting = .prettyPrinted

do {
  jsonData = try encoder.encode(students)
} catch {
  print(error)
}

if let jsonString = String(data: jsonData, encoding: .utf8) {
  print(jsonString)
}

Khi tạo bộ mã hóa, bạn đặt keyEncodingStrategy thành .convertToSnakeCase. Nhìn vào giao diện console của bạn, bạn sẽ thấy:

var studentsInfo: [Student] = []
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

do {
  studentsInfo = try decoder.decode([Student].self, from: jsonData)
} catch {
  print(error)
}

for studentInfo in studentsInfo {
  print("\(studentInfo.firstName) \(studentInfo.averageGrade)")
} 

Lần này, bạn đặt keyDecodingStrategy thành .convertFromSnakeCase.

Equatable và Hashable Protocols Conformance

Swift 4 yêu cầu bạn viết mã boilerplate để tạo các cấu trúc phù hợp với EquatableHashable:

struct Country: Hashable {
  let name: String
  let capital: String
  
  static func ==(lhs: Country, rhs: Country) -> Bool {
    return lhs.name == rhs.name && lhs.capital == rhs.capital
  }
  
  var hashValue: Int {
    return name.hashValue ^ capital.hashValue &* 16777619
  }
}

Sử dụng mã này, bạn đã triển khai thực hiện == (lhs: rhs 😃hashValue để hỗ trợ cả EquatableHashable. Bạn có thể so sánh các quốc gia, thêm chúng vào Set và thậm chí sử dụng chúng làm khóa Dictionary:

let france = Country(name: "France", capital: "Paris")
let germany = Country(name: "Germany", capital: "Berlin")
let sameCountry = france == germany

let countries: Set = [france, germany]
let greetings = [france: "Bonjour", germany: "Guten Tag"]

Swift 4.1 bổ sung các triển khai mặc định trong các struct cho EquatableHashable miễn là tất cả các thuộc tính của chúng là EquatableHashable cũng [SE-0185]. Điều này giúp đơn giản hóa mã của bạn, có thể viết lại đơn giản là:

struct Country: Hashable {
  let name: String
  let capital: String
}

Các Enumerations với các associated values cũng cần thêm mã để làm việc với EquatableHashable trong Swift 4:

enum BlogPost: Hashable {
  case tutorial(String, String)
  case article(String, String)
  
  static func ==(lhs: BlogPost, rhs: BlogPost) -> Bool {
    switch (lhs, rhs) {
    case let (.tutorial(lhsTutorialTitle, lhsTutorialAuthor), .tutorial(rhsTutorialTitle, 
               rhsTutorialAuthor)):
      return lhsTutorialTitle == rhsTutorialTitle && lhsTutorialAuthor == rhsTutorialAuthor
    case let (.article(lhsArticleTitle, lhsArticleAuthor), .article(rhsArticleTitle, rhsArticleAuthor)):
      return lhsArticleTitle == rhsArticleTitle && lhsArticleAuthor == rhsArticleAuthor
    default:
      return false
    }
  }
  
  var hashValue: Int {
    switch self {
    case let .tutorial(tutorialTitle, tutorialAuthor):
      return tutorialTitle.hashValue ^ tutorialAuthor.hashValue &* 16777619
    case let .article(articleTitle, articleAuthor):
      return articleTitle.hashValue ^ articleAuthor.hashValue &* 16777619
    }
  }
}

Bạn đã sử dụng các trường hợp liệt kê để viết các triển khai cho == (lhs: rhs 😃hashValue. Điều này cho phép bạn so sánh các bài đăng trên blog và sử dụng chúng trong Set và Dictionary:

let swift3Article = BlogPost.article("What's New in Swift 3.1?", "Cosmin Pupăză")
let swift4Article = BlogPost.article("What's New in Swift 4.1?", "Cosmin Pupăză")
let sameArticle = swift3Article == swift4Article

let swiftArticlesSet: Set = [swift3Article, swift4Article]
let swiftArticlesDictionary = [swift3Article: "Swift 3.1 article", swift4Article: "Swift 4.1 article"]

Như trường hợp đã có với Hashable, kích thước của mã này được giảm đáng kể trong Swift 4.1 nhờ các triển khai EquatableHashable mặc định:

enum BlogPost: Hashable {
  case tutorial(String, String)
  case article(String, String)
}

Bạn vừa tự cứu mình khỏi việc duy trì 20 dòng mã soạn sẵn!

(còn tiếp...) Nguồn: https://www.raywenderlich.com


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí