본문 바로가기

iOS/PROJECT

[두깃] 깃허브 유저 정보 확인하기 (2022.04.08)

유저 정보: Singleton 패턴

깃허브에서 필요한 정보가 유저이름과 저장소라서 둘다 Realm 객체로 생성하여 저장하려고 했으나

유저이름은 간단하게 저장, 확인, 수정만 할 것이라서 굳이 DB에 저장하지 않아도 된다고 생각하여.. singleton 패턴을 사용하기로 결정

 

-> 바보였다. Singleton 패턴은 앱 종료시 사라진다. 다시 Realm에 저장하도록 하자.

 

Github API : 존재하는 유저인지

https://docs.github.com/en/rest/reference/users

 

Users - GitHub Docs

Users The Users API allows to get public and private information about the authenticated user. Many of the resources on the users API provide a shortcut for getting information about the currently authenticated user. If a request URL does not include a {us

docs.github.com

내가 생각한 로직

 

1. 확인 버튼 클릭 시 유저 정보를 가져오는 요청을 날린다. (통신하는 struct 만들기)

2. 코드가 404 이면 유저 정보가 없는 상태 -> Error handling -> alert 창 띄우기 (UIAlertController)

코드가 200~299 이면 성공

 

구현은 했는데 에러핸들링까지 코드를 넣으려니 ?.? 살짝 패닉이 왔다. 

throws 를 넣어서 던지려고 하니 모든 코드에 do try를 넣어야하나...? 어디서.. 에러핸들링을..

해서 async / await 로 한번 도전해볼까 해서 도전을 하다가?

안되길래 ㅠ.ㅠ 예상가는 곳에 디버거 걸어도 안되고 exception 이라고 딱히 뜨는 것도 없어서..

테스트 코드를 작성해보기로 했다.

 

https://fomaios.tistory.com/entry/iOS-Unit-Test%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EA%B0%84%EB%8B%A8%ED%9E%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

 

우선 자세하게는 다음에 알아보고 사용법을 알아보기 위해 블로그를 참고하였다.

+ 왼쪽에 다이아몬드가 왜 안뜨지.. 했는데 함수 이름 앞에 test를 작성해 주어야 했다.

 

당연한 소리일지는 몰라도 async 함수를 호출하려니 똑같이 테스트에 async throws를 작성해 주어야 했다. (+try await)

// func 테스트할 클래스 이름_테스트 상황_예상 결과()
func testUserDataManager_WhenRightIdProvided_ShouldReturnId() async throws {
    // arrange : 테스트에 필요한 클래스나 객체
    let id: String = "NEULiee"

    // action: 테스트
    let providedId = try await sut.fetchUser(userId: id)

    // assert: 결과 확인
    XCTAssertEqual(id, providedId)
}

 

api 통신은 잘 됐지만 에러핸들링에 관한 실패 원인을 알아내기에는 실패했다. 쓰는 글마다 다 실패네..? 

<unknown>:0: error: -[DoGitTests.UserDataManagerTests testUserDataManager_WhenWrongIdProvided_ShouldReturnDoGitException] : failed: caught error: "The operation couldn’t be completed. (DoGit.DoGitError error 0.)"

 

구글링도 해보았지만 원인을 정확하게 파악하지 못해서.. 

XCTAssertThrowsError 안에 try await sut.fetchUsert() 함수를 넣어 클로저를 사용하고 싶었는데 XCTAssertThrowsError 앞에 async, async throws, try, try await, await 별짓을 다해도 에러가 안사라져서 ^**^

 

나름 생각하면서 테스트도 짜보고 에러핸들링도 한 것 같은데 아직 기초가 부족해서 무엇이 원인인지 파악하기 어려운 것 같다.

우선 에러핸들링은 잠시 미뤄두고 원래 코드로 돌아가 작업을 진행해야겠다. 

 

나중을 위한 async / awiat 코드

더보기
import Foundation

struct UserDataManager {
    
    private let githubUserURL = "https://api.github.com/users/"
    
    @discardableResult func fetchUser(userId: String) async throws -> String? {
        guard let url = URL(string: githubUserURL + userId) else { return nil }
        
        let (data, response) = try await URLSession.shared.data(from: url)
        
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            throw DoGitError.userNameNotFound
        }
        
        guard let name = userJSONDecode(data: data) else {
            throw DoGitError.userNameNotFound
        }
        
        User.shared.name = name
        return name
    }
    
    private func userJSONDecode(data: Data) -> String? {
        
        let decoder = JSONDecoder()
        
        do {
            let decodeData = try decoder.decode(UserData.self, from: data)
            return decodeData.name
        } catch {
            print("decode failed")
            return nil
        }
    }
}
@objc func didPressDoneButton(_ sender: UIButton) async throws {
    guard let name = self.nameTextField.text else { return }

    do {
        if let fetchName = try await userDataManager.fetchUser(userId: name) {
            print("Success: \(fetchName)")
            User.shared.name = fetchName
        }
    } catch {
        DispatchQueue.main.sync {
            showAlert(error: .userNameNotFound)
        }
    }
}

 

 

Modal 창으로 띄우기

생각한 로직

메인 뷰컨으로 진입 -> User 객체의 이름이 "" (초기화 상태) 이면 modal 창 띄우기

 

배운 것

viewDidLoad() 에서 모달창을 호출하려니 whose view is not in the window hierarchy 에러가 발생했다.

 

해결

viewDidAppear() 함수에 작성한다.

viewDidLoad() 는 뷰컨이 메모리에 올라갔을 때 한번만 호출되는 함수이다. 따라서 아직 뷰컨이 생성되지 않았기 때문에 모달을 띄우려고 하니 오류가 난 것이었다.

viewDidAppear() 은 뷰컨이 view 계층에 추가 되고 난 후에 호출되는 함수이므로 새로운 모달을 호출하면 된다.

 

그래서 호출했더니..

보자마자 웃음이 나오긴했다.

모달로 쓰일 뷰컨의 백그라운드 색을 white로 지정해준 것으로 해결하였다.

왜 이렇게 되나 찾아보니 fullscreen으로 모달을 띄울 시 이전 view를 지워버리고, 모달 창은 투명하게 띄워진다는?

 

문제는 이름이 존재하지 않을 때 alert 창도 잘 띄워지는데 모달창이 한번 닫혔다가 다시 열린다는 에러가 발생했다.

오,, 정말 끝이 없다. dismiss -> 찾기