Stop scattering your domain logic across your view controller. Consolidate it to a single conceptual unit with Elevate.
@login_task = async username: username.text, password: password.text do
# This block runs on a background thread.
task do
# @username and @password correspond to the Hash keys provided to async.
args = { username: @username, password: @password }
credentials = Elevate::HTTP.post(LOGIN_URL, args)
if credentials
UserRegistration.store(credentials.username, credentials.token)
end
# Anything yielded from this block is passed to on_update
yield "Logged in!"
# Return value of block is passed back to on_finish
credentials != nil
end
on_start do
# This block runs on the UI thread after the operation has been queued.
SVProgressHUD.showWithStatus("Logging In...")
end
on_update do |status|
# This block runs on the UI thread with anything the task yields
puts status
end
on_finish do |result, exception|
# This block runs on the UI thread after the task block has finished.
SVProgressHUD.dismiss
if exception == nil
if result
alert("Logged in successfully!")
else
alert("Invalid username/password!")
end
else
alert(exception)
end
end
end
Many iOS/OS X apps have fairly simple domain logic that is obscured by several programming 'taxes':
- UI management
- asynchronous network requests
- I/O-heavy operations, such as storing large datasets to disk
These are necessary to ensure a good user experience, but they splinter your domain logic (that is, what your application does) through your view controller. Gross.
Elevate is a mini task queue for your app, much like Resque or Sidekiq. Rather than defining part of an operation to run on the UI thread, and a CPU-intensive portion on a background thread, Elevate is designed so you run the entire operation in the background, and receive notifications at various times. This has a nice side effect of consolidating all the interaction for a particular task to one place. The UI code is cleanly isolated from the non-UI code. When your tasks become complex, you can elect to extract them out to a service object.
In a sense, Elevate is almost a control-flow library: it bends the rules of app development a bit to ensure that the unique value your application provides is as clear as possible.
If you're new to Elevate, please start with the tutorial.
Update your Gemfile:
gem "elevate", "~> 0.6.0"
Bundle:
$ bundle install
Include the module in your view controller:
class ArtistsSearchViewController < UIViewController
include Elevate
Launch an async task with the async
method:
@track_task = async artist: searchBar.text do
task do
response = Elevate::HTTP.get("http://example.com/artists", query: { artist: @artist })
artist = Artist.from_hash(response)
ArtistDB.update(artist)
response["name"]
end
on_start do
SVProgressHUD.showWithStatus("Adding...")
end
on_finish do |result, exception|
SVProgressHUD.dismiss
end
end
If you might need to cancel the task later, call cancel
on the object returned by async
:
@track_task.cancel
Elevate 0.6.0 includes support for timeouts. Timeouts are declared using the timeout
method within the async
block. They start when an operation is queued, and automatically abort the task when the duration passes. If the task takes longer than the specified duration, the on_timeout
callback is run.
Example:
async do
timeout 0.1
task do
Elevate::HTTP.get("http://example.com/")
end
on_timeout do
puts 'timed out'
end
on_finish do |result, exception|
puts 'completed!'
end
end
- Must use Elevate's HTTP client instead of other iOS networking libs
- Hexagonal Architecture
- Android SDK's AsyncTask
- Go (asynchronous IO done correctly)
MIT License