[译] Alamofire Tutorial: Getting Started
[译] Alamofire Tutorial: Getting Started转载请注明出处:http://leejunhui.com/2017/01/23/Alamofire-Tutorial/
注:已更新到
你应该对于 Getting Started下载项目代码 摸我 下面介绍的项目名为
编译并运行该项目,你就可以看到如下界面:
点击 The Imagga APIImagga是一个为开发者和企业提供构建可伸缩的,以图片为主的云产品的图像识别服务的网站。你可以先尝试下官方的自动标记服务demo:地址. 你需要再Imagga为本文的项目创建一个免费的开发者账号。因为Imagga对于每个 在
稍后你将会用到Imagga的api来上传图片, Installing Dependencies 安装依赖在项目主目录里创建 platform :ios,'10.0' inhibit_all_warnings! use_frameworks! target 'PhotoTagger' do pod 'Alamofire','~> 4.2.0' end 然后打开终端,来到项目主目录下,执行
关闭Xcode项目,然后打开新生成的 REST,HTTP,JSON — 是什么呢?如果你对使用HTTP不是特别有经验的话,那么你可能会很好奇这些缩写词到底是什么含义。 REST,或者表征状态转移,是用来设计可持续的,易于使用的以及易于维护的WEB API的一套规范。REST有几个体系结构规则,用于强制执行某些操作,例如不在请求之间保持状态,使请求可缓存,并提供统一的接口。这样,像您这样的应用开发者就可以轻松地将API集成到您的应用中,而无需跟踪请求之间的数据状态。
Alamofire适合做什么?你为什么需要
这些 // With URLSession public func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) { let url = URL(string: "http://localhost:5984/rooms/_all_docs?include_docs=true")! var urlRequest = URLRequest( url: url,cachePolicy: .reloadIgnoringLocalAndRemoteCacheData,timeoutInterval: 10.0 * 1000) urlRequest.httpMethod = "GET" urlRequest.addValue("application/json",forHTTPHeaderField: "Accept") let task = urlSession.dataTask(with: urlRequest) { (data,response,error) -> Void in guard error == nil else { print("Error while fetching remote rooms: (error)") completion(nil) return } guard let data = data,let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { print("Nil data received from fetchAllRooms service") completion(nil) return } guard let rows = json["rows"] as? [[String: Any]] else { print("Malformed data received from fetchAllRooms service") completion(nil) return } let rooms = rows.flatMap({ (roomDict) -> RemoteRoom? in return RemoteRoom(jsonData: roomDict) }) completion(rooms) } task.resume() } 对比: // With Alamofire func fetchAllRooms(completion: @escaping ([RemoteRoom]?) -> Void) { Alamofire.request( URL(string: "http://localhost:5984/rooms/_all_docs")!,method: .get,parameters: ["include_docs": "true"]) .validate() .responseJSON { (response) -> Void in guard response.result.isSuccess else { print("Error while fetching remote rooms: (response.result.error)") completion(nil) return } guard let value = response.result.value as? [String: Any],let rows = value["rows"] as? [[String: Any]] else { print("Malformed data received from fetchAllRooms service") completion(nil) return } let rooms = rows.flatMap({ (roomDict) -> RemoteRoom? in return RemoteRoom(jsonData: roomDict) }) completion(rooms) } } 你可以看到 上传文件打开ViewController.swift文件,然后在顶部添加以下代码: import Alamofire 这使你可以在代码中使用 // Networking calls extension ViewController { func upload(image: UIImage,progressCompletion: @escaping (_ percent: Float) -> Void,completion: @escaping (_ tags: [String],_ colors: [PhotoColor]) -> Void) { guard let imageData = UIImageJPEGRepresentation(image,0.5) else { print("Could not get JPEG representation of UIImage") return } } } 将图片上传到Imagga的第一步是将图片转换为适用于API的正确格式。上面的图片选择方法会返回一个转化成JPEG格式的图片实例。 // 1 takePictureButton.isHidden = true progressView.progress = 0.0 progressView.isHidden = false activityIndicatorView.startAnimating() upload( image: image,progressCompletion: { [unowned self] percent in // 2 self.progressView.setProgress(percent,animated: true) },completion: { [unowned self] tags,colors in // 3 self.takePictureButton.isHidden = false self.progressView.isHidden = true self.activityIndicatorView.stopAnimating() self.tags = tags self.colors = colors // 4 self.performSegue(withIdentifier: "ShowResults",sender: self) })
1.隐藏上传按钮,并显示进度条和活动视图。 然后,我们回到upload(image:progressCompletion:completion:)方法,然后在转化UIImage实例代码后面添加如下代码: Alamofire.upload( multipartFormData: { multipartFormData in multipartFormData.append(imageData,withName: "imagefile",fileName: "image.jpg",mimeType: "image/jpeg") },to: "http://api.imagga.com/v1/content",headers: ["Authorization": "Basic xxx"],encodingCompletion: { encodingResult in } ) 请确保从Imagga网站上获取到你自己的 switch encodingResult { case .success(let upload,_,_): upload.uploadProgress { progress in progressCompletion(Float(progress.fractionCompleted)) } upload.validate() upload.responseJSON { response in } case .failure(let encodingError): print(encodingError) } 这两段代码通过调用
接下来,在 // 1. guard response.result.isSuccess else { print("Error while uploading file: (response.result.error)") completion([String](),[PhotoColor]()) return } // 2. guard let responseJSON = response.result.value as? [String: Any],let uploadedFiles = responseJSON["uploaded"] as? [[String: Any]],let firstFile = uploadedFiles.first,let firstFileID = firstFile["id"] as? String else { print("Invalid information received from service") completion([String](),[PhotoColor]()) return } print("Content uploaded with ID: (firstFileID)") // 3. completion([String](),[PhotoColor]()) 下面是上面代码每个步骤的解释:
你可以通过如下所示的添加.validate参数来使用手动验证响应结果: Alamofire.request("https://httpbin.org/get",parameters: ["foo": "bar"]) .validate(statusCode: 200..<300) .validate(contentType: ["application/json"]) .response { response in // response handling code } 编译并运行整个项目,选择一张图片然后静观其变。进度条渐进式的前进,你应该可以在控制台看到如下输出:
恭喜你,你已经成功的通过网络上传了一张图片了。
获取数据将图片上传到Imagga之后,下一步则是从Imagga获取到它分析之后的标签数据了。在ViewController文件的扩展方法upload(image:progress:completion:):下添加以下代码: func downloadTags(contentID: String,completion: @escaping ([String]) -> Void) { Alamofire.request( "http://api.imagga.com/v1/tagging",parameters: ["content": contentID],headers: ["Authorization": "Basic xxx"] ) .responseJSON { response in guard response.result.isSuccess else { print("Error while fetching tags: (response.result.error)") completion([String]()) return } guard let responseJSON = response.result.value as? [String: Any] else { print("Invalid tag information received from the service") completion([String]()) return } print(responseJSON) completion([String]()) } } 请再一次确认替换的Basic xxx是你自己账号的授权token。上述代码执行了一个对标签服务的GET请求,参数是通过上传成功后返回的一个ID。接着,我们回到upload(image:progress:completion:)方法,然后替换成功条件下的完成回调方法代码如下: self.downloadTags(contentID: firstFileID) { tags in completion(tags,[PhotoColor]()) } 上述代码将获得到的标签数据通过完成回调函数返回给上级调用者。 本教程里你不用在意返回结果里的confidence score是什么意思,只需要关心标签名称的数组即可。下一步,回到downloadTags(contentID:completion:)方法,然后替换里面的.responseJSON方法,代码如下: // 1. guard response.result.isSuccess else { print("Error while fetching tags: (response.result.error)") completion([String]()) return } // 2. guard let responseJSON = response.result.value as? [String: Any],let results = responseJSON["results"] as? [[String: Any]],let firstObject = results.first,let tagsAndConfidences = firstObject["tags"] as? [[String: Any]] else { print("Invalid tag information received from the service") completion([String]()) return } // 3. let tags = tagsAndConfidences.flatMap({ dict in return dict["tag"] as? String }) // 4. completion(tags) 我们来逐步分解上面的代码:
编译然后运行整个项目,上传一张图片,然后你可以在界面上看到如下效果:
纵享丝滑,Imagga不愧是一个智能的api。下一步,你将要获取的是图片的颜色。添加如下代码到ViewController中的downloadTags(contentID:completion:):方法下面: func downloadColors(contentID: String,completion: @escaping ([PhotoColor]) -> Void) { Alamofire.request( "http://api.imagga.com/v1/colors",// 1. headers: ["Authorization": "Basic xxx"] ) .responseJSON { response in // 2. guard response.result.isSuccess else { print("Error while fetching colors: (response.result.error)") completion([PhotoColor]()) return } // 3. guard let responseJSON = response.result.value as? [String: Any],let firstResult = results.first,let info = firstResult["info"] as? [String: Any],let imageColors = info["image_colors"] as? [[String: Any]] else { print("Invalid color information received from service") completion([PhotoColor]()) return } // 4. let photoColors = imageColors.flatMap({ (dict) -> PhotoColor? in guard let r = dict["r"] as? String,let g = dict["g"] as? String,let b = dict["b"] as? String,let closestPaletteColor = dict["closest_palette_color"] as? String else { return nil } return PhotoColor(red: Int(r),green: Int(g),blue: Int(b),colorName: closestPaletteColor) }) // 5. completion(photoColors) } } 下面按照编号来依次解析: 最后,回到upload(image:progress:completion:)方法,然后替换掉成功条件下的调用完成回调函数: self.downloadTags(contentID: firstFileID) { tags in self.downloadColors(contentID: firstFileID) { colors in completion(tags,colors) } } 上述代码嵌套了上传图片,获取标签以及获取颜色的操作。
这块主要使用了映射到PhotoColor结构体的RGB颜色来渲染UI。你已经成功的往Imagga上传了一张图片,以及调用了2个不同的api来获取数据。你已经做得很不错了,但是在如何使用 改进 PhotoTagger你应该注意到了我们前面的代码中有许多重复代码。如果Imagga官方宣布废除掉v1版本的api,并推出v2版本的api。我们的应用将不再可用直到你将每个方法里面的URL都修改过来。同样的道理,如果你的Basic认证token发生了变化,那么你需要在所有用到的地方做出修改。 import Foundation import Alamofire public enum ImaggaRouter: URLRequestConvertible { static let baseURLPath = "http://api.imagga.com/v1" static let authenticationToken = "Basic xxx" case content case tags(String) case colors(String) var method: HTTPMethod { switch self { case .content: return .post case .tags,.colors: return .get } } var path: String { switch self { case .content: return "/content" case .tags: return "/tagging" case .colors: return "/colors" } } public func asURLRequest() throws -> URLRequest { let parameters: [String: Any] = { switch self { case .tags(let contentID): return ["content": contentID] case .colors(let contentID): return ["content": contentID,"extract_object_colors": 0] default: return [:] } }() let url = try ImaggaRouter.baseURLPath.asURL() var request = URLRequest(url: url.appendingPathComponent(path)) request.httpMethod = method.rawValue request.setValue(ImaggaRouter.authenticationToken,forHTTPHeaderField: "Authorization") request.timeoutInterval = TimeInterval(10 * 1000) return try URLEncoding.default.encode(request,with: parameters) } } 替换Basic xxx为你自己账号的认证Token。这个路由类通过提供三个不同的分类: Alamofire.upload( multipartFormData: { multipartFormData in multipartFormData.append(imageData, 替换为: Alamofire.upload( multipartFormData: { multipartFormData in multipartFormData.append(imageData,with: ImaggaRouter.content, 然后替换掉downloadTags(contentID:completion:)方法里的代码: Alamofire.request(ImaggaRouter.tags(contentID)) 最后,替换掉downloadColors(contentID:completion:)方法里的代码: Alamofire.request(ImaggaRouter.colors(contentID)) 编译然后运行项目,结果应与之前的效果保持一致。这意味着你已经在不破坏你的app的前提下完成了重构,干得漂亮! 接下来做什么?所有的代码文件都上传到了github上,不要忘了替换你自己的Basic认证token。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |