Avatar
Q6 import UIKit import Combine final class UserViewController: UIViewController { private let state: UserViewState private let iconImageView: UIImageView = .init() private let nameLabel: UILabel = .init() private var cancellables: Set<AnyCancellable> = [] init(id: User.ID) { self.state = UserViewState(id: id) super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() // レイアウト iconImageView.translatesAutoresizingMaskIntoConstraints = false iconImageView.layer.cornerRadius = 40 iconImageView.layer.borderWidth = 4 iconImageView.layer.borderColor = UIColor.systemGray3.cgColor iconImageView.clipsToBounds = true view.addSubview(iconImageView) nameLabel.translatesAutoresizingMaskIntoConstraints = false view.addSubview(nameLabel) NSLayoutConstraint.activate([ iconImageView.widthAnchor.constraint(equalToConstant: 80), iconImageView.heightAnchor.constraint(equalToConstant: 80), iconImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor), iconImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16), nameLabel.centerXAnchor.constraint(equalTo: iconImageView.centerXAnchor), nameLabel.topAnchor.constraint(equalTo: iconImageView.bottomAnchor, constant: 16), ]) // View への反映 state .$user .receive(on: DispatchQueue.main) .sink { [weak self] user in self?.nameLabel.text = user?.name } .store(in: &cancellables) state .$iconImage .receive(on: DispatchQueue.main) .sink { [weak self] iconImage in self?.iconImageView.image = iconImage } .store(in: &cancellables) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) Task { await state.loadUser() } } } (edited)
1:06 PM
import Combine import Foundation import class UIKit.UIImage final class UserViewState { let id: User.ID @Published private(set) var user: User? @Published private(set) var iconImage: UIImage? init(id: User.ID) { self.id = id } func loadUser() async { do { // User の JSON の取得 let url: URL = .init(string: "https://koherent.org/fake-service/api/user?id=\(id)")! let data = try await downloadData(from: url) // JSON のデコード let user: User = try JSONDecoder().decode(User.self, from: data) // state への反映 self.user = user // アイコン画像の取得 let iconData = try await downloadData(from: user.iconURL) // Data を UIImage に変換 guard let iconImage: UIImage = .init(data: iconData) else { // エラーハンドリング print("The icon image at \(user.iconURL) has an illegal format.") return } // state への反映 self.iconImage = iconImage } catch { // エラーハンドリング print(error) } } }