入門篇主要講述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"}