URL로 되어 있는 외부 API를 사용해서 메모 앱에 추가하려고 한다.
The Cat API에서 제공하는 API를 활용하였고, 자세한 가이드는 공식 홈페이지에서 참조하였다.
시작에 앞서, 해당 API를 호출하면 다음과 같은 결과를 받을 수 있다.
[
{
"id": "e4f",
"url": "https://cdn2.thecatapi.com/images/e4f.jpg",
"width": 500,
"height": 375
}
]
해당 결과값은 JSON 형태로 제공되므로, 데이터를 받아올 때 변환해줘야 한다.
무료로 제공하는 갯수는 10개이며, 그 이상의 데이터 수를 얻기 위해서는 가입해서 API KEY를 받아 설정하면 된다.
그리고 고려해야 할 점은 ViewController의 Life Cycle을 고려하여 호출되는 시점을 정해야 한다.
간단한 예제코드
// 1. URL 생성
let apiUrl = URL(string: "https://api.example.com/data")
// 2. URLSession 인스턴스 생성
let session = URLSession.shared
// 3. Data Task 생성
let dataTask = session.dataTask(with: apiUrl!) { (data, response, error) in
if let error = error {
print("Error: \(error)")
return
}
// 4. 데이터 처리
if let data = data {
// 데이터 파싱 및 활용
print("Received data: \(data)")
}
}
// 5. Data Task 실행
dataTask.resume()
1. URL 생성
먼저 가져올 데이터를 제공하는 API의 URL을 생성한다. 이 때 필요한 파라미터나 경로 등을 포함할 수 있다.
let url = URL(string: "https://api.thecatapi.com/v1/images/search")!
// 만약 api key 나 품종 등 추가 설정이 존재할 경우 커스터마이징
guard let url = URL(String: "https://api.thecatapi.com/v1/images/search") else { return }
2. URLSession 인스턴스 생성
URLSession 클래스의 인스턴스를 생성하여 네트워크 작업을 수행할 수 있다.
let session = URLSession(configuration: .default)
Data Task를 생성하기 전에 해당 url의 데이터를 받을 객체를 생성해야 한다.
해당 url은 id, url, width, height 로 구성되어 있으며 나는 url만 필요해서 아래와 같이 간단하게 모델을 생성했다.
struct RandomImage: Codable {
let url: String
}
3. Data Task 생성
URLSession 인스턴스를 사용하여 데이터를 가져오기 위한 Data Task를 생성한다. 생성된 Task는 백그라운드에서 실행되며 네트워크 요청을 처리한다.
session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return
}
// Data Task 실행 코드
}
4. Data Task 실행
생성한 Data Task를 실행하여 데이터를 가져온다. 이 때 다양한 옵션을 설정하여 요청을 커스터마이즈 할 수 있다.
session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
return
}
...
}.resume()
5. 데이터 처리
Data Task가 완료되면 서버로부터 받은 데이터 또는 에러 정보를 처리한다. 성공적으로 데이터를 가져온 경우 원하는 형식으로 파싱하고 활용할 수 있다.
let decoder = JSONDecoder() // (1)
guard let randomCatImage = try? decoder.decode([RandomImage].self, from: data) else { return } // (2)
if let imageUrl = URL(string: randomCatImage.first?.url ?? "") { // (3)
URLSession.shared.dataTask(with: imageUrl) { imageData, _, _ in // (4)
if let imageData = imageData, let image = UIImage(data: imageData) { // (5)
DispatchQueue.main.async { // (6)
self.randomImageView.image = image
}
}
}.resume()
}
6. 에러 처리
Data Task 실행 중에 에러가 발생할 경우를 대비라여 적정한 에러 처리를 구현한다. 네트워크 연결 오류, 서버 응답 오류 등을 처리할 수 있다.
URLSession.shared.dataTask(with: imageUrl) { data, response, error in
if let error = error {
print("Error: \(error)")
}
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
}
if let imageData = data, let image = UIImage(data: imageData) {
DispatchQueue.main.async {
self.randomImageView.image = image
}
}
}
7. 세션 종료
필요한 모든 데이터를 가져온 뒤에는 URLSession을 종료하여 리소스 관리와 메모리 누수를 방지한다.
session.finishTasksAndInvalidate()
import UIKit
import SkeletonView
class PetViewController: UIViewController {
var imageList: [RandomImage] = []
@IBOutlet weak var randomImageView: UIImageView!
@IBOutlet weak var randomButton: UIButton!
@IBOutlet weak var catImageView: UIImageView!
@IBAction func randomButton(_ sender: Any) {
getRandomCatImage()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
randomImageView.showAnimatedGradientSkeleton()
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { [weak self] in
self?.randomImageView.hideSkeleton()
}
}
override func viewDidLoad() {
super.viewDidLoad()
getRandomCatImage()
}
func getRandomCatImage() {
guard let url = URL(string: "https://api.thecatapi.com/v1/images/search") else { return }
let session = URLSession(configuration: .ephemeral)
let task = session.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let decoder = JSONDecoder()
guard let randomCatImage = try? decoder.decode([RandomImage].self, from: data) else { return }
if let imageUrl = URL(string: randomCatImage.first?.url ?? "") {
let imageTask = URLSession.shared.dataTask(with: imageUrl) { data, response, error in
if let error = error {
print("Error: \(error)")
}
if let httpResponse = response as? HTTPURLResponse {
print("Status Code: \(httpResponse.statusCode)")
}
if let imageData = data, let image = UIImage(data: imageData) {
DispatchQueue.main.async {
self.randomImageView.image = image
session.finishTasksAndInvalidate()
print("세션 종료")
}
}
}
imageTask.resume()
}
}
task.resume()
}
}
🤳🏻 적용화면
세션이 종료되는 시점을 확인할 수 있으며, 해당 페이지로 접근 또는 꾹꾹이라는 버튼을 누르지 않는 이상 이미지를 로드할 일은 없으니 이미지뷰에 데이터를 불러오자마자 세션을 종료하도록 설정했다.
실행하고보니 문제아닌 문제로 느끼는건 바로 로딩 속도... 너무 느리다... 한국패치 plz 🤮