API 주소
Http 요청을 통해 URL 이미지 주소를 가져온 후, 페이지에 이미지를 업데이트 하는 방법을 알아보도록 하겠습니다.
이미지는 강아지 사진 Open API를 통해 가지고 오겠습니다.
랜덤으로 강아지 사진을 반환하는 API 주소는 다음과 같습니다.
https://dog.ceo/api/breeds/image/random
ObservedObject 생성
import SwiftUI
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
// 이미지가 들어갈 곳입니다.
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
이미지 URL 을 저장하기 위해 viewModel 라는 ObservedObject를 만듭니다.
(ObservedObject를 사용해서 UI를 업데이트 하는 방법을 알고 싶다면, https://hwanny7.tistory.com/50)
ObservableObject 생성 및 Http 요청
struct DogImage: Hashable, Codable {
let message: String
let status: String
}
class ViewModel: ObservableObject {
@Published var dogImage: UIImage?
func fetchData() {
guard let url = URL(string: "https://dog.ceo/api/breeds/image/random") else { return }
let task = URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let data = data else { return }
do {
let dogImage = try JSONDecoder().decode(DogImage.self, from: data)
guard let imageUrl = URL(string: dogImage.message) else { return }
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: imageUrl) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.dogImage = image
print("바꿨는데?")
}
}
}
}
} catch {
print("Error: \(error.localizedDescription)")
}
}
task.resume()
}
}
ViewModel 이라는 클래스를 만든 후 Published 속성의 dogImage를 만듭니다.
dogImage 에는 강아지 이미지를 저장하게 되는데, fetch data 함수가 완료되기 전까지는 nil (비어있음) 값을 가집니다.
이후 fetch data를 수행할 URLSession 을 만들어줍니다.
만약 데이터를 불러오는데 성공했다면, DogImage struct에 미리 정해놓은 속성에 따라 Json 데이터를 디코딩 해줍니다.
이후 DispatchQueue.global().async 를 통해 비동기적으로 이미지를 불러옵니다.
(DispatchQueue.global().async를 사용하면 메인 스레드에서 실행하지 않고 백그라운드에서 처리하기 때문에 앱의 성능과 응답성을 향상 시킬 수 있습니다.)
Image를 불러오는데 성공했다면 DispatchQueue.main.async 를 통해 메인스레드에 UI 업데이트를 요청합니다.
dogImage가 nil 값에서 Image로 대체되면서 이미지 업데이트가 완료됩니다.
SwiftUI 생성
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
if self.viewModel.dogImage != nil {
Image(uiImage: self.viewModel.dogImage!)
} else {
Text("사진을 불러오는 중입니다.")
.multilineTextAlignment(.center)
}
}
.onAppear {
viewModel.fetchData()
}
}
}
UI 를 보여주는 View에서는 dogImage가 nil 이 아닐 경우 이미지를 보여주고,
이미지를 아직 불러오지 않은 상황이라면 Text를 통해 이미지 로딩 중이라는 것을 표시합니다.
OnAppear를 통해 View가 나타날 때 FetchData를 호출하여 데이터를 가져옵니다.
전체 소스코드 및 결과
import SwiftUI
struct DogImage: Hashable, Codable {
let message: String
let status: String
}
class ViewModel: ObservableObject {
@Published var dogImage: UIImage?
func fetchData() {
guard let url = URL(string: "https://dog.ceo/api/breeds/image/random") else { return }
let task = URLSession.shared.dataTask(with: url) { [weak self] (data, response, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let data = data else { return }
do {
let dogImage = try JSONDecoder().decode(DogImage.self, from: data)
guard let imageUrl = URL(string: dogImage.message) else { return }
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: imageUrl) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.dogImage = image
}
}
}
}
} catch {
print("Error: \(error.localizedDescription)")
}
}
task.resume()
}
}
struct ContentView: View {
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
if self.viewModel.dogImage != nil {
Image(uiImage: self.viewModel.dogImage!)
} else {
Text("사진을 불러오는 중입니다.")
.multilineTextAlignment(.center)
}
}
.onAppear {
viewModel.fetchData()
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
'iOS \ Swift' 카테고리의 다른 글
[Swift] if let vs guard let 에 대해 알아보자 (0) | 2023.06.19 |
---|---|
[Swift] 접근제어 (0) | 2023.06.19 |
[Swift] UI를 업데이트 하는 방법 (State, ObservedObject) (0) | 2023.06.18 |
[Swift] 데이터 타입 (0) | 2023.06.17 |
[Swift] 변수, 상수 생성 방법 (0) | 2023.06.17 |