24.07.18 Today I Learned

2024. 7. 18. 19:31

오늘은 포켓몬 전화번호부 만들기 Lv.8까지 구현했다.

추가로 전화번호 목록을 슬라이드 했을 때 전화번호 목록을 지우는 기능까지 추가해봤다.

 

//  Friend.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import Foundation

// 연락처 정보를 저장할 구조체 정의
struct Friend: Codable {
    let name: String         // 친구의 이름
    let phoneNumber: String  // 친구의 전화번호
    let profileImageData: Data? // 친구의 프로필 이미지 데이터를 저장 (선택 사항)
}
//  ContactManager.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import Foundation

class ContactManager {
    static let shared = ContactManager() // 싱글톤 인스턴스
    private let contactsKey = "contacts" // 데이터를 저장할 키

    // 연락처 데이터를 디스크에 저장하는 메서드
    func saveFriend(_ friend: Friend) {
        var friends = loadFriends() // 기존의 연락처 데이터를 불러옴
        if let index = friends.firstIndex(where: { $0.name == friend.name && $0.phoneNumber == friend.phoneNumber }) {
            friends[index] = friend // 기존 데이터를 업데이트
        } else {
            friends.append(friend) // 새로운 연락처 데이터를 배열에 추가
        }
        friends.sort { $0.name < $1.name } // 이름순으로 정렬
        if let data = try? JSONEncoder().encode(friends) {
            UserDefaults.standard.set(data, forKey: contactsKey) // 인코딩된 JSON 데이터를 UserDefaults에 저장
        }
    }

    // 디스크에서 연락처 데이터를 불러오는 메서드
    func loadFriends() -> [Friend] {
        if let data = UserDefaults.standard.data(forKey: contactsKey), // UserDefaults에서 데이터를 불러옴
           let friends = try? JSONDecoder().decode([Friend].self, from: data) { // 불러온 데이터를 디코딩하여 [Friend] 배열로 변환
            return friends.sorted { $0.name < $1.name } // 이름순으로 정렬하여 반환
        }
        return []
    }

    // 특정 연락처를 업데이트하는 메서드
    func updateFriend(_ friend: Friend, with newFriend: Friend) {
        var friends = loadFriends()
        if let index = friends.firstIndex(where: { $0.name == friend.name && $0.phoneNumber == friend.phoneNumber }) {
            friends[index] = newFriend
            friends.sort { $0.name < $1.name }
            if let data = try? JSONEncoder().encode(friends) {
                UserDefaults.standard.set(data, forKey: contactsKey)
            }
        }
    }

    // 특정 연락처를 삭제하는 메서드
    func deleteFriend(_ friend: Friend) {
        var friends = loadFriends()
        if let index = friends.firstIndex(where: { $0.name == friend.name && $0.phoneNumber == friend.phoneNumber }) {
            friends.remove(at: index)
            if let data = try? JSONEncoder().encode(friends) {
                UserDefaults.standard.set(data, forKey: contactsKey)
            }
        }
    }
}

Model 부분


//  FriendListViewLabel.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import UIKit
import SnapKit

class FriendListViewLabel: UIView {
    let titleLabel = UILabel() // 친구 목록 레이블
    let addButton = UIButton() // 추가 버튼
    let tableView = UITableView() // 테이블 뷰

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI() // UI 설정 메서드 호출
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupUI() {
        backgroundColor = .white // 배경색 설정

        // 친구 목록 레이블 설정
        titleLabel.text = "친구 목록"
        titleLabel.font = UIFont.boldSystemFont(ofSize: 20) // 글자 크기 설정
        addSubview(titleLabel) // 레이블 추가

        // 추가 버튼 설정
        addButton.setTitle("추가", for: .normal) // 버튼 제목 설정
        addButton.setTitleColor(.lightGray, for: .normal) // 버튼 글자 색 설정
        addButton.backgroundColor = .clear // 버튼 배경색 설정
        addSubview(addButton) // 버튼 추가

        // 테이블 뷰 추가
        addSubview(tableView) // 테이블 뷰 추가

        // 제약조건 설정
        setupConstraints()
    }

