1、項目代碼結構
如下為我的項目結構,使用的cocoapods 管理。

Metadata 存放model數據,Modules 存放功能模塊
使用的第三方庫
#source " https://github.com/CocoaPods/Specs"
#source " https://github.com/shuleihen/Specs"
# Uncomment this line to define a global platform for your project
platform :ios, '8.0'
target 'CrossBorder' do
# Comment this line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
# Pods for CrossBorder
pod 'ReactiveCocoa'
pod 'Alamofire', '~> 3.4'
pod 'ObjectMapper', '~> 1.3'
# pod 'AlamofireObjectMapper', '~> 3.0'
pod 'CryptoSwift'
pod 'KeychainAccess'
pod 'XCGLogger', '~> 3.3'
pod 'PKHUD'
pod 'SQLite.swift', '~> 0.10.1'
pod 'DynamicColor'
pod 'SnapKit', '~> 0.30.0.beta1'
pod 'MBProgressHUD', '~> 0.9.2'
pod 'RealmSwift', '~> 1.0.0'
pod 'R.swift'
end
以上為Podfile 文件使用到的類庫,Alamofire 為網絡庫,OjectMapper 為json轉model庫等。具體可以自己到github上查看。
2、API 數據格式
一般服務器返回的json數據格式是:
{
"retCode”:"200"
"retMsg":""
"retCode”:"200"
"retMsg":""
"retResult
”
:{}
}
retCode 為協議code,這里的code和http返回的code不同,這里的code是在http請求成功,服務器響應的請求返回的處理code。一般會用200或是0000表示成功,如果服務器處理錯誤,就在retMsg給出錯誤描述。
retMsg 為服務器返回的錯誤內容。由於多平台客服端和跨國問題,retMsg並不一定能作為
提示文案。因此,app端會保存一份服務器code碼和錯誤描述的對應表(可以是多語種的),如果服務器返回錯誤code,根據code查詢對應的錯誤文案,封裝成NSError 傳遞給上層使用。
retResult 為API接口處理成功返回的數據,有時候API接口處理成功沒有數據返回會為空。關於retResult 數據是否要轉成model網上有爭論,我們暫且不論,我們考慮代碼簡潔性和易讀性,依然使用轉成model模式。oc 已經有很多比較成熟的json轉model庫(例如:MJExtension),swift 也有不錯的類庫,我使用的是 ObjectMapper。
3、數據model
使用ObjectMapper 以登錄返回User對象為例:
import ObjectMapper
class User: Mappable {
var accessToken: String?
var expressTime: String?
var gesturePassword: String?
var userid: String?
var phone: String?
init (){
}
required init?(_ map: Map) {
}
func mapping(map: Map) {
accessToken <- map["accessToken"]
expressTime <- map["expressTime"]
gesturePassword <- map["gesturePassword"]
userid <- map["userid"]
phone <- map["phone"]
}
}
4、Response 返回處理
// Restfull API Host
let URL_HOST = ""
// Restfull API Path
enum URLPath:String {
case Login = "user/login"
case Login = "user/login"
case LoginOut = "remit/loginout"
}
// Restfull API Response Struct
enum ResponseData: String {
case Code = "retCode"
case Code = "retCode"
case Msg = "retMsg"
case Result = "retResult"
case Success = "0000"
}
class Network {
class func request(method method:Alamofire.Method,
path:URLPath,
parameters:[String: AnyObject]? = nil)-> Alamofire.Request {
let URLString = URL_HOST + path.rawValue
let encoding = Alamofire.ParameterEncoding.JSON
let headers = AppContext.sharedInstance.commRESTHeader()
let request = Manager.sharedInstance.request(method, URLString, parameters: parameters, encoding: encoding, headers: headers)
NSLog("\n/*---- Request ----\n\(request.debugDescription)\n------------------*/\n\n")
return request
}
static let queue = dispatch_queue_create("com.crossborder.network", DISPATCH_QUEUE_SERIAL)
}
// MARK: Object
extension Request {
public func responseObject<T: Mappable>(completionHandler: Response<T, NSError> -> Void) -> Self {
return response_cb { response in
if response.result.isSuccess {
let value = Mapper<T>().map(response.result.value)
let result = Result<T, NSError>.Success(value!)
let rsp = Response<T, NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
} else {
let result = Result<T, NSError>.Failure(response.result.error!)
let rsp = Response<T, NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
}
}
}
}
// MARK: Array
extension Request {
public func responseArray<T: Mappable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
return response_cb { response in
if response.result.isSuccess {
let value = Mapper<T>().mapArray(response.result.value)
let result = Result<[T], NSError>.Success(value!)
let rsp = Response<[T], NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
} else {
let result = Result<[T], NSError>.Failure(response.result.error!)
let rsp = Response<[T], NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
}
}
}
}
// MARK: Dictionary
extension Request {
public func responseDictionary(completionHandler: Response<AnyObject, NSError> -> Void) -> Self {
return response_cb { response in
dispatch_async(dispatch_get_main_queue()) { completionHandler(response) }
}
}
public func response_cb(queue queue: dispatch_queue_t? = Network.queue, completionHandler: Response<AnyObject, NSError> -> Void) -> Self {
return response(queue: queue, responseSerializer: Request.DictionaryMapperSerializer(), completionHandler: {response in
if response.result.isFailure {
dispatch_async(dispatch_get_main_queue()) { ErrorHandler.handler(response.result.error) }
}
completionHandler(response)
})
}
public static func DictionaryMapperSerializer() -> ResponseSerializer<AnyObject, NSError> {
return ResponseSerializer { request, response, data, error in
guard error == nil else { return .Failure(error!) }
if let response = response where response.statusCode == 204 { return .Success(NSNull()) }
guard let validData = data where validData.length > 0 else {
let reason = "JSON could not be serialized. Input data was nil or zero length."
let error = ErrorHandler.error(code: ErrorCode.SerializationFailed.rawValue, reason: reason)
return .Failure(error)
}
let json: AnyObject?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments)
} catch {
return .Failure(error as NSError)
}
NSLog("\n/*---- Response ----\nRequest URL = \(request?.URLString)\n\nResult = \(json)\n------------------*/\n")
let code = json?[ResponseData.Code.rawValue] as? String
guard let _ = code else {
let reason = "Data struct invalid."
let error = ErrorHandler.error(code: ErrorCode.DataStructInvalid.rawValue, reason: reason)
return .Failure(error)
}
guard code! == ResponseData.Success.rawValue else {
let reason = json?[ResponseData.Msg.rawValue] as? String
let error = ErrorHandler.error(code: Int(code!)!, reason: reason)
return .Failure(error)
}
guard let result = json?[ResponseData.Result.rawValue] else {
return .Success(NSNull())
}
return .Success(result!)
}
}
}
class Network {
class func request(method method:Alamofire.Method,
path:URLPath,
parameters:[String: AnyObject]? = nil)-> Alamofire.Request {
let URLString = URL_HOST + path.rawValue
let encoding = Alamofire.ParameterEncoding.JSON
let headers = AppContext.sharedInstance.commRESTHeader()
let request = Manager.sharedInstance.request(method, URLString, parameters: parameters, encoding: encoding, headers: headers)
NSLog("\n/*---- Request ----\n\(request.debugDescription)\n------------------*/\n\n")
return request
}
static let queue = dispatch_queue_create("com.crossborder.network", DISPATCH_QUEUE_SERIAL)
}
// MARK: Object
extension Request {
public func responseObject<T: Mappable>(completionHandler: Response<T, NSError> -> Void) -> Self {
return response_cb { response in
if response.result.isSuccess {
let value = Mapper<T>().map(response.result.value)
let result = Result<T, NSError>.Success(value!)
let rsp = Response<T, NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
} else {
let result = Result<T, NSError>.Failure(response.result.error!)
let rsp = Response<T, NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
}
}
}
}
// MARK: Array
extension Request {
public func responseArray<T: Mappable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
return response_cb { response in
if response.result.isSuccess {
let value = Mapper<T>().mapArray(response.result.value)
let result = Result<[T], NSError>.Success(value!)
let rsp = Response<[T], NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
} else {
let result = Result<[T], NSError>.Failure(response.result.error!)
let rsp = Response<[T], NSError>(
request: response.request,
response: response.response,
data: response.data,
result: result,
timeline: response.timeline
)
dispatch_async(dispatch_get_main_queue()) { completionHandler(rsp) }
}
}
}
}
// MARK: Dictionary
extension Request {
public func responseDictionary(completionHandler: Response<AnyObject, NSError> -> Void) -> Self {
return response_cb { response in
dispatch_async(dispatch_get_main_queue()) { completionHandler(response) }
}
}
public func response_cb(queue queue: dispatch_queue_t? = Network.queue, completionHandler: Response<AnyObject, NSError> -> Void) -> Self {
return response(queue: queue, responseSerializer: Request.DictionaryMapperSerializer(), completionHandler: {response in
if response.result.isFailure {
dispatch_async(dispatch_get_main_queue()) { ErrorHandler.handler(response.result.error) }
}
completionHandler(response)
})
}
public static func DictionaryMapperSerializer() -> ResponseSerializer<AnyObject, NSError> {
return ResponseSerializer { request, response, data, error in
guard error == nil else { return .Failure(error!) }
if let response = response where response.statusCode == 204 { return .Success(NSNull()) }
guard let validData = data where validData.length > 0 else {
let reason = "JSON could not be serialized. Input data was nil or zero length."
let error = ErrorHandler.error(code: ErrorCode.SerializationFailed.rawValue, reason: reason)
return .Failure(error)
}
let json: AnyObject?
do {
json = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments)
} catch {
return .Failure(error as NSError)
}
NSLog("\n/*---- Response ----\nRequest URL = \(request?.URLString)\n\nResult = \(json)\n------------------*/\n")
let code = json?[ResponseData.Code.rawValue] as? String
guard let _ = code else {
let reason = "Data struct invalid."
let error = ErrorHandler.error(code: ErrorCode.DataStructInvalid.rawValue, reason: reason)
return .Failure(error)
}
guard code! == ResponseData.Success.rawValue else {
let reason = json?[ResponseData.Msg.rawValue] as? String
let error = ErrorHandler.error(code: Int(code!)!, reason: reason)
return .Failure(error)
}
guard let result = json?[ResponseData.Result.rawValue] else {
return .Success(NSNull())
}
return .Success(result!)
}
}
}
5、接口調用
HUD.show(.LabeledProgress(title: "",subtitle:"登錄中..."))
Network.request(method: Alamofire.Method.POST, path: URLPath.Login, parameters: ["account": account,"password":passwordMD5])
.responseObject { (response: Alamofire.Response<User, NSError>) in
if response.result.isSuccess {
UserHelp.sava(user: response.result.value!,password: password)
HUD.flash(.Success, delay: 0.0,completion: { finish in
AppDelegate.switchToMain()
})
} else {
HUD.flash(.LabeledError(title: "",subtitle: response.result.error?.localizedDescription), delay: 0.5)
}
Network.request(method: Alamofire.Method.POST, path: URLPath.Login, parameters: ["account": account,"password":passwordMD5])
.responseObject { (response: Alamofire.Response<User, NSError>) in
if response.result.isSuccess {
UserHelp.sava(user: response.result.value!,password: password)
HUD.flash(.Success, delay: 0.0,completion: { finish in
AppDelegate.switchToMain()
})
} else {
HUD.flash(.LabeledError(title: "",subtitle: response.result.error?.localizedDescription), delay: 0.5)
}
}