4

I am observing UISearchBar.rx.text attributes to perform some search related Action when User types some text. But at some time, I also would like to trigger this search Action programmatically. For instance at the creation of the view like in this example, where unfortunately the "Searching for [...]" text is not printed.

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {
  @IBOutlet weak var mySearchBar: UISearchBar!

  let disposeBag = DisposeBag()

  override func viewDidLoad() {
    super.viewDidLoad()
    // Trigger search when text changes
    mySearchBar.rx.text.subscribe(onNext: { (text)
         print("Searching for \(text)...")
         // do some search Action
    }
    .disposed(by: disposeBag)

    // Programmatically trigger a search
    mySearchBar.text = "Some text to search"
  }

}

The problem is changing mySearchBar.text does not trigger a new rx.text Event. Is there some way to do so?

For instance, I know thanks to this post that with an UITextField, this is possible using the UITextField.sendActions(for: .ValueChanged) function. Is there some similar way to do so with UISearchBar?

PhilippeC
  • 51
  • 1
  • 3

3 Answers3

5

You could use a Variable<String?> as a sink for your search bar updates. That way you could also set its value programmatically, and use the variable instead of the search bar directly to drive your action:

class ViewController: UIViewController {
    let searchText = Variable<String?>(nil)
    let searchBar = UISearchBar()
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        searchBar.rx.text.asDriver()
            .drive(searchText)
            .disposed(by: disposeBag)

        searchText.asObservable().subscribe(onNext: { [weak self] (text) in
            if let welf = self, welf.searchBar.text != text {
                welf.searchBar.text = text
            }
        })
            .disposed(by: disposeBag)

        searchText.value = "variables so cool"
        searchText.asObservable().subscribe(onNext: { [weak self] (text) in
            self?.doStuff(text)
        })
        .disposed(by: disposeBag)
    }
}
jefflovejapan
  • 2,047
  • 3
  • 20
  • 34
  • No problem. You should also look at the `<->` operator from [RxExamples](https://github.com/ReactiveX/RxSwift/blob/master/RxExample/RxExample/Operators.swift). – jefflovejapan Nov 26 '17 at 03:22
1

This is a simple usage of Drivers

import UIKit
import RxCocoa
import RxSwift
class SearchViewController: UIViewController {

    @IBOutlet weak var searchBar: UISearchBar!
    let disposeBag = DisposeBag()
    override func viewDidLoad() {
        super.viewDidLoad()
        let dataModels = Driver.just(["David", "Behrad","Tony","Carl","Davidov"])
        let searchingTextDriver = searchBar.rx.text.orEmpty.asDriver()
        let matchedCases = searchingTextDriver.withLatestFrom(dataModels){ searchingItem,models in
            return models.filter({ (item) -> Bool in
                 return item.contains(searchingItem)
            })
        }
        matchedCases.do(onNext: { (items) in
            print(items)            
        }).drive().disposed(by: disposeBag)
    }
}

As you see we created a dataModels Driver that it could be a response of your network or dataBase fetching. the searching text converted to Driver then mixed with the dataModels to create matchedCases.

Behrad Kazemi
  • 302
  • 1
  • 10
1

Yes same works for UITextBar.

Setting .text programmatically does not trigger the action automatically, I don't know why! but this is the case for UITextField too.

Call sendActions after each time you update .text property of a UIView subclass.

Try this :

// Programmatically trigger a search
    mySearchBar.text = "Some text to search"    
    mySearchBar.sendActions(for: .valueChanged)
Iman Nia
  • 2,255
  • 2
  • 15
  • 35
  • 1
    This works on UISearchBar in iOS 13 and later by calling the `searchTextField.sendActions(for:)` method – Jav Solo Feb 22 '22 at 16:16