iOS 呼叫 Web API 的五種方式

在 iOS 中呼叫 Web API 的方式有五種不同類型,使用同步呼叫方式有一種,其餘四種為非同步呼叫,專案中要使用哪一種方式都可以,隨個人的風格偏好自由挑選。

程式碼中使用到的 url 變數,定義如下,這是我給的一個測試網址,其傳回內容為「hello everyone」,當然可以換成任何實際的網址。另外,範例程式碼並未加上任何的錯誤處理,一切都當作不會有錯誤或是 nil 發生。

let url = URL(string: "https://raw.githubusercontent.com/kirkchu/testdata/main/hello")!

Storyboard 專案

將一個 Label 元件拖放到預設的 ViewController 上,並設定 IBOutlet 名稱為 label。 然後拖放五個 Button 元件,IBAction 函數名稱無所謂,按下後分別用不同的方式呼叫 Web API。

第一種:同步呼叫

@IBAction func buttonSync(_ sender: Any) {
    let html = try! String(contentsOf: url)
    label.text = html
}

若呼叫後傳回的內容為 binary 格式(例如 jpeg、mp3…),將 String 型態換成 Data 型態即可。

第二種:使用 GCD 的非同步呼叫

@IBAction func buttonAsync_GCD(_ sender: Any) {
    DispatchQueue.global().async {
        let html = try! String(contentsOf: url)
        DispatchQueue.main.async {
            self.label.text = html
        }
    }
}

第三種:使用 URLSession 的 Closure 函數

@IBAction func buttonURLSession_Closure(_ sender: Any) {
    URLSession.shared.dataTask(with: url) { data, response, error in
        let html = String(data: data!, encoding: .utf8)
        DispatchQueue.main.async {
            self.label.text = html
        }
    }.resume()
}

第四種:使用 URLSession 的 async 函數

@IBAction func buttonURLSession_Async(_ sender: Any) {
    Task {
        let (data, _) = try await URLSession.shared.data(from: url)
        let html = String(data: data, encoding: .utf8)
        label.text = html
    }
}

第五種:使用 Combine 框架

先在 ViewController.swift 中匯入 Combine 框架以及加上 cancellable 變數 。

import UIKit
import Combine

class ViewController: UIViewController {
    var cancellable: AnyCancellable? = nil

呼叫方式如下。

@IBAction func buttonCombine(_ sender: Any) {
    cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .sink { value in
            switch value {
            case .finished:
                break
            case .failure(let error):
                print(error)
            }
        } receiveValue: { (data, response) in
            let html = String(data: data, encoding: .utf8)
            DispatchQueue.main.async {
                self.label.text = html
            }
        }
}

SwiftUI 專案

調整預設的 ContentView 如下,之後的幾種呼叫方式都是換掉 Button 元件中的內容。

struct ContentView: View {
    @State var text = ""

    var body: some View {
        VStack {
            Button("") {
                // code here
            }

            Text(text)
                .padding()
        }
    }
}

第一種:同步呼叫

Button("同步") {
    text = try! String(contentsOf: url)
}

若傳回的內容為 binary 格式(例如 jpeg、mp3…),將 String 型態換成 Data 型態即可。

第二種:使用 GCD 的非同步呼叫

Button("非同步+GCD") {
    DispatchQueue.global().async {
        text = try! String(contentsOf: url)
    }
}

第三種:使用 URLSession 的 Closure 函數

Button("URLSession + Closure") {
    URLSession.shared.dataTask(with: url) { data, response, error in
        text = String(data: data!, encoding: .utf8)!
    }.resume()
}

第四種:使用 URLSession 的 async 函數

Button("URLSession + Async") {
    Task {
        let (data, _) = try await URLSession.shared.data(from: url)
        text = String(data: data, encoding: .utf8)!
    }
}

第五種:使用 Combine 框架

先在 ContentView 中匯入 Combine 框架,並且加上 cancellable 變數。

import Combine

struct ContentView: View {
    @State var cancellable: AnyCancellable? = nil

呼叫方式如下。

Button("Combine") {
    cancellable = URLSession.shared.dataTaskPublisher(for: url)
        .sink { value in
            switch value {
            case .finished:
                break
            case .failure(let error):
                print(error)
            }
        } receiveValue: { (data, response) in
            text = String(data: data, encoding: .utf8)!
        }
}

發表迴響