以前開發的時候,通常用第三方元件像是Alamofire或是AFNetworking用慣了,一兩行就直接可以Call api做get/post的動作,但其實他們的底層還是用NSURLSession的東西。
最近看了原始的文件,順便筆記一下。
我會先介紹不使用delegation methods下如何簡單使用URLSessionTask下載,然後再修改一下加入delegation methods並使用progress bar,最後使用URLSessionDownloadTask 來完成background 下載。
最近看了原始的文件,順便筆記一下。
- URLSessionDataTask - 下載資料到memory.
- URLSessionDownloadTask - 下載資料到檔案系統,如果要在背景下載則需使用URLSessionDownloadTask
- URLSessionConfiguaration - 在上傳或下載中的相關設定放在這,在使用URLSession前就應該先把URLSessionConfiguration先設定好。
詳細的資料可以參考Apple document - URLSession.
直接寫個簡單的Code會比較有概念,Let's go!
我會先介紹不使用delegation methods下如何簡單使用URLSessionTask下載,然後再修改一下加入delegation methods並使用progress bar,最後使用URLSessionDownloadTask 來完成background 下載。
首先我們先新增一個Single view專案。在專案首頁(也才一個Viewcontroller) 加入一個Imageview 和 一個 button
接著連結IBOutlet到這兩個物件上,並且加入button的action。
我們要做的效果是當按下Load Image按鈕後從網址載入圖片。
可以看到當使用者按下按鈕後,先disable按鈕,然後去觸發startLoad這個方法開始下載圖片。
很簡單吧!
進階部分(optional)
我們可以把URLSession下載的邏輯這部分抽出來,方便之後可以重複使用。
新增一個HTTPNetworking.swift 檔案(File -> New File -> Swift File)
打開HTTPNetworking.swift 將下面的程式碼複製上去
我把completionhandle直接typealias一個新的type(本質上不變),然後公開了一個request方法。
當程式呼叫request這個方法時,會去產生URLSessionTask並且開始下載,如果有錯誤則是用AlertView顯示出來。
好了,回到原來的startLoad,我們要稍微修改一下。
這樣如果之後有需要用到URLSession的時候,我們直接呼叫Networking就可以了,不用重複寫一樣的代碼,也方便維護。
下一章我們加入progress bar的應用。
專案的Source code在此
接著連結IBOutlet到這兩個物件上,並且加入button的action。
我們要做的效果是當按下Load Image按鈕後從網址載入圖片。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@IBAction func action_loadImage(_ sender: UIButton) { | |
//disable button in case user click twice | |
sender.isEnabled = false | |
//start loading image | |
startLoad() | |
} | |
func startLoad() { | |
let url = URL(string: "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png")! | |
let task = URLSession.shared.dataTask(with: url) { data, response, error in | |
//if has error then return | |
if let error = error { | |
self.handleClientError(error) | |
} | |
//if status code not in 200~299 , then something happen on server side | |
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { | |
self.handleServerError(response) | |
return | |
} | |
//load image data | |
if let mimeType = httpResponse.mimeType, mimeType == "image/png", let data = data{ | |
DispatchQueue.main.async { | |
self.imageview1.image = UIImage(data: data) | |
self.button1.isEnabled = true | |
} | |
} | |
} | |
task.resume() | |
} | |
//handle client side error here | |
func handleClientError(_ error: Error) { | |
print("client side error") | |
} | |
//handle server side error here | |
func handleServerError(_ response: URLResponse?) { | |
print("server side error") | |
} |
很簡單吧!
進階部分(optional)
我們可以把URLSession下載的邏輯這部分抽出來,方便之後可以重複使用。
新增一個HTTPNetworking.swift 檔案(File -> New File -> Swift File)
打開HTTPNetworking.swift 將下面的程式碼複製上去
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HTTPNetworking.swift | |
// urlSessionDemo | |
// | |
// Created by Nick on 12/2/18. | |
// Copyright © 2018 NickOwn. All rights reserved. | |
// | |
import Foundation | |
import UIKit | |
protocol Networking { | |
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void | |
func request(from currentVC:UIViewController, url:URL, completion:@escaping(_ data:Data?) ->() ) | |
} | |
struct HTTPNetworking: Networking { | |
func request(from currentVC:UIViewController,url: URL, completion:@escaping (_ data:Data?) -> ()) { | |
let task = createDataTask(from: currentVC, url: url, completion: { data,response, error in | |
//if error, call handleError method | |
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else { | |
self.handleError(currentVC, response) | |
return | |
} | |
completion(data) | |
}) | |
task.resume() | |
} | |
//handle server side error with alert view controller | |
func handleError(_ vc:UIViewController,_ response: URLResponse?) { | |
let alertVC = UIAlertController(title: "Error", message: "Something wrong", preferredStyle: .alert) | |
let action_OK = UIAlertAction(title: "Got it", style: .cancel, handler: nil) | |
alertVC.addAction(action_OK) | |
vc.present(alertVC, animated: true, completion: nil) | |
} | |
private func createDataTask(from currentVC:UIViewController, url:URL, completion:@escaping CompletionHandler) -> URLSessionTask{ | |
return URLSession.shared.dataTask(with: url){ data, response, error in | |
completion(data, response, error) | |
} | |
} | |
} |
當程式呼叫request這個方法時,會去產生URLSessionTask並且開始下載,如果有錯誤則是用AlertView顯示出來。
好了,回到原來的startLoad,我們要稍微修改一下。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// func startLoad() { | |
// let url = URL(string: "https://lemulotdotorg.files.wordpress.com/2016/10/dsc00737.jpg")! | |
// //init data | |
// receivedData = Data() | |
// let task = session.dataTask(with: url) | |
// task.resume() | |
// | |
// } | |
func startLoad(){ | |
let url = URL(string: "https://lemulotdotorg.files.wordpress.com/2016/10/dsc00737.jpg")! | |
let networking = HTTPNetworking() | |
networking.request(from: self, url: url, completion: { data in | |
if let data = data { | |
DispatchQueue.main.async { | |
self.button1.isEnabled = true | |
self.imageview1.image = UIImage(data: data) | |
} | |
} | |
}) | |
} |
這樣如果之後有需要用到URLSession的時候,我們直接呼叫Networking就可以了,不用重複寫一樣的代碼,也方便維護。
下一章我們加入progress bar的應用。
專案的Source code在此
留言
張貼留言