swift tips - 1~10

weixin_34087301發表於2018-07-25

1. 利用巢狀型別,定義名稱空間

public struct Map {
  public struct Model {
    public let size: Size
    public let theme: Theme
    public var terrain: [Position: Terrain.Model]
    public var units: [Position: Unit.Model]
    public buildings: [Position: Building.Model]
  }

  public enum Direction {
    case up
    case right
    case down
    case left
  }

  public struct Position {
    public var x: Int
    public var y: Int
  }

  public enum Size: String {
    case small = "S"
    case medium = "M"
    case large = "L"
    case extraLarge = "XL"
  }
}

此處只是名稱空間的冰山一角,可以查閱更多資料瞭解更多名稱空間的知識點。

2. 使用@autoclosure

extension Dictionary {
  mutating func value(for key: Key, orAdd valueClosure: @autoclosure () -> Value) -> Value {
    if let value = self[key] {
      return value
    }

    let value = valueClosure()
    self[key] = value
    return value
  }
}

使用@autoclosure,可以讓api更加的nice。在呼叫的時候,api更加的簡潔。具體可以參考文章:Swift中@autoclosure

3. 註釋的風格

// 內部(internal)引數的註釋
class Foo {
  /**
  * - parameter string: A string
  */
  func bar(with string: String) {}
}

// 外部(external)引數的註釋
class Foo {
  /**
  * - parameter string with: A string
  */
  func bar(with string: String) {}
}

方法的註釋,既可以對內部引數解釋,也可對外部引數解釋,不過務必保持統一。

4. 使用typealias減少方法簽名的長度

public class PathFinder<Object: PathFinderObject> {
  public typealias Map = Object.Map
  public typealias Node = Map.Node
  public typealias Path = PathFinderPath<Object>

  public static func possiblePaths(for object: Object, at rootNode: Node, on map: Map) -> Path.Sequence {
    return .init(object: object, rootNode: rootNode, map: map)
  }
}

使用typealias定義型別,在方法定義的時候,可以有效減少方法名(方法簽名)的長度。

5. 使用Wrap元件去實現Equatable

protocol AutoEquatable: Equatable {}

extension AutoEquatable {
  static func ==(lhs: Self, rhs: self) -> Bool {
    let lhsData = try! wrap(lhs) as Data
    let rhsData = try! wrap(lhs) as Data
    return lhsData == rhsData
  }
}

在swift中,若要實現Equatable協議,需要重寫static func ==(lhs, rhs) -> Bool方法,這無疑增加了程式碼量,使用Wrap可以減少程式碼量。

6. 使用標準庫中已經存在的類名

extension Command {
  enum Error: Swift.Error {
    case missin
    case invalid(String)
  }
}

如果一個名稱已經被標準庫佔用,使用Swift.作為字首即可。

7. Using #function for UserDefaults key consistency

extension userDefaults {
  var onboardingCompleted: Bool {
    get { return bool(forKey: #function) }
    set { set(newValue, forKey: #function) }
  }
}

? 通常我們使用UserDefaults時候,都是通過字串作為Key,這有可能會因為書寫錯誤,導致存取時候出錯,這類錯誤很難排查。我們可以給UserDefaults擴充套件計算屬性,如上面的程式碼,key的值設定為#function,這是swift中的關鍵字,表示方法的簽名,在這裡表示"onboardingCompleted",這樣就避免了書寫錯誤。

8. 將函式和操作符作為必包傳遞

let array = [3, 9, 1, 4, 6, 2]
let sorted = array.sorted(by: <)

❤️ 在swift中,可以將函式function&操作符operators作為必包傳遞給函式引數,例如為一個陣列排序時,可以使用上述程式碼,

9. 在不同的條件判斷下,使用guard語句

// you can use the 'guard' statement to...

for string in strings {
  guard shouldProcess(string) else {
    continue
  }

  guard !shouldBreak(for: string) else {
    break
  }

  guard !shouldReturn(for: string) else {
    return
  }

  // throw an error
  guard string.isValid else {
    throw StringError.invalid(string)
  }

  // exit the program
  guard !shouldExist(for: string) else {
    exit(1)
  }
}

guard不僅可以用於return,還能用於continue、break、throw等,合理使用guard可以有效避免程式碼多層巢狀。

10. 在switch語句中,避免使用default

enum State {
  case loggedIn
  case loggedOut
  case onboarding
}

func handle(_ state: State) {
  switch state {
  case .loggedIn:
    showMiainUI()
  case .loggedOut:
    showLoginUI()
  // Compiler error: switch must be exhaustive
  }
}

在swift中,使用enum的switch語法時,避免使用default是一個很好的實踐。這樣,它將會在新增一個case的時候,強制要求你更新邏輯。

參考連結