Skip to content

Commit ccd32f8

Browse files
committed
Updates to latest version of RxFeedback.
1 parent e0e3445 commit ccd32f8

File tree

7 files changed

+522
-133
lines changed

7 files changed

+522
-133
lines changed

RxExample/RxExample.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@
166166
C8C46DAC1B47F7110020D71E /* WikipediaSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8C46DA71B47F7110020D71E /* WikipediaSearchViewController.swift */; };
167167
C8CDF0C11D688DF700C18F99 /* UITableView+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CDF0C01D688DF700C18F99 /* UITableView+Extensions.swift */; };
168168
C8D132151C42B54B00B59FFF /* UIImagePickerController+RxCreate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D132141C42B54B00B59FFF /* UIImagePickerController+RxCreate.swift */; };
169+
C8D3DDE21FB5DB6900BFE7D4 /* Feedbacks.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8D3DDD21FB5DB6900BFE7D4 /* Feedbacks.swift */; };
169170
C8DF92CD1B0B2F84009BCF9A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DF92C81B0B2F84009BCF9A /* AppDelegate.swift */; };
170171
C8DF92E31B0B32DA009BCF9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E01B0B32DA009BCF9A /* LaunchScreen.xib */; };
171172
C8DF92E41B0B32DA009BCF9A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C8DF92E11B0B32DA009BCF9A /* Main.storyboard */; };
@@ -560,6 +561,7 @@
560561
C8C46DA71B47F7110020D71E /* WikipediaSearchViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WikipediaSearchViewController.swift; sourceTree = "<group>"; };
561562
C8CDF0C01D688DF700C18F99 /* UITableView+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableView+Extensions.swift"; sourceTree = "<group>"; };
562563
C8D132141C42B54B00B59FFF /* UIImagePickerController+RxCreate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImagePickerController+RxCreate.swift"; sourceTree = "<group>"; };
564+
C8D3DDD21FB5DB6900BFE7D4 /* Feedbacks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Feedbacks.swift; sourceTree = "<group>"; };
563565
C8DF92C81B0B2F84009BCF9A /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
564566
C8DF92E01B0B32DA009BCF9A /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = "<group>"; };
565567
C8DF92E11B0B32DA009BCF9A /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
@@ -806,6 +808,7 @@
806808
C890A65C1AEC084100AFF7E6 /* ViewController.swift */,
807809
C833670F1AD029AE00C668A7 /* Example.swift */,
808810
C849EF9B1C31A8750048AC4A /* String+URL.swift */,
811+
C8D3DDD21FB5DB6900BFE7D4 /* Feedbacks.swift */,
809812
C84E5BA51E7893A4001F659A /* Observable+Extensions.swift */,
810813
C817728F1E7DF1A600EA679B /* Result.swift */,
811814
C819DAE81ED0DDD50043A770 /* Version.swift */,
@@ -1547,6 +1550,7 @@
15471550
C82FF12C1F93E84600BDB34D /* Changeset.swift in Sources */,
15481551
B1604CB51BE49F8D002E1279 /* DownloadableImage.swift in Sources */,
15491552
075F13101B4E9D5A000D7861 /* APIWrappersViewController.swift in Sources */,
1553+
C8D3DDE21FB5DB6900BFE7D4 /* Feedbacks.swift in Sources */,
15501554
C82FF1251F93E84600BDB34D /* SectionModel.swift in Sources */,
15511555
B18F3BE21BDB2E8F000AAC79 /* ReachabilityService.swift in Sources */,
15521556
B18F3BBC1BD92EC8000AAC79 /* Reachability.swift in Sources */,

