포스트

7주차 과제 (7)

기존에는 API에서 값을 전달할때 Protocol을 사용하여 VC로 값을 넘겼는데,

여러튜터님들에게 여쭤보며 Insight를 구해보니, 단일 메서드일때는 클로저가 더 좋다는 말씀도 있고, 두개 다 할줄 알아야 한다고 하셨기에, 그 부분만 좀 구현을 해보려 한다.

잘 쓰지않아서 생소하기에 처음부터 하나하나 좀 파헤쳐가면서 기능을 구현하려고한다

Escaping Closure를 사용한 통신

1. 기본틀 작성

1
2
3
func fetchRequestWithClosure(completion: @escaping() -> Void) {
        
    }

기본틀은 다음과 같다. 완료되었을때 Escaping Clousre를 사용하여 데이터를 넘긴다는 의미.

2. Escaping Closure?

API호출 함수가 종료되면 종료 직후 그 값을 전달하기 위해서 escaping closure를 사용한다.

현재 API를 통해 결과값을 가져와야하므로

Escaping Clousre안에 어떤 값이 들어와햐하는지 생각을 해보자.

우리는 Result를 사용할 것이다.

Result는 들어가보면

CleanShot 2024-04-12 at 13 30 30@2x

이런식으로 된다.

성공했을때, 실패했을때 나눠서 작성하면된다.

Failure일때는 Error를 사용하면되고,

Success일때는 우리가 원하는 데이터 타입을 넘겨주면 된다.

프로토콜로 생각한다면 어떤 값을 넘길지에대한 부분이 바로 successs에 들어간다고 생각하면 되겠다.

1
2
3
4
func fetchRequestWithClosure(completion: @escaping(Result<DataModel, Error>) -> Void) {
     //                                            -------------------------

    }

이렇게 된다.

url사용은 생략하겠다.

이번에는 독특하게 httpMethod도 포함시켜서 작성 해보았다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
func fetchRequestWithClosure(completion: @escaping(Result<DataModel, Error>) -> Void) {
        
        let pageNumber = (1...100).randomElement() ?? 1
        
        if let url = URL(string: "https://dummyjson.com/products/\(pageNumber)") {
            
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            
            let task = URLSession.shared.dataTask(with: request) { (data, response ,error) in
                
                if let error {
                    completion(.failure(error))
                    return
                }
                
                if let safeData = data {
                    if let decodedData = self.decodingJson(data: safeData) {
                        completion(.success(decodedData))
                        return
                    }
                }
            }
            task.resume()
        } else {
            
        }
        
    }

VC에서 해당 메서드 호출

1
2
3
4
5
6
7
8
dataManager.fetchRequestWithClosure { result in
            switch result {
            case .success(let data) :
                print("OK")
            case .failure(let error) :
                print("Fail")
            }
        }

출력했을때 OK 확인.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
self.dataManager.fetchRequestWithClosure { result in
                switch result {
                case .success(let data) :
                    self.list = data
                    DispatchQueue.main.async {
                        let price = self.numberFormatter.string(from: Double(self.list.price) * (100.00 - self.list.discountPercentage) / 100 as NSNumber)
                        self.titleLabel.text = self.list.title
                        self.bodyLabel.text = self.list.description
                        self.priceLabel.text = "\(self.numberFormatter.string(from: self.list.price as NSNumber) ?? "0") $"
                        self.discountedLabel.text = "할인 적용: \(price ?? "0")$"
                        self.setPageCount()
                        self.makingImageView.makingImage(list: self.list, scrollView: self.imageScrollView)
                        self.scrollViewDidScroll(self.imageScrollView)
                        
                    }
                case .failure(let error) :
                    let alert = self.alertManager.makingAlert(title: "에러 발생", body: "데이터를 로드 하던 중 \(error)가 발생했습니다.")
                    self.present(alert, animated: true)
                }
            }

작동확인 완료.

warning 수정

기존에

1
2
3
4
5
6
7
8
9
10
11
12
13
extension UIImageView {
    func load(url: URL) {
        DispatchQueue.global().async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self?.image = image
                    }
                }
            }
        }
    }
}
1
2
Thread Performance Checker: Thread running at User-initiated quality-of-service class waiting on a lower QoS thread running at Default quality-of-service class. Investigate ways to avoid priority inversions
PID: 16569, TID: 604505

첨에 URL주소를 바로 쓰는거라 사용을 한것이었는데 이런 Warning이 나올줄은 몰랐다.

여기서 나는 warning이 너무 거슬려서 수정

thread에서 우선순위 역전에 관한 내용이다.

무튼 해당 문제를 해결하기위해

ImageView관련 extension을 지웠다.

그리고 아래 코드를 적용하려 했다.

1
2
3
let url = URL(string: "")
let data = try Data(contentsOf: url!)
uiImageView.image = UIImage(data: data)

하지만 위의 url,data 부분에서 해당 명령은 URLSession단에서 행하라는 swift의 message를 보고

사이트를 참고하여 수정했다.

해결완료.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func makingImage (list: DataModel, scrollView: UIScrollView) {
        for i in 0 ..< list.images.count - 1 {
            let imageView = UIImageView()
            guard let url = URL(string: list.images[i]) else { return }
            URLSession.shared.dataTask(with: url) {
                (data, response, error) in
                if let error {
                    print(error)
                }
                guard let imageData = data else { return }
                DispatchQueue.main.async {
                    imageView.image = UIImage(data: imageData)
                }
            }.resume()
            let xPos = scrollView.frame.width * CGFloat(i)
            imageView.frame = CGRect(x: xPos, y: 0, width: scrollView.bounds.width, height: scrollView.bounds.height)
            
            scrollView.addSubview(imageView)
            scrollView.contentSize.width = imageView.frame.width * CGFloat(i + 1)
            
        }
    }

여러 튜터님과 대화를 해보았는데,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension UIImageView {
    func load(url: URL) {
        DispatchQueue.global(qos: .background).async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                if let image = UIImage(data: data) {
                    DispatchQueue.main.async {
                        self?.image = image
                    }
                }
            }
        }
    }
}

qos를 Background로 더 뒤로 보내니 에러가 뜨지 않았다.

Thread 심오한녀석이다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.