[UIKit] Spotify Clone: Sign In Wrap Up

Junyoung Park·2022년 9월 1일
0

UIKit

목록 보기
3/142
post-thumbnail

Building Spotify App in Swift 5 & UIKit - Sign In Wrap Up (Xcode 12, 2022, Swift 5) - Build App

Spotify Clone: Sign In Wrap Up

구현 목표

  • 스포티파이 인증 API: 웹뷰 사용 익히기
  • 인증 완료 후 유저 디폴트를 통해 토큰 정보 저장

구현 태스크

  1. 웹뷰 → 스포티파이 인증 화면 띄우기
  2. 인증 담당 매니저 클래스 AuthManager: 스포티파이 인증 사용되는 URLRequest 정보의 쿼리 정보를 통해 토큰 값 저장

    Spotify Authorization API

  • 스포티파이 웹 API를 사용(iOS SDK도 사용 가능) → 웹뷰를 통해 앱이 보다 유연해지고 사이즈가 더 작아질 수 있음
  1. 인증 성공 → 웹뷰 전환, 기존 탭뷰 띄우기

핵심 코드

    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        guard let url = webView.url else {
            return }
        // Exchange the code for access token
        guard let code = URLComponents(string: url.absoluteString)?.queryItems?.first(where: {$0.name == "code"})?.value else {
            return
        }
        webView.isHidden = true
        print("Code: \(code)")
        AuthManager.shared.exchangeCodeForToken(code: code) { [weak self] success in
            guard let self = self else { return }
            DispatchQueue.main.async {
                self.navigationController?.popToRootViewController(animated: true)
                self.completionHandler?(success)
            }
        }
    }
  • 스포티파이 인증에 사용하는 URL 정보를 통해 현재 사용자가 입력한 토큰 값을 읽어올 수 있음
  • API를 통해 받아오려는 데이터 패치 성공 시 현재 웹뷰를 끄고 성공 여부를 불리언 변수 값을 통해 컴플리션 핸들러로 전달 → AuthManager에서 해당 값을 유저 디폴트로 저장하기 위한 비동기적 데이터 전달
    public func exchangeCodeForToken(code: String, completionHandler: @escaping (Bool) -> ()) {
        // GET TOKEN
        guard let url = URL(string: Constants.tokenAPIURLString) else { return }
        
        var components = URLComponents()
        components.queryItems = [
            URLQueryItem(name: "grant_type", value: "authorization_code"),
            URLQueryItem(name: "code", value: code),
            URLQueryItem(name: "redirect_uri", value: Constants.redirectURI),
        ]
        
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"
        urlRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        let basicToken = Constants.clientID + ":" + Constants.clientSecret
        let data = basicToken.data(using: .utf8)
        guard let base64String = data?.base64EncodedString() else {
            completionHandler(false)
            return
        }
        urlRequest.setValue("Basic \(base64String)", forHTTPHeaderField: "Authorization")
        urlRequest.httpBody = components.query?.data(using: .utf8)
        URLSession.shared.dataTask(with: urlRequest) { [weak self] data, response, error in
            guard let self = self else { return }
            if let error = error {
                print(error.localizedDescription)
                completionHandler(false)
                return
            } else if let data = data {
                do {
                    let result = try JSONDecoder().decode(AuthResponse.self, from: data)
                    print("SUCCESSFULLY REFRESH")
                    self.cacheToken(result: result)
                    completionHandler(true)
                } catch {
                    print(error.localizedDescription)
                    completionHandler(false)
                }
            }
        }
        .resume()
    }
  • 스포티파이 API가 기본적으로 제공하는 정보(grant_type, code, redirect_uri) 등을 받아오기 위한 POST
  • AuthResponse 데이터 모델을 API를 통해 리턴
struct AuthResponse: Codable {
    let access_token: String
    let expires_in: Int
    let refresh_token: String?
    let scope: String
    let token_type: String
}
  • 현재 스포티파이에 로그인한 유저의 접근 토큰, 파기 시간, 전환되는 시간, 범위, 토큰 타입 등을 리턴(API 문서에 정의)

구현 화면

URL 부분 문자가 틀리면 곧바로 다르게 인식되기 때문에 복사/붙여넣기를 통해 접근하자...!

profile
JUST DO IT

0개의 댓글