转载

iOS 9 by Tutorials 笔记(一)

最近入了 Raywenderlich 家的 《iOS 9 by Tutorials》 ,来刷下书做个笔记吧,感兴趣请支持正版

iOS 9 by Tutorials 笔记(一)

Chapter 1: Swift 2.0

第一章主要介绍了 Swift 2.0

一、Control Flow

1、repeat

do/while 变成了 repeat/while ,语义更加明确

var jamJarBeer = Beer() repeat {   jamJarBeer.sip() } while (!jamJarBeer.isEmpty) // 啤酒不为空就喝到空为止

2、guard

这个是条件预判断,也没啥好说的,Swift 的创始人 Chris Lattner 在 WWDC 2015 上说推出新关键字 guard 的原因是: 在 early exit 时觉得用 if let 要缩进太丑了

struct Beer {   var percentRemaining = 100   var isEmpty: Bool { return percentRemaining <= 0 }   var owner: Patron?   mutating func sip() {     guard percentRemaining > 0 else {       print("Your beer is empty, order another!")       return     }     percentRemaining -= 10     print("Mmmm /(percentRemaining)% left")   } }

二、Error handling

本质上一个纯粹 Swift 错误(A pure Swift error),可以看做是遵循 ErrorType 协议的 enum 。了解到这一点,我们就可以定制自己的 error type。

