IOS9-by-Tutorials-Study Notes 1: Swift-2-0
IOS9-by-Tutorials-Study Notes 1: Swift-2-0
Apple opened up Swift some time ago and made a stir in the iOS development field. He gave a look at Swift's development roadmap and planned to release Swift 3.0 next autumn. Apple is now more developing on Swift, encouraging communities to contribute code, and accepting some feedback from the community. Apple changed its previous closed posture, indicating that it attaches great importance to the Swift language. It also shows that Apple will make greater efforts to optimize the Swift language. Therefore, for our iOS developers, it's time to start learning iOS.
I interviewed several people some time ago, and I wrote several well-versed Swift in my resume. But I couldn't answer many questions. My resume is true ..... More people seem to have not started to learn Swift, but at last I suggest them learn Swift.
This article is my study note, not my original content, but some notes for reading the book "iOS 9 by Tutorials, then add your own understanding.
Several important improvements have been added to Swift 2 (as I think) as follows:
* New Control Flow
* (Relatively) complete error handling Model
* Protocol Extension
* Enhanced pattern matching
* API availability Detection
* Others ......
Control Flow
In the book, the author first explains the control flow. It feels good: any structure or keyword in the program that can affect the execution of the program to different paths can be called control flow: any construct or keyword that causes the execution of your program to follow a different path can be considered "control flow ".
Repeat... While
Repeat... While indicates repetition, similar to do… in other languages... While. In fact, do… is still used in Swift 1.x... While, in 2.x... Catch is differentiated, so it is changed to repeat, but the semantics remains unchanged. Here I would like to say that many Swift improvements are made to make the program read more clearly, such as Optional and guard.
The code in this example is implemented in Playground.
var x = 2
repeat {
print ("x: \ (x)")
x + = 1 // Swift plans to remove ++ in 3.0-so use it as little as possible
} while x <10 // brackets can be added in this place
The parentheses can not be used after the above while. This is also an improvement in Swift. Swift only requires parentheses when it is necessary (that is, the semantics are not clear).
guard
I don't know how to translate the word guard, so I won't translate it here. But what this keyword does is a prerequisite check. First look at the following example:
func printName (name: String) {
guard! name.isEmpty else {
print ("no name")
return
}
print (name)
}
printName ("")
printName ("MengXiangYue")
The above example is a meaningless example, just for demonstration. A function is defined to print the incoming name. This function requires that if the incoming name is empty, a program error is determined, and then the code is not executed. The guard is followed by a condition. If the condition is true, the else will not be executed. When the condition is false, the else will be executed, so that we can meet our requirements. But it may be said again that I can achieve this function with an if-else, but if it is combined with Optional, it is much more convenient than if-else. Let's continue to see this example:
func printName (inName: String?) {// This is now an optional value
guard let name = inName else {
print ("no name")
return
}
guard! name.isEmpty else {
print ("no name")
return
}
print (name)
}
printName ("")
printName ("MengXiangYue")
The parameter passed in the above example is an optional value. At this time, use "guard let name = _name else ...", this is similar to the way of if let unpacking, but look at the name variable we declared using guard It can be used normally, but consider that if you use if let this can not be used, so I think guard combined with Optional is the most convenient to use. In addition, this thing can also achieve similar functions like NSAssert, but this will not crash.
(Relatively) perfect error handling model
Here I added a relative, mainly referring to the error handling of Swift 1.x, 2.x is a lot better, but compared to other languages such as java, it is still not perfect, error handling in Swift, For throwing an error, you just know that the function throws an error, but it is not clear what error this function throws. There is a sentence in the book that is written correctly. This requirement must be written in the document when writing a program It is clear that various exceptions will be thrown (Exception will be thrown explicitly in java, Exception is consistent with Swift's Error function).
In addition, passing the pointer into NSError of Objective-C, and then checking after the function execution is completed, has advanced a lot, applauding. . . . .
Define one of the following protocols:
{% codeblock%} swift
protocol JSONParsable {
static func parse (json: [String: AnyObject]) throws-> Self
}
<code class = "hljs coffeescript">
This protocol defines a static method, which cannot be called a class method here, because the protocol can be applied to Struct at the same time, and can be called a type method. This function uses the keyword ** throws **. This keyword indicates that the method may throw an error, and you ca n’t see what error is thrown here (you sister, you do n’t know any errors), so it is more prominent. The importance of notes at this time (you can write an article: on the importance of notes, hahaha).
Now that when it comes to throwing errors, we have to define errors. It is easier to define errors in Swift, as long as you define an enumerated type and then follow the ** ErrorType ** protocol. NSError in OC also implements the ** ErrorType ** protocol, so we can use NSError in OC and Swift without problems. The following defines an error:
`` `swift
enum ParseError: ErrorType {
case MissingAttribute (message: String)
}
</ code>
Defining an error is relatively simple and is no different from ordinary enumeration. Here, an enumeration with associated values is defined. There is one more word about the associated value. The associated value can solve a lot of type-related things in Swift. Sometimes we often encounter a type and a value. For example, in our own projects, network request errors need to be brought With the error code and error prompt, I may need to return three parameters in OC at this time, but in Swift I can just return an enumeration and then associate the other two values. For multiple related values, you can also use tuples. When I saw kingfisher, the author put all the configuration parameters of a class into a tuple, and then parsed the tuple, so that the parameters may be clearer.
Farther away, back to the topic. Below we implement a structure Person:
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)
}
}
The code is relatively simple but it doesn't explain too much, that is, it throws different exceptions in different situations. When we call this method, we need to handle these exceptions. At this time, we use do ... catch in Swift. Here is the code:
do {
let person = try Person.parse (["foo": "bar"])
} catch ParseError.MissingAttribute (let message) {
print (message)
} catch {
print ("Unexpected ErrorType")
}
After do, you need to use {} to wrap the function that throws the exception. When calling the method that throws the exception, you need to use the try keyword, followed by the exception that needs to be caught. The exception type is added after it. If there is no exception type, it means that all exceptions are caught. Exceptions will be matched one by one in the order of catch until the end of the first match is found.
If we don't care about exceptions, we can use try ?, try! Call method, where try? Call method will return an Optional value, if the call is successful, it will return the corresponding result, if it fails, it returns nil, the program will not crash , But if we use try directly! If an exception is thrown, the program will crash. So only try to use try! When we guarantee that the function we call will not throw an exception.
let p1 = try? Person.parse (["foo": "bar"]) // nil
let p2 = try! Person.parse (["first_name": "Ray", "last_name": "Wenderlich"]) // Person
let p3 = try! Person.parse (["foo": "bar"]) // error crash
Protocol extension
In this part, an example is used to introduce protocol extension. Protocol extension is a more important idea in Swift 2.x. See WWDC 2015 Session 408 for details. The following defines a protocol for verifying string rules:
protocol StringValidationRule {
func validate (string: String) throws-> Bool // method to verify whether it is legal
var errorType: StringValidationError {get} // Type of error
}
The protocol of the verification rule is defined above, and a validator protocol is defined below:
protocol StringValidator {
var validationRules: [StringValidationRule] {get}
func validate (string: String)-> (valid: Bool, errors: [StringValidationError])
}
StringValidator, a validator, has an array of validation rules, and then a validation method that returns a primitive that contains the final validation result and errors. Here we consider that the logic we may process for the validator is the same, that is, to loop through all the validation rules, and then check whether the validation is successful. This logic is relatively consistent. If we put this in every type that implements the protocol, the code may be repeated. At this time we can provide a default implementation, which is the protocol extension (similar to the function of virtual function).
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)
}
}
Below we implement the rule that a string starts with certain characters and ends with certain characters. First define the above StringValidationError
// Error type
enum StringValidationError: ErrorType {
case MustStartWith (set: NSCharacterSet, description: String)
case MustEndWith (set: NSCharacterSet, description: String)
var description: String {
let errorString: String
switch self {
case .MustStartWith (\ _, let description):
errorString = "Must start with \ (description)."
case .MustEndWith (\ _, let description):
errorString = "Must end with \ (description)."
}
return errorString
}
}
// Expand String
extension String {
public func startsWithCharacterFromSet (set: NSCharacterSet)-> Bool {
guard! isEmpty else {
return false
}
return rangeOfCharacterFromSet (set, options: [], range: startIndex .. Bool {
guard! isEmpty else {
return false
}
return rangeOfCharacterFromSet (set, options: [], range: endIndex.predecessor () .. Bool {
string
if string.startsWithCharacterFromSet (characterSet) {
return true
} else {
throw errorType // 4
}
}
}
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
}
}
}
The two verification rules are created. Let's create a validator:
// This validator implements StringValidator, but due to the extension of StringValidator, it is not necessary to implement the func validate (string: String)-> (valid: Bool, errors: [StringValidationError]) method
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)
]
}
}
// Use it below
et numberSet = NSCharacterSet.decimalDigitCharacterSet ()
let startsAndEndsWithValidator = StartsAndEndsWithStringValidator (startsWithSet: letterSet, startsWithDescription: "letter", endsWithSet: numberSet, endsWithDescription: "number")
startsAndEndsWithValidator.validate ("1foo"). errors.description
The above content is a simple example, I have simplified some of the examples in the book.
Let's look at another example below. When extending the protocol, we can combine the where keyword to make the type that meets the where condition automatically exist the default protocol extension.
// The MutableCollectionType protocol has been extended. This protocol is only valid for the types whose Index is Int and implements MutableCollectionType
// Index is the associated type defined in MutableIndexable, the parent protocol of MutableCollectionType
extension MutableCollectionType where Index == Int {
// This method randomly exchanges collection elements
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])
}
}
}
var people = ["Chris", "Ray", "Sam", "Jake", "Charlie"]
people.shuffleInPlace ()
Pattern matching enhancements
In Swift, it can be used not only when implementing protocol extensions, but also in for loops, and if-let, switch, and if-case.
let names = ["Charlie", "Chris", "Mic", "John", "Craig", "Felipe"]
var namesThatStartWithC = [String] ()
// Add names starting with "C" to the array namesThatStartWithC
for cName in names where cName.hasPrefix ("C") {
namesThatStartWithC.append (cName)
}
// Define an Author
public struct Author {
public let name: String
public let status: Additional_Things_PageSources.AuthorStatus
public init (name: String, status: Additional_Things_PageSources.AuthorStatus)
}
let authors = [
Author (name: "Chris Wagner", status: .Late (daysLate: 5)),
Author (name: "Charlie Fulton", status: .Late (daysLate: 10)),
Author (name: "Evan Dekhayser", status: .OnTime)
]
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"
}
}
API availability detection
To detect whether an API is available in Swift 2.x, instead of determining whether it can respond to an API as before, use the following code directly to make it effective under this version system:
if #available (iOS 9.0, \ *) {
// Call API that can only be used under iOS 9
}
defer keyword
Defer said in Swift, the code that will be called when the method ends. In the program, we often put some actions such as memory recovery and status recovery at the end of the code, but if an exception occurs during the execution of the previous code, then the subsequent code may not be executed, causing a program error. But using the defer keyword can ensure that the code will be executed regardless of whether the program ends normally.
For example, when using ATM, no matter what abnormality occurs during use, it must be guaranteed that the bank card must be returned to the user at the end. This is more appropriate to use the defer keyword here.
struct ATM {
mutating func dispenseFunds (amount: Float, inout account: Account) throws {
defer {// Guarantee to be able to successfully refund the card
log + = "Card for \ (account.name) has been returned to customer. \ n"
ejectCard ()
}
// Other logic processing
}
func ejectCard () {
// physically eject card
}
}
Finally, I have finished writing this article. The next part is some small knowledge points. Slowly accumulate it. My reading notes, I hope to help others.