写在开头
在完成《移动互联网》大作业的过程中,开发Swift项目构建iOS移动端项目是一件令人愉悦的过程,storyboard快速构建项目搭配流行的几种开源项目实用库,能够大大提高开发效率,今天在这里就简单的介绍下大作业中使用的几种开源项目
Alamofire
Alamofire
是用Swift语言编写的HTTP网络库,由此前热门开源项目AFNetworking的作者mattt开发,可以便捷的应用于异步网络通信
安装
要求 Xcode 6.0, iOS 7.0+ / Mac OS X 10.9+
- DownLoad项目中的xcodeproj并导入到自己的项目中
- 选择项目并添加
Embedded Binaries
,导入Alamofire.framework
- 使用
command + b
重新构建本项目
使用
发送一个简单的请求
import Alamofire
Alamofire.request("https://httpbin.org/get")
Response处理
很多情况下,我们需要对返回的结果进行一些处理,比如消息验证,返回值信息的提取等等,在
Alamofire
中我们使用一种链式的方式进行Response的处理
Alamofire.request("https://httpbin.org/get").responseJSON { response in
print(response.request) // original URL request
print(response.response) // HTTP URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
在上述例子中,我们在Alamofire.request(url)
后注册了一个responseJSON
的方法,作用等价于一个闭包的callback
函数,在异步调用完成request
后,才开始responseJSON
方法
Alamofire
中由如下五种不同的response handlers
// Response Handler - Unserialized Response
func response(
queue: DispatchQueue?,
completionHandler: @escaping (DefaultDataResponse) -> Void)
-> Self
// Response Data Handler - Serialized into Data
func responseData(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Data>) -> Void)
-> Self
// Response String Handler - Serialized into String
func responseString(
queue: DispatchQueue?,
encoding: String.Encoding?,
completionHandler: @escaping (DataResponse<String>) -> Void)
-> Self
// Response JSON Handler - Serialized into Any
func responseJSON(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Any>) -> Void)
-> Self
// Response PropertyList (plist) Handler - Serialized into Any
func responsePropertyList(
queue: DispatchQueue?,
completionHandler: @escaping (DataResponse<Any>) -> Void))
-> Self
链式处理
Response Handlers被链式的组织了起来
Alamofire.request("https://httpbin.org/get")
.responseString { response in
print("Response String: \(response.result.value)")
}
.responseJSON { response in
print("Response JSON: \(response.result.value)")
}
Response验证
Alamofire
默认认为每个已经完成的请求都是成功的,我们可以在Response Handler处理Response之前调用validate
方法来验证response是否符合规范,比如status code和 MIME类型等等,不符合则会在 response.result
注入.failure
,此时在response handler
中判断异常即可
Alamofire.request("https://httpbin.org/get")
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.responseData { response in
switch response.result {
case .success:
print("Validation Successful")
case .failure(let error):
print(error)
}
}
不同的HTTP Methods
public enum HTTPMethod: String {
case options = "OPTIONS"
case get = "GET"
case head = "HEAD"
case post = "POST"
case put = "PUT"
case patch = "PATCH"
case delete = "DELETE"
case trace = "TRACE"
case connect = "CONNECT"
}
Alamofire.request("https://httpbin.org/get") // method defaults to `.get`
Alamofire.request("https://httpbin.org/post", method: .post)
Alamofire.request("https://httpbin.org/put", method: .put)
Alamofire.request("https://httpbin.org/delete", method: .delete)
HTTP参数的编码方式
- URL Encoding
.methodDependent
- 根据当前的HTTP请求格式进行参数编码
.queryString
- 将请求参数编码到query string中
.httpBody
- 将请求参数编码到 HTTP body中
let parameters: Parameters = ["foo": "bar"]
// All three of these calls are equivalent
Alamofire.request("https://httpbin.org/get", parameters: parameters) // encoding defaults to `URLEncoding.default`
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .methodDependent))
// https://httpbin.org/get?foo=bar
let parameters: Parameters = [
"foo": "bar",
"baz": ["a", 1],
"qux": [
"x": 1,
"y": 2,
"z": 3
]
]
// All three of these calls are equivalent
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: URLEncoding.httpBody)
// HTTP body: foo=bar&baz[]=a&baz[]=1&qux[x]=1&qux[y]=2&qux[z]=3
- JSON Encoding
JSON Encoding
会在请求的http body
中创建json表示的参数,并且会把请求头中的Content-Type
设置成application/json
let parameters: Parameters = [
"foo": [1,2,3],
"bar": [
"baz": "qux"
]
]
// Both calls are equivalent
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding.default)
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters, encoding: JSONEncoding(options: []))
// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
HTTP Headers设置
Alamofire
允许用户创建一个自定义的Header
let headers: HTTPHeaders = [
"Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"Accept": "application/json"
]
Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
验证与认证
在本次的项目中,调用api接口时经常需要进行Basic基本认证,在
Alamofire
中,有基于HTTP Basic
,URLCredential
等等的验证方式
//Basic
let user = "user"
let password = "password"
Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(user: user, password: password)
.responseJSON { response in
debugPrint(response)
}
//Authorization
let user = "user"
let password = "password"
var headers: HTTPHeaders = [:]
if let authorizationHeader = Request.authorizationHeader(user: user, password: password) {
headers[authorizationHeader.key] = authorizationHeader.value
}
Alamofire.request("https://httpbin.org/basic-auth/user/password", headers: headers)
.responseJSON { response in
debugPrint(response)
}
//URLCredential
let user = "user"
let password = "password"
let credential = URLCredential(user: user, password: password, persistence: .forSession)
Alamofire.request("https://httpbin.org/basic-auth/\(user)/\(password)")
.authenticate(usingCredential: credential)
.responseJSON { response in
debugPrint(response)
}
其它
其它的一些比较高阶的网络请求,比如下载/上传,session,请求序列化等等,可以参考 Alamofire
SwiftJSON
在我们处理网络请求返回给我们的数据时,由于数据通常会是JSON格式,并且Swift自带的
JSONSerialization
使用起来会十分的麻烦,因此本项目中采用了Swift JSON
的开源项目来处理JSON转换相关的问题
JSONSerialization的弊端
if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
let user = statusesArray[0]["user"] as? [String: Any],
let username = user["name"] as? String {
// Finally we got the username
}
if let JSONObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]],
let username = (JSONObject[0]["user"] as? [String: Any])?["name"] as? String {
// There's our username
}
在使用了SwiftJson
项目后,我们读取JSON对象中数据变得尤其简单
let json = JSON(data: dataFromNetworking)
if let userName = json[0]["user"]["name"].string {
//Now you got your value
}
更为轻松的是,我们不再需要为JSON解析错误而引起的APP崩溃而感到束手无策,SwiftJSON
为我们进行了全面的封装和出错信息的处理
let json = JSON(data: dataFromNetworking)
if let userName = json[999999]["wrong_key"]["wrong_name"].string {
//Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety
} else {
//Print the error
print(json[999999]["wrong_key"]["wrong_name"])
}
使用Subscript
//Getting a double from a JSON Array
let name = json[0].double
//Getting an array of string from a JSON Array
let arrayNames = json["users"].arrayValue.map({$0["name"].stringValue})
//Getting a string from a JSON Dictionary
let name = json["name"].stringValue
//Getting a string using a path to the element
let path: [JSONSubscriptType] = [1,"list",2,"name"]
let name = json[path].string
//Just the same
let name = json[1]["list"][2]["name"].string
//Alternatively
let name = json[1,"list",2,"name"].string
//With a hard way
let name = json[].string
//With a custom way
let keys:[SubscriptType] = [1,"list",2,"name"]
let name = json[keys].string
LOOP循环
//If json is .Dictionary
for (key,subJson):(String, JSON) in json {
//Do something you want
}
//If json is .Array
//The `index` is 0..<json.count's string value
for (index,subJson):(String, JSON) in json {
//Do something you want
}
出错信息处理
let json = JSON(["name", "age"])
if let name = json[999].string {
//Do something you want
} else {
print(json[999].error) // "Array[999] is out of bounds"
}
let json = JSON(["name":"Jack", "age": 25])
if let name = json["address"].string {
//Do something you want
} else {
print(json["address"].error) // "Dictionary["address"] does not exist"
}
let json = JSON(12345)
if let age = json[0].string {
//Do something you want
} else {
print(json[0]) // "Array[0] failure, It is not an array"
print(json[0].error) // "Array[0] failure, It is not an array"
}
if let name = json["name"].string {
//Do something you want
} else {
print(json["name"]) // "Dictionary[\"name"] failure, It is not an dictionary"
print(json["name"].error) // "Dictionary[\"name"] failure, It is not an dictionary"
}
填充JSON对象的数据
json["name"] = JSON("new-name")
json[0] = JSON(1)
json["id"].int = 1234567890json["coordinate"].double = 8766.766json["name"].string = "Jack"json.arrayObject = [1,2,3,4]json.dictionaryObject = ["name":"Jack", "age":25]
//With subscript in array
var json: JSON = [1,2,3]
json[0] = 100
json[1] = 200
json[2] = 300
json[999] = 300 //Don't worry, nothing will happen
//With subscript in dictionary
var json: JSON = ["name": "Jack", "age": 25]
json["name"] = "Mike"
json["age"] = "25" //It's OK to set String
json["address"] = "L.A." // Add the "address": "L.A." in json
//Array & Dictionary
var json: JSON = ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]]
json["list"][3]["what"] = "that"
json["list",3,"what"] = "that"
let path: [JSONSubscriptType] = ["list",3,"what"]
json[path] = "that"
//With other JSON objectslet user: JSON = ["username" : "Steve", "password": "supersecurepassword"]let auth: JSON = [ "user": user.object //use user.object instead of just user "apikey": "supersecretapitoken"]
与Alamofire
搭配使用
配合上我们在上一节提到过的HTTP网络请求库,我们可以做到完美的处理网络请求,发送各种形式的http请求,然后使用SwiftJSON
很好的处理服务器传回来的JSON数据
Alamofire.request(url, method: .get).validate().responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
Swift-Charts
在进行信息展示的时候,恰当的图表往往能够比文字、列表等更有说服力和表现力,因此我们做移动端设计时需要考虑到图表的应用,因此我们采用了
ios-charts library
由于在制作图标过程中在网上看到过关于这个开源库的详细使用教程,因此只在这里贴出教程的网址 - 如何在 Swift 语言下使用 iOS Charts API 制作漂亮图表