๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
IOS๐ŸŽ/์•„ํ‚คํ…์ณ

[์•„ํ‚คํ…์ณ] RiBs tutorial1 ๋”ฐ๋ผํ•˜๊ธฐ

by Jouureee 2021. 11. 13.

RIBs๋ž€ ์šฐ๋ฒ„์—์„œ ๋งŒ๋“  cross ์•„ํ‚คํ…์ณ ํŒจํ„ด์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค

https://github.com/uber/RIBs

 

GitHub - uber/RIBs: Uber's cross-platform mobile architecture framework.

Uber's cross-platform mobile architecture framework. - GitHub - uber/RIBs: Uber's cross-platform mobile architecture framework.

github.com

 

rib ์ด๋ฆ„์—์„œ ์•Œ ์ˆ˜ ์žˆ๋“ฏ์ด 

router, interactor, builder ์ด ์„ธ๊ฐ€์ง€๊ฐ€ ํ•ต์‹ฌ์ด๊ตฌ ์ด๋“ค์„ ์ฐจ์ฐจ ์‚ดํŽด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค !

 

๊ทธ์ค‘ ์˜ค๋Š˜์€ tutorial1์„ ์šฐ๋ฒ„์—์„œ ์ œ๊ณตํ•œ wiki์™€ ํ•จ๊ป˜ ์ž์„ธํžˆ ๋”ฐ๋ผ ํ•ด๋ณผ๊ฑฐ์—์š”

https://github.com/uber/RIBs/wiki/iOS-Tutorial-1

 

ํŒŒ์ผ์„ ์—ด๋ฉด LoggedOut, Root ํด๋”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค

 

์•ฑ์ด ์‹œ์ž‘๋˜๋ฉด AppDelegate๊ฐ€ ๋ฃจํŠธ RIB๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ์ œ์–ด๋ฅผ ๋ฃจํŠธ RIB๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

์ด ๋ถ€๋ถ„์€ ์—๋Ÿฌ๊ฐ€ ๋‚˜์„œ 

์ด๋ ‡๊ฒŒ ๋น„์Šทํ•œ ํ•จ์ˆ˜ ์ฐพ์•„ ๋Œ€์ฒดํ•ด ์ฃผ์—ˆ์–ด์š” !

 
๋ฃจํŠธ RIB์˜ ๋ชฉ์ ์€ RIB ํŠธ๋ฆฌ์˜ ๋ฃจํŠธ ์—ญํ• ์„ ํ•˜๊ณ  ํ•„์š”ํ•  ๋•Œ ์ž์‹์—๊ฒŒ ์ œ์–ด๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

 

์šฐ์„  LoggedOut ํด๋”์— new File ํด๋ฆญ

์•„๋ž˜๋กœ ๋‚ด๋ ค RIBs ์„ ํƒํ•œ ๋’ค

 

์ด๋ฆ„์„ LoggedOut์ด๋ผ ํ•˜๊ณ  Owns corresponding view ์ฒดํฌ ๋ฐ•์Šค ํด๋ฆญํ•ด์„œ ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด์ค๋‹ˆ๋‹ค. 

๊ทธ๋ฆฌ๊ณ  ์ด์ œ DELETE_Me ํŒŒ์ผ์€ ์‚ญ์ œํ•ด์ค๋‹ˆ๋‹ค.

 

ํ›„ํ›„ ์ด์ œ ์ƒ์„ฑ๋œ ํŒŒ์ผ๋“ค์„ ์ดํ•ดํ•ด๋ด…์‹œ๋‹คใ…

LoggedOutBuilder 

์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด LoggedOutBuildable protocol์„ ์ค€์ˆ˜ํ•˜๊ณ  ์žˆ๋Š”๋ฐ ์—ฌ๊ธฐ์—” build๋ผ๋Š” ํ•จ์ˆ˜๊ฐ€ ์ •์˜๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. 

