Swift - URLSession
Aus Wikizone
Links[Bearbeiten]
Swift (Programmiersprache)
Einführung[Bearbeiten]
Mit URLSession kannst du Anfragen an einen Webserver generieren und Antworten verarbeiten.
Schritte[Bearbeiten]
- Create a URL
- Create a URLSession (kind of Browser Object)
- Give URLSession a Task (give the browser a url to fetch)
- Start the task
Beispiel[Bearbeiten]
struct WeatherManager{
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?units=metric&appid=63981dbcfa548021dd77394d24f34674"
func fetchWeather(cityName: String){
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(urlString: urlString)
}
func performRequest(urlString: String){
// 1. create URL
if let url = URL(string: urlString) {
// 2. create URLSession
let session = URLSession(configuration: .default)
// 3. give session a task
let task = session.dataTask(with: url, completionHandler: handle(data:response:error:))
// 4. start the task
task.resume()
}
}
func handle(data: Data?, response: URLResponse?, error: Error?){
if error != nil {
print(error!)
return
}
if let safeData = data {
let dataString = String(data: safeData, encoding: .utf8)
print(dataString!)
}
}
}
Oder besser mit Closure statt separater Funktion:
struct WeatherManager{
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?units=metric&appid=63981dbcfa548021dd77394d24f34674"
func fetchWeather(cityName: String){
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(urlString: urlString)
}
func performRequest(urlString: String){
// 1. create URL
if let url = URL(string: urlString) {
// 2. create URLSession
let session = URLSession(configuration: .default)
// 3. give session a task
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
if let safeData = data {
let dataString = String(data: safeData, encoding: .utf8)
print(dataString!)
}
}
// 4. start the task
task.resume()
}
}
func handle(data: Data?, response: URLResponse?, error: Error?){
if error != nil {
print(error!)
return
}
if let safeData = data {
let dataString = String(data: safeData, encoding: .utf8)
print(dataString!)
}
}
}
Best Practice[Bearbeiten]
Da Netzwerkzugriffe eine gewisse Zeit benötigen, nutzt man in der Praxis oft das Delegate Pattern. Wenn der Request abgeschlossen ist sendet man die Daten via Delegate an den View, der dann die Ausgabe im UI macht.
Vorsicht wenn das Senden aus einem Completion Handler kommt (wie im Beispiel unten), dann musst du im ViewController dafür sorgen, dass das UI im Main Thread upgedatet wird. Sonst gibt es einen Fehler.
Kommentiertes Beispiel:
// ViewController.swift
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var bitcoinLabel: UILabel!
@IBOutlet weak var currencyLabel: UILabel!
@IBOutlet weak var currencyPicker: UIPickerView!
var coinManager = CoinManager()
override func viewDidLoad() {
super.viewDidLoad()
// set the ViewController as the datasource for the picker
currencyPicker.dataSource = self
// set the ViewController as the delegate for the picker
currencyPicker.delegate = self
// set the ViewController as the delegate for the CoinManager
coinManager.delegate = self
}
}
//MARK: - UIPickerViewDataSource protocol
extension ViewController:UIPickerViewDataSource{
// return number of columns of the picker
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
// return number of rows
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return coinManager.currencyArray.count
}
}
//MARK: - UIPickerViewDelegate
extension ViewController: UIPickerViewDelegate{
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
// executed for every row
return coinManager.currencyArray[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let selectedCurrency = coinManager.currencyArray[row]
coinManager.getCoinPrice(for: selectedCurrency)
self.currencyLabel.text = selectedCurrency
}
}
//MARK: - CoinManagerDelegate
extension ViewController: CoinManagerDelegate{
func didUpdatePrice(_ coinManager: CoinManager, lastPrice: Double) {
// use DispatchQueue as this is not called via main thread
DispatchQueue.main.async {
self.bitcoinLabel.text = String(format: "%.2f", lastPrice)
}
}
func didFailWithError(error: Error) {
print(error)
}
}
// CoinManager.swift
import Foundation
protocol CoinManagerDelegate{
func didUpdatePrice( _ coinManager: CoinManager, lastPrice: Double)
func didFailWithError( error: Error)
}
struct CoinManager {
var delegate: CoinManagerDelegate?
let baseURL = "https://rest.coinapi.io/v1/exchangerate/BTC"
let apiKey = "E7E77171-F196-4E85-BC01-5D260ECB96E4"
let currencyArray = ["AUD", "BRL","CAD","CNY","EUR","GBP","HKD","IDR","ILS","INR","JPY","MXN","NOK","NZD","PLN","RON","RUB","SEK","SGD","USD","ZAR"]
func getCoinPrice (for currency: String){
fetchCoinData(currency: currency)
}
// we could override fetch to create different url strings depending on the input
func fetchCoinData(currency: String){
let urlString = String( "\(baseURL)/\(currency)?apikey=\(apiKey)" )
performRequest(with: urlString)
}
func performRequest(with urlString: String){
// 1. create URL
let allowedCharacters = CharacterSet.urlQueryAllowed
let encodedString = urlString.addingPercentEncoding(withAllowedCharacters: allowedCharacters) ?? ""
if let url = URL(string: encodedString) {
// 2. create URLSession
let session = URLSession(configuration: .default)
// 3. give session a task
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
delegate?.didFailWithError(error: error!)
return
}
if let safeData = data {
if let lastPrice = parseJSON( safeData ){
delegate?.didUpdatePrice(self, lastPrice: lastPrice)
}
}
}
// 4. start the task
task.resume()
}
}
func parseJSON( _ data: Data) -> Double?{
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(CoinData.self, from: data)
return decodedData.rate
}catch{
return nil
}
}
}
// CoinData.swift
// This is used by the JSONEncoder
import Foundation
struct CoinData: Codable {
let time: String
let asset_id_base: String
let asset_id_quote: String
let rate: Double
}