Swift從入門到轉(zhuǎn)行

入門篇主要講述Swift語法,轉(zhuǎn)行篇主要講述用Swift編寫服務(wù)器程序。

入門篇

Swift語法的設(shè)計(jì)原則:簡潔、安全、現(xiàn)代化。

基于簡潔的原則:每條語句后的分號可以不用寫了(寫上分號也沒錯(cuò)),OC數(shù)據(jù)類型字面量語法中的@符號去了,import語句簡潔到令人窒息。

基于安全原則:字典的value為nil不會崩潰。

基于現(xiàn)代化原則:加入了泛型等。

1、打?。?/h3>

還能想到比print更合適的方式么

print("Hello, world!")

2、常量(constant)和變量(variable)

聲明常量用let,變量用var。

var myVariable = 42
let myConstant = 42
let explicitDouble: Double = 70

可以在聲明一個(gè)變量或常量的時(shí)候提供類型標(biāo)注,來明確變量或常量能夠儲存值的類型。添加類型標(biāo)注的方法是在變量或常量的名字后邊加一個(gè)冒號,再跟上要使用的類型名稱。

可變數(shù)據(jù)類型(如數(shù)組、字典等)用var,不可變數(shù)據(jù)類型用let。

3、數(shù)據(jù)類型

整形值:Int

在32位平臺上, Int 的長度和 Int32 相同。在64位平臺上, Int 的長度和 Int64 相同。

let minInt:Int = -2_147_483_648   //整數(shù)和浮點(diǎn)數(shù)都可以添加額外的零或者添加下劃線來增加代碼的可讀性。
let minValue = Int32.min
print(minValue)                   // -2147483648

浮點(diǎn)數(shù)值:Double、Float

Double代表 64 位的浮點(diǎn)數(shù)。Float 代表 32 位的浮點(diǎn)數(shù)。

布爾量值:Bool

Swift為布爾量提供了兩個(gè)常量值:true 和 false 。

Swift的類型安全機(jī)制會阻止用一個(gè)非布爾量的值替換掉 Bool 。下面的栗子在編譯時(shí)會報(bào)錯(cuò):

let i = 1
if I {
    // this example will not compile, and will report an error
}

字符串值:String

字符串的拼接最常用的有:1、使用 “+”號,如Java、JavaScript。2、使用占位符或函數(shù)(如append方法),如OC。

Swift還支持用轉(zhuǎn)義字符加小括號\()的方式。

let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."

為什么是 \:\是轉(zhuǎn)義字符,大家都知道啊。
為什么是(),而不是[]、{}:在小括號里面做數(shù)據(jù)運(yùn)算等操作是如此的順其自然、優(yōu)雅。

Swift同樣支持使用+和append方法進(jìn)行字符串的拼接。

數(shù)組:Array

Swift 數(shù)組類型完整的寫法是 Array<Element>, Element是數(shù)組允許存入的值的類型。你同樣可以簡寫數(shù)組的類型為 [Element]。

使用初始化器創(chuàng)建數(shù)組

var someInts = [Int]()
print("someInts is of type [Int] with \(someInts.count) items.")
// prints "someInts is of type [Int] with 0 items."
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]

使用數(shù)組字面量創(chuàng)建數(shù)組

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

集合:Set

Swift 的集合類型寫做 Set<Element>,這里的 Element是合集要儲存的類型。不同與數(shù)組,合集沒有等價(jià)的簡寫。

var letters = Set<Character>()
print("letters is of type Set<Character> with \(letters.count) items.")
// prints "letters is of type Set<Character> with 0 items."

字典:Dictionary

Swift 字典類型完整的寫法是 Dictionary<Key, Value>,簡寫形式:[Key: Value]。

使用初始化器創(chuàng)建字典

var namesOfIntegers = [Int: String]()
// namesOfIntegers is an empty [Int: String] dictionary

使用字典字面量創(chuàng)建字典

