Swiftyinsta: WebLogin์ด ์ž‘๋™ํ•˜์ง€ ์•Š์Œ

์— ๋งŒ๋“  2019๋…„ 07์›” 22์ผ  ยท  16์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: TheM4hd1/SwiftyInsta

์—…๋ฐ์ดํŠธ ํ›„ Pod weblogin์ด ios 10 ๋ฒ„์ „์—์„œ ์ปดํŒŒ์ผ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
11.0 ๋ฒ„์ „์˜ ios๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

  • 'loginDelegate'๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  InstagramLoginWebView ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์„ธ์š”.
  • 'InstagramLoginWebViewDelegate'๋Š” ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  InstagramLoginWebViewProtocol ํด๋กœ์ € ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

InstagramLoginWebViewDelegate๋ฅผ InstagramLoginWebViewProtocol๋กœ ๋ณ€๊ฒฝํ–ˆ์„ ๋•Œ:

  • 'InstagramLoginWebViewProtocol'์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด 'LoginViewController'๊ฐ€ 'UIView'์—์„œ ์ƒ์†๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ์ฝ”๋“œ:

ํด๋ž˜์Šค LoginViewController: UIViewController {

var loginWebView: InstagramLoginWebView! = nil

override func viewDidLoad() {
    super.viewDidLoad()

    loginWebView = InstagramLoginWebView(frame: self.view.frame)
    self.view.addSubview(loginWebView!)
    loginWebView?.loginDelegate = self **-- Cannot assign value of type 'LoginViewController' to type 'InstagramLoginWebViewDelegate?'**
    self.loginWebView?.loadInstagramLogin(isNeedPreloadForCookieSync: true) **--Argument passed to call that takes no arguments**
}

}

extension LoginViewController : InstagramLoginWebViewProtocol { --'InstagramLoginWebViewProtocol'์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด 'LoginViewController'๊ฐ€ 'UIView'์—์„œ ์ƒ์†๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

func userLoggedSuccessfully() {
    print("User Logged Successfully")

    DispatchQueue.main.async {
        self.loginWebView.removeFromSuperview()
    }

}
help wanted

๋ชจ๋“  16 ๋Œ“๊ธ€

์•ˆ๋…•ํ•˜์„ธ์š” @ ์ดํŽ™ํŠธ

InstagramLoginWebView ๋Š” ์ง€๋‚œ ์—…๋ฐ์ดํŠธ์—์„œ ๋งŽ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์œผ๋ฉฐ iOS 10.* ๋Œ€ํ•œ ์ง€์›์€ ๋น„ํ˜ธํ™˜์„ฑ์œผ๋กœ ์ธํ•ด ์ค‘๋‹จ๋˜์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
loginDelegate ์€ ๋” ์ด์ƒ ์ œ์•ˆ๋˜์ง€ ์•Š์ง€๋งŒ ์—ฌ์ „ํžˆ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
์ต์ˆ™ํ•œ ๋™์ž‘์„ ์–ป์œผ๋ ค๋ฉด loginWebView ์— ๋‹ค์Œ ์†์„ฑ์„ ์„ค์ •ํ•˜๊ธฐ

  • didReachEndOfLoginFlow ๋Š” redirected ์ฒ˜๋Ÿผ ํ˜ธ์ถœ๋˜๋Š” _ํด๋กœ์ €_์ž…๋‹ˆ๋‹ค.
  • didSuccessfullyLogIn ๋Š” _delegate_์˜ userLoggedSuccessfully() ๋Œ€์ฒดํ•˜๋Š” _closure_์ž…๋‹ˆ๋‹ค.
  • completionHandler ๋Š” _delegate_์˜ webViewFinishedToLoadUser(sessionChache:handler:) ๋Œ€์ฒดํ•˜๋Š” _closure_์ž…๋‹ˆ๋‹ค.

1.7.* ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๋œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

class LoginViewController: UIViewController {
    var loginWebView: InstagramLoginWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        loginWebView = InstagramLoginWebView(frame: self.view.frame,
                                             didSuccessfullyLogIn: { [weak self] in DispatchQueue.main.async { self?.loginWebView.removeFromSuperview() }},
                                             completionHandler: { sessionCache, handler in /* do whaterver you need to */ })
        view.addSubview(loginWebView)
        loginWebView.loadInstagramLogin()
    }
}

