Reactive5 flatMap observeValues block did not call - ios

override func viewDidLoad() {
commitButton.reactive.controlEvents(.touchUpInside).flatMap(.latest) { (button) -> Signal<Bool, NoError> in
return self.createSignInSignal()
}.observeValues({
// did not call
print("Sign in result: \($0)")
})
}
func signin(name: String, password: String, result: ((Bool)->Void)) {
...
}
// create SignIn Signal
private func createSignInSignal() -> Signal<Bool, NoError> {
let (signInSignal, observers) = Signal<Bool, NoError>.pipe()
self.signin(name: nameTextField.text!, password: passworkTextField.text!, result: ({success in
observers.send(value: success)
observers.sendCompleted()
}))
return signInSignal
}
When click login button, FlatMap observeValues block did not call. When I use map, It work well, I know the difference between map and flatmap, How can I let the code work using flatmap?

Related

Why RxSwift Subscribe just run once in First launch viewWillAppear?

I write a subscribe in viewWillAppear.
But it also run once in first launch app.
When I push to another viewcontroller, I use dispose().
Then I back in first viewcontroller, my subscribe func in viewWillAppear don't run.
What's wrong with my rx subscribe?
var listSubscribe:Disposable?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
listSubscribe = chatrooms.notifySubject.subscribe({ json in
print("*1") //just print once in first launch
self.loadContents()
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let controllers = tabBarController?.navigationController?.viewControllers
if (controllers?.count)! > 1 {
listSubscribe?.dispose()
}
}
RxSwift documentation says "Note that you usually do not want to manually call dispose; this is only an educational example. Calling dispose manually is usually a bad code smell."
Normally, you should be doing something like this -
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
whatever.subscribe(onNext: { event in
// do stuff
}).disposed(by: self.disposeBag)
}
As for your question, I believe you don't need to re-subscribe because you subscription will be alive and 'notifySubject' will send you updates whenever there are any.
Maybe you can get some reactive implementation of viewWillAppear and similar functions? And forget about manual disposables handling... For example your UIViewController init will contain something like this:
rx.driverViewState()
.asObservable()
.filter({ $0 == .willAppear })
.take(1) // if you need only first viewWillAppear call
.flatMapLatest({ _ in
// Do what you need
})
And the implementation of driverViewState:
public extension UIViewController {
public enum ViewState {
case unknown, didAppear, didDisappear, willAppear, willDisappear
}
}
public extension Reactive where Base: UIViewController {
private typealias _StateSelector = (Selector, UIViewController.ViewState)
private typealias _State = UIViewController.ViewState
private func observableAppearance(_ selector: Selector, state: _State) -> Observable<UIViewController.ViewState> {
return (base as UIViewController).rx
.methodInvoked(selector)
.map { _ in state }
}
func driverViewState() -> Driver<UIViewController.ViewState> {
let statesAndSelectors: [_StateSelector] = [
(#selector(UIViewController.viewDidAppear(_:)), .didAppear),
(#selector(UIViewController.viewDidDisappear(_:)), .didDisappear),
(#selector(UIViewController.viewWillAppear(_:)), .willAppear),
(#selector(UIViewController.viewWillDisappear(_:)), .willDisappear)
]
let observables = statesAndSelectors
.map({ observableAppearance($0.0, state: $0.1) })
return Observable
.from(observables)
.merge()
.asDriver(onErrorJustReturn: UIViewController.ViewState.unknown)
.startWith(UIViewController.ViewState.unknown)
.distinctUntilChanged()
}
}

ReactiveSwift - Bind `Action` to `UIControl`

I am trying to bind an Action on a UISwitch.
I have created the Action using the following code
action = Action<UISwitch, Bool, NoError> { (input: UISwitch) -> SignalProducer<Bool, NoError> in
return SignalProducer{ (observer, disposable) in
observer.send(value: input.isOn)
observer.sendCompleted()
}
}
but I am having trouble connecting it to the UISwitch.
Can someone help?
there is another class CocoaAction that is used to wrap Actions and connect them to UIControls (now in ReactiveCocoa and not in core ReactiveSwift, so if you are using RAC 5 you will have to import both)
var switch: UISwitch!
//switch.addTarget() does not retain the target, so if we do not
//keep a strong reference here the cocoaAction will be deallocated
//at the end of viewDidLoad() and you will get unrecognized selector
//errors when the switch tries to execute the action
var switchCocoaAction: CocoaAction!
override func viewDidLoad() {
let action = Action<UISwitch, Bool, NoError> { (input: UISwitch) -> SignalProducer<Bool, NoError> in
return SignalProducer { (observer, disposable) in
observer.send(value: input.isOn)
observer.sendCompleted()
}
}
//unsafe because it will cast anyObject as! UISwitch
self.switchCocoaAction = action.unsafeCocoaAction
switch.addTarget(switchCocoaAction,
action: CocoaAction.selector,
forControlEvents: .ValueChanged
)
}
however, if all you want is a signal emitting the switch.isOn value whenever it changes, you can accomplish that much more easily using the builtin rac_signalForControlEvents
func switchSignal() -> SignalProducer<Bool, NoError> {
switch.rac_signalForControlEvents(.ValueChanged) //returns legacy RACsignal
.toSignalProducer() //legacy RACSignal -> swift SignalProducer
.flatMapError { _ in .empty } //NSError -> NoError, errors not possible here, so ignore them
.map { ($0 as! UISwitch).isOn }
}

Completion block not called (Swift)

I have written a function with a completion block as an argument. When I call this function in my view controller, the completion block is not executed. This is my first time writing my own custom completion block, so I may be making just an easy mistake. Here is the function in my model:
func iterateThrough2(userInput: String, completion: (completed: Bool) -> Bool) -> Double {
while numberOfCharactersDone < userInput.characters.count {
let startIndex = userInput.startIndex.advancedBy(numberOfCharactersDone)
let letterIndex = userInput.characters[startIndex]
let letter = String(letterIndex)
if isLetter(letter) {
isALetter(letter, input: userInput)
}
else {
isNotLetter(letter)
}
numberOfCharactersDone++
}
return total
}
And in my view controller, I call it:
#IBAction func calculate(sender: UIButton) {
if let text = self.enteredText.text {
let result = calculator.iterateThrough2(text, completion: { (completed) -> Bool in
if completed {
print("good")
return true
}
else {
print("not good")
return false
}
})
self.outputLabel.text = String(result)
calculator.resetData()
}
}
The function does successfully return a double, as it should, and it is working otherwise. It just doesn't call the closure. What am I doing wrong?
Do I have to include and specify in my method when exactly I want the closure to be ran? I read here: https://www.codefellows.org/blog/writing-completion-blocks-with-closures-in-swift and tried to call the completion block at the end of my method like:
completion(completed: completed)
return total
but I got an error "use of unresolved identifier "completed""

callback function from caller to callee in swift

I have a view controller and a class for doing the bits to call the services and get the data from server.
The ViewController code is below,
class ViewController : UIViewController
{
override func viewDidLoad() {
let parser = Parser()
parser.connectServer("abc URL" , ..... <gotDataFromServer> ..... )
}
func gotDataFromServer(response:String)
{
...... Do our things here .......
}
}
and the parser code is below,
class Parser
{
func connectServer(apiURL:String,...<call back function name>...)
{
let manager = RequestOperationManager.sharedManager()
manager.GET(apiURL ,
parameters: nil,
success: { (operation,responseObject) ->Void in
.....<Call back the function which is passed in parameter> ....
},
failure: { (operation , error) in
print ("error occurred")
})
}
}
Now in the above sample code i want to pass call back function "gotDataFromServer" as a parameter and when the inner function get the response from the server then i want to call this function back.
Can anyone please help.
You can use delegates to achieve that. Try out following code
class ViewController : UIViewController, DataDelegate
{
override func viewDidLoad() {
let parser = Parser()
parser.delegate = self
parser.connectServer("abc URL" , ..... <gotDataFromServer> ..... )
}
func gotDataFromServer(response:String)
{
...... Do our things here .......
}
}
And add protocol in parser as follows
protocol DataDelegate {
func gotDataFromServer(response:String)
}
class Parser
{
var delegate : DataDelegate!
func connectServer(apiURL:String,...<call back function name>...)
{
let manager = RequestOperationManager.sharedManager()
manager.GET(apiURL ,
parameters: nil,
success: { (operation,responseObject) ->Void in
delegate.gotDataFromServer("") //parameter is your data
},
failure: { (operation , error) in
print ("error occurred")
})
}
}
Here's an example how you can do it using closure
class Parser {
func connectServer(apiURL: String, completion: String -> Void) {
// ... make call, get data
// share the results via completion closure
completion("data")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
let parser = Parser()
// Option #1
parser.connectServer("mybackend.com/connect") {
print("received data \($0)")
}
// Option #2 is the same as Option #1 but a bit longer
parser.connectServer("mybackend.com/connect") { (data) -> Void in
print("received data \(data)")
}
// Option #3 - Or if you have a separate funciton
// be careful with retain cycle
parser.connectServer("mybackend.com/connect", completion: gotDataFromServer)
}
func gotDataFromServer(response:String) { }
}

Sending a function to another function as a parameter

I want to send a func as a parameter to another func but I know how to do it only in case the func being sent has an input and an output parameters:
aFunc (sentFunc: Int -> String) {
...
}
But I want to achieve something like the following code where the function being sent does not have parameters:
func firstFunc(<i want to declare paramenter for a function here that itself does not have any parameters>) {
if (servicedCities != nil) {
<execute sent fuction>
} else {
objectMapper().getServicedCitiesifAvailable()
<execute sent fuction>
}
}
the func calling the above func should look like
func secondFunc() {
func myNestedFunction() {
....
}
....
firstFunc(myNestedFunction())
....
}
func firstFunc(passedFunc:()->()) {
passedFunc()
}
func secondFunc() {
func myNestedFunction() {
print("hi")
}
firstFunc(myNestedFunction)
}
secondFunc()
will ouput
hi
in the context of the firstFunc call.
Note that you have to omit the () in the last line after the myNestedFunction because you dont want to call the method but pass the method itself.
func firstFunc(sentFunc: () -> ()) {
sentFunc()
}
or
func firstFunc(sentFunc: Void -> Void) {
sentFunc()
}
I think you are looking for Swift closures.
Check this Swift closures and functions
func someFunc(paramFunc: () -> ()) {
paramFunc()
}
...
self.someFunc { () -> () in
//Do some code here;
}

Resources