build ํ•จ์ˆ˜ ๋‚ด์—์„œ๋Š” ~Component, ViewController, Interactor ๋“ฑ๋“ฑ ์•„์ง์€ ์•Œ ์ˆ˜ ์—†๋Š” ์š”์†Œ๋“ค์„ ์ƒ์„ฑํ•˜๊ณ  ์žˆ๋„ค์š” ํ  .. ์ผ๋‹จ ์—ฌ๊ธฐ์„œ ๋„˜์–ด๊ฐ€๊ณ  ๋‹ค์Œ !!

 

LoggedOutInteractor

'Brain of the app' ์ด๋ผ๊ณ  ์†Œ๊ฐœํ•˜๊ณ  ์žˆ๋„ค์š” ์•ฑ์˜ ํ•ต์‹ฌ์ธ๊ฐ€๋ด…๋‹ˆ๋‹ค.

๋‹ค๋ฅธ RIBs๋กœ ์ „ํ™˜ํ•˜๊ธฐ๋„ ํ•˜๊ณ ,  view controller์˜ model์„ ๋ณด์—ฌ์ฃผ๊ธฐ๋„ ํ•œ๋‹ค๊ณ  ํ•˜๋„ค์š” ์ผ๋‹จ ๋˜ ๋„˜์–ด๊ฐ€ ๋ด…์‹œ๋‹ค ..

 

LoggedOutRouter

Interactor์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•ด LoggedOutInteractable์—์„œ ํ•„์š”ํ•œ ๊ฒƒ์„ ์„ ์–ธํ•˜๊ณ , LoggedOutViewControllable์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์™€ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค

LoggedOutViewController

LoggedOutPresentableListener๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค 

์ด์ œ loggedOut UI๋ฅผ ๋งŒ๋“ค๋Ÿฌ ๋„˜์–ด๊ฐ€๋ด…์‹œ๋‹ค !!

์นœ์ ˆํ•˜๊ฒŒ๋„ loggedoutViewController์— ๋„ฃ์„ ์ฝ”๋“œ๋ฅผ ์ œ๊ณตํ•ด์คฌ์–ด์š” ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ

listener ๋ฐ‘์— ๋„ฃ์–ด์ค๋‹ˆ๋‹ค

 

