在 GitHub 有許多開源的專案是值得我們學習的, 尤其是當接觸新事物的時候, SwiftUI-Combine 是一個使用 SwiftUI 及 Combine 的資料顯示樣板, 是新接觸的人可以有的不錯練習!
本篇文章將注重與 repo 內 Combine的部分.
// SearchUserViewModel.swift
import UIKit.UIImage
import Combine
final class SearchUserViewModel: ObservableObject {
    // 1
    @Published var name = "ra1028"
    @Published private(set) var users = [User]()
    @Published private(set) var userImages = [User: UIImage]()
    
    private var searchCancellable: Cancellable? {
        didSet { oldValue?.cancel() }
    }
    // 2
    func search() {...}
    func fetchImage(for user: User) {...}
}
private(set) 的方式限制 Subjcet.value 可用範圍 func search() {
     ...
        _ = URLSession.shared.dataTaskPublisher(for: request) // 1
            .map { $0.data } // 2
            .decode(type: SearchUserResponse.self, decoder: JSONDecoder())
            .map { $0.items }
            .replaceError(with: []) // 3
            .receive(on: RunLoop.main)
            .assign(to: \.users, on: self)
    }
Data, Decoder 要使用注入的方式使用如此一來, 就可以在 SwiftUI 內使用
import SwiftUI
struct SearchUserView: View {
    @ObservedObject var viewModel = SearchUserViewModel()
    var body: some View {
        NavigationView {
            VStack {
                SearchUserBar(text: $viewModel.name) { // 1
                    self.viewModel.search()
                }
                List(viewModel.users) { user in // 2
                    SearchUserRow(viewModel: self.viewModel, user: user) 
                        .onAppear { self.viewModel.fetchImage(for: user) }
                }
                }
                .navigationBarTitle(Text("Users"))
        }
    }
}
在 UIKit 與 非 Combine 的情形下, 要實作出同樣的功能時需要使用 UITableView.reloadData() 的方式注入 URLSession.dataTask. 使用 Combine 之後, 仿佛 FlowChart(控制流程圖)在程式碼現型一般, 是很好理解的 code!