๋”ฐ๋ผ์„œ ํ™•์žฅ InstagramLoginWebViewDelegate๋ฅผ InstagramLoginWebViewProtocol๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  'InstagramLoginWebViewProtocol'์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด 'LoginViewController'๊ฐ€ 'UIView'์—์„œ ์ƒ์†๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•”ํ˜ธ:

ํ™•์žฅ LoginViewController : InstagramLoginWebViewDelegate {

func userLoggedSuccessfully() {
    print("User Logged Successfully")

}

func webViewFinishedToLoadUser(sessionCache: SessionCache, handler: APIHandlerProtocol) {
    print("Loading user with sessioncache is successful")

    do{
        let encoder = JSONEncoder()
        let data = try encoder.encode(sessionCache)

        let sessionCacheString = String(data: data, encoding: .utf8)

    }catch {
        print("Whoops, an error occured: \(error)")
    }


}

InstagramLoginWebViewProtocol ์ค€์ˆ˜ํ•ด์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค .
internal ํ‘œ์‹œํ•˜์ง€ ์•Š์€ ๋‚ด ์ž˜๋ชป์ž…๋‹ˆ๋‹ค. ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ํ•„์š”ํ•œ ๋ชจ๋“  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์•„๋‹ˆ์š” InstagramLoginWebViewProtocol . ์•„๋‹ˆ์š” InstagramLoginWebViewDelegate .

import UIKit
import SwiftyInsta

class LoginViewController: UIViewController {
    var loginWebView: InstagramLoginWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        loginWebView = InstagramLoginWebView(frame: view.frame,
                                             didReachEndOfLoginFlow: nil,
                                             didSuccessfullyLogIn: { [weak self] in
                                                // I would actually put this in `didReachEndOfLoginFlow`,
                                                // so the user never sees their timeline blinking.
                                                DispatchQueue.main.async {
                                                    self?.loginWebView.removeFromSuperview()
                                                }
            },
                                             completionHandler: { sessionCache, _ in
                                                print("Loading user with sessioncache is successful")

                                                do {
                                                    let encoder = JSONEncoder()
                                                    let data = try encoder.encode(sessionCache)

                                                    let sessionCacheString = String(data: data, encoding: .utf8)
                                                } catch {
                                                    print("Whoops, an error occured: \(error)")
                                                }
        })
        view.addSubview(loginWebView)
        loginWebView.loadInstagramLogin()
    }
}

@sbertix ๋ชจ๋“  ๋„์›€์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ์ธ์Šคํƒ€๊ทธ๋žจ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š๊ณ  ํ™”๋ฉด์— ํฐ์ƒ‰ ํŽ˜์ด์ง€๋งŒ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ํ•„์š”ํ•œ ๋ชจ๋“  ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์•„๋‹ˆ์š” InstagramLoginWebViewProtocol . ์•„๋‹ˆ์š” InstagramLoginWebViewDelegate .

@effecttwins ๋Š” ์œ„์—์„œ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ์Šต๋‹ˆ๋‹ค. loadInstagramLogin() ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์‹ญ์‹œ์˜ค.
๋ง ๊ทธ๋Œ€๋กœ ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค ๐Ÿ˜Š
ํšจ๊ณผ๊ฐ€์žˆ๋‹ค

๋„ค, ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ๋ณต์‚ฌํ•˜์—ฌ ๋ถ™์—ฌ๋„ฃ๊ณ  loadInstagramLogin์„ ํ˜ธ์ถœํ–ˆ์Šต๋‹ˆ๋‹ค. :)
InstagramLoginWebViewProtocol ๋ฐ InstagramLoginWebViewDelegate๋„ ์‚ญ์ œ๋จ