    private func setupConstraints() {
        // 친구 목록 레이블 제약조건
        titleLabel.snp.makeConstraints {
            $0.top.equalTo(safeAreaLayoutGuide).offset(0) // 안전 영역 위쪽 여백
            $0.centerX.equalToSuperview() // 수평 중앙 정렬
        }

        // 추가 버튼 제약조건
        addButton.snp.makeConstraints {
            $0.right.equalToSuperview().offset(-16) // 오른쪽 여백
            $0.centerY.equalTo(titleLabel) // 레이블과 수직 중앙 정렬
            $0.height.equalTo(50) // 높이 설정
            $0.width.equalTo(80) // 넓이 설정
        }

        // 테이블 뷰 제약조건
        tableView.snp.makeConstraints {
            $0.top.equalTo(titleLabel.snp.bottom).offset(16) // 레이블 아래 여백
            $0.left.right.bottom.equalToSuperview() // 좌우 및 하단 여백 설정
        }
    }
}
//  TableView.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import UIKit

// 테이블 뷰 데이터 소스와 델리게이트를 구현하는 클래스
class FriendTableView: UITableView, UITableViewDataSource, UITableViewDelegate {
    var friends: [Friend] = [] // 친구 목록 배열

    override init(frame: CGRect, style: UITableView.Style) {
        super.init(frame: frame, style: style)
        self.dataSource = self // 데이터 소스 설정
        self.delegate = self // 델리게이트 설정
        self.register(TableViewCell.self, forCellReuseIdentifier: "cell") // 커스텀 셀 등록
        self.backgroundColor = .white // 테이블 뷰 배경색 설정
        self.separatorInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) // 구분선 여백
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // 섹션 당 행의 수를 반환하는 메서드 (필수 데이터 소스 메서드)
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return friends.count // 친구 목록의 수 반환
    }

    // 각 셀에 대한 설정을 수행하는 메서드 (필수 데이터 소스 메서드)
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // 재사용 가능한 셀을 가져옴
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
        // 셀에 친구 이름과 전화번호 설정
        cell.nameLabel.text = friends[indexPath.row].name
        cell.phoneNumberLabel.text = friends[indexPath.row].phoneNumber
        // 프로필 이미지 설정
        if let imageData = friends[indexPath.row].profileImageData {
            cell.profileImageView.image = UIImage(data: imageData)
        } else {
            cell.profileImageView.image = UIImage(systemName: "person.circle")
        }
        return cell // 설정된 셀 반환
    }

    // 테이블 뷰 셀의 높이를 반환하는 메서드 (선택적 델리게이트 메서드)
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80 // 셀 높이 설정
    }
}
//  TableViewCell.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import UIKit
import SnapKit

class TableViewCell: UITableViewCell {
    let profileImageView = UIImageView() // 프로필 사진 이미지 뷰
    let nameLabel = UILabel() // 이름 레이블
    let phoneNumberLabel = UILabel() // 전화번호 레이블

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupUI() // UI 설정 메서드 호출
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupUI() {
        profileImageView.contentMode = .scaleAspectFill // 이미지 비율 유지
        profileImageView.layer.cornerRadius = 30 // 원형으로 만들기
        profileImageView.clipsToBounds = true // 코너 잘리게 설정
        profileImageView.layer.borderWidth = 2 // 테두리 두께 설정
        profileImageView.layer.borderColor = UIColor.lightGray.cgColor // 테두리 색 설정
        addSubview(profileImageView) // 이미지 뷰 추가

        nameLabel.font = UIFont.systemFont(ofSize: 16)
        addSubview(nameLabel) // 이름 레이블 추가

        phoneNumberLabel.font = UIFont.systemFont(ofSize: 16) // 일반체로 설정
        addSubview(phoneNumberLabel) // 전화번호 레이블 추가

        setupConstraints() // 제약조건 설정
    }

