I implemented my own LiveData for iOS using Swift. Here’s how:
So, I was working on an app, using the MVVM architecture and I needed a way to notify my ViewController of changes happening in the ViewModel. I knew this could be done with KVO, RxSwift, NotificationCenter or even the Delegate pattern. But as an android developer, I wanted something that will function like LiveData in android, so I went online and found a talk by Ray Wenderlich on ‘MVVM in Practice’. There, he gave a Box model that looks like this:
class Box<T> { typealias Listener = T -> Void var listener: Listener? var value: T { didSet { listener?(value) } } init(_ value: T) { self.value = value } func bind(listener: Listener?) { self.listener = listener listener?(value) } }
With that, all you need to do is to wrap your variable with the Box in your ViewModel and then bind the variable in your ViewController, Just like this:
class UserViewModel { var username: Box<String> = Box("Default username") func setUsername(uName: String) { username.value = uName } }
class UserViewController: UIViewController { @IBOutlet weak var usernameLabel: UILabel! let userViewModel = UserViewModel() override func viewDidLoad() { super.viewDidLoad() userViewModel.username.bind { (username) in //Display new username value self.usernameLabel.text = username } } }
Looking at the code above, once the username
value is updated in the setUsername()
function of the UserViewModel
, the corresponding listener in the UserViewController
is triggered with the new value. Soft right?
So, I thought, how could I make this seem more like LiveData. So I did a few editings. Here it is:
class LiveData<T> { typealias Listener = T -> Void var listener: Listener? var value: T { didSet { listener?(value) } } init(_ value: T) { self.value = value } func observe(listener: Listener?) { self.listener = listener listener?(value) } }
Notice a few changes? The class now has the name, LiveData and the bind
function is now observe
, meaning you now ‘observe’ your variable for changes instead. The implementation now looks like below:
class UserViewModel { var username: LiveData<String> = LiveData("Default username") func setUsername(uName: String) { username.value = uName } }
class UserViewController: UIViewController { @IBOutlet weak var usernameLabel: UILabel! let userViewModel = UserViewModel() override func viewDidLoad() { super.viewDidLoad() userViewModel.username.observe { (username) in //Display new username value self.usernameLabel.text = username } } }
Something we can do further is to manage the retain cycle. Right now, observe
is retaining self
, we need a way to break the retain cycle and still keep the observer as long as we need it (as long as the ViewController is in use). We can introduce the [unowned self]
reference, as shown below:
class UserViewController: UIViewController { @IBOutlet weak var usernameLabel: UILabel! let userViewModel = UserViewModel() override func viewDidLoad() { super.viewDidLoad() userViewModel.username.observe { [unowned self] (username) in //Display new username value self.usernameLabel.text = username } } }
You can also make use of the [weak self]
reference instead:
class UserViewController: UIViewController { @IBOutlet weak var usernameLabel: UILabel! let userViewModel = UserViewModel() override func viewDidLoad() { super.viewDidLoad() userViewModel.username.observe { [weak self] (username) in //Display new username value self?.usernameLabel.text = username } } }
Can you see how easy it is to use? And it looks more native than just a ‘Box’. Pretty nice!
Surely, there are a lot more overheads that could be handled, like multiple bindings and disposals, but this solves pretty much a simple problem with just a few lines of code. Why not try it out and let me know what you think?
Gracias! ✌?
But RxSwift already has this out of the box right? And with RxSwift, you don’t even have to set the text on the label by yourself, you can just bind to the label’s text property and boooom, you’re done.
Yeah. This is just to have observable data without using RxSwift or similar.
Yeah make sense to understand and get the gist of how stuff works under the hood.
Great article anyways…
Thanks!