포스트

ByteCoin (3)

pickerview를 통해 내가 원하는 원화로 바꾸기

현재는 json값을 제대로 파싱하는지 확인하기위해 baseURL을 USD로 자체적으로 설정을 해두었다.

그리고 print를 사용해서 pikcerview에 해당하는 그 값만 출력하게 해두었다.

이젠 이렇게 출력되는 값을 \()을 사용하여 집어 넣어보려고 한다.

우선 fetchCoin 함수를 하나 더 만들어 주었다.

1
2
3
4
func fetchCoin (curreny: String) {
        let urlString = "\(baseURL)\(curreny)?apikey=\(storage.apiKey)"
        performRequest(with: urlString)
    }

기존껀 파라미터로 뭔가를 가져오지 않았지만 이번엔 가져와야하기에 위와 같이 해주었다.

https://rest.coinapi.io/v1/exchangerate/BTC/USD?apikey=

값을 받아오기위한 url양식은 위와 같다.

이젠 저기 usd를 빼고 만들어보자.

그리고 getCoinPrice도 다음과 같이 해주었다.

1
2
3
func getCoinPrice(for currency: String){
        fetchCoin(curreny: currency)
    }

이전 글에서 print로 각각의 currency가 출력이 되는것을 확인 했으니. 위의 함수를 이용해서 테스트를 한번 해보도록 하자.

이제 실행을 시키고 제대로 나오는지 테스트를 해보도록 하자

잘나오는걸 확인할 수 있다.

이젠 우리가 이 모든 값을 알 필요가 없기에 실제 코인 가격만 나오게끔 바꿔보자.

우선 coinModel에서 다음과 같음 computed value를 하나 만들어 준다

1
2
3
var stringRate : String {
        return String(format:"%.2f", rate)
    }

그리고 performRequest의 부분에 stringRate를 출력하게 해두었다.

1
2
3
4
5
6
if let safeData = data { // 데이터를 정상적으로 받아온다면 옵셔널 바인딩을 해준다.
                    if let coin = self.parseJSON(safeData) { // 클로저 안이라 self를 명시
                        print(coin.stringRate)
                    }
                
                }

테스트 해보자

잘나온다!

다음과 같이 이젠 콘솔이 아닌 UI를 통해 보이도록 표현을 해보자.

UI로 해당 값을 보이게 하기.

일단 현재 가격을 어떻게 해서 가져오는지에 대해서 생각을 먼저 했다.

1. pickerView 실행

viewController의 pickerView를 통해서

1
2
3
4
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        let selectedCurrency = coinManager.currencyArray[row]
        coinManager.getCoinPrice(for: selectedCurrency)
    }

코인의 가격을 가져오게한다.

2. getCoinPrice 실행

coinManager로 가서 getCoinPrice 작동.

1
2
3
func getCoinPrice(for currency: String){
        fetchCoin(curreny: currency)
    }

3. fetchCoin 실행

1
2
3
4
func fetchCoin (curreny: String) {
        let urlString = "\(baseURL)\(curreny)?apikey=\(storage.apiKey)"
        performRequest(with: urlString)
    }

4. performRequest 실행

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
func performRequest(with urlString : String) {
        if let url = URL(string: urlString) { // 1. URL 생성
            
            let session = URLSession(configuration: .default) // 2. URL Session 생성
            
            let task = session.dataTask(with: url) { data, response, error in // 3. Session에 task 부여
                
                if error != nil { // 에러가 발생하는 경우
                    
                    print(error ?? "Error")
                    
                    return // 리턴을 하여 아무것도 하지 못하게 한다.
                }
                
                if let safeData = data { // 데이터를 정상적으로 받아온다면 옵셔널 바인딩을 해준다.
                    if let coin = self.parseJSON(safeData) { // 클로저 안이라 self를 명시
                        print(coin.stringRate)
                    }
                
                }
            }
            
            // 4. task 실시
            task.resume()
        }
    }

5. parseJSON 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func parseJSON(_ coinData : Data) -> CoinModel? {
        let decoder = JSONDecoder()
        do {
            let decodedData = try
            decoder.decode(CoinData.self, from: coinData)
            let asset_id_base = decodedData.asset_id_base
            let asset_id_quote = decodedData.asset_id_quote
            let rate = decodedData.rate
            
            let coin = CoinModel(base: asset_id_base, quote: asset_id_quote, rate: rate)
            return coin
            
        } catch {
            
            print(error)
            
            return nil
        }
    }

5. parsing된 값 출력

1
2
3
if let coin = self.parseJSON(safeData) { // 클로저 안이라 self를 명시
                        print(coin.stringRate)
                    }

출력부분을 리턴으로 받아서 그걸다시 전달을 하면 어떨까 라는 생각이 들었다.

그래서 performRequest부터 리턴을 하도록 고쳐주었다.

바로 에러가 발생하였다.

어떻게 해야할지 기억도 나지않아 이전에 작성했던 글을 참고해서 보았다.

clima를 할때 didupdate라는 함수를 사용하였고 파라미터 타입을 우리가 만든 모델로 하여 전달을 받았다.

해당 함수를 사용하여 한번 진행해보자.

1
2
3
4
func didUpdateCoin(coin : CoinModel) {
        print(coin.stringRate)

    }

이렇게 viewController에 만들어 주었다.

이젠 CoinManager에 해당 함수를 호출해보자.

1
2
3
4
5
6
7
8
9
10
let coinVC = CoinViewController()

 if let safeData = data { // 데이터를 정상적으로 받아온다면 옵셔널 바인딩을 해준다.
                    if let coin = self.parseJSON(safeData) { // 클로저 안이라 self를 명시
                                                                  // safeData를 parseJSON을통해 파싱 해준 값을 저장
                        coinVC.didUpdateCoin(coin: coin)
                    
                    }
                
                }

잘된다.

그전에 잘못생각해둔게 있어 적는다. 바로 viewController를 인스턴스화 할때의 생성 위치이다.

처음엔 아무생각없이 여기 안에 넣었다.

메모리 누수가 발생하는것으로 보인다.

뭔가 서로 인스턴스화가 계속 무한반복생성? 그런 느낌이 들어서 튜터님께 찾아가보았고 여쭤보았다.

그게 맞다고 하셨다.

그래서 인스턴스화하는 위치를 struct밖으로 빼주었다.

그렇게 해보니 잘된다.

하지만 에러는 아닌데 뭔가 warning같은 경고 메세지가 뜬다.

1
2
Main Thread Checker: UI API called on a background thread: -[UIViewController init]
PID: 33377, TID: 2572006, Thread name: (none), Queue name: com.apple.NSURLSession-delegate, QoS: 0

Background Thread 에서 실행이 되는데, 우린 Main Thread 에서의 실행이 필요하다.

확실히 여기서부터가 어려워지기 시작한다.

일단 label의 text를 변경하려고하니 optional이 된다.

튜터님께 여쭤보니 ViewController를 인스턴스화 했는데, 그게 아마 전달이 안될거라고 하셨다.

print는 되었지만, 값이 제대로 전달이 안되는걸까…

결국 고민을 하다 수업시간에 했던 프로토콜을 사용해보기로했다.

그건 다음글에서 계속 하겠다.

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