    private func setupConstraints() {
        profileImageView.snp.makeConstraints {
            $0.width.height.equalTo(60) // 프로필 이미지 크기 설정
            $0.left.equalToSuperview().offset(16) // 왼쪽 여백 설정
            $0.centerY.equalToSuperview() // 수직 중앙 정렬
        }

        nameLabel.snp.makeConstraints {
            $0.left.equalTo(profileImageView.snp.right).offset(30) // 프로필 이미지 오른쪽 여백 설정
            $0.centerY.equalToSuperview() // 수직 중앙 정렬
        }

        phoneNumberLabel.snp.makeConstraints {
            $0.left.equalTo(nameLabel.snp.right).offset(8) // 이름 레이블 오른쪽 여백 설정
            $0.centerY.equalToSuperview() // 수직 중앙 정렬
            $0.right.equalToSuperview().offset(-30) // 오른쪽 여백 설정
        }
    }
}
//  AddFriendView.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import UIKit
import SnapKit

// AddFriendView 클래스 정의
class AddFriendView: UIView {
    let addLabel = UILabel() // 추가 레이블
    let profileImageView = UIImageView() // 프로필 이미지 뷰
    let applyButton = UIButton() // 적용 버튼
    let randomImageButton = UIButton() // 랜덤 이미지 버튼
    let nameTextView = UITextView() // 이름 입력 텍스트 뷰
    let phoneTextView = UITextView() // 전화번호 입력 텍스트 뷰

    // 초기화 메서드
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupUI() // UI 설정 메서드 호출
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // UI 설정 메서드
    private func setupUI() {
        backgroundColor = .white // 배경색 설정

        // 프로필 이미지 뷰 설정
        profileImageView.contentMode = .scaleAspectFill // 이미지 비율 유지
        profileImageView.layer.cornerRadius = 100 // 원형으로 만들기
        profileImageView.clipsToBounds = true // 코너 잘리게 설정
        profileImageView.layer.borderWidth = 4 // 테두리 두께 설정
        profileImageView.layer.borderColor = UIColor.lightGray.cgColor // 테두리 색 설정
        addSubview(profileImageView) // 이미지 뷰 추가

        // 적용 버튼 설정
        applyButton.setTitle("적용", for: .normal) // 버튼 제목 설정
        applyButton.setTitleColor(.blue, for: .normal) // 버튼 글자 색 설정
        applyButton.backgroundColor = .clear // 버튼 배경색 설정
        addSubview(applyButton) // 버튼 추가

        // 랜덤 이미지 버튼 설정
        randomImageButton.setTitle("랜덤 이미지 생성", for: .normal) // 버튼 제목 설정
        randomImageButton.setTitleColor(.lightGray, for: .normal) // 버튼 글자 색 설정
        randomImageButton.backgroundColor = .clear // 버튼 배경색 설정
        randomImageButton.titleLabel?.font = UIFont.systemFont(ofSize: 16) // 버튼 글꼴 설정
        addSubview(randomImageButton) // 버튼 추가

        // 이름 입력 텍스트 뷰 설정
        nameTextView.font = UIFont.systemFont(ofSize: 16) // 글꼴 설정
        nameTextView.layer.borderColor = UIColor.lightGray.cgColor // 테두리 색 설정
        nameTextView.layer.borderWidth = 1 // 테두리 두께 설정
        nameTextView.layer.cornerRadius = 5 // 코너 둥글게 설정
        addSubview(nameTextView) // 텍스트 뷰 추가

        // 전화번호 입력 텍스트 뷰 설정
        phoneTextView.font = UIFont.systemFont(ofSize: 16) // 글꼴 설정
        phoneTextView.layer.borderColor = UIColor.lightGray.cgColor // 테두리 색 설정
        phoneTextView.layer.borderWidth = 1 // 테두리 두께 설정
        phoneTextView.layer.cornerRadius = 5 // 코너 둥글게 설정
        addSubview(phoneTextView) // 텍스트 뷰 추가

        setupConstraints() // 제약조건 설정 메서드 호출

        randomImageButton.addTarget(self, action: #selector(randomImageTapped), for: .touchUpInside) // 랜덤 이미지 버튼 클릭 시 동작 설정
    }

    // 랜덤 이미지 버튼 클릭 시 호출 메서드
    @objc private func randomImageTapped() {
        randomImageButtonTapped?()
    }

    // 랜덤 이미지 버튼 클릭 시 동작 클로저
    var randomImageButtonTapped: (() -> Void)?
    
    // 제약조건 설정 메서드
    private func setupConstraints() {
        profileImageView.snp.makeConstraints {
            $0.width.height.equalTo(200) // 프로필 이미지 크기 설정
            $0.centerX.equalToSuperview() // 수평 중앙 정렬
            $0.top.equalTo(safeAreaLayoutGuide).offset(20) // 안전 영역 위쪽 여백
        }

        randomImageButton.snp.makeConstraints {
            $0.centerX.equalToSuperview() // 수평 중앙 정렬
            $0.top.equalTo(profileImageView.snp.bottom).offset(10) // 프로필 이미지 아래 여백
            $0.height.equalTo(40) // 높이 설정
            $0.width.equalTo(150) // 넓이 설정
        }

        nameTextView.snp.makeConstraints {
            $0.top.equalTo(randomImageButton.snp.bottom).offset(20) // 랜덤 이미지 버튼 아래 여백
            $0.left.equalToSuperview().offset(16) // 왼쪽 여백
            $0.right.equalToSuperview().offset(-16) // 오른쪽 여백
            $0.height.equalTo(40) // 높이 설정
        }

        phoneTextView.snp.makeConstraints {
            $0.top.equalTo(nameTextView.snp.bottom).offset(5) // 이름 텍스트 뷰 아래 여백
            $0.left.equalToSuperview().offset(16) // 왼쪽 여백
            $0.right.equalToSuperview().offset(-16) // 오른쪽 여백
            $0.height.equalTo(40) // 높이 설정
        }
    }
}

View 부분


//  FriendListViewController.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import UIKit
import SnapKit

class FriendListViewController: UIViewController {
    let friendListView = FriendListViewLabel() // 친구 목록 뷰
    var friends: [Friend] = [] // 연락처 데이터 배열

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white // 배경색 설정
        view.addSubview(friendListView) // 친구 목록 뷰 추가

