🪶
Swift Result<S, F>
how to result?
Result
A value that represents either a success or a failure, including an associated value in each case.
結果
成功または失敗を表す値で、それぞれの場合に関連する値を含む。
Result.success(_:)
A success, storing a Success value.
Result.success(_:)
成功、Success値を格納する。
こちらの動画が参考になった!
Swift5で導入された結果タイプ。古いネットワークを呼び出し、クリーンアップ。
これは、2つのケースを含む列挙型。失敗ケースの中に成功ケースがあり、ネットワークの呼び出しでおこることは、2つだけ。ネットワークが成功を呼び出して必要なデータを取得するか、何が怒って失敗し、それに応じて行動するかのどちらか。
ここから長い解説と完成したコードを書き換えて、解説が続く💦
Github Searchで使ってみた!
全体のコード
import SwiftUI
struct APIResponse: Codable {
var items: [User]
}
struct User: Codable, Identifiable {
let id = UUID()
var login: String
var url: String
var avatar_url: String
var html_url: String
}
class UserViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
@Published var errorMessage: String?
func getUsers(searchText: String) {
let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
guard !trimmedSearchText.isEmpty else {
return
}
isLoading = true
errorMessage = nil
guard let apiURL = URL(string: "https://api.github.com/search/users?q=\(trimmedSearchText)") else {
isLoading = false
errorMessage = "Invalid URL"
return
}
var request = URLRequest(url: apiURL)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
DispatchQueue.main.async {
self?.isLoading = false
let result: Result<[User], Error> = {
if let error = error {
return .failure(error)
}
guard let data = data else {
return .failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "No data received"]))
}
do {
let apiResponse = try JSONDecoder().decode(APIResponse.self, from: data)
return .success(apiResponse.items)
} catch {
return .failure(error)
}
}()
switch result {
case .success(let fetchedUsers):
self?.users = fetchedUsers
case .failure(let error):
self?.errorMessage = error.localizedDescription
}
}
}.resume()
}
}
struct ContentView: View {
@StateObject private var viewModel = UserViewModel()
@State private var searchText = ""
var body: some View {
NavigationStack {
Group {
if viewModel.isLoading {
LoadingView()
} else if let errorMessage = viewModel.errorMessage {
ErrorView(message: errorMessage)
} else {
UserListView(users: viewModel.users)
}
}
.navigationTitle("GitHub Users")
}
.searchable(text: $searchText)
.onSubmit(of: .search) {
viewModel.getUsers(searchText: searchText)
}
}
}
struct LoadingView: View {
var body: some View {
VStack {
ProgressView().padding()
Text("Fetching Users...")
}
}
}
struct ErrorView: View {
let message: String
var body: some View {
Text("Error: \(message)")
.foregroundColor(.red)
}
}
struct UserListView: View {
let users: [User]
var body: some View {
List(users) { user in
Link(destination: URL(string: user.html_url)!) {
HStack {
AsyncImage(url: URL(string: user.avatar_url)) { phase in
switch phase {
case .success(let image):
image.resizable().frame(width: 50, height: 50)
default:
Image(systemName: "nosign")
}
}
VStack(alignment: .leading) {
Text(user.login)
Text(user.url)
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}
}
}
}
#Preview {
ContentView()
}
思いつきで作りましたが、成功したら、User型を返す、失敗したら、Error型を返すResult<[User], Error>を定義しました。
let result: Result<[User], Error> = {
if let error = error {
return .failure(error)
}
guard let data = data else {
return .failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "No data received"]))
}
do {
let apiResponse = try JSONDecoder().decode(APIResponse.self, from: data)
return .success(apiResponse.items)
} catch {
return .failure(error)
}
}()
switch result {
case .success(let fetchedUsers):
self?.users = fetchedUsers
case .failure(let error):
self?.errorMessage = error.localizedDescription
}
}
}.resume()
成功するとこんな感じで、Githubに登録されているユーザーの情報が表示されます。
Lastly
今回は、Swift5から追加されていた、Result Type を使ってみました。やってることは単純で、成功と失敗の値を返すときに使います。
Discussion