์ฝ˜์†”์—์„œ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ํ”„๋กœ์ ํŠธ์™€ ๊ด€๋ จ์ด ์žˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
์„œ๋น„์Šค com.apple.WebKit.Networking์— ์‹ ํ˜ธ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์—†์Œ: 113: ์ง€์ •๋œ ์„œ๋น„์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ๋‚ด ์ฝ”๋“œ์—์„œ loginWebView ๋ฅผ view ์— ์ถ”๊ฐ€ํ•œ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค.
์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ํ™•์ธํ•˜์‹ญ์‹œ์˜ค. ์ด์ œ @effecttwins์—์„œ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹น์‹ ์€ ๊ต‰์žฅํ•ฉ๋‹ˆ๋‹ค @sbertix
์ง€๊ธˆ ์ž‘๋™ ์ค‘์ž…๋‹ˆ๋‹ค
๋ชจ๋“  ๋„์›€ ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค

์•ˆ๋…•ํ•˜์„ธ์š” @sbertix

MVVM ํŒจํ„ด์„ ๋”ฐ๋ฅด๊ณ  ์žˆ๋Š”๋ฐ ๋‚ด ๋ณด๊ธฐ์™€ ๋‚ด viewController๊ฐ€ ๋‹ค๋ฅธ ํŒŒ์ผ์— ์žˆ์œผ๋ฉด ์–ด๋–ป๊ฒŒ ๋ฉ๋‹ˆ๊นŒ? ๋ณด๊ธฐ๋Š” ๋ณด๊ธฐ ์ปจํŠธ๋กค๋Ÿฌ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. InstagramLoginWebView ์ดˆ๊ธฐํ™” ํ›„ didSuccessfullyLogIn ๊ธฐ๋Šฅ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๊นŒ?

๋‹น์‹ ์€ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค init InstagramLoginWebView ์™€ didSuccessfullyLogIn (์ด๊ฒƒ์€ ์„ ํƒ ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค).
๊ทธ๋ฆฌ๊ณ  ๋‹ค๋ฅธ _closures_๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋„: ์ผ๋‹จ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉด ๋‹จ์ˆœํžˆ ๋‹ค๋ฅธ ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
@anonrig

์—ฌ์ญค๋ด๋„ ๋ ๊นŒ์š”? ์˜ˆ๋ฅผ ๋“ค์–ด์ฃผ์‹ค ์ˆ˜ ์žˆ๋‚˜์š”? @sbertix :)

ํ™•์‹ ํ•˜๋Š”. ์ฝ”๋“œ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ? ์˜ˆ๋ฅผ ๋“ค์–ด ๋ชจ๋ธ๊ณผ ๋ทฐ๊ฐ€ ์„ค์ •๋˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๊ทธ๋ž˜ ๋ฌผ๋ก !

1.7.x ์ด์ „์—๋Š” delegates ์‚ฌ์šฉํ•˜์—ฌ ViewController ์ˆ˜์ค€์—์„œ ๋กœ๊ทธ์ธ ์„ฑ๊ณต ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ณด๊ธฐ ๋ณด๊ธฐ:

final class LoginWebView: UIView {
    // MARK: - Properties
    var bag = DisposeBag()

    private(set) lazy var webView: InstagramLoginWebView = InstagramLoginWebView(frame: UIScreen.main.bounds)

    private(set) lazy var closeButtonLabel: UILabel = {
        let label: UILabel = .create(text: "Close".localized(), numberOfLines: 1, textAlignment: .center, textColor: .white, font: .light(size: 16))
        label.isUserInteractionEnabled = true
        return label
    }()

    private(set) lazy var closeButtonBarItem: UIBarButtonItem = UIBarButtonItem(customView: closeButtonLabel)

