ReactorKit?
- ReactorKit is a framework for a reactive and unidirectional Swift application architecture.
ReactorKit ์ฅ์
- ๋ทฐ์ ๋ฆฌ์กํฐ๋ก UI์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ถ๋ฆฌ
- ๋ชจ๋๊ฐ ๊ฒฐํฉ๋๊ฐ ๋ฎ์์ง๊ณ ํ ์คํธํ๊ธฐ ์ฝ๋ค.
- ์ํ๊ด๋ฆฌ ์ฉ์ด : API๋ฅผ ํตํด ์ฑ์์ ์ฐ์์ ์ธ ํ์ด์ง ๋ก๋ํ ๋ ์ด์ ํ์ด์ง๋ฅผ ๊ธฐ๋กํด๋์์ผ ํ๋ฏ์ด, ์ด์ ํ์ด์ง๋ฅผ ๊ธฐ๋กํ๋ ์ํ ์ปดํฌ๋ํธ๊ฐ ๋ฐ๋ก ๊ด๋ฆฌ๋๊ฒ๋ ์ค๊ณ๋ ๊ตฌ์กฐ
Unidirectional Data Flow
1. View๋ Action(์ฌ์ฉ์ ์ ๋ ฅ ๋ฑ)์ Reactor์๊ฒ ์ ๋ฌํ๋ค
2. Reactor๋ ์ ๋ฌ๋ฐ์ Action์ ๋ฐ๋ผ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ๋ค.
2-1. mutate()
- Action ์คํธ๋ฆผ์ Mutation ์คํธ๋ฆผ์ผ๋ก ๋ณํํ๋ ์ญํ
- action์ด state๋ฅผ ๋ฐ๋ก ๋ณ๊ฒฝํ์ง๋ ์์ (ex : ์ฌ์ฉ์๋ฅผ ํ๋ก์ฐํ๋ api ์์ฒญ ์ดํ state ๋ณ๊ฒฝ) -> action๊ณผ state ์ฌ์ด์ mutation์ ๋ฌ์ ๋น๋๊ธฐ ์ฒ๋ฆฌ
- ์ด๊ณณ์์ ๋คํธ์ํน์ด๋ ๋น๋๊ธฐ ๋ก์ง ๋ฑ์ ์ฌ์ด๋ ์ดํํธ๋ฅผ ์ฒ๋ฆฌํ๋ค.
- ๊ทธ ๊ฒฐ๊ณผ๋ก Mutation์ ๋ฐฉ์ถํ๋๋ฐ, ๊ทธ ๊ฐ์ด reduce() ํจ์๋ก ์ ๋ฌ๋๋ค.
2-2. reduce()
- ์ด์ ์ํ์ Mutation์ ๋ฐ์์ ๋ค์ ์ํ๋ฅผ ๋ฐํํ๋ค.
3. ๊ทธ ํ Reactor๋ ์ํ๋ฅผ ๋ณ๊ฒฝํ์ฌ View์๊ฒ ์ ๋ฌํ๋ค
View
- ๋ทฐ๋ ์ํ๋ฅผ ํํ, ๋ทฐ ์ปจํธ๋กค๋ฌ๋ ์ ๋ ๋ชจ๋ ๋ทฐ์ ํด๋น.
- View ํ๋กํ ์ฝ์ ์ค์ํ๋ฉด DisposeBag ์์ฑ๊ณผ bind(reactor:) ๋ฉ์๋๋ฅผ ํ์๋ก ์ ์ํจ์ผ๋ก์จ ๋ทฐ๋ฅผ ์ ์
- bind()๋ฉ์๋๋ฅผ ํตํด ๋ทฐ์ ๋ฆฌ์ํฐ ์ฌ์ด์ ์ก์ ์คํธ๋ฆผ๊ณผ ์ํ ์คํธ๋ฆผ์ ๋ฐ์ธ๋ฉ
Reactor
- ๋ฆฌ์กํฐ๋ ๋ทฐ์ ์ํ๋ฅผ ๊ด๋ฆฌ.
- ๋ทฐ์์ ์ ๋ฌ๋ฐ์ action์ ๋ฐ๋ผ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ ๋ค ์ํ๋ฅผ ๋ณ๊ฒฝํ์ฌ ๋ค์ ๋ทฐ์ ์ ๋ฌ.
- Reactor ํ๋กํ ์ฝ์ ์ค์ํ๋ฉด, Action, Mutation, State, initialState ํ๋กํผํฐ ์ ์
- Action : represent user actions
- Mutation : represent state changes
- State : represents the current view state
- mutate() : mutate() receives an Action and generates an Observable<Mutation>
- reduce() : reduce() generates a new State from a previous State and a Mutation.
View Testing
func testAction_refresh() {
// 1. Stub ๋ฆฌ์กํฐ๋ฅผ ์ค๋นํฉ๋๋ค.
let reactor = MyReactor()
reactor.stub.isEnabled = true
// 2. Stub๋ ๋ฆฌ์กํฐ๋ฅผ ์ฃผ์
ํ ๋ทฐ๋ฅผ ์ค๋นํฉ๋๋ค.
let view = MyView()
view.reactor = reactor
// 3. ์ฌ์ฉ์ ์ธํฐ๋์
์ ๋ฐ์์ํต๋๋ค.
view.refreshControl.sendActions(for: .valueChanged)
// 4. Reactor์ ์ก์
์ด ์ ์ ๋ฌ๋์๋์ง๋ฅผ ๊ฒ์ฆํฉ๋๋ค.
XCTAssertEqual(reactor.stub.actions.last, .refresh)
}
func testState_isLoading() {
// 1. Stub ๋ฆฌ์กํฐ๋ฅผ ์ค๋นํฉ๋๋ค.
let reactor = MyReactor()
reactor.stub.isEnabled = true
// 2. Stub๋ ๋ฆฌ์กํฐ๋ฅผ ์ฃผ์
ํ ๋ทฐ๋ฅผ ์ค๋นํฉ๋๋ค.
let view = MyView()
view.reactor = reactor
// 3. ๋ฆฌ์กํฐ์ ์ํ๋ฅผ ์์๋ก ์ค์ ํฉ๋๋ค.
reactor.stub.state.value = MyReactor.State(isLoading: true)
// 4. ๊ทธ ๋ ๋ทฐ ์ปดํฌ๋ํธ์ ์์ฑ์ด ์ ๋ณํ๋์ง๋ฅผ ๊ฒ์ฆํฉ๋๋ค.
XCTAssertEqual(view.activityIndicator.isAnimating, true)
}
- ์ฌ์ฉ์ ์ธํฐ๋์ ์ด ๋ฐ์ํ์ ๋ Action์ด ๋ฆฌ์กํฐ๋ก ์ ์ ๋ฌ๋๋์ง
- ๋ฆฌ์กํฐ์ ์ํ๊ฐ ๋ฐ๋์์ ๋ ๋ทฐ์ ์ปดํฌ๋ํธ ์์ฑ์ด ์ ๋ณ๊ฒฝ๋๋์ง
- ๋ฆฌ์กํฐ์ stub ๊ธฐ๋ฅ์ ์ด์ฉํ๋ฉด ๋ฆฌ์กํฐ๊ฐ ๋ฐ์ Action์ ๋ชจ๋ ๊ธฐ๋กํ๊ณ , mutate()์ reduce()๋ฅผ ์คํํ๋ ๋์ ์ธ๋ถ์์ ์ํ๋ฅผ ์ค์
Reactor Testing
func testBookmark() {
// 1. ๋ฆฌ์กํฐ๋ฅผ ์ค๋นํฉ๋๋ค.
let reactor = MyReactor()
// 2. ๋ฆฌ์กํฐ์ ์ก์
์ ์ ๋ฌํฉ๋๋ค.
reactor.action.onNext(.toggleBookmarked)
// 3. ๋ฆฌ์กํฐ์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋์ง๋ฅผ ๊ฒ์ฆํฉ๋๋ค.
XCTAssertEqual(reactor.currentState.isBookmarked, true)
}
func testUnbookmark() {
// 1. ๋ฆฌ์กํฐ๋ฅผ ์ค๋นํฉ๋๋ค. ์ก์
์ ๋ฏธ๋ฆฌ ํ ๋ฒ ์ ๋ฌํด์ ํ
์คํธ ํ๊ฒฝ์ ๋ง๋ค์ด๋ก๋๋ค.
let reactor = MyReactor()
reactor.action.onNext(.toggleBookmarked)
// 2. ๋ฆฌ์กํฐ์ ์ก์
์ ํ ๋ฒ ๋ ์ ๋ฌํฉ๋๋ค.
reactor.action.onNext(.toggleBookmarked)
// 3. ๋ฆฌ์กํฐ์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋์ง๋ฅผ ๊ฒ์ฆํฉ๋๋ค.
XCTAssertEqual(reactor.currentState.isBookmarked, false)
}
- Action์ ๋ฐ์์ ๋ ์ํ๋ State๋ก ์ ๋ณ๊ฒฝ๋๋์ง
์ฐธ๊ณ ์๋ฃ
https://velog.io/@sso0022/iOS-ReactorKit-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0
https://www.slideshare.net/devxoul/reactorkit/1
https://eunjin3786.tistory.com/100
https://github.com/ReactorKit/ReactorKit
https://ios-development.tistory.com/783
https://medium.com/styleshare/reactorkit-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-c7b52fbb131a
'iOS ๐ > iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Pixel, PT, PPI (0) | 2022.03.16 |
---|---|
Deep Link : URI Scheme vs Universal Link (0) | 2022.03.16 |
iOS ๋ฉ๋ชจ๋ฆฌ ๊ตฌ์กฐ (0) | 2022.03.04 |
iOS File System = SandBox ๊ตฌ์กฐ (0) | 2022.03.04 |
Frame-base layout vs Auto layout (0) | 2022.03.02 |