RxExample/RxExample/Examples/Calculator/CalculatorViewController.swift

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -39,48 +39,51 @@ class CalculatorViewController: ViewController {
3939
@IBOutlet weak var nineButton: UIButton!
4040

4141
override func viewDidLoad() {
42-
let commands: Observable<CalculatorCommand> = Observable.merge([
43-
allClearButton.rx.tap.map { _ in .clear },
44-
45-
changeSignButton.rx.tap.map { _ in .changeSign },
46-
percentButton.rx.tap.map { _ in .percent },
47-
48-
divideButton.rx.tap.map { _ in .operation(.division) },
49-
multiplyButton.rx.tap.map { _ in .operation(.multiplication) },
50-
minusButton.rx.tap.map { _ in .operation(.subtraction) },
51-
plusButton.rx.tap.map { _ in .operation(.addition) },
52-
53-
equalButton.rx.tap.map { _ in .equal },
54-
55-
dotButton.rx.tap.map { _ in .addDot },
56-
57-
zeroButton.rx.tap.map { _ in .addNumber("0") },
58-
oneButton.rx.tap.map { _ in .addNumber("1") },
59-
twoButton.rx.tap.map { _ in .addNumber("2") },
60-
threeButton.rx.tap.map { _ in .addNumber("3") },
61-
fourButton.rx.tap.map { _ in .addNumber("4") },
62-
fiveButton.rx.tap.map { _ in .addNumber("5") },
63-
sixButton.rx.tap.map { _ in .addNumber("6") },
64-
sevenButton.rx.tap.map { _ in .addNumber("7") },
65-
eightButton.rx.tap.map { _ in .addNumber("8") },
66-
nineButton.rx.tap.map { _ in .addNumber("9") }
67-
])
42+
typealias FeedbackLoop = (ObservableSchedulerContext<CalculatorState>) -> Observable<CalculatorCommand>
43+
44+
let uiFeedback: FeedbackLoop = bind(self) { this, state in
45+
let subscriptions = [
46+
state.map { $0.screen }.bind(to: this.resultLabel.rx.text),
47+
state.map { $0.sign }.bind(to: this.lastSignLabel.rx.text)
48+
]
49+
50+
let events: [Observable<CalculatorCommand>] = [
51+
this.allClearButton.rx.tap.map { _ in .clear },
52+
53+
this.changeSignButton.rx.tap.map { _ in .changeSign },
54+
this.percentButton.rx.tap.map { _ in .percent },
55+
56+
this.divideButton.rx.tap.map { _ in .operation(.division) },
57+
this.multiplyButton.rx.tap.map { _ in .operation(.multiplication) },
58+
this.minusButton.rx.tap.map { _ in .operation(.subtraction) },
59+
this.plusButton.rx.tap.map { _ in .operation(.addition) },
60+
61+
this.equalButton.rx.tap.map { _ in .equal },
62+
63+
this.dotButton.rx.tap.map { _ in .addDot },
64+
65+
this.zeroButton.rx.tap.map { _ in .addNumber("0") },
66+
this.oneButton.rx.tap.map { _ in .addNumber("1") },
67+
this.twoButton.rx.tap.map { _ in .addNumber("2") },
68+
this.threeButton.rx.tap.map { _ in .addNumber("3") },
69+
this.fourButton.rx.tap.map { _ in .addNumber("4") },
70+
this.fiveButton.rx.tap.map { _ in .addNumber("5") },
71+
this.sixButton.rx.tap.map { _ in .addNumber("6") },
72+
this.sevenButton.rx.tap.map { _ in .addNumber("7") },
73+
this.eightButton.rx.tap.map { _ in .addNumber("8") },
74+
this.nineButton.rx.tap.map { _ in .addNumber("9") }
75+
]
76+
77+
return Bindings(subscriptions: subscriptions, events: events)
78+
}
6879

69-
let system = Observable.system(
70-
CalculatorState.initial,
71-
accumulator: CalculatorState.reduce,
80+
Observable.system(
81+
initialState: CalculatorState.initial,
82+
reduce: CalculatorState.reduce,
7283
scheduler: MainScheduler.instance,
73-
feedback: { _ in commands }
84+
scheduledFeedback: uiFeedback
7485
)
75-
.debug("calculator state")
76-
.share(replay: 1)
77-
78-
system.map { $0.screen }
79-
.bind(to: resultLabel.rx.text)
80-
.disposed(by: disposeBag)
81-
82-
system.map { $0.sign }
83-
.bind(to: lastSignLabel.rx.text)
86+
.subscribe()
8487
.disposed(by: disposeBag)
8588
}
8689

RxExample/RxExample/Examples/GitHubSearchRepositories/GitHubSearchRepositories.swift

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -66,51 +66,53 @@ import RxCocoa
6666

6767
*/
6868
func githubSearchRepositories(
69-
searchText: Driver<String>,
70-
loadNextPageTrigger: @escaping (Driver<GitHubSearchRepositoriesState>) -> Driver<()>,
69+
searchText: Signal<String>,
70+
loadNextPageTrigger: @escaping (Driver<GitHubSearchRepositoriesState>) -> Signal<()>,
7171
performSearch: @escaping (URL) -> Observable<SearchRepositoriesResponse>
7272
) -> Driver<GitHubSearchRepositoriesState> {
7373

74-
let searchPerformerFeedback: (Driver<GitHubSearchRepositoriesState>) -> Driver<GitHubCommand> = { state in
74+
let searchPerformerFeedback: (Driver<GitHubSearchRepositoriesState>) -> Signal<GitHubCommand> = { state in
7575
// this is a general pattern how to model a most common feedback loop
7676
// first select part of state describing feedback control
7777
return state.map { (searchText: $0.searchText, shouldLoadNextPage: $0.shouldLoadNextPage, nextURL: $0.nextURL) }
7878
// only propagate changed control values since there could be multiple feedback loops working in parallel
7979
.distinctUntilChanged { $0 == $1 }
8080
// perform feedback loop effects
81-
.flatMapLatest { value -> Driver<GitHubCommand> in
81+
.flatMapLatest { value -> Signal<GitHubCommand> in
8282
if !value.shouldLoadNextPage {
83-
return Driver.empty()
83+
return Signal.empty()
8484
}
8585

8686
if value.searchText.isEmpty {
87-
return Driver.just(GitHubCommand.gitHubResponseReceived(.success((repositories: [], nextURL: nil))))
87+
return Signal.just(GitHubCommand.gitHubResponseReceived(.success((repositories: [], nextURL: nil))))
8888
}
8989

9090
guard let nextURL = value.nextURL else {
91-
return Driver.empty()
91+
return Signal.empty()
9292
}
9393

9494
return performSearch(nextURL)
95-
.asDriver(onErrorJustReturn: .failure(GitHubServiceError.networkError))
95+
.asSignal(onErrorJustReturn: .failure(GitHubServiceError.networkError))
9696
.map(GitHubCommand.gitHubResponseReceived)
9797
}
9898
}
9999

100100
// this is degenerated feedback loop that doesn't depend on output state
101-
let inputFeedbackLoop: (Driver<GitHubSearchRepositoriesState>) -> Driver<GitHubCommand> = { state in
101+
let inputFeedbackLoop: (Driver<GitHubSearchRepositoriesState>) -> Signal<GitHubCommand> = { state in
102102
let loadNextPage = loadNextPageTrigger(state).map { _ in GitHubCommand.loadMoreItems }
103103
let searchText = searchText.map(GitHubCommand.changeSearch)
104104

105-
return Driver.merge(loadNextPage, searchText)
105+
return Signal.merge(loadNextPage, searchText)
106106
}
107107

108108
// Create a system with two feedback loops that drive the system
109109
// * one that tries to load new pages when necessary
110110
// * one that sends commands from user input
111-
return Driver.system(GitHubSearchRepositoriesState.initial,
112-
accumulator: GitHubSearchRepositoriesState.reduce,
113-
feedback: searchPerformerFeedback, inputFeedbackLoop)
111+
return Driver.system(
112+
initialState: GitHubSearchRepositoriesState.initial,
113+
reduce: GitHubSearchRepositoriesState.reduce,
114+
feedback: searchPerformerFeedback, inputFeedbackLoop
115+
)
114116
}
115117

116118
func == (

RxExample/RxExample/Examples/GitHubSearchRepositories/GitHubSearchRepositoriesViewController.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,13 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat
3939
super.viewDidLoad()
4040

4141
let tableView: UITableView = self.tableView
42-
let loadNextPageTrigger: (Driver<GitHubSearchRepositoriesState>) -> Driver<()> = { state in
42+
let loadNextPageTrigger: (Driver<GitHubSearchRepositoriesState>) -> Signal<()> = { state in
4343
tableView.rx.contentOffset.asDriver()
4444
.withLatestFrom(state)
4545
.flatMap { state in
4646
return tableView.isNearBottomEdge(edgeOffset: 20.0) && !state.shouldLoadNextPage
47-
? Driver.just(())
48-
: Driver.empty()
47+
? Signal.just(())
48+
: Signal.empty()
4949
}
5050
}
5151

@@ -54,7 +54,7 @@ class GitHubSearchRepositoriesViewController: ViewController, UITableViewDelegat
5454
let searchBar: UISearchBar = self.searchBar
5555

5656
let state = githubSearchRepositories(
57-
searchText: searchBar.rx.text.orEmpty.changed.asDriver().throttle(0.3),
57+
searchText: searchBar.rx.text.orEmpty.changed.asSignal().throttle(0.3),
5858
loadNextPageTrigger: loadNextPageTrigger,
5959
performSearch: { URL in
6060
GitHubSearchRepositoriesAPI.sharedAPI.loadSearchURL(URL)

RxExample/RxExample/Examples/TableViewWithEditingCommands/TableViewWithEditingCommandsViewController.swift

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ class TableViewWithEditingCommandsViewController: ViewController, UITableViewDel
6060
override func viewDidLoad() {
6161
super.viewDidLoad()
6262

63+
typealias Feedback = (ObservableSchedulerContext<TableViewEditingCommandsViewModel>) -> Observable<TableViewEditingCommand>
64+
6365
self.navigationItem.rightBarButtonItem = self.editButtonItem
6466

6567
let superMan = User(
@@ -82,40 +84,43 @@ class TableViewWithEditingCommandsViewController: ViewController, UITableViewDel
8284
.concat(loadFavoriteUsers)
8385
.observeOn(MainScheduler.instance)
8486

85-
let deleteUserCommand = tableView.rx.itemDeleted.map(TableViewEditingCommand.deleteUser)
86-
let moveUserCommand = tableView
87-
.rx.itemMoved
88-
.map({ val in
89-
return TableViewEditingCommand.moveUser(from: val.0, to: val.1)
90-
})
87+
let uiFeedback: Feedback = bind(self) { this, state in
88+
let subscriptions = [
89+
state.map {
90+
[
91+
SectionModel(model: "Favorite Users", items: $0.favoriteUsers),
92+
SectionModel(model: "Normal Users", items: $0.users)
93+
]
94+
}
95+
.bind(to: this.tableView.rx.items(dataSource: this.dataSource)),
96+
this.tableView.rx.itemSelected
97+
.withLatestFrom(state) { i, latestState in
98+
let all = [latestState.favoriteUsers, latestState.users]
99+
return all[i.section][i.row]
100+
}
101+
.subscribe(onNext: { [weak self] user in
102+
self?.showDetailsForUser(user)
103+
}),
104+
]
105+
106+
let events: [Observable<TableViewEditingCommand>] = [
107+
108+
this.tableView.rx.itemDeleted.map(TableViewEditingCommand.deleteUser),
109+
this.tableView .rx.itemMoved.map({ val in return TableViewEditingCommand.moveUser(from: val.0, to: val.1) })
110+
]
111+
112+
return Bindings(subscriptions: subscriptions, events: events)
113+
}
91114

92-
let initialState = TableViewEditingCommandsViewModel(favoriteUsers: [], users: [])
115+
let initialLoadFeedback: Feedback = { _ in initialLoadCommand }
93116

94-
let viewModel = Observable.system(
95-
initialState,
96-
accumulator: TableViewEditingCommandsViewModel.executeCommand,
117+
Observable.system(
118+
initialState: TableViewEditingCommandsViewModel(favoriteUsers: [], users: []),
119+
reduce: TableViewEditingCommandsViewModel.executeCommand,
97120
scheduler: MainScheduler.instance,
98-
feedback: { _ in initialLoadCommand }, { _ in deleteUserCommand }, { _ in moveUserCommand })
99-
.share(replay: 1)
100-
101-
viewModel
102-
.map {
103-
[
104-
SectionModel(model: "Favorite Users", items: $0.favoriteUsers),
105-
SectionModel(model: "Normal Users", items: $0.users)
106-
]
107-
}
108-
.bind(to: tableView.rx.items(dataSource: dataSource))
109-
.disposed(by: disposeBag)
110-
111-
tableView.rx.itemSelected
112-
.withLatestFrom(viewModel) { i, viewModel in
113-
let all = [viewModel.favoriteUsers, viewModel.users]
114-
return all[i.section][i.row]
115-
}
116-
.subscribe(onNext: { [weak self] user in
117-
self?.showDetailsForUser(user)
118-
})
121+
scheduledFeedback: uiFeedback, initialLoadFeedback
122+
)
123+
.subscribe()
119124
.disposed(by: disposeBag)
120125

121126
// customization using delegate

0 commit comments

Comments
 (0)