var occupations = 
[
   "Malcolm": "Captain",
   "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"

元組類型:用來創(chuàng)建復(fù)合值,作為返回值、參數(shù)等。小括號內(nèi)放置n個(gè)數(shù)據(jù),任何類型的組合都是可以的。為什么是小括號--中括號和大括號的意義被占用了。

let (x, y) = (1, 2)
// x is equal to 1, and y is equal to 2

存和?。?/p>

let http404Error = (404, "Not Found")
let (statusCode, statusMessage) = http404Error
print("The status code is \(statusCode)")
print("The status code is \(http404Error.0)")

存和?。?/p>

let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")

類型別名

類型別名可以為已經(jīng)存在的類型定義了一個(gè)新的可選名字。用 typealias 關(guān)鍵字定義類型別名。

typealias NameType = String
var name: NameType = "Kobe"

4、控制流

4.1 if語句

let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores 
{
    if score > 50 
    {
        teamScore += 3
    } 
    else 
    {
        teamScore += 1
    }
}
print(teamScore)

重要語法:可選

Swift新增一個(gè)“可選”的概念,在一個(gè)值的類型后面加上一個(gè)問號來把某個(gè)值標(biāo)記為可選。只有可選的值才有可能被賦值為nil。

var optionalString:String? = "Hello"
print(optionalString == nil)

Swift 中的 nil 和Objective-C 中的 nil 不同,在 Objective-C 中 nil是一個(gè)指向不存在對象的指針。在 Swift中, nil 不是指針,他是值缺失的一種特殊類型,任何類型的可選項(xiàng)都可以設(shè)置成 nil 而不僅僅是對象類型。

nil 不能用于非可選的常量或者變量,如果你的代碼中變量或常量需要作用于特定條件下的值缺失,可以給他聲明為相應(yīng)類型的可選項(xiàng)。

可選項(xiàng)綁定

if 和 let搭配使用可以替代非空操作。使用可選綁定而不是強(qiáng)制展開來重寫 possibleNumber。

if let actualNumber = Int(possibleNumber) 
{
    print("\"\(possibleNumber)\" has an integer value of \(actualNumber)")
} 
else 
{
    print("\"\(possibleNumber)\" could not be converted to an integer")
}

可選值的強(qiáng)制展開

一旦確定可選中包含值,你可以在可選的名字后面加一個(gè)感嘆號 ( ! ) 來獲取值,感嘆號的意思是“我知道這個(gè)可選項(xiàng)里邊有值,展開吧?!边@就是所謂的可選值的強(qiáng)制展開。

if convertedNumber != nil 
{
   print("convertedNumber has an integer value of \(convertedNumber!).")
}

使用 ! 來獲取一個(gè)不存在的可選值會導(dǎo)致運(yùn)行錯(cuò)誤,在使用!強(qiáng)制展開之前必須確保可選項(xiàng)中包含一個(gè)非 nil 的值。

guard 語句

類似于 if 語句,基于布爾值表達(dá)式來執(zhí)行語句。使用 guard 語句來要求一個(gè)條件必須是真才能執(zhí)行 guard 之后的語句。與 if 語句不同, guard 語句總是有一個(gè) else 分句—— else 分句里的代碼會在條件不為真的時(shí)候執(zhí)行。

func greet(person: [String: String]) 
{
    guard let name = person["name"] 
    else 
    {
        return
    }
    
    print("Hello \(name)!")
    
    guard let location = person["location"] 
    else 
    {
        print("I hope the weather is nice near you.")
        return
    }
    
    print("I hope the weather is nice in \(location).")
}
 
greet(["name": "John"])
// prints "Hello John!"
// prints "I hope the weather is nice near you."
greet(["name": "Jane", "location": "Cupertino"])
// prints "Hello Jane!"
// prints "I hope the weather is nice in Cupertino."

如果 guard 語句的條件被滿足,代碼會繼續(xù)執(zhí)行直到 guard 語句后的花括號。任何在條件中使用可選項(xiàng)綁定而賦值的變量或者常量在 guard 所在的代碼塊中隨后的代碼里都是可用的。

如果這個(gè)條件沒有被滿足,那么在 else 分支里的代碼就會被執(zhí)行。這個(gè)分支必須轉(zhuǎn)移控制結(jié)束 guard 所在的代碼塊。要這么做可以使用控制轉(zhuǎn)移語句比如 return , break , continue 或者 throw ,或者它可以調(diào)用一個(gè)不帶有返回值的函數(shù)或者方法,比如 fatalError() 。

4.2 Switch

case支持了更多的數(shù)據(jù)類型,default是必要的且后面至少要有一條語句。

break能不能不寫?為了讓代碼更加安全、優(yōu)雅、簡潔,答案是可以。

let vegetable = "red pepper"
switch vegetable 
{
    case "celery":
        print("Add some raisins and make ants on a log.")
    case "cucumber", "watercress":
        print("That would make a good tea sandwich.")
    case let x where x.hasSuffix("pepper"):
        print("Is it a spicy \(x)?")
    default:
        print("Everything tastes good in soup.")
}

4.3 for-in

let interestingNumbers = [
   "Prime": [2, 3, 5, 7, 11, 13],
   "Fibonacci": [1, 1, 2, 3, 5, 8],
   "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers 
{
   for number in numbers 
   {
       if number > largest 
       {
           largest = number
       }
   }
}
print(largest)

4.4 while

var n = 2
while n < 100 
{
   n = n * 2
}
print(n)
         
var m = 2
repeat 
{
   m = m * 2
} while m < 100
print(m)

repeat-while 類似do-while,在每次循環(huán)結(jié)束的時(shí)候計(jì)算它自己的條件。

5、函數(shù)和閉包

5.1 定義和調(diào)用函數(shù)

定義了一個(gè)函數(shù)的時(shí)候,你可以選擇定義一個(gè)或者多個(gè)命名的分類的值作為函數(shù)的輸入(所謂的形式參數(shù)),并且/或者定義函數(shù)完成后將要傳回作為輸出的值的類型(所謂它的返回類型)。

關(guān)鍵字是function還是func?基于簡潔的原則,選擇了更簡潔的 func。

Swift重新定義了返回值。

func greet(person:String , day: String) -> String
{
   return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")

5.2 參數(shù)標(biāo)簽和參數(shù)名

參數(shù)格式:(參數(shù)標(biāo)簽 參數(shù)名:參數(shù)類型)

默認(rèn)情況下,函數(shù)使用他們的形式參數(shù)名來作為實(shí)際參數(shù)標(biāo)簽。

在形式參數(shù)前可以寫自定義的實(shí)際參數(shù)標(biāo)簽,或者使用 _ 來避免使用實(shí)際參數(shù)標(biāo)簽。

func greet(_ person:String, on day:String) ->String
{
   return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")

5.3 輸入輸出形式參數(shù)

在形式參數(shù)定義開始的時(shí)候在前邊添加一個(gè) inout關(guān)鍵字可以定義一個(gè)輸入輸出形式參數(shù)。
輸入輸出形式參數(shù)能輸入值到函數(shù),函數(shù)能對其進(jìn)行修改,還能輸出到函數(shù)外邊替換原來的值。

只能把變量作為輸入輸出形式參數(shù)的實(shí)際參數(shù)。不能用常量或者字面量作為實(shí)際參數(shù),因?yàn)槌A亢妥置媪坎荒苄薷摹?br> 在將變量作為實(shí)際參數(shù)傳遞給輸入輸出形式參數(shù)的時(shí)候,直接在它前邊添加一個(gè)和符合 ( &) 來明確可以被函數(shù)修改。

func swapTwoInts(_ a: inout Int, _ b: inout Int) 
{
   let temporaryA = a
   a = b
   b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

5.4 函數(shù)作為返回值、參數(shù)

這時(shí)候的語法只需要(傳入什么,返回什么):(參數(shù)類型)-> 返回類型

作為返回值:

fun makeIncrementer() -> ((Int) -> Int) 
{
   func addOne(number:Int) ->Int
   {
       return 1 + number
   }
   return addOne
}
var increment = makeIncrementer()
increment(7)

作為參數(shù):

func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool
{
   for item in list 
   {
       if condition(item) 
       {
           return true
       }
   }
   return false
}

6、閉包

閉包能夠捕獲和存儲定義在其上下文中的任何常量和變量的引用,這也就是所謂的閉合并包裹那些常量和變量,因此被稱為“閉包”。

函數(shù)其實(shí)就是閉包的一種特殊形式:一段可以被隨后調(diào)用的代碼塊。

閉包表達(dá)式語法一般形式:

{ (parameters) -> (return type) in
    statements
}

使用花括號({})括起一個(gè)沒有名字的閉包。

使用 in來分隔函數(shù)體和實(shí)際參數(shù)、返回類型。(為什么選擇這么做?)

numbers.map(
{
   (number:Int) -> Int in
   let result = 3 * number
   return result
})

當(dāng)一個(gè)閉包的類型已經(jīng)可知,可以去掉它的參數(shù)類型、返回類型,或者都去掉。

let mappedNumbers = numbers.map({ number in 3 * number })
print(mappedNumbers)

7、對象和類(class)

類的定義和使用,使用class關(guān)鍵字定義類

class Shape 
{
   var numberOfSides = 0
   func simpleDescription() -> String
   {
       return "A shape with \(numberOfSides) sides."
   }
}
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

基于簡潔的原則,很多語言采用的關(guān)鍵字new是不需要的。

重新定義了import機(jī)制:使用同一個(gè)模塊(module)的swift文件不需要import。import反而編譯會報(bào)“No such Module”錯(cuò)誤。

init來創(chuàng)建一個(gè)初始化器,deinit來創(chuàng)建一個(gè)反初始化器。

class NamedShape 
{
   var numberOfSides:Int = 0
   var name:String          
   init(name:String) 
   {
       self.name = name
   }
            
   func simpleDescription() -> String
   {
       return "A shape with \(numberOfSides) sides."
   }
}

子類的方法如果要重寫父類的實(shí)現(xiàn),則需要使用override——不使用override關(guān)鍵字來標(biāo)記則會導(dǎo)致編譯器報(bào)錯(cuò)。

class Square:NamedShape
{
   var sideLength:Double 
            
   init(sideLength:Double , name:String) 
   {
       self.sideLength = sideLength
       super.init(name: name)
       numberOfSides = 4
   }
            
   func area() -> Double
   {
       return sideLength * sideLength
   }
            
   override func simpleDescription() -> String
   {
       return "A square with sides of length \(sideLength)."
   }
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()

Getter 和 Setter

class EquilateralTriangle: NamedShape
{
   var sideLength: Double = 0.0
            
   init(sideLength:Double , name:String) 
   {
       self.sideLength = sideLength
       super.init(name: name)
       numberOfSides = 3
   }
            
   var perimeter:Double 
   {
        get {return 3.0 * sideLength}
        set {sideLength = newValue / 3.0}
   }
            
   override func simpleDescription() -> String 
   {
       return "An equilateral triangle with sides of length \(sideLength)."
   }
}

如果不需要計(jì)算屬性但仍然需要在設(shè)置一個(gè)新值的前后執(zhí)行代碼,使用 willSet和 didSet。

class TriangleAndSquare 
{
   var triangle: EquilateralTriangle
   {
       willSet {square.sideLength = newValue.sideLength}
   }
   var square:Square 
   {
       willSet {triangle.sideLength = newValue.sideLength}
   }
   init(size:Double, name:String) 
   {
       square = Square(sideLength: size, name: name)
       triangle = EquilateralTriangle(sideLength: size, name: name)
   }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)

8、枚舉和結(jié)構(gòu)體

8.1 枚舉(Enumerations)

使用 enum來創(chuàng)建枚舉

enum CompassPoint 
{
    case north
    case south
    case east
    case west
}

不像 C 和 Objective-C 那樣,Swift 的枚舉成員在被創(chuàng)建時(shí)不會分配一個(gè)默認(rèn)的整數(shù)值。在上面 CompassPoint的例子中, north, south, east和 west并不代表 0, 1, 2和 3,它們在自己的權(quán)限中都是完全合格的值。

枚舉能夠包含方法?。?!

enum Rank:Int
{
   case ace = 1
   case two, three, four, five, six, seven, eight, nine, ten
   case jack, queen, king
   func simpleDescription() -> String 
   {
       switch self 
       {
       case .ace:
           return "ace"
       case .jack:
           return "jack"
       case .queen:
           return "queen"
       case .king:
           return "king"
       default:
           return String(self.rawValue)
       }
   }
}
let ace = Rank.ace          
print(ace)              //print ace

每個(gè)枚舉都定義了一個(gè)全新的類型。正如 Swift 中其它的類型那樣,它們的名稱(例如: CompassPoint和 Planet)需要首字母大寫。給枚舉類型起一個(gè)單數(shù)的而不是復(fù)數(shù)的名字,從而使得它們能夠顧名思義。

當(dāng)與 CompassPoint中可用的某一值一同初始化時(shí) directionToHead的類型會被推斷出來。一旦 directionToHead以 CompassPoint類型被聲明,你就可以用一個(gè)點(diǎn)語法把它設(shè)定成不同的 CompassPoint值。

var directionToHead = CompassPoint.west
directionToHead = .east

directionToHead的類型是已知的,所以當(dāng)設(shè)定它的值時(shí)你可以不用寫類型。這樣做可以使得你在操作確定類型的枚舉時(shí)讓代碼非常易讀。

8.2 結(jié)構(gòu)體(Structures)

使用 struct來創(chuàng)建結(jié)構(gòu)體,結(jié)構(gòu)體提供很多類似與類的行為,包括方法和初始化器。

struct Card 
{
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String 
    {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
   }
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

結(jié)構(gòu)體和類最重要的一點(diǎn)區(qū)別就是結(jié)構(gòu)體總是:結(jié)構(gòu)體(和枚舉)是值類型,類是引用類型。結(jié)構(gòu)體會在傳遞的時(shí)候拷貝其自身,而類則會傳遞引用。

9、協(xié)議和擴(kuò)展

9.1 協(xié)議

使用 protocol來聲明協(xié)議。

protocol ExampleProtocol 
{
    var simpleDescription:String{ get }
    mutating func adjust()
}

類,枚舉以及結(jié)構(gòu)體都可實(shí)現(xiàn)(adopt)協(xié)議。

類實(shí)現(xiàn)協(xié)議

class SimpleClass:ExampleProtocol 
{
    var simpleDescription:String = "A very simple class."
    var anotherProperty:Int = 69105
    func adjust() 
    {
        simpleDescription += "Now 100% adjusted."
    }
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription

結(jié)構(gòu)體實(shí)現(xiàn)協(xié)議

struct SimpleStructure: ExampleProtocol
{
   var simpleDescription: String = "A simple structure"
   mutating func adjust() 
   {
       simpleDescription += " (adjusted)"
   }
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription

使用mutating關(guān)鍵字來聲明在SimpleStructure中使方法可以修改struct或enum的變量。

在 SimpleClass中則不需要這樣聲明,因?yàn)轭惱锏姆椒偸强梢孕薷钠渥陨韺傩缘摹?/p>

9.2 擴(kuò)展

使用 extension 來給現(xiàn)存的類型增加功能。比如說新的方法和計(jì)算屬性。

extension Int: ExampleProtocol 
{
    var simpleDescription: String 
    {
        return "The number \(self)"
    }
    
    mutating func adjust() 
    {
        self += 42
    }
}
print(7.simpleDescription)

10、錯(cuò)誤處理

用任何遵循 Error 協(xié)議的類型來表示錯(cuò)誤。

enum PrinterError:Error 
{
    case outOfPaper
    case noToner
    case onFire
}

用 throw 來拋出一個(gè)錯(cuò)誤。
用 throws 來說明一個(gè)函數(shù)可以拋出錯(cuò)誤。

func send(job:Int, toPrinter printerName: String) throws ->String
{
    if printerName == "Never Has Toner" 
    {
        throw PrinterError.noToner
    }
    return "Job sent"
}

do-catch

在 do 代碼塊里,你用 try 來在能拋出錯(cuò)誤的函數(shù)前標(biāo)記。
在 catch 代碼塊,錯(cuò)誤會自動(dòng)賦予名字 error ,如果你不給定其他名字的話。

do 
{
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} 
catch
{
    print(error)
}

可以提供多個(gè) catch 代碼塊來處理特定的錯(cuò)誤。你可以在 catch 后寫一個(gè)模式,用法和 switch 語句里的 case 一樣。

do 
{
    let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
    print(printerResponse)
} 
catch PrinterError.onFire 
{
    print("I'll just put this over here, with the rest of the fire.")
} 
catch let printerError as PrinterError
{
    print("Printer error: \(printerError).")
} 
catch 
{
    print(error)
}

11、泛型

把名字寫在尖括號里來創(chuàng)建一個(gè)泛型方法或者類型。

func makeArray<Item>(repeating item:Item, numberOfTimes:Int) -> [Item] 
{
    var result = [Item]()
    for _ in 0..< numberOfTimes 
    {
        result.append(item)
    }
    return result
}
makeArray(repeating: "knock", numberOfTimes:4)

可以從函數(shù)和方法、類、枚舉、結(jié)構(gòu)體創(chuàng)建泛型。

enum OptionalValue<Wrapped> 
{
    case none
    case some(Wrapped)
}
var possibleInteger:OptionalValue<Int> = .none
possibleInteger = .some(100)

在類型名稱后加 where可用來明確一些需求——比如要求類型實(shí)現(xiàn)一個(gè)協(xié)議,要求兩個(gè)類型必須相同,要求類必須繼承自特定的父類。

func anyCommonElements<T:Sequence , U:Sequence>(_ lhs: T, _ rhs: U) -> Bool
    where T.Iterator.Element:Equatable,T.Iterator.Element == U.Iterator.Element
    {
        for lhsItem in lhs 
        {
            for rhsItem in rhs 
            {
                if lhsItem == rhsItem 
                {
                    return true
                }
            }
        }
        return false
}
anyCommonElements([1, 2, 3], [3])
<T: Equatable>和 <T where T: Equatable>是一樣的。

轉(zhuǎn)行篇

目前主要的 Swift 服務(wù)端開發(fā)框架有:Perfect(perfect.org)、Vapor(vapor.codes)、Kitura(kitura.io) 和 Zewo(zewo.io)。

Perfect是目前最流行的框架,這里也只介紹 Perfect。

1、Perfect簡介

包含:完整強(qiáng)大工具箱、軟件框架、Web應(yīng)用服務(wù)器。

可運(yùn)行平臺:Linux、iOS和MacOS (OS X)。

可用于:開發(fā)Web應(yīng)用和REST服務(wù)。

2、Perfect示例

(1)配置環(huán)境(MacOS下有Xcode 8.0+即可)。(2)下載示例Demo。(3)編譯運(yùn)行。

執(zhí)行以下命令能夠克隆并編譯一個(gè)空的入門項(xiàng)目。編譯后可以啟動(dòng)一個(gè)本地的服務(wù)器,監(jiān)聽計(jì)算機(jī)的8181端口。

git clone https://github.com/PerfectlySoft/PerfectTemplate.git
cd PerfectTemplate
swift build
.build/debug/PerfectTemplate

使用SPM生成一個(gè)Xcode項(xiàng)目

Swift軟件包管理器(SPM) 是一個(gè)用于Swift項(xiàng)目開發(fā)、測試、依存關(guān)系管理的命令行工具。所有的Perfect組件針對SPM設(shè)計(jì)的。如果使用Perfect進(jìn)行項(xiàng)目開發(fā),一定需要SPM。

SPM根據(jù) Package.swift 文件可以創(chuàng)建對應(yīng)的Xcode項(xiàng)目,該項(xiàng)目允許您使用Xcode編譯和調(diào)試。在項(xiàng)目根目錄下使用以下命令行:

swift package generate-xcodeproj

以上clone下來的項(xiàng)目代表了一個(gè)基本的Swift服務(wù)器項(xiàng)目,下邊介紹下這個(gè)項(xiàng)目。

[站外圖片上傳中...(image-8cf71d-1566791538242)]

打開PerfectTemplate.xcodeproj工程文件,編譯。這時(shí)可能會報(bào)錯(cuò)(Xcode8.1后修復(fù)了)。

ld: library not found for -lCOpenSSL for architecture x86_64

解決該問題的方法就是設(shè)置 Library Search Paths 為 "$(PROJECT_DIR)/**"(包括雙引號也)

3、Perfect目錄

在Perfect Template項(xiàng)目模板中,有兩個(gè)很重要的文件:

(1)Sources目錄:包含了所有Perfect項(xiàng)目的Swift源程序文件。

(2)Package.swift:SPM文件管理清單,包含了整個(gè)項(xiàng)目對其它庫函數(shù)的依存關(guān)系。這個(gè)文件可以理解為CocoaPod中的Podfile。

(3)webroot:存儲相應(yīng)的靜態(tài)文件的根目錄。可以在Source目錄中的main.swift中對靜態(tài)文件的根目錄進(jìn)行配置。

所有的SPM項(xiàng)目至少要包括一個(gè) Sources 目錄和一個(gè) Package.swift 文件。

[站外圖片上傳中...(image-a0dcd7-1566791538242)]

在 Package.swift 文件中由兩個(gè)重要的內(nèi)容可能需要編輯。
第一個(gè)是 name 項(xiàng)目名稱,用于說明當(dāng)前項(xiàng)目的目標(biāo)名稱,因此最后可執(zhí)行文件的名字也會按照這個(gè)名稱進(jìn)行編譯。

第二個(gè)是 dependencies 依存關(guān)系清單。該內(nèi)容說明了您的應(yīng)用程序需要的所有子項(xiàng)目列表,在這個(gè)數(shù)組中其中每一個(gè)條目都包含了一個(gè)“.Package”軟件包,及其來源URL和版本。

[站外圖片上傳中...(image-2bbd00-1566791538242)]

4、用 Perfect 寫一個(gè) RESTful API

功能:實(shí)現(xiàn)表單提交,返回Json數(shù)據(jù)。

1、靜態(tài)文件的添加與訪問

在PHP開發(fā)或者Java Web開發(fā)中,都有一個(gè)根目錄來存儲相應(yīng)的靜態(tài)文件,比如wwwroot, htdoc, webroot等等這些文件。使用Perfect開發(fā)服務(wù)器端也是如此,我們可以在Source目錄中的main.swift中對靜態(tài)文件的根目錄進(jìn)行配置,下方就是我們的配置代碼:

// Set a document root.
// This is optional. If you do not want to serve static content then do not set this.
// Setting the document root will automatically add a static file handler for the route /**
server.documentRoot = "./webroot"

配置完成后,如果我們的項(xiàng)目不用Xcode進(jìn)行管理的話,當(dāng)我們對Perfect工程進(jìn)行編譯和運(yùn)行時(shí),會在相應(yīng)的模板目錄下創(chuàng)建相應(yīng)的靜態(tài)文件的根目錄(webroot)。如下所示:

在webroot中我們添加上相應(yīng)的靜態(tài)文件,我們就可以通過Perfect服務(wù)進(jìn)行訪問了,下方是index.html的內(nèi)容——一個(gè)提交帳號密碼的表單。

<html>
<head>
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    <form action = "http://0.0.0.0:8181/login" method = "POST">
        UserName : <input type = "text" name = "userName"><br/>
        Password : <input type = "text" name = "password"><br/>
        <input type = "submit" value = "Submit">
    </form>
</body>
</html>

在訪問上述index.html文件之前,我們需要將main.swift中添加的路由進(jìn)行刪除,下方選中的部分就是要?jiǎng)h除的代碼。如果你不刪除下方這段代碼的話,如果你訪問localhost:8181的話,那么就是下方路由所返回的內(nèi)容。修改完代碼后,要重新進(jìn)行編譯運(yùn)行才生效的,這一點(diǎn)與解釋性語言PHP是不同的。

routes.add(method: .get,
           uri: "/",
           handler:{
        request, response in
        response.setHeader(.contentType, value: "text/html")
        response.appendBody(string: "<html><title>Hello, world!</title><body>Hello, Jinghuang Liu!</body></html>")
        response.completed()
    }
)

經(jīng)過上述步驟后,我們就可以通過訪問localhost:8181來加載我們的index.html文件了。

http://0.0.0.0:8181

2、路由配置

main.swift中的代碼段,首先創(chuàng)建了一個(gè)HTTPServer()的對象,然后又創(chuàng)建了一個(gè)路由對象,接著又將這個(gè)路由對象添加到了服務(wù)對象上。

下方我們添加了一個(gè)“/login”的路由路徑,其請求方法是.post的方式,后方的閉包是對響應(yīng)的處理。具體代碼如下:

routes.add(method: .post,
           uri: "/login",
           handler:{request, response in
            
            //賬戶密碼到驗(yàn)證
            guard let userName = request.param(name:"userName") else
            {
                return
            }
            guard let password = request.param(name:"password") else
            {
                return
            }
            
            //json的包裝:可以用一個(gè)專門的類來做
            var resBody = ["userName" : userName , "password" : password]
            let responseDic : [String : Any] = ["responseBody" : resBody,
                                                "result" : "Success",
                                                "Message" : "request Successfully"]
            do
            {
                let json = try responseDic.jsonEncodedString()
                response.setBody(string:json)
            }
            catch
            {
                response.setBody(string:"json encode error")
            }

            response.completed()
    }
)

表單提交后會跳到以下的地址

http://0.0.0.0:8181/login

得到以下結(jié)果

{"responseBody":{"userName":"ljh","password":"123456"},"result":"Success","Message":"request Successfully"}

參考:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/index.html#//apple_ref/doc/uid/TP40014097-CH3-ID0

https://www.perfect.org

https://swift.org/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容