        friendListView.snp.makeConstraints {
            $0.edges.equalTo(view.safeAreaLayoutGuide) // 안전 영역에 맞추기
        }

        // 테이블 뷰 데이터 소스와 델리게이트 설정
        friendListView.tableView.dataSource = self
        friendListView.tableView.delegate = self

        // 셀 클래스 등록
        friendListView.tableView.register(TableViewCell.self, forCellReuseIdentifier: "cell")

        // 추가 버튼 액션 설정
        friendListView.addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        // 저장된 연락처 데이터를 로드하여 이름순으로 정렬
        friends = ContactManager.shared.loadFriends()
        // 테이블 뷰를 리로드하여 데이터를 업데이트
        friendListView.tableView.reloadData()
    }

    @objc private func addButtonTapped() {
        // 연락처 추가 화면으로 이동
        let addFriendVC = AddFriendViewController()
        navigationController?.pushViewController(addFriendVC, animated: true)
    }
}

extension FriendListViewController: UITableViewDataSource, UITableViewDelegate {
    // 섹션 당 행의 수 반환
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return friends.count
    }

    // 각 셀에 대한 설정
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
        let friend = friends[indexPath.row]
        cell.nameLabel.text = friend.name // 친구 이름 설정
        cell.phoneNumberLabel.text = friend.phoneNumber // 친구 전화번호 설정
        if let imageData = friend.profileImageData {
            cell.profileImageView.image = UIImage(data: imageData) // 프로필 이미지 설정
        } else {
            cell.profileImageView.image = UIImage(systemName: "person.circle") // 기본 프로필 이미지 설정
        }
        return cell
    }

    // 셀 높이 설정
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80
    }

    // 셀 선택 시 호출되는 메서드
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let selectedFriend = friends[indexPath.row]
        let addFriendVC = AddFriendViewController()
        addFriendVC.friend = selectedFriend // 선택된 친구 정보를 설정
        navigationController?.pushViewController(addFriendVC, animated: true)
    }

    // 셀 삭제를 위한 메서드 추가
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            // 연락처 배열에서 삭제
            let friendToDelete = friends.remove(at: indexPath.row)
            // 디스크에서 연락처 삭제
            ContactManager.shared.deleteFriend(friendToDelete)
            // 테이블 뷰에서 셀 삭제
            tableView.deleteRows(at: [indexPath], with: .automatic)
        }
    }
}
//  AddFriendViewController.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//