override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = UIColor.white
        let playerFields = buildPlayerFields()
        buildLoginButton(withPlayer1Field: playerFields.player1Field, player2Field: playerFields.player2Field)
    }

    // MARK: - Private

    private var player1Field: UITextField?
    private var player2Field: UITextField?

    private func buildPlayerFields() -> (player1Field: UITextField, player2Field: UITextField) {
        let player1Field = UITextField()
        self.player1Field = player1Field
        player1Field.borderStyle = UITextBorderStyle.line
        view.addSubview(player1Field)
        player1Field.placeholder = "Player 1 name"
        player1Field.snp.makeConstraints { (maker: ConstraintMaker) in
            maker.top.equalTo(self.view).offset(100)
            maker.leading.trailing.equalTo(self.view).inset(40)
            maker.height.equalTo(40)
        }

        let player2Field = UITextField()
        self.player2Field = player2Field
        player2Field.borderStyle = UITextBorderStyle.line
        view.addSubview(player2Field)
        player2Field.placeholder = "Player 2 name"
        player2Field.snp.makeConstraints { (maker: ConstraintMaker) in
            maker.top.equalTo(player1Field.snp.bottom).offset(20)
            maker.left.right.height.equalTo(player1Field)
        }

        return (player1Field, player2Field)
    }

    private func buildLoginButton(withPlayer1Field player1Field: UITextField, player2Field: UITextField) {
        let loginButton = UIButton()
        view.addSubview(loginButton)
        loginButton.snp.makeConstraints { (maker: ConstraintMaker) in
            maker.top.equalTo(player2Field.snp.bottom).offset(20)
            maker.left.right.height.equalTo(player1Field)
        }
        loginButton.setTitle("Login", for: .normal)
        loginButton.setTitleColor(UIColor.white, for: .normal)
        loginButton.backgroundColor = UIColor.black
        loginButton.addTarget(self, action: #selector(didTapLoginButton), for: .touchUpInside)
    }

    @objc private func didTapLoginButton() {

    }

 

๊ทธ๋ฆฌ๊ณ  build ํ•ด๋ณด๋ฉด ?

์œ„์˜ ํ™”๋ฉด๊ณผ ๊ฐ™์ด ์ž˜ ๋‚˜์˜ค๋„ค์š” !

 

์ด์ œ๋Š” ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„๋•Œ์˜ ๋กœ์ง์— ๋Œ€ํ•ด ์ƒ๊ฐํ•ด๋ด…์‹œ๋‹ค.

login button์„ ํด๋ฆญํ•˜๋ฉด 

LoggedOutViewController๋Š” ๋ฆฌ์Šค๋„ˆ(LoggedOutPresentableListener)๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ? ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ์ง„ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ๊ฒŒ์ž„์— ์ฐธ์—ฌํ•˜๋Š” ํ”Œ๋ ˆ์ด์–ด์˜ ์ด๋ฆ„์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„œ ๋ทฐ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋กœ๊ทธ์ธ ์š”์ฒญ์„ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฆฌ์Šค๋„ˆ๋ฅผ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 

 

LoggedOutPresentableListener ํ”„๋กœํ† ์ฝœ์— ์•„๋ž˜  ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด ์ค๋‹ˆ๋‹ค.

protocol LoggedOutPresentableListener: AnyObject {
    // TODO: Declare properties and methods that the view controller can invoke to perform
    // business logic, such as signIn(). This protocol is implemented by the corresponding
    // interactor class.
    func login(withPlayer1Name player1Name: String?, player2Name: String?)
}

login player์˜ name์ด ์˜ต์…”๋„๋กœ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์„์ˆ˜๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์ฃ  !

 

๋”ฐ๋ผ์„œ LoggedOutPresentableListener์„ ์ค€์ˆ˜ํ•˜๋Š” ํŒŒ์ผ์„ ์ฐพ์•„๊ฐ€๋ณด๋ฉด ?

LoggedOutInteractor๊ฐ€ ์žˆ๋„ค์š”  !! login ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.

func login(withPlayer1Name player1Name: String?, player2Name: String?) {
        let player1NameWithDefault = playerName(player1Name, withDefaultName: "Player 1")
        let player2NameWithDefault = playerName(player2Name, withDefaultName: "Player 2")

        print("\(player1NameWithDefault) vs \(player2NameWithDefault)")
    }
    
    private func playerName(_ name: String?, withDefaultName defaultName: String) -> String {
        if let name = name {
            return name.isEmpty ? defaultName : name
        } else {
            return defaultName
        }
    }

 

๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ login ํด๋ฆญํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๋Œ์•„๊ฐ€ 

 

@objc private func didTapLoginButton() {
            listener?.login(withPlayer1Name: player1Field?.text, player2Name: player2Field?.text)
        }

listener๋ฅผ ์—ฐ๊ฒฐํ•ด์ค๋‹ˆ๋‹ค.

 

๋‹ค์‹œ ๋นŒ๋“œํ•ด๋ณผ๊นŒ์š” ?

์ด๋ ‡๊ฒŒ ํŠœํ† ๋ฆฌ์–ผ 1์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค ใ…Žใ…Ž

๋ญ”๊ฐ€ RIBs๋ฅผ  ์ด๋ฃจ๊ณ  ์žˆ๋Š” ๋ผˆ๋Œ€์™€ ์ด๋ฆ„์— ๋”ฐ๋ฅธ ์“ฐ์ž„์ƒˆ๋ฅผ ์•Œ๊ฒƒ ๊ฐ™๊ธฐ๋„ ํ•˜๊ณ  ์•„์ง์€ ๊ตฌ์กฐ๊ฐ€ ์–ฝํ˜€ ์žˆ๋Š” ๋Š๋‚Œ์ด ๋“œ๋„ค์š” .. 

๋‹ค์Œ ํŠœํ† ๋ฆฌ์–ผ์„ ๋ณด๋ฉด์„œ ๋” ์ดํ•ดํ•ด๋ณด๋„๋ก ํ•ด์•ผ๊ฒ ์–ด์š” 

๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ~~

๋Œ“๊ธ€