一般 Error handling 也记住三步:

  1. 创建自己的 ErrorType

    enum ParseError: ErrorType {     case MissingAttribute(message: String) }
  2. 声明一个方法会 throw 错误,然后在实现中 throw 第一步定义的 errorType 枚举对象具体的错误分支(case)

    struct Person: JSONParsable {  let firstName: String  let lastName: String  static func parse(json: [String : AnyObject]) throws   -> Person {   guard let firstName = json["first_name"] as? String else {    let message = "Expected first_name String"    throw ParseError.MissingAttribute(message: message) // 1   }   guard let lastName = json["last_name"] as? String else {    let message = "Expected last_name String"    throw ParseError.MissingAttribute(message: message) // 2   }   return Person(firstName: firstName, lastName: lastName)  } } 
  3. 在 do/try/catch 中调用第二步创建的这个方法,并捕获错误

    do {     let person = try Person.parse(["foo": "bar"]) } catch ParseError.MissingAttribute(let message) {     print(message) } catch {     print("Unexpected ErrorType") }

    如果你能保证不出错误,也可以用 try!

    let p1 = try! Person.parse(["foo": "bar"])

三、The Project

1、String Validation Error

①.字符串验证协议

// 字符串验证规则 protocol StringValidationRule {   func validate(string: String) throws -> Bool   var errorType: StringValidationError { get } }

为了使用多个规则一起用,再定义一个 StringValidator 协议

protocol StringValidator {   var validationRules: [StringValidationRule] { get }   // 这种返回值是一个元组,带 errors 信息,就不用 throw 了   func validate(string: String) -> (valid: Bool,     errors: [StringValidationError]) }

现在 Protocol Extensions 里可以直接写协议的方法实现了,只要遵循了该协议将自动享受到这些实现,比如在下面的 Protocol Extensions 中实现 validate 方法

extension StringValidator {   func validate(string: String) -> (valid: Bool,     errors: [StringValidationError]) {     var errors = [StringValidationError]()     for rule in validationRules {       do {         try rule.validate(string)       } catch let error as StringValidationError {         errors.append(error)       } catch let error {         fatalError("Unexpected error type: /(error)")       }     }     return (valid: errors.isEmpty, errors: errors)   }  }

②.具体的验证实例

以 xx 类型字符开头的验证规则,遵循 StringValidationRule 协议

struct StartsWithCharacterStringValidationRule   : StringValidationRule {   let characterSet: NSCharacterSet   let description: String    var errorType: StringValidationError {     return .MustStartWith(set: characterSet,       description: description)   }    func validate(string: String) throws -> Bool {     if string.startsWithCharacterFromSet(characterSet) {       return true     } else {       throw errorType     }    } }

调用看输出

let letterSet = NSCharacterSet.letterCharacterSet() let startsWithRule = StartsWithCharacterStringValidationRule(   characterSet: letterSet,   description: "letter") do {   try startsWithRule.validate("foo")   try startsWithRule.validate("123") } catch let error {   print(error) }

iOS 9 by Tutorials 笔记(一)

必须以某种字符结尾的验证规则

struct EndsWithCharacterStringValidationRule   : StringValidationRule {   let characterSet: NSCharacterSet   let description: String   var errorType: StringValidationError {     return .MustEndWith(set: characterSet,       description: description) }   func validate(string: String) throws -> Bool {     if string.endsWithCharacterFromSet(characterSet) {       return true     } else {       throw errorType     }   }  }

结合上面两条 rules(StartsWithCharacterStringValidationRule,EndsWithCharacterStringValidationRule)创建一个 StartsAndEndsWithStringValidator,验证开头和结尾的字符

struct StartsAndEndsWithStringValidator: StringValidator {   let startsWithSet: NSCharacterSet                let startsWithDescription: String   let endsWithSet: NSCharacterSet                  let endsWithDescription: String   var validationRules: [StringValidationRule] {      return [       StartsWithCharacterStringValidationRule(         characterSet: startsWithSet,         description: startsWithDescription),       EndsWithCharacterStringValidationRule(         characterSet: endsWithSet ,         description: endsWithDescription)     ]    } }

其实 StringValidator 协议还有个 validate 方法,但是在之前的 extension 方法已经实现了

验证:以字母开头,数字结尾

let numberSet = NSCharacterSet.decimalDigitCharacterSet()  let startsAndEndsWithValidator =   StartsAndEndsWithStringValidator(     startsWithSet: letterSet,     startsWithDescription: "letter",     endsWithSet: numberSet,     endsWithDescription: "number")  startsAndEndsWithValidator.validate("1foo").errors.description startsAndEndsWithValidator.validate("foo").errors.description startsAndEndsWithValidator.validate("foo1").valid

2、Password Requirement Validation

现在将 StringValidator 投入到实际的工作中,用来验证密码是否符合规范

  • 密码长度至少 8 位
  • 必须包含一个大写字母
  • 必须包含一个小写字母
  • 必须包含一个数字
  • 必须包含一个以下特殊字符:"!@#$%^&*()_-+<>?/[]{}"

首先来创建一条验证字符串长度的 LengthStringValidationRule

public struct LengthStringValidationRule   : StringValidationRule {   public enum Type {     case Min(length: Int)     case Max(length: Int)   }   public let type: Type   public var errorType: StringValidationError { get }   public init(type: Type)   public func validate(string: String) throws -> Bool }

接着创建验证包含某些字符的 ContainsCharacterStringValidationRule

public struct ContainsCharacterStringValidationRule   : StringValidationRule {   public enum Type {     case MustContain     case CannotContain     case OnlyContain     case ContainAtLeast(Int)   }   public let characterSet: NSCharacterSet   public let description: String   public let type: Type   public var errorType: StringValidationError { get }   public init(characterSet: NSCharacterSet,     description: String,     type: Type)   public func validate(string: String) throws -> Bool }

两条 rules 在手,下面我们来验证密码的有效性:

struct PasswordRequirementStringValidator: StringValidator {   var validationRules: [StringValidationRule] {     let upper = NSCharacterSet.uppercaseLetterCharacterSet()     let lower = NSCharacterSet.lowercaseLetterCharacterSet()     let number = NSCharacterSet.decimalDigitCharacterSet()     let special = NSCharacterSet(       charactersInString: "!@#$%^&*()_-+<>?///[]}{")     return [       LengthStringValidationRule(type: .Min(length: 8)),       ContainsCharacterStringValidationRule(         characterSet:upper ,         description: "upper case letter",         type: .ContainAtLeast(1)),       ContainsCharacterStringValidationRule(         characterSet: lower,         description: "lower case letter",         type: .ContainAtLeast(1)),       ContainsCharacterStringValidationRule(         characterSet:number ,         description: "number",         type: .ContainAtLeast(1)),       ContainsCharacterStringValidationRule(         characterSet:special,         description: "special character",         type: .ContainAtLeast(1))     ]    } }

验证:

let passwordValidator = PasswordRequirementStringValidator() passwordValidator.validate("abc1").errors.description passwordValidator.validate("abc1!Fjk").errors.description

四、Additional Things

1、Going further with Extensions

我们可以在某些类型上通过 Extensions 添加一些方法实现,比如在数组类型上添加一个 shuffles 方法,对数组内的元素进行随机排序

extension MutableCollectionType where Index == Int {   mutating func shuffleInPlace() {     let c = self.count     for i in 0..<(c-1) {       let j = Int(arc4random_uniform(UInt32(c - i))) + i       guard i != j else { continue }       swap(&self[i], &self[j])      }    } }

验证:

// 因为数组遵循 MutableCollectionType 协议 var people = ["Chris", "Ray", "Sam", "Jake", "Charlie"] people.shuffleInPlace()

iOS 9 by Tutorials 笔记(一)

Extending functionality to generic type parameters is only available to classes and protocols.

2、Using defer

使用 defer { ... } 来确保 defer 紧跟的代码块在离开当前范围之前总是被执行,这里书上举了个 ATM 机的例子:

struct ATM {   var log = ""    mutating func dispenseFunds(amount: Float,     inout account: Account) throws {      defer {       log += "Card for /(account.name) has been returned " +         "to customer./n"       ejectCard()     }      log += "====================/n"     log += "Attempted to dispense /(amount) from " +       "/(account.name)/n"      guard account.locked == false else {       log += "Account Locked/n"       throw ATMError.AccountLocked     }      guard account.balance >= amount else {       log += "Insufficient Funds/n"       throw ATMError.InsufficientFunds     }      account.balance -= amount     log += "Dispensed /(amount) from /(account.name)."     log += " Remaining balance: /(account.balance)/n"   }    func ejectCard() {     // physically eject card   }  }

在 ATM 机这个例子中,多个地方会对账户和取钱金额进行检查,不合适就会出错退出,我们这里用 defer 来保证用户的卡一定会被退回,即 ejectCard() 方法一定会被执行

验证一个被锁定的帐号:

do {   try atm.dispenseFunds(200.00, account: &billsAccount) } catch let error {   print(error) }

虽然帐号被锁了,但卡还是退回来了

iOS 9 by Tutorials 笔记(一)

3、Pattern Matching

  • 在 swift 2.0 中 for...in 循环和 where 可以一起用

    var namesThatStartWithC = [String]()  for cName in names where cName.hasPrefix("C") {     namesThatStartWithC.append(cName) }
  • for...in 和 case 结合同样可以用在枚举集合里了

    var totalDaysLate = 0 // 遍历枚举集合,针对特定 case 过滤出 for case let .Late(daysLate) in authorStatuses {     totalDaysLate += daysLate }
  • if case 可以直接用来判断某个枚举对象属于哪一分支,不用写switch,更不用 default

    var slapLog = "" for author in authors {   if case .Late(let daysLate) = author.status where daysLate > 2 {     slapLog += "Ray slaps /(author.name) around a bit " +      "with a large trout./n"  }  }

4、Option Sets

你现在可以创建自己的 option set,其实就是创建一个遵循 OptionSetType 协议的结构体

struct RectangleBorderOptions: OptionSetType {   let rawValue: Int   init(rawValue: Int) { self.rawValue = rawValue }   static let Top = RectangleBorderOptions(rawValue: 0)   static let Right = RectangleBorderOptions(rawValue: 1)   static let Bottom = RectangleBorderOptions(rawValue: 2)   static let Left = RectangleBorderOptions(rawValue: 3)   static let All: RectangleBorderOptions =     [Top, Right, Bottom, Left] }

5、OS Availability

swift 2.0 可以让编译器帮你检查系统版本了

guard #available(iOS 9.0, *) else { return } // do some iOS 9 or higher only thing
-EOF-
正文到此结束
Loading...