iOS/Swift

[Swift] ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ํ•ด๊ฒฐํ•ด๋ณด์ž(feat.map)

๊ฒฝํ‘ธ 2024. 2. 20. 14:00
๋ฐ˜์‘ํ˜•

๊ฐœ๋ฐœ ์ค‘์ธ ์•ฑ์—์„œ MVP๋ฅผ ์œ„ํ•œ ๊ธฐ๋Šฅ ๊ตฌํ˜„์„ ํ•˜๋˜ ์ค‘์— ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ํ™•์ธํ•œ ์ ์ด ์žˆ๋‹ค.

๋‹น์‹œ์—๋Š” ๋น ๋ฅธ ๊ฐœ๋ฐœ์ด ํ•„์š”ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์—

์ •ํ•ด์ง„ ์ผ์ •์„ ๋งˆ์น˜๊ณ  ํ•ด๊ฒฐํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.

์•ž์œผ๋กœ๋Š” ๋ฐ”๋กœ ํ•  ๊ฑฐ๋‹ค. ์ƒ๊ฐ๋ณด๋‹ค ์˜ค๋ž˜ ๊ฑธ๋ ธ๋‹ค : (

 

์•„๋ฌดํŠผ

๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„๋ฅผ ๋ณด๋ฉด์„œ ์ด๋Ÿฐ์ €๋Ÿฐ ์ƒํ™ฉ์„ ํ…Œ์ŠคํŠธ๋ฅผ ํ•ด๋ณธ ๊ฒฐ๊ณผ

Coordinator์—์„œ ์ƒ์„ฑ๋œ ViewController์™€ ViewModel, ์•Œ๋ฆผ ํ™”๋ฉด ๋“ฑ

๋ฉ”๋ชจ๋ฆฌ์—์„œ deinit ๋˜์ง€ ์•Š๋Š” ์ด์Šˆ๋กœ ํ™•์ธ๋๋‹ค.

 

์ด์ƒํ•˜๋‹ค.

์ฝ”๋“œ๋ฅผ ์—ฌ๊ธฐ์ €๊ธฐ ๋œฏ์–ด๋ด๋„

์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ์บก์ณํ•ด ์ž˜ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด ์‚ด์•„์žˆ์„ ๋งŒํ•œ ๋…€์„์ด ๋ณด์ด์ง€ ์•Š์•˜๋‹ค.

 

Profile Instrument๋ฅผ ์ด์šฉํ•ด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ–ˆ์ง€๋งŒ ์ •์ƒ๋™์ž‘ ์ค‘์ด๋ผ๊ณ  ํ‘œ์‹œ๋˜๊ณ  ์žˆ์—ˆ๋‹ค.

์ฝ”๋“œ๋ฅผ ์–ด๋–ป๊ฒŒ ์ž‘์„ฑํ–ˆ๊ธธ๋ž˜ Profile Instrument์—์„œ๋Š” ์ •์ƒ์ด๋ผ๊ณ  ํ‘œ์‹œ๋˜๋Š”์ง€ ์–ด์ด๊ฐ€ ์—†๋‹ค.

 

๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„๋ฅผ ์‚ดํŽด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋˜๊ณ  ์žˆ๋Š” ์ง€์ ์„ ์ฐพ์•˜๋‹ค. ( ์บก์ฒ˜ํ•ด๋’€์–ด์•ผ ํ–ˆ๋Š”๋ฐ ์ด๋ฏธ์ง€๊ฐ€ ์—†๋‹ค ๐Ÿ™ƒ )

RxSwift.๋ธ”๋ผ๋ธ”๋ผ ํ•˜๋Š” ๊ณณ์—์„œ ๊ฐ•ํ•œ ์ฐธ์กฐ๊ฐ€ ๋ฐœ์ƒํ•ด

ViewController, ViewModel ๋“ฑ์ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜์ง€ ์•Š๊ณ  ์žˆ์—ˆ๋‹ค. 

[ ์ฐธ๊ณ  ์‚ฌํ•ญ ]

- ๋ฉ”๋ชจ๋ฆฌ ๊ทธ๋ž˜ํ”„์—์„œ ๊ตต์€ ์„ ์€ ๊ฐ•ํ•œ ์ฐธ์กฐ๋ฅผ, ๊ฐ€๋Š” ์„ ์€ ๊ฐ•ํ•œ ์ฐธ์กฐ ํ˜น์€ ์•ฝํ•œ ์ฐธ์กฐ์ž„์„ ์•Œ๋ ค์ค€๋‹ค.

 

ViewModel์„ Input, Output ๋ฐฉ์‹์„ ํ†ตํ•ด์„œ ๊ฐœ๋ฐœํ–ˆ๋Š”๋ฐ

์—ฌ๊ธฐ์„œ Input์— ์ธ์ž๋ฅผ ๋„ฃ์„ ๋•Œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋‹ค.

์•„๋ž˜๋Š” ์˜ˆ์‹œ ์ฝ”๋“œ๋‹ค.

let input = EditProfileViewModel.Input(
            editPhotoSign: ...,
            gender: ...,
            introduceText: ...,
            saveSign: saveButton.rx.tap
                .map { _ in
                return UpdateUserInformation(
                    profilePictrue: ...,
                    nickname: self.nicknameTextField.text ?? ...,
                    gender: self.womanButton.getStatus(),
                    bio: self.introduceTextField.text ?? ...)
            }.asSignal(onErrorJustReturn: nil))

๋ณด์ด๋Š” ๊ฒƒ์ฒ˜๋Ÿผ map์„ ํ†ตํ•ด์„œ UpdateUserInformation์„ ๋„˜๊ฒจ์ฃผ๊ณ  ์žˆ๋‹ค.

์—ฌ๊ธฐ์„œ self ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ–ˆ๊ณ , ์ด๋ฅผ ์•ฝํ•œ ์ฐธ์กฐ๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ–ˆ๋‹ค. 

์ฆ‰, ํด๋กœ์ €๋กœ ์บก์ฒ˜ํ•  ๋•Œ, ์ธ์Šคํ„ด์Šค๋ฅผ ์บก์ฒ˜ํ•˜๊ฒŒ ๋˜๋ฉด์„œ ํด๋กœ์ €์™€ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ฐ•ํ•œ ์ฐธ์กฐ ์ˆœํ™˜์„ ๋ฐœ์ƒ์‹œํ‚จ ๊ฒƒ์ด๋‹ค.

let input = EditProfileViewModel.Input(
            editPhotoSign: ...,
            gender: ...,
            introduceText: ...,
            saveSign: saveButton.rx.tap
                .withUnretained(self)
                .map { owner, _ in
                return UpdateUserInformation(
                    profilePictrue: "",
                    nickname: owner.nicknameTextField.text ?? ...,
                    gender: owner.womanButton.getStatus(),
                    bio: owner.introduceTextField.text ?? ...)
            }.asSignal(onErrorJustReturn: nil))

๋”ฐ๋ผ์„œ ์œ„์™€ ๊ฐ™์ด ์ˆ˜์ •ํ–ˆ๋‹ค.

์œ„์™€ ๋น„์Šทํ•œ ์ผ€์ด์Šค๋“ค์ด ์žˆ์–ด ๋ชจ๋‘ ์ˆ˜์ •ํ–ˆ๊ณ , ๋‹คํ–‰ํžˆ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ์–ด๋Š ์ •๋„ ํ•ด๊ฒฐ์ด ๋˜์—ˆ๋‹ค.

 

์•„์ง๋„ ์–ด๋””์„ ๊ฐ€ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ๋‹ค.

์•ž์„œ ๋งํ–ˆ๋“ฏ์ด ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ๋˜ ๊ณณ์€ ViewController์™€ ViewModel ๋ง๊ณ ๋„ Alert๋‚˜ ์ปค์Šคํ…€ํ•œ ์•Œ๋ฆผ ํ™”๋ฉด์ด ์žˆ์—ˆ๋‹ค.

Coordinator ๋‚ด๋ถ€์—์„œ ์ƒ์„ฑ๋œ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๊ณ , ํ™”๋ฉด์—์„œ ์‚ฌ๋ผ์งˆ ๋•Œ, ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œ๋˜์–ด์•ผ ํ•˜์ง€๋งŒ ๊ทธ๋ ‡์ง€ ์•Š์•˜๋‹ค.

Coordinator์—์„œ ํ”„๋กœํ•„ ํ™”๋ฉด์„ ๋ณด์—ฌ์ฃผ๋Š” ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž.

func showProfileBottomSheet(nickname: String) {
    let viewController = getProfileBottomSheet(nickname: nickname)
    viewController.modalPresentationStyle = .overFullScreen

    viewController.getViewTapGesture().subscribe(onNext: { [weak self] _ in
        self?.navigationController.unsetBackground()
        viewController.dismiss(animated: true)
    })
    .disposed(by: disposeBag)

    navigationController.setBackground()
    navigationController.present(viewController, animated: true)
}

ํ”„๋กœํ•„ ํ™”๋ฉด ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑ ๋ฐ ํ• ๋‹นํ•˜๊ณ  ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ์ฝ”๋“œ๋‹ค.

ํ™”๋ฉด ์™ธ๋ถ€๋ฅผ ํด๋ฆญํ–ˆ์„ ๊ฒฝ์šฐ, ํ˜„์žฌ NavigationController์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ปฌ๋Ÿฌ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ subscribe ์•ˆ์— ์ž‘์„ฑ๋˜์–ด ์žˆ๋‹ค.

 

์œ„์˜ ์ฝ”๋“œ์˜ ๋ฌธ์ œ์ ์€ ๋ญ˜๊นŒ.

weak๋ฅผ ํ†ตํ•ด self(Coordinator)๋ฅผ ์ฐธ์กฐํ•ด ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด์„œ ๋ฌธ์ œ๊ฐ€ ๋  ๊ฑฐ๋ผ๊ณ ๋Š” ์ƒ๊ฐ ๋ชปํ–ˆ์—ˆ๋‹ค. 

๋ฌธ์ œ๋Š” ํ”„๋กœํ•„ ํ™”๋ฉด์˜ ์ธ์Šคํ„ด์Šค์˜€๋‹ค.

getProfileBottomSheet ๋‚ด๋ถ€ ์ฝ”๋“œ์—๋Š” self ํ‚ค์›Œ๋“œ๊ฐ€ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๊ธฐ๋„ ํ•˜๊ณ 

subscribe ํด๋กœ์ € ๋‚ด๋ถ€์— ๊ฐ•ํ•œ ์ฐธ์กฐ์˜ ํ˜•ํƒœ๋กœ dismiss ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์•ผ ํ–ˆ๋‹ค.

func showProfileBottomSheet(nickname: String) {
    let viewController = getProfileBottomSheet(nickname: nickname)
    viewController.modalPresentationStyle = .overFullScreen

    viewController.getViewTapGesture().subscribe(onNext: { [weak self, weak viewController] _ in
        self?.navigationController.unsetBackground()
        viewController?.dismiss(animated: true)
    })
    .disposed(by: disposeBag)

    navigationController.setBackground()
    navigationController.present(viewController, animated: true)
}

์œ„์˜ ๋‘ ๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ ์ˆ˜์ •ํ•œ ๋‹ค์Œ, ๋™์ผํ•œ ๋™์ž‘์„ ํ–ˆ์„ ๋•Œ์˜ ๋ฉ”๋ชจ๋ฆฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

 

 

 

ํ .. ์ œ๋Œ€๋กœ ์ˆ˜์ •๋œ ๊ฒƒ ๊ฐ™์€๋ฐ

์•„์ง ํ•œ ๊ฐ€์ง€ ์ฐ์ฐํ•œ ๊ฒŒ ์žˆ๋‹ค. ์›๋ž˜ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์ €๋ ‡๊ฒŒ ๋†’์•˜๋‚˜ ์‹ถ๋‹ค.

๋ญ”๊ฐ€ ์ˆ˜์ •ํ•  ๊ฒŒ ์žˆ์œผ๋ฉด ์ˆ˜์ •ํ•˜๊ณ  ํฌ์ŠคํŒ…ํ•ด์•ผ๊ฒ ๋‹ค.

์•„๋ฌด์ชผ๋ก ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ํ•ด๊ฒฐ ์™„๋ฃŒ.

 

์ถœ์‹œ ์ดํ›„์— ํ™๋ณด์ฐจ ๊ธ€ ์ž‘์„ฑํ•˜๋Ÿฌ ์˜ค๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์ด๋งŒ ๐Ÿ‘‹๐Ÿป๐Ÿ‘‹๐Ÿป๐Ÿ‘‹๐Ÿป

๋ฐ˜์‘ํ˜•