import UIKit
import SnapKit

class AddFriendViewController: UIViewController {
    let addFriendView = AddFriendView() // 연락처 추가 뷰
    let randomImageFetcher = RandomImageFetcher() // 랜덤 이미지 가져오는 클래스
    var friend: Friend? // 편집할 친구 정보

    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI() // UI 설정

        // 랜덤 이미지 버튼이 클릭되었을 때 호출될 클로저 설정
        addFriendView.randomImageButtonTapped = { [weak self] in
            self?.fetchRandomImage()
        }

        // 친구 정보가 있을 경우 해당 정보를 설정
        if let friend = friend {
            configure(with: friend)
        }
    }

    private func setupUI() {
        // 연락처 추가 뷰를 뷰에 추가
        view.addSubview(addFriendView)
        // 제약조건 설정
        addFriendView.snp.makeConstraints {
            $0.edges.equalToSuperview()
        }

        title = friend?.name ?? "연락처 추가" // 네비게이션 타이틀 설정
        // 네비게이션 바에 적용 버튼 추가
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "적용", style: .plain, target: self, action: #selector(applyButtonTapped))
    }

    @objc private func applyButtonTapped() {
        // 이름과 전화번호를 가져옴
        let name = addFriendView.nameTextView.text ?? ""
        let phoneNumber = addFriendView.phoneTextView.text ?? ""
        // 프로필 이미지를 데이터로 변환
        let profileImage = addFriendView.profileImageView.image
        let profileImageData = profileImage?.jpegData(compressionQuality: 0.8)

        // 새로운 연락처 데이터를 생성
        let newFriend = Friend(name: name, phoneNumber: phoneNumber, profileImageData: profileImageData)

        // 기존 연락처가 있으면 업데이트, 없으면 새로 추가
        if let friend = friend {
            ContactManager.shared.updateFriend(friend, with: newFriend)
        } else {
            ContactManager.shared.saveFriend(newFriend)
        }

        // 메인 화면으로 돌아가기
        navigationController?.popViewController(animated: true)
    }

    private func fetchRandomImage() {
        // 랜덤 이미지를 가져오는 메서드 호출
        randomImageFetcher.fetchRandomImage { [weak self] image in
            DispatchQueue.main.async {
                // 가져온 이미지를 프로필 이미지 뷰에 설정
                self?.addFriendView.profileImageView.image = image
            }
        }
    }

    // 친구 정보를 설정하는 메서드
    func configure(with friend: Friend) {
        addFriendView.nameTextView.text = friend.name
        addFriendView.phoneTextView.text = friend.phoneNumber
        if let imageData = friend.profileImageData {
            addFriendView.profileImageView.image = UIImage(data: imageData)
        } else {
            addFriendView.profileImageView.image = UIImage(systemName: "person.circle")
        }
    }
}
//  RandomImageFetcher.swift
//  PokeapiProject
//
//  Created by t2023-m0112 on 7/16/24.
//
// JSON 응답을 파싱하고 이미지 URL을 추출
                // JSON에서 URL을 가져온 후 데이터를 다운로드하여 이미지로 사용하는 경우, 데이터를 일시적으로 저장하는 곳이 필요하지만, 그러나 이 데이터는 메모리 내에서 처리되므로 디스크에 저장할 필요가 없다. 이미지를 다운로드하고 이를 UIImage로 변환한 후, 이를 UI에 반영한다. 이는 주로 메모리를 사용하여 처리된다. -> gpt 피셜
                // <Json은 gpt에게 물어가면서해서 이해가 아직 안되므로 따로 더 공부하기>
