BubbleWrap
Cocoa wrappers and helpers for RubyMotion (Ruby for iOS and OS X) - Making Cocoa APIs more Ruby like, one API at a time. Fork away and send your pull requests
Install / Use
/learn @rubymotion-community/BubbleWrapREADME
BubbleWrap for RubyMotion
A collection of (tested) helpers and wrappers used to wrap Cocoa Touch and AppKit code and provide more Ruby like APIs.
Installation
gem install bubble-wrap
Setup
- Edit the
Rakefileof your RubyMotion project and add the following require line:
require 'bubble-wrap'
If you use Bundler:
gem 'bubble-wrap', '~> 1.9.7'
BubbleWrap is split into multiple modules so that you can easily choose which parts are included at compile-time.
If you wish to only include the RSS Parser wrapper:
require 'bubble-wrap/rss_parser'
If you wish to only include the Reactor wrapper:
require 'bubble-wrap/reactor'
If you wish to only include the UI-related wrappers:
require 'bubble-wrap/ui'
If you wish to only include the Camera wrapper:
require 'bubble-wrap/camera'
If you wish to only include the Location wrapper:
require 'bubble-wrap/location'
If you wish to only include the Media wrapper:
require 'bubble-wrap/media'
If you wish to only include the Mail wrapper:
require 'bubble-wrap/mail'
If you wish to only include the SMS wrapper:
require 'bubble-wrap/sms'
If you wish to only include the Motion (CoreMotion) wrapper:
require 'bubble-wrap/motion'
If you wish to only include the NetworkIndicator wrapper:
require 'bubble-wrap/network-indicator'
If you want to include everything (ie kitchen sink mode) you can save time and do:
require 'bubble-wrap/all'
You can also do this directly in your Gemfile like so:
gem 'bubble-wrap', require: %w[bubble-wrap/core bubble-wrap/location, bubble-wrap/reactor]
Note: DON'T use app.files = in your Rakefile to set up your files once you've required BubbleWrap.
Make sure to append onto the array or use +=.
- Now, you can use BubbleWrap extension in your app:
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
puts "#{App.name} (#{App.documents_path})"
true
end
end
Note: You can also vendor this repository but the recommended way is to use the versioned gem.
Core
Misc
UUID generator:
BubbleWrap.create_uuid
=> "68ED21DB-82E5-4A56-ABEB-73650C0DB701"
Localization (using NSBundle.mainBundle.localizedStringForKey):
BubbleWrap.localized_string(:foo, 'fallback')
=> "fallback"
Color conversion:
BubbleWrap.rgba_color(23, 45, 12, 0.4)
=> #<UIDeviceRGBColor:0x6db6ed0>
BubbleWrap.rgb_color(23, 45, 12)
=> #<UIDeviceRGBColor:0x8ca88b0>
'blue'.to_color
=> #<UICachedDeviceRGBColor:0xda535c0>
'dark_gray'.to_color
=> #<UICachedDeviceWhiteColor:0x8bb5be0>
'#FF8A19'.to_color
=> #<UIDeviceRGBColor:0x8d54110>
'#88FF8A19'.to_color # ARGB format
=> #<UIDeviceRGBColor:0xca0fe00>
Debug flag:
BubbleWrap.debug?
=> false
BubbleWrap.debug = true
=> true
BubbleWrap.debug?
=> true
App
A module with useful methods related to the running application
> App.documents_path
# "/Users/mattetti/Library/Application Support/iPhone Simulator/5.0/Applications/EEC6454E-1816-451E-BB9A-EE18222E1A8F/Documents"
> App.resources_path
# "/Users/mattetti/Library/Application Support/iPhone Simulator/5.0/Applications/EEC6454E-1816-451E-BB9A-EE18222E1A8F/testSuite_spec.app"
> App.name
# "testSuite"
> App.identifier
# "io.bubblewrap.testSuite"
> App.alert("BubbleWrap is awesome!")
# creates and shows an alert message.
> App.alert("BubbleWrap is awesome!", {cancel_button_title: "I know it is!", message: "Like, seriously awesome."})
# creates and shows an alert message with optional parameters.
> App.run_after(0.5) { p "It's #{Time.now}" }
# Runs the block after 0.5 seconds.
> App.open_url("http://matt.aimonetti.net")
> App.open_url("tel://123456789")
# Opens the url using the device's browser. Can also open custom URL schemas (accepts a string url or an instance of `NSURL`.)
> App.can_open_url("tel://")
# Returns whether the app can open a given URL resource.
> App::Persistence['channels'] # application specific persistence storage
# ['NBC', 'ABC', 'Fox', 'CBS', 'PBS']
> App::Persistence['channels'] = ['TF1', 'France 2', 'France 3']
# ['TF1', 'France 2', 'France 3']
> App.environment
# 'test'
Other available methods:
App.notification_centerApp.user_cacheApp.statesApp.frameApp.delegateApp.sharedApp.windowApp.current_localeApp.release?App.test?App.development?
Device
A collection of useful methods about the current device:
Examples:
> Device.iphone?
# true
> Device.ipad?
# false
> Device.camera.front?
# true
> Device.camera.rear?
# true
> Device.orientation
# :portrait
> Device.interface_orientation
# :portrait
> Device.simulator?
# true
> Device.ios_version
# "6.0"
> Device.retina?
# false
> Device.screen.width
# 320
> Device.screen.height
# 480
> Device.screen.width_for_orientation(:landscape_left)
# 480
> Device.screen.height_for_orientation(:landscape_left)
# 320
> Device.vendor_identifier
# <NSUUID>
Camera
Added interface for better camera access:
# Uses the front camera
BW::Device.camera.front.picture(media_types: [:movie, :image]) do |result|
image_view = UIImageView.alloc.initWithImage(result[:original_image])
end
# Uses the rear camera
BW::Device.camera.rear.picture(media_types: [:movie, :image]) do |result|
image_view = UIImageView.alloc.initWithImage(result[:original_image])
end
# Uses the photo library
BW::Device.camera.any.picture(media_types: [:movie, :image]) do |result|
image_view = UIImageView.alloc.initWithImage(result[:original_image])
end
# Lets the user edit the photo (with access to the edited and original photos)
BW::Device.camera.any.picture(allows_editing: true, media_types: [:image]) do |result|
edited_image_view = UIImageView.alloc.initWithImage(result[:edited_image])
original_image_view = UIImageView.alloc.initWithImage(result[:original_image])
end
# Capture a low quality movie with a limit of 10 seconds
BW::Device.camera.front.picture(media_types: [:movie], video_quality: :low, video_maximum_duration: 10) do |result|
video_file_path = result[:media_url]
end
Options include:
:allows_editing- Boolean; whether a user can edit the photo/video before picking:animated- Boolean; whether to display the camera with an animation (default true):on_dismiss- Lambda; called instead of the default dismissal logic:media_types- Array; containing any of[:movie, :image]:video_quality- Symbol; one of:high,:medium,low,"640x480".to_sym,iframe1280x720, oriframe960x540. Defaults to:medium:video_maximum_duration- Integer; limits movie recording length. Defaults to 600.
JSON
BW::JSON wraps NSJSONSerialization available in iOS5 and offers the same API as Ruby's JSON std lib. For apps building for iOS4, we suggest a different JSON alternative, like AnyJSON.
BW::JSON.generate({'foo' => 1, 'bar' => [1,2,3], 'baz' => 'awesome'})
=> "{\"foo\":1,\"bar\":[1,2,3],\"baz\":\"awesome\"}"
BW::JSON.parse "{\"foo\":1,\"bar\":[1,2,3],\"baz\":\"awesome\"}"
=> {"foo"=>1, "bar"=>[1, 2, 3], "baz"=>"awesome"}
NSIndexPath
Helper methods added to give NSIndexPath a bit more of a Ruby
interface.
index_path = table_view.indexPathForCell(cell)
index_path + 1 # NSIndexPath for next cell in the same section
=> #<NSIndexPath:0x120db8e0>
NSNotificationCenter
Helper methods to give NSNotificationCenter a Ruby-like interface:
def viewWillAppear(animated)
@foreground_observer = App.notification_center.observe UIApplicationWillEnterForegroundNotification do |notification|
loadAndRefresh
end
@reload_observer = App.notification_center.observe 'ReloadNotification' do |notification|
loadAndRefresh
end
end
def viewWillDisappear(animated)
App.notification_center.unobserve @foreground_observer
App.notification_center.unobserve @reload_observer
end
def reload
App.notification_center.post 'ReloadNotification'
end
NSUserDefaults
Helper methods added to the class repsonsible for user preferences used
by the App::Persistence module shown below.
Persistence
Offers a way to persist application specific information using a very simple interface:
> App::Persistence['channels'] # application specific persistence storage
# ['NBC', 'ABC', 'Fox', 'CBS', 'PBS']
> App::Persistence['channels'] = ['TF1', 'France 2', 'France 3']
# ['TF1', 'France 2', 'France 3']
> App::Persistence.delete('channels')
# ['TF1', 'France 2', 'France 3']
> App::Persistence['something__new'] # something previously never stored
# nil
> App::Persistence.all
# {'all':'values', 'stored':'by', 'bubblewrap':'as a hash!'}
Observers
Since: > version 0.4
You can observe for object's changes and trigger blocks:
class ExampleViewController < UIViewController
include BW::KVO
def viewDidLoad
@label = UILabel.alloc.initWithFrame [[20,20],[280,44]]
@label.text = ""
view.addSubview @label
observe(@label, :text) do |old_value, new_value|
puts "Hello from viewDidLoad!"
end
end
def viewDidAppear(animated)
observe(@label, :text) do |old_value, new_value|
puts "Hello from viewDidAppear!"
end
end
end
You can remove observers using unobserve method.
Since: > version 1.9.0
Optionally, multiple key paths can be passed to the observer method:
class ExampleViewController < UIViewC

