👉🏻 아래는 RandomUser.me의 API를 사용해서 유저를 앱에서 랜덤하게 호출하는 내용입니다.
Below is how to randomly call a user in your app using RandomUser.me’s API.
👉🏻 서버를 따로 구성하지않고 api를 사용하여 앱을 만들어 볼 수 있습니다.
You can create an app using the API without configuring a separate server.
👉🏻RandomUser.me Api
✔️ 브라우저에서 https://randomuser.me/api 이렇게 실행시 사용 할 수 있습니다.
You can use it by running https://randomuser.me/api in your browser.
✔️ 데이터를 새롭게 요청시 랜덤으로 아래의 api정보가 바뀝니다.
When you request new data, the API information below changes randomly.
⭐️ 코드 설명은 주석을 참조하시면됩니다. 추가 설명이 필요하면 따로 포스팅하겠습니다.
Please refer to the comments for code explanations. If further explanation is needed, I will post it separately.
{
"results": [
{
"gender": "female",
"name": {
"title": "Mrs",
"first": "Lisa",
"last": "Fowler"
},
"location": {
"street": {
"number": 2580,
"name": "Oaks Cross"
},
"city": "Armagh",
"state": "Highlands and Islands",
"country": "United Kingdom",
"postcode": "HH16 2NA",
"coordinates": {
"latitude": "-10.3689",
"longitude": "101.2611"
},
"timezone": {
"offset": "+5:00",
"description": "Ekaterinburg, Islamabad, Karachi, Tashkent"
}
},
"email": "lisa.fowler@example.com",
"login": {
"uuid": "a46a4808-b2fd-452b-b923-212f144a5349",
"username": "browndog164",
"password": "bones",
"salt": "k8ZwiUoK",
"md5": "bc71f9e4f2a4960d2e309be7b51c1f31",
"sha1": "ff2dde7c2e1d208e8ad05e06f5324d2d849fed0a",
"sha256": "ee02e23c14550ae433bf8052c1d7d23fb7cd8b4fd42cf10a0900ef3e6c9c4605"
},
"dob": {
"date": "1954-07-09T14:01:43.714Z",
"age": 71
},
"registered": {
"date": "2022-02-02T03:34:03.950Z",
"age": 3
},
"phone": "017687 96364",
"cell": "07149 397609",
"id": {
"name": "NINO",
"value": "LP 72 77 54 S"
},
"picture": {
"large": "https://randomuser.me/api/portraits/women/8.jpg",
"medium": "https://randomuser.me/api/portraits/med/women/8.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/women/8.jpg"
},
"nat": "GB"
}
],
"info": {
"seed": "42e207ecbc7085df",
"results": 1,
"page": 1,
"version": "1.4"
}
}
👉🏻 코드 / Code
✔️ ContentView.swift
import SwiftUI
struct ContentView: View {
// SwiftUI는 이 ViewModel 안의 @Published 프로퍼티(user, errorMessage)가 바뀌면
// 자동으로 뷰를 다시 그리도록 함
// SwiftUI automatically redraws the view when the @Published properties (user, errorMessage) in this ViewModel change.
@StateObject private var viewModel = RandomUserViewModel()
var body: some View {
VStack(spacing: 20) {
// 프로필 / Profile name
if let user = viewModel.user {
// 프로필 네임 / profile name
Text("\(user.name.first) \(user.name.last)")
.font(.title)
// 프로필 이미지 URL이미지 비동기 로딩
// Profile image URL image asynchronous loading
AsyncImage(url: URL(string: user.picture.large)) { image in
image
.resizable()
.scaledToFit() // 비율유지하며 맞춤 / Custom while maintaining proportions
.frame(width: 180, height: 180)
.clipShape(Circle())
} placeholder: {
ProgressView() // 로딩중에는 ProgressView표시 / Display ProgressView while loading
}
}
// 에러 메세지 / error message
if let error = viewModel.errorMessage {
Text("Error: \(error)")
.foregroundColor(.red)
}
// 버튼 / button
Button(action: {
viewModel.fetchRandomUser()
}) {
Text("랜덤 유저 불러오기/Load random users")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(12)
}
}
.padding()
}
}
#Preview {
ContentView()
}
✔️ RandomUserModel.swift
import Foundation
// Codable을 쓰면 JSON ↔ Struct 변환이 자동
// If you use Codable, JSON ↔ Struct conversion is automatic.
struct RandomUserResponse: Codable {
let results: [RandomUser] // "results": [
}
struct RandomUser: Codable {
let name: Name // "results": [ { "name": {
let picture: Picture
struct Name: Codable {
let first: String // "results": [ { "name": { "first": "Lisa","last": "Fowler"
let last: String
}
struct Picture: Codable {
let large: String
}
}
✔️ RandomUserSwiftApp
import SwiftUI
@main
struct RandomUserSwiftApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
✔️ RandomUserViewModel
import Foundation
import Combine // add
class RandomUserViewModel: ObservableObject {
@Published var user: RandomUser?
@Published var errorMessage: String?
private var cancellables = Set<AnyCancellable>()
func fetchRandomUser() {
guard let url = URL(string: "https://randomuser.me/api/") else { return }
/*
Combine의 Publisher 흐름
URLSession.shared.dataTaskPublisher(for: url)
기본적으로 백그라운드 스레드에서 실행됨
네트워크 응답, 데이터 디코딩 등은 비동기 작업
이 상태에서 UI를 바로 업데이트하면 문제가 생길 수 있음
Combine's Publisher Flow
URLSession.shared.dataTaskPublisher(for: url)
By default, runs on a background thread.
Network responses, data decoding, etc. are asynchronous tasks.
Updating the UI immediately in this state can cause problems.
*/
URLSession.shared.dataTaskPublisher(for: url)
//Publisher가 (data: Data, response: URLResponse) 형태인데 그중 data만 사용하겠다는 뜻
//Publisher is in the form of (data: Data, response: URLResponse), which means that only data will be used.
.map { $0.data }
// JSON → Swift Struct 변환.
// Convert JSON → Swift Struct.
.decode(type: RandomUserResponse.self, decoder: JSONDecoder())
// UI 업데이트는 반드시 메인 스레드에서 해야 하기 때문에 이 시점(sink) 이후 모든 downstream 작업을 메인 스레드에서 실행.(백그랄운드 스레드에서 실행시 크래시 발생할 수 있음)
// Since UI updates must be done on the main thread, all downstream work after this point (sink) is executed on the main thread. (Crash may occur if executed on a background thread.)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in // 구독시작,에러처리 / Subscription start, error handling
switch completion {
case .finished:
break
case .failure(let error):
self.errorMessage = error.localizedDescription
}
}, receiveValue: { response in
// <-- 여기서 실제 @Published user 프로퍼티가 바뀜(UI갱신 시키는 위치)
// <-- Here, the actual @Published user property changes (where the UI is updated)
self.user = response.results.first
})
// 구독 유지하기 위해 저장 , inout키워드 in은 파라메터이름(레이블),.store함수가 inout키워드를 사용함
// To maintain subscription, store, inout keyword in is parameter name (label), .store function uses inout keyword
.store(in: &cancellables)
}
}
👉🏻 스크린샷/ScreenShot