import UIKit

// 랜덤 이미지를 가져오는 클래스를 정의
class RandomImageFetcher {
    // 랜덤 이미지를 가져오는 메서드
    func fetchRandomImage(completion: @escaping (UIImage?) -> Void) {
        // 1부터 100 사이의 랜덤 숫자를 생성
        let randomNumber = Int.random(in: 1...100)
        // 랜덤 숫자를 포함한 URL 문자열을 생성
        let urlString = "https://pokeapi.co/api/v2/pokemon/\(randomNumber)"
        // URL 문자열을 URL 객체로 변환
        guard let url = URL(string: urlString) else {
            completion(nil) // URL이 유효하지 않으면 nil을 반환
            return
        }
        
        // URL 요청 객체를 생성하고, HTTP 메서드를 GET으로 설정
        var request = URLRequest(url: url)
        request.httpMethod = "GET"

        // URLSession을 사용하여 데이터 태스크를 생성 및 실행
        URLSession.shared.dataTask(with: request) { data, response, error in
            // 에러가 발생한 경우, 에러를 출력하고 nil을 반환
            if let error = error {
                print("Error fetching data: \(error)")
                DispatchQueue.main.async {
                    completion(nil)
                }
                return
            }

            // 데이터가 없는 경우, 로그를 출력하고 nil을 반환
            guard let data = data else {
                print("No data received")
                DispatchQueue.main.async {
                    completion(nil)
                }
                return
            }

            do {
                // JSON 데이터를 파싱하여 딕셔너리로 변환
                if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                    // JSON 응답을 출력하여 확인
                    print("JSON response: \(json)")
                    
                    // JSON에서 이미지 URL을 추출
                    if let sprites = json["sprites"] as? [String: Any],
                       let imageUrlString = sprites["front_default"] as? String,
                       let imageUrl = URL(string: imageUrlString) {
                        
                        // 이미지 다운로드 메서드를 호출
                        self.downloadImage(from: imageUrl, completion: completion)
                    } else {
                        // JSON 구조가 예상과 다른 경우
                        print("Invalid JSON structure")
                        DispatchQueue.main.async {
                            completion(nil)
                        }
                    }
                } else {
                    // JSON 파싱이 실패한 경우
                    print("Invalid JSON structure")
                    DispatchQueue.main.async {
                        completion(nil)
                    }
                }
            } catch {
                // JSON 파싱 중 에러가 발생한 경우
                print("Error parsing JSON: \(error)")
                DispatchQueue.main.async {
                    completion(nil)
                }
            }
        }.resume() // 데이터 태스크를 시작
    }

    // URL에서 이미지를 다운로드하는 메서드
    private func downloadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        // URLSession을 사용하여 데이터 태스크를 생성 및 실행
        URLSession.shared.dataTask(with: url) { data, response, error in
            // 에러가 발생한 경우, 에러를 출력하고 nil을 반환
            if let error = error {
                print("Error downloading image: \(error)")
                DispatchQueue.main.async {
                    completion(nil)
                }
                return
            }

            // 데이터가 없거나 이미지 데이터가 유효하지 않은 경우
            guard let data = data, let image = UIImage(data: data) else {
                print("No data or invalid image data")
                DispatchQueue.main.async {
                    completion(nil)
                }
                return
            }

            // 다운로드한 이미지를 반환
            DispatchQueue.main.async {
                completion(image)
            }
        }.resume() // 데이터 태스크를 시작
    }
}

Controller부분


 

'Today I Learned > 2024' 카테고리의 다른 글

24.07.25 Today I Learned  (0) 2024.07.25
24.07.22 Today I Learned  (0) 2024.07.22
24.07.17 Today I Learned  (0) 2024.07.17
24.07.16 Today I Learned  (0) 2024.07.16
24.07.15 Today I Learned  (2) 2024.07.15

BELATED ARTICLES

more