Wiretap
An iOS / OS X wrapper heavily inspired by ReactiveCocoa.
gem install motion-wiretap
Run the OSX specs using rake spec platform=osx
First things first
You MUST call cancel! on any wiretaps you create, otherwise they will live
in memory forever. The upside is that you can create
Showdown
Open these side by side to see the comparison:
Usage
These are taken from the specs.
Key-Value Observation
class Person
attr_accessor :name
attr_accessor :email
attr_accessor :address
end
# listen for changes
person = Person.new
person.wiretap(:name) do |name|
puts "name is now #{name}"
end
person.name = 'Joe'
# puts => "name is now Joe"
# bind the property of one object to the value of another
person_1 = Person.new
person_2 = Person.new
wiretap = person_1.wiretap(:name).bind_to(person_2.wiretap(:name))
person_2.name = 'Bob'
person_1.name # => "Bob"
# cancel a wiretap
wiretap.cancel!
person_2.name = 'Jane'
person_1.name
# => "BOB"
# bind the property of one object to the value of another, but change it using `map`
wiretap = person_1.wiretap(:name).bind_to(person_2.wiretap(:name).map { |value| value.upcase })
person_2.name = 'Bob'
person_1.name # => "BOB"
Working with arrays
# combine the values `name` and `email`
person = Person.new
[
person.wiretap(:name),
person.wiretap(:email),
].wiretap.combine do |name, email|
puts "#{name} <#{email}>"
end
person.name = 'Kazuo'
# puts => "Kazuo <>"
person.email = '[email protected]'
# puts => "Kazuo <[email protected]>"
# reduce/inject
person_1 = Person.new
person_2 = Person.new
[
person_1.wiretap(:name),
person_2.wiretap(:name),
].wiretap.reduce do |memo, name|
memo ||= []
memo + [name]
end
person_1.name = 'Mr. White'
person_1.name = 'Mr. Blue'
# => ['Mr. White', 'Mr. Blue']
# you can provide an initial 'memo' (default: nil)
[
person_1.wiretap(:name),
person_2.wiretap(:name),
].wiretap.reduce([]) do |memo, name|
memo + [name] # you should not change memo in place, the same one will be used on every change event
end
Monitoring jobs
# monitor for background job completion
-> {
this_will_take_forever!
}.wiretap(Dispatch::Queue.concurrent) do
puts "done!"
end
# Note: this is convenient shorthand for calling `queue`, `and_then`, and `start`
-> {}.wiretap.queue(...).and_then do ... end.start
# send a stream of values. a lambda is passed in that will forward change
# events to the `listen` block
-> (on_change) do
5.times do |count|
on_change.call count
end
end.wiretap.queue(Dispatch::Queue.concurrent).listen do |index|
puts "...#{index}"
end.and_then do
puts "done!"
end.start
# => puts "...0", "...1", "...2", "...3", "...4", "done!"
Let's do something practical!
# bind the `enabled` property to the Wiretap object to a check on whether
# `username_field.text` and `password_field.text` are blank
@login_button.wiretap(:enabled).bind_to(
[
@username_field.wiretap(:text),
@password_field.wiretap(:text),
].wiretap.combine do |username, password|
username && username.length > 0 && password && password.length > 0
end
)
Notifications
notification = "NotificationName".wiretap do
puts "notification received!"
end
# rememder: it's *important* to cancel all wiretaps!
notification.cancel!