    // MARK: - Initialization
    init() {
        super.init(frame: .zero)

        [webView].forEach(addSubview(_:))

        webView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }

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

๋ณด๊ธฐ ์ปจํŠธ๋กค๋Ÿฌ ์˜ˆ:

final class LoginWebViewController: UIViewController, View, ErrorDisplayer {
    // MARK: - Properties
    private lazy var viewSource = LoginWebView()

    private(set) var bag: DisposeBag
    private(set) var viewModel: LoginWebViewModel
    private(set) var completionObservable = PublishSubject<(SessionCache?, APIHandlerProtocol?)?>()

    // MARK: - Initialization
    init() {
        bag = DisposeBag()
        viewModel = LoginWebViewModel()

        super.init(nibName: nil, bundle: nil)

        bindErrorHandling()
        observeDatasource()
    }

    // MARK: - Life cycle
    override func loadView() {
        view = viewSource
        view.backgroundColor = .white
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        viewSource.webView.loginDelegate = self
        viewSource.webView.loadInstagramLogin(isNeedPreloadForCookieSync: true)

        // viewSource.webView.didSuccessfullyLogIn
        configureNavBar(with: "Login".localized(), prefersLargeTitle: false)

        navigationItem.rightBarButtonItem = viewSource.closeButtonBarItem
    }

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

LoginWebViewModel ์ด ๋ฌด์—‡์ธ์ง€ ์ •ํ™•ํžˆ ๋ชจ๋ฅด์ง€๋งŒ... ๋ชฉํ‘œ๊ฐ€ ๋ชจ๋“  ๊ฒƒ์„ completeObservable ๋„ฃ๋Š” ๊ฒƒ์ด๋ผ๋ฉด ๊ฐ„๋‹จํžˆ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

final class LoginWebView: UIView {
    // MARK: - Properties
    var bag = DisposeBag()

    private(set) lazy var webView: InstagramLoginWebView = InstagramLoginWebView(frame: UIScreen.main.bounds, completionHandler: nil)

    private(set) lazy var closeButtonLabel: UILabel = {
        let label: UILabel = .create(text: "Close".localized(), numberOfLines: 1, textAlignment: .center, textColor: .white, font: .light(size: 16))
        label.isUserInteractionEnabled = true
        return label
    }()

    private(set) lazy var closeButtonBarItem: UIBarButtonItem = UIBarButtonItem(customView: closeButtonLabel)

    // MARK: - Initialization
    init() {
        super.init(frame: .zero)

        [webView].forEach(addSubview(_:))

        webView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
final class LoginWebViewController: UIViewController, View, ErrorDisplayer {
    // MARK: - Properties
    private lazy var viewSource = LoginWebView()

    private(set) var bag: DisposeBag
    private(set) var viewModel: LoginWebViewModel
    private(set) var completionObservable = PublishSubject<(SessionCache?, APIHandlerProtocol?)?>()

    // MARK: - Initialization
    init() {
        bag = DisposeBag()
        viewModel = LoginWebViewModel()

        super.init(nibName: nil, bundle: nil)

        bindErrorHandling()
        observeDatasource()
    }

    // MARK: - Life cycle
    override func loadView() {
        view = viewSource
        view.backgroundColor = .white
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        viewSource.webView.completionHandler = { [weak self] cache, handler in
            self?.completionObservable.accept((cache, handler))
        }
        viewSource.webView.loadInstagramLogin()

        // viewSource.webView.didSuccessfullyLogIn
        configureNavBar(with: "Login".localized(), prefersLargeTitle: false)

        navigationItem.rightBarButtonItem = viewSource.closeButtonBarItem
    }

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

InstagramLoginWebView init(frame:) ๋ฅผ init(frame:completionHandler:) ๋กœ ๋ณ€๊ฒฝํ•œ ๋‹ค์Œ completionHandler ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜์—ฌ ์ฃผ์ œ์— ๊ฐ’์„ ํ‘ธ์‹œํ–ˆ์Šต๋‹ˆ๋‹ค.

@sbertix ๊ฐ์‚ฌ

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