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
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
A collection of (tested) helpers and wrappers used to wrap Cocoa Touch and AppKit code and provide more Ruby like APIs.
gem install bubble-wrapRakefile of 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 +=.
class AppDelegate
def application(application, didFinishLaunchingWithOptions:launchOptions)
puts "#{App.name} (#{App.documents_path})"
true
end
endNote: You can also vendor this repository but the recommended way is to use the versioned gem.
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)
=> #
BubbleWrap.rgb_color(23, 45, 12)
=> #
'blue'.to_color
=> #
'dark_gray'.to_color
=> #
'#FF8A19'.to_color
=> #
'#88FF8A19'.to_color # ARGB format
=> #Debug flag:
BubbleWrap.debug?
=> false
BubbleWrap.debug = true
=> true
BubbleWrap.debug?
=> trueA module with useful methods related to the running application
…Other available methods:
App.notification_centerApp.user_cacheApp.statesApp.frameApp.delegateApp.sharedApp.windowApp.current_localeApp.release?App.test?App.development?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
# Added interface for better camera access:
…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, or iframe960x540. Defaults to :medium:video_maximum_duration - Integer; limits movie recording length. Defaults to 600.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"}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
=> #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'
endHelper methods added to the class repsonsible for user preferences used
by the App::Persistence module shown below.
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!'}Since: > version 0.4
You can observe for object's changes and trigger blocks:
class ExampleViewController version 1.9.0**
Optionally, multiple key paths can be passed to the `observer` method:
``` ruby
class ExampleViewController "matt_aimonetti".camelize
=> "MattAimonetti"
> "MattAimonetti".underscore
=> "matt_aimonetti"The Time Ruby class was added a class level method to convert a
iso8601 formatted string into a Time instance.
> Time.iso8601("2012-05-31T19:41:33Z")
=> 2012-05-31 21:41:33 +0200Interface for Ruby-like GPS and compass access (the CoreLocation framework):
> BW::Location.enabled? # Whether location services are enabled on the device
=> true
> BW::Location.authorized? # If your app is authorized to use location services
=> falseBW::Location.get(purpose: 'We need to use your GPS because...') do |result|
p "From Lat #{result[:from].latitude}, Long #{result[:from].longitude}"
p "To Lat #{result[:to].latitude}, Long #{result[:to].longitude}"
end
…
ruby
BW::Location.get_compass do |result|
p result[:magnetic_heading] # Heading towards magnetic north
p result[:true_heading] # Heading towards true north
p result[:accuracy] # Potential error between magnetic and true heading
p result[:timestamp] # Timestamp of the heading calculation
endBW::Location.get_significant is also available, for monitoring significant location changes.
BW::Location also supports get_once-style methods, which will return the first result before ending the search:
BW::Location.get_once(desired_accuracy: :three_kilometers, ...) do |result|
if result.is_a?(CLLocation)
p result.coordinate.latitude
p result.coordinate.longitude
else
p "ERROR: #{result[:error]}"
end
end
BW::Location.get_compass_once do |heading|
p result[:magnetic_heading]
p result[:true_heading]
p result[:accuracy]
p result[:timestamp]
endiOS 8 introduced stricter location services requirements. Although BubbleWrap will handle most of this for you automatically, you are required to add a few key/value pairs to the Info.plist. Add these two lines to your Rakefile (with your descriptions, obviously):
app.info_plist['NSLocationAlwaysUsageDescription'] = 'Description'
app.info_plist['NSLocationWhenInUseUsageDescription'] = 'Description'
…
ruby
BW::Motion.accelerometer.available?
BW::Motion.accelerometer.data # returns CMAccelerometerData object or nil
# ask the CMMotionManager to update every 5 seconds
BW::Motion.accelerometer.every(5) do |result|
# result contains the following data (from CMAccelerometerData#acceleration):
p result[:data] # the CMAccelerometerData object
p result[:acceleration] # the CMAcceleration struct
p result[:x] # acceleration in the x direction
p result[:y] # " y direction
p result[:z] # " z direction
end
# every, start, and repeat all need to be stopped later.
BW::Motion.accelerometer.stop
# repeat, but don't set the interval
BW::Motion.accelerometer.repeat do |result|
end
# you can specify a :queue where the operations will be executed. See above for details
BW::Motion.accelerometer.every(5, queue: :background) { |result| ... }
BW::Motion.accelerometer.every(5, queue: :main) { |result| ... }
BW::Motion.accelerometer.every(5, queue: :current) { |result| ... }
BW::Motion.accelerometer.every(5, queue: 'my queue') { |result| ... }
BW::Motion.accelerometer.once do |result|
# ...
endBW::Motion.gyroscope.available?
BW::Motion.gyroscope.data # returns CMGyroData object or nil
# ask the CMMotionManager to update every second.
BW::Motion.gyroscope.every(1) do |result|
# result contains the following data (from CMGyroData#rotationRate):
p result[:data] # the CMGyroData object
p result[:rotation] # the CMRotationRate struct
p result[:x] # rotation in the x direction
p result[:y] # " y direction
p result[:z] # " z direction
end
BW::Motion.gyroscope.stop
BW::Motion.gyroscope.once do |result|
# ...
endBW::Motion.magnetometer.available?
BW::Motion.magnetometer.data # returns CMMagnetometerData object or nil
# ask the CMMotionManager to update every second
BW::Motion.magnetometer.every(1) do |result|
# result contains the following data (from CMMagnetometerData#magneticField):
p result[:data] # the CMMagnetometerData object
p result[:field] # the CMMagneticField struct
p result[:x] # magnetic field in the x direction
p result[:y] # " y direction
p result[:z] # " z direction
end
BW::Motion.magnetometer.stop
BW::Motion.magnetometer.once do |result|
# ...
endThis is an amalgam of all the motion sensor data.
BW::Motion.device.available?
BW::Motion.device.data # returns CMDeviceMotion object or nil
BW::Motion.device.every(1) do |result|
# result contains the following data:
p result[:data] # the CMDeviceMotion object
# orientation data, from CMDeviceMotion#attitude
p result[:attitude] # the CMAttitude struct
p result[:roll]
p result[:pitch]
p result[:yaw]
# rotation data, from CMDeviceMotion#rotationRate
p result[:rotation] # the CMRotationRate struct
p result[:rotation_x]
p result[:rotation_y]
p result[:rotation_z]
# gravity+acceleration vector, from CMDeviceMotion#gravity
p result[:gravity] # the CMAcceleration struct
p result[:gravity_x]
p result[:graviNo open issues yet, or sync has not completed.