Commit 72a1b8d8 authored by Romain Bertozzi's avatar Romain Bertozzi

wizard: complete add account pipe

This patch completes the pipe of creating a dummy account using
RxSwift and MVVM.

The AccountsService now exposes a public shared responseStream that
observers can listen to.
Callers will call this service's methods directly and wait for any
interesting response on the stream.

Tuleap: #1391
Change-Id: I2eb5abac9eb012892a806ee2fb73df8730edba87
parent 545255d0
......@@ -22,7 +22,7 @@
0273C3081E0C68BF00CF00BA /* RoundedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0273C3071E0C68BF00CF00BA /* RoundedButton.swift */; };
02AED8191DD4C4B100F740BA /* librestbed.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 02AED8181DD4C4B100F740BA /* librestbed.a */; };
02B22DFC1DF755BB000358C9 /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B22DFA1DF755BB000358C9 /* AccountModel.swift */; };
02B22DFD1DF755BB000358C9 /* AccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B22DFB1DF755BB000358C9 /* AccountViewModel.swift */; };
02B22DFD1DF755BB000358C9 /* CreateRingAccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B22DFB1DF755BB000358C9 /* CreateRingAccountViewModel.swift */; };
02B22DFF1DF755DB000358C9 /* AccountsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B22DFE1DF755DB000358C9 /* AccountsService.swift */; };
02B22E011DF755E5000358C9 /* MainTabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02B22E001DF755E5000358C9 /* MainTabBarViewController.swift */; };
02B22E031DF755F7000358C9 /* WalkthroughStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 02B22E021DF755F7000358C9 /* WalkthroughStoryboard.storyboard */; };
......@@ -134,7 +134,7 @@
028568301DF610A9003A8D8D /* RingTests-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RingTests-Bridging-Header.h"; sourceTree = "<group>"; };
02AED8181DD4C4B100F740BA /* librestbed.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = librestbed.a; path = ../DEPS/x86_64/lib/librestbed.a; sourceTree = "<group>"; };
02B22DFA1DF755BB000358C9 /* AccountModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AccountModel.swift; path = Account/AccountModel.swift; sourceTree = "<group>"; };
02B22DFB1DF755BB000358C9 /* AccountViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AccountViewModel.swift; path = Account/AccountViewModel.swift; sourceTree = "<group>"; };
02B22DFB1DF755BB000358C9 /* CreateRingAccountViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CreateRingAccountViewModel.swift; path = Account/CreateRingAccountViewModel.swift; sourceTree = "<group>"; };
02B22DFE1DF755DB000358C9 /* AccountsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AccountsService.swift; path = Services/AccountsService.swift; sourceTree = "<group>"; };
02B22E001DF755E5000358C9 /* MainTabBarViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MainTabBarViewController.swift; path = MainTabBar/MainTabBarViewController.swift; sourceTree = "<group>"; };
02B22E021DF755F7000358C9 /* WalkthroughStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = WalkthroughStoryboard.storyboard; path = Walkthrough/WalkthroughStoryboard.storyboard; sourceTree = "<group>"; };
......@@ -365,7 +365,7 @@
isa = PBXGroup;
children = (
02B22DFA1DF755BB000358C9 /* AccountModel.swift */,
02B22DFB1DF755BB000358C9 /* AccountViewModel.swift */,
02B22DFB1DF755BB000358C9 /* CreateRingAccountViewModel.swift */,
);
name = Account;
sourceTree = "<group>";
......@@ -719,7 +719,7 @@
02B22DFC1DF755BB000358C9 /* AccountModel.swift in Sources */,
043866331D22CE8C00E06CE2 /* MeViewController.swift in Sources */,
04399AAE1D1C304300E99CD9 /* Utils.mm in Sources */,
02B22DFD1DF755BB000358C9 /* AccountViewModel.swift in Sources */,
02B22DFD1DF755BB000358C9 /* CreateRingAccountViewModel.swift in Sources */,
043999FA1D1C2D9D00E99CD9 /* Ring.xcdatamodeld in Sources */,
0438663B1D2313B700E06CE2 /* AccountDetailsViewController.swift in Sources */,
0273C3081E0C68BF00CF00BA /* RoundedButton.swift in Sources */,
......
......@@ -21,44 +21,63 @@
import RxSwift
/**
A structure representing the ViewModel (MVVM) of the accounts managed by Ring.
A class representing the ViewModel (MVVM) of the accounts managed by Ring.
Its responsabilities:
- expose to the Views a public API for its interactions concerning the Accounts,
- react to the Views user events concerning the Accounts (add an account...)
*/
struct AccountViewModel {
class CreateRingAccountViewModel {
/**
Dispose bag that contains the Disposable objects of the ViewModel, and managing their disposes.
*/
fileprivate let disposeBag = DisposeBag()
/**
Retains the currently active stream adding an account.
Useful to dispose it before starting a new one.
*/
fileprivate var addAccountDisposable: Disposable?
/**
Create the observers to the streams passed in parameters.
It will allow this ViewModel to react to other entities' events.
- Parameter observable: An observable stream to subscribe on.
Any observed event on this stream will trigger the action of creating an account.
- Parameter onStart: Closure that will be triggered when the action will begin.
- Parameter onError: Closure that will be triggered in case of error.
*/
- Parameter onStartCallback: Closure that will be triggered when the action will begin.
- Parameter onSuccessCallback: Closure that will be triggered when the action will succeed.
- Parameter onErrorCallback: Closure that will be triggered in case of error.
*/
func configureAddAccountObservers(observable: Observable<Void>,
onStart: ((() -> Void)?),
onSuccess: ((() -> Void)?),
onError: (((Error?) -> Void)?)) {
onStartCallback: ((() -> Void)?),
onSuccessCallback: ((() -> Void)?),
onErrorCallback: (((Error?) -> Void)?)) {
_ = observable
.subscribe(onNext: {
if onStart != nil {
onStart!()
}
AccountsService.sharedInstance.addAccount()
}, onError: { (error) in
if onError != nil {
onError!(error)
}
}, onCompleted: {
//~ Nothing to do.
}, onDisposed: {
//~ Nothing to do.
.subscribe(
onNext: { [weak self] in
//~ Let the caller know that the action has just begun.
onStartCallback?()
//~ Dispose any previously running stream. There is only one add account action
//~ simultaneously authorized.
self?.addAccountDisposable?.dispose()
//~ Subscribe on the AccountsService responseStream to get results.
self?.addAccountDisposable = AccountsService.sharedInstance
.sharedResponseStream
.subscribe(onNext:{ (event) in
if event == AccountRxEvent.AccountChanged {
onSuccessCallback?()
}
}, onError: { error in
onErrorCallback?(error)
})
self?.addAccountDisposable?.addDisposableTo((self?.disposeBag)!)
//~ Launch the action.
AccountsService.sharedInstance.addAccount()
},
onError: { (error) in
onErrorCallback?(error)
})
.addDisposableTo(disposeBag)
}
......
......@@ -22,6 +22,15 @@
import RxCocoa
import RxSwift
/**
Events that can be sent to the response stream
- AccountChanged: the accounts have been changed daemon-side
*/
enum AccountRxEvent {
case AccountChanged
}
class AccountsService: AccountAdapterDelegate {
// MARK: Private members
/**
......@@ -38,6 +47,18 @@ class AccountsService: AccountAdapterDelegate {
*/
fileprivate var accountList: Array<AccountModel>
fileprivate let disposeBag = DisposeBag()
/**
PublishSubject forwarding AccountRxEvent events.
This stream is used strictly inside this service.
External observers should use the public shared responseStream.
- SeeAlso: `AccountRxEvent`
- SeeAlso: `sharedResponseStream`
*/
fileprivate let responseStream = PublishSubject<AccountRxEvent>()
// MARK: - Public members
/**
Accounts list public interface.
......@@ -53,15 +74,29 @@ class AccountsService: AccountAdapterDelegate {
}
}
/**
Public shared stream forwarding the events of the responseStream.
External observers must subscribe to this stream to get results.
- SeeAlso: `responseStream`
- SeeAlso: `AccountRxEvent`
*/
var sharedResponseStream: Observable<AccountRxEvent>
// MARK: - Singleton
static let sharedInstance = AccountsService()
fileprivate init() {
accountList = []
self.accountList = []
self.responseStream.addDisposableTo(disposeBag)
//~ Create a shared stream based on the responseStream one.
self.sharedResponseStream = responseStream.share()
//~ Registering to the AccountConfigurationManagerAdaptator with self as delegate in order
//~ to receive delegation callbacks.
confAdapter.delegate = self
self.confAdapter.delegate = self
}
// MARK: - Methods
......@@ -100,5 +135,6 @@ class AccountsService: AccountAdapterDelegate {
// MARK: - AccountAdapterDelegate
func accountsChanged() {
print("Accounts changed.")
self.responseStream.onNext(.AccountChanged)
}
}
......@@ -25,7 +25,7 @@ import RxSwift
class CreateRingAccountViewController: UIViewController {
var mAccountViewModel: AccountViewModel = AccountViewModel()
var mAccountViewModel = CreateRingAccountViewModel()
@IBOutlet weak var mCreateAccountButton: RoundedButton!
......@@ -48,14 +48,14 @@ class CreateRingAccountViewController: UIViewController {
mAccountViewModel.configureAddAccountObservers(
observable: createAccountObservable,
onStart: { [weak self] in
onStartCallback: { [weak self] in
self?.setCreateAccountAsLoading()
},
onSuccess: { [weak self] in
onSuccessCallback: { [weak self] in
print("Account created.")
self?.setCreateAccountAsIdle()
},
onError: { [weak self] (error) in
onErrorCallback: { [weak self] (error) in
print("Error creating account...")
if error != nil {
print(error!)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment