SlideShare a Scribd company logo
1 of 37
Download to read offline
Rapid App Development
with RubyMotion
Stefán Hafliðason
http://stefan.haflidason.com
@styrmis
External libraries that make development even easier
• Promises increased developer productivity
• Brings the flexibility of Ruby to iOS and OSX development
• Bridges directly to Obj-C libraries: no intermediate glue
code
• A REPL for working with your app live!
• Make tweaks quickly
• Build whole views programmatically on the fly
Why
RubyMotion?
RubyMotion +
3rd Party Libs
• Stock RubyMotion makes life (generally) easier
• Like Rails, there’s a healthy (and growing)
ecosystem of libraries
• These libraries can help speed up development
even further
• If the cost of experimenting is reduced, we’re
more likely to try out new ideas, and that’s my
goal.
What are we looking for?
• Non-Polluting!
• Zero/Minimal pollution of our current namespace.
• Minimal Magic!
• Because when magic breaks, we’ll need to fix it.
• Allows fallback to ‘plain’ RubyMotion
• There when you need it, unobtrusive when you
don’t.
The Libraries
• View management: Ruby Motion Query (RMQ),
MotionKit
• More Ruby-like view creation: ProMotion!
• Core Data: ruby-xcdm + Core Data Query (CDQ)
• Various helpers: BubbleWrap, SugarCube,
MotionAwesome
Ruby Motion Query
• “It’s like jQuery for RubyMotion. Stylesheets,
templates, events, and more”
• Supercharges the REPL/console
• Easy to add, access and modify views
• Also: event binding, styling, animation and
more.
Layout Experimentation
Quickly laying out 8 table views on the screen (iPad).
Started in the console, then moved code into the app itself.
	
  1	
  class	
  RootViewController	
  <	
  UIViewController	
  
	
  2	
  	
  	
  def	
  viewDidLoad	
  
	
  3	
  	
  	
  	
  	
  #	
  Start	
  by	
  laying	
  out	
  with	
  RMQ	
  
	
  4	
  	
  	
  	
  	
  @week	
  =	
  UITableView.alloc.init	
  
	
  5	
  	
  	
  	
  	
  rmq.append(@week).layout({	
  t:0,	
  l:0,	
  w:217,	
  h:704})	
  
	
  6	
  	
  
	
  7	
  	
  	
  	
  	
  @monday	
  =	
  UITableView.alloc.init	
  
	
  8	
  	
  	
  	
  	
  rmq.append(@monday).layout({	
  t:0,	
  l:221,	
  w:200,	
  h:350})	
  
	
  9	
  	
  
10	
  	
  	
  	
  	
  @tuesday	
  =	
  UITableView.alloc.init	
  
11	
  	
  	
  	
  	
  rmq.append(@tuesday).layout({	
  t:0,	
  l:422,	
  w:200,	
  h:350})	
  
12	
  	
  
13	
  	
  	
  	
  	
  #	
  ...	
  
14	
  	
  
15	
  	
  	
  	
  	
  @saturday	
  =	
  UITableView.alloc.init	
  
16	
  	
  	
  	
  	
  rmq.append(@saturday).layout({	
  t:354,	
  l:422,	
  w:200,	
  h:350})	
  
17	
  	
  
18	
  	
  	
  	
  	
  @sunday	
  =	
  UITableView.alloc.init	
  
19	
  	
  	
  	
  	
  rmq.append(@sunday).layout({	
  t:354,	
  l:623,	
  w:200,	
  h:350})	
  
20	
  	
  
21	
  	
  	
  	
  	
  #	
  ...	
  
22	
  	
  	
  end	
  
23	
  end
Wiring it up
Let’s give those tableviews a data source
1	
  class	
  TableViewDataSource	
  
2	
  	
  	
  #	
  Implement	
  a	
  simple	
  data	
  source	
  delegate	
  
3	
  end	
  
4	
  	
  
5	
  rmq(UITableView).each	
  do	
  |tv|	
  
6	
  	
  	
  tv.dataSource	
  =	
  TableViewDataSource.new	
  
7	
  end
Live Experimentation
Trying out an inverted colour scheme:
1	
  (main)>	
  rmq(UITableView).each	
  do	
  |tv|	
  
2	
  (main)>	
  	
  	
  tv.backgroundColor	
  =	
  rmq.color.black	
  
3	
  (main)>	
  	
  	
  rmq(tv).find(UITableViewCell).each	
  do	
  |cell|	
  
4	
  (main)>	
  	
  	
  	
  	
  cell.backgroundColor	
  =	
  rmq.color.from_hex("#333")	
  
5	
  (main)>	
  	
  	
  	
  	
  cell.textColor	
  =	
  rmq.color.from_hex("#EEE")	
  
6	
  (main)>	
  	
  	
  end	
  
7	
  (main)>	
  end
Exploring View Hierarchies
	
  1	
  (main)>	
  rmq.log	
  :tree	
  
	
  2	
  	
  
	
  3	
  ───	
  UIView	
  	
  250313120	
  	
  {l:	
  0,	
  t:	
  64,	
  w:	
  1024,	
  h:	
  704}	
  
	
  4	
  	
  	
  	
  	
  ├───	
  UITableView	
  	
  172116992	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  704}	
  
	
  5	
  	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewWrapperView	
  	
  250322496	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  704}	
  
	
  6	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCell	
  	
  250561184	
  	
  {l:	
  0,	
  t:	
  88,	
  w:	
  217,	
  h:	
  44}	
  
	
  7	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCellScrollV	
  	
  250561584	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  44}	
  
	
  8	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCellContent	
  	
  250562720	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  43}	
  
	
  9	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UILabel	
  	
  250563392	
  	
  {l:	
  15,	
  t:	
  0,	
  w:	
  187,	
  h:	
  43}	
  
10	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  _UITableViewCellSepara	
  	
  250564720	
  	
  {l:	
  15,	
  t:	
  43,	
  w:	
  202,	
  h:	
  1}	
  
11	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCell	
  	
  250552688	
  	
  {l:	
  0,	
  t:	
  44,	
  w:	
  217,	
  h:	
  44}	
  
12	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCellScrollV	
  	
  250553088	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  44}	
  
13	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCellContent	
  	
  250554640	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  43}	
  
14	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UILabel	
  	
  250555312	
  	
  {l:	
  15,	
  t:	
  0,	
  w:	
  187,	
  h:	
  43}	
  
15	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  _UITableViewCellSepara	
  	
  250556592	
  	
  {l:	
  15,	
  t:	
  43,	
  w:	
  202,	
  h:	
  1}	
  
16	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCell	
  	
  250531888	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  44}	
  
17	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCellScrollV	
  	
  250533056	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  44}	
  
18	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UITableViewCellContent	
  	
  250533840	
  	
  {l:	
  0,	
  t:	
  0,	
  w:	
  217,	
  h:	
  43}	
  
19	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  UILabel	
  	
  250538544	
  	
  {l:	
  15,	
  t:	
  0,	
  w:	
  187,	
  h:	
  43}	
  
20	
  	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  │	
  	
  	
  	
  ├───	
  _UITableViewCellSepara	
  	
  250543888	
  	
  {l:	
  15,	
  t:	
  43,	
  w:	
  202,	
  h:	
  1}
Useful Helpers
1 # App!
2 !
3 rmq.app.window!
4 rmq.app.delegate!
5 rmq.app.environment!
6 rmq.app.production?!
7 rmq.app.test?!
8 rmq.app.development?!
9 rmq.app.version!
10 rmq.app.name!
11 rmq.app.identifier!
12 rmq.app.resource_path!
13 rmq.app.document_path!
14 !
15
1 # Device!
2 !
3 rmq.device.screen!
4 rmq.device.width # screen width!
5 rmq.device.height # screen height!
6 rmq.device.ipad?!
7 rmq.device.iphone?!
8 rmq.device.four_inch?!
9 rmq.device.retina?!
10 !
11 # return values are :unknown, :portrait,!
12 # :portrait_upside_down, :landscape_left,!
13 # :landscape_right, :face_up, :face_down!
14 rmq.device.orientation!
15 rmq.device.landscape?!
16 rmq.device.portrait?
Why these are not easy to get at in the iOS
SDK is beyond me…
1 class LoginLayout < MotionKit::Layout	
2 include LoginStyles	
3 	
4 def layout	
5 add UIImageView, :logo do	
6 frame [[0, 0], ['100%', :scale]]	
7 end	
8 	
9 add UIView, :button_container do	
10 frame from_bottom(height: 50, width: '100%')	
11 add UIButton, :login_button do	
12 background_color superview.backgroundColor	
13 frame [[ 10, 5 ], [ 50, parent.height - 10 ]]	
14 end	
15 end	
16 	
17 add UIView, :inputs do	
18 frame x: 0, y: 0, width: '100%', height: '100% - 50'	
19 autoresizing_mask :pin_to_top, :flexible_height, :flexible_width	
20 add UITextField, :username_input do	
21 frame [[10, 10], ['100% - 10', :auto]]	
22 end	
23 add UITextField, :password_input do	
24 frame below(:username_input, margin: 8)	
25 end	
26 end	
27 end	
28 end
• Flexible DSL for view
layouts
• Simpler handling of device
rotation
• Support for constraints /
Auto Layout
• Build your own DSL on top
ProMotion
1 class HelpScreen < PM::TableScreen	
2 title "Table Screen"	
3 	
4 def table_data	
5 [{	
6 title: "Help",	
7 cells: [	
8 { title: "About this app", action: :tapped_about },	
9 { title: "Log out", action: :log_out }	
10 ]	
11 }]	
12 end	
13 	
14 def tapped_about(args={})	
15 open AboutScreen	
16 end	
17 	
18 def log_out	
19 # Log out!	
20 end	
21 end
• Aims to remove as much
boilerplate code as possible
• More intuitive, Ruby-style view
controller building
• Built-in classes for common view
types
BubbleWrap
• The first major extension library, contains a wide
array of helpers:
• Camera, JSON handling, notifications, key-value
persistence, location API, message API, SMS,
Timers…
• An extremely easy to use HTTP library for
working with remote APIs
• And more!
SugarCube: Sugar coating for verbose APIs
1	
  (main)>	
  tree	
  
2	
  	
  	
  0:	
  .	
  UIWindow(#d282f80,	
  [[0.0,	
  0.0],	
  [768.0,	
  1024.0]])	
  
3	
  	
  	
  1:	
  `-­‐-­‐	
  UILayoutContainerView(#9d23f70,	
  [[0.0,	
  0.0],	
  [768.0,	
  1024.0]])	
  
4	
  	
  	
  2:	
  	
  	
  	
  	
  +-­‐-­‐	
  UINavigationTransitionView(#a28bf30,	
  [[0.0,	
  0.0],	
  [1024.0,	
  768.0]])	
  
5	
  	
  	
  3:	
  	
  	
  	
  	
  |	
  	
  	
  `-­‐-­‐	
  UIViewControllerWrapperView(#a2c2b20,	
  [[0.0,	
  0.0],	
  [1024.0,	
  768.0]])	
  
6	
  	
  	
  4:	
  	
  	
  	
  	
  |	
  	
  	
  	
  	
  	
  	
  `-­‐-­‐	
  UIView(#a2b23f0,	
  [[0.0,	
  64.0],	
  [1024.0,	
  704.0]])	
  
7	
  	
  	
  5:	
  	
  	
  	
  	
  |	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  +-­‐-­‐	
  UITableView(#aa7f200,	
  [[0.0,	
  0.0],	
  [217.0,	
  704.0]])
1 (main)> a 4 # alias for 'adjust'!
2 => UIView(#a2b23f0, [[0.0, 64.0], [1024.0, 704.0]]), child of
UIViewControllerWrapperView(#a2c2b20)!
3 (main)> d 100 # alias for 'down'!
4 [[0.0, 164.0], [1024.0, 704.0]]!
5 => UIView(#a2b23f0, [[0.0, 164.0], [1024.0, 704.0]]), child of
UIViewControllerWrapperView(#a2c2b20)!
6 (main)> thinner 50!
7 [[0.0, 164.0], [974.0, 704.0]]
Quick aliases for adjusting any view quickly, e.g. up, down, left,
right, thinner, wider, taller, shorter…
SugarCube: Sugar coating for verbose APIs
1 (main)> tree root!
2 0: . #<UINavigationController:0x9d243c0>!
3 1: -- #<RootViewController:0x9d24650>!
4 !
5 => #<UINavigationController:0x9d243c0>!
6 (main)> a 1!
7 => #<RootViewController:0x9d24650>!
8 (main)> $sugarcube_view!
9 => #<RootViewController:0x9d24650>!
10 (main)> $sugarcube_view.any_public_method
Works for view controllers too:
Now when testing a particular method you have the
option to simply invoke it directly.
MotionAwesome
1	
  label(:check_square_o,	
  size:	
  18,	
  text:	
  @items[indexPath.row])	
  do	
  |label|	
  
2	
  	
  	
  view	
  =	
  UIView.alloc.initWithFrame(cell.contentView.frame)	
  
3	
  	
  	
  rmq(label).layout({	
  l:10,	
  t:10,	
  w:200,	
  h:25	
  })	
  
4	
  	
  	
  view.addSubview(label)	
  
5	
  	
  	
  cell.contentView.addSubview(view)	
  
6	
  end
Core Data and RubyMotion
• No equivalent of Xcode’s visual data modeller
• How do I define my data model?!
• What about versioning?!
• How will I handle migrations?
What we need
• Our data model (NSEntityDescriptions +
NSRelationshipDescriptions forming our
NSManagedObject)
• A Core Data Stack (NSManagedObjectModel +
NSPersistentStoreCoordinator +
NSManagedObjectContext)
• A workflow for versioning and migrating
between versions
Defining Our Data Model
• We would normally do this in Xcode
• Visual Editor for .xcdatamodel bundles
• Integrated handling of versioning and custom
migration code
• Automatic lightweight (schema) migrations
• How do we achieve this with RubyMotion?
Options for RubyMotion
• Handle everything programmatically (low
level)
• Use Xcode to work with .xcdatamodel files,
copy in each time
• Use a Ruby library for creating .xcdatamodel
files
Handling Everything
Programmatically
entity = NSEntityDescription.alloc.init	
entity.name = 'Task'	
entity.managedObjectClassName = 'Task'	
entity.properties =	
[ 'task_description', NSStringAttributeType,	
'completed', NSBooleanAttributeType ].each_slice(2).map do |name, type|	
property = NSAttributeDescription.alloc.init	
property.name = name	
property.attributeType = type	
property.optional = false	
property	
end
Handling Everything
Programmatically
entity = NSEntityDescription.alloc.init	
entity.name = 'Task'	
entity.managedObjectClassName = 'Task'	
entity.properties =	
[ 'task_description', NSStringAttributeType,	
'completed', NSBooleanAttributeType ].each_slice(2).map do |name, type|	
property = NSAttributeDescription.alloc.init	
property.name = name	
property.attributeType = type	
property.optional = false	
property	
end
Not all that bad, but we want to use .xcdatamodel files so
that we can benefit from versioning, automatic schema
migrations…
.xcdatamodel files are just
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>!
<model name="" userDefinedModelVersionIdentifier="001"
type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0"
lastSavedToolsVersion="2061" systemVersion="12D78"
minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic"
iOSVersion="Automatic">!
<entity name="Article" syncable="YES">!
<attribute name="title" optional="YES" attributeType="String"
syncable="YES"/>!
<relationship name="author" optional="YES" minCount="1"
maxCount="1" deletionRule="Nullify" destinationEntity="Author"
inverseName="articles" inverseEntity="Article" syncable="YES"/>!
</entity>!
<entity name="Author" syncable="YES">!
<attribute name="name" optional="YES" attributeType="String"
syncable="YES"/>!
<relationship name="articles" optional="YES" minCount="1"
maxCount="1" deletionRule="Nullify" destinationEntity="Article"
inverseName="author" inverseEntity="Author" syncable="YES"/>!
</entity>!
</model>
Using a library to generate
.xcdatamodel files (ruby-xcdm)
1 schema "001" do!
2 entity "Article" do!
3 string :body, optional: false!
4 integer32 :length!
5 boolean :published, default: false!
6 datetime :publishedAt, default: false!
7 string :title, optional: false!
8 !
9 belongs_to :author!
10 end!
11 !
12 entity "Author" do!
13 float :fee!
14 string :name, optional: false!
15 has_many :articles!
16 end!
17 end
Workflow
• Create schema file in schemas directory, e.g.
schemas/001_initial.rb
• Build the schema
• Add a new schema version, e.g.
002_add_new_fields.rb
• Rebuild the schema
• That’s it!
Workflow
$ echo "gem 'ruby-xcdm', '0.0.5'" >> Gemfile	
$ bundle install	
$ rake schema:build	
Generating Data Model learn-xcdm	
Loading schemas/001_initial.rb	
Writing resources/learn-xcdm.xcdatamodeld/1.xcdatamodel/
contents	
$ rake # The default rake task is to run the app in the simulator	
(main)> mom = NSManagedObjectModel.mergedModelFromBundles(nil)	
=> #<NSManagedObjectModel:0x8fa7690>	
(main)> mom.entities.count	
=> 2	
(main)> mom.entities.first.name	
=> "Article"	
(main)> mom.entities.first.propertiesByName	
=> {"body"=>#<NSAttributeDescription:0x8e5db30>,
"title"=>#<NSAttributeDescription:0x8ea4770>}
Advantages of using ruby-
xcdm
• No magic: generates XML from a schema
• Schema versions are fully text-based and
readable, making them well-suited to version
control
• Can compile our versions into .xcdatamodeld
bundles, completely removing dependence on
Xcode
Basic Core Data Stack
1 model = NSManagedObjectModel.mergedModelFromBundles(nil)	
2 	
3 store = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(model)	
4 store_path = File.join(NSHomeDirectory(), 'Documents', 'LearnXcdm.sqlite')	
5 store_url = NSURL.fileURLWithPath(store_path)	
6 	
7 options = { NSMigratePersistentStoresAutomaticallyOption => true,	
8 NSInferMappingModelAutomaticallyOption => true }	
9 	
10 error_ptr = Pointer.new(:object)	
11 	
12 unless store.addPersistentStoreWithType(NSSQLiteStoreType,	
13 configuration: nil,	
14 URL: store_url,	
15 options: options,	
16 error: error_ptr)	
17 raise "[ERROR] Failed to create persistent store: #{error_ptr[0].description}"	
18 end	
19 	
20 @context = NSManagedObjectContext.alloc.init	
21 @context.persistentStoreCoordinator = store
Core Data Query
• From the developers of ruby-xcdm and
RubyMotionQuery (RMQ)
• Abstracts away much of the complexity of Core
Data
• All you need is your .xcdatamodeld bundle (that
we just created using ruby-xcdm)
Core Data Query in Action
# app/models/task.rb	
class Task < CDQManagedObject	
end	
!
# app/app_delegate.rb	
class AppDelegate	
include CDQ	
!
def application(application,
didFinishLaunchingWithOptions:launchOptions)	
cdq.setup	
true	
end	
end
Core Data Query in Action
(main)> Task.count	
=> 0	
(main)> t1 = Task.create(task_description: "Complete presentation")	
(main)> t2 = Task.create(task_description: "File tax return")	
(main)> cdq.save	
=> true	
(main)> exit	
$ rake	
...	
(main)> Task.count	
=> 2	
(main)> t1, t2 = Task.all.array	
(main)> t1.task_description	
=> "Complete chapter"	
(main)> t2.task_description	
=> "File tax return"	
(main)> t2.destroy	
=> #<NSManagedObjectContext:0x914cbe0>	
(main)> cdq.save	
=> true	
(main)> Task.count	
=> 1
Author.where(:name).eq("Emily")	
Author.where(:name).not_equal("Emily")	
Author.limit(1)	
Author.offset(10)	
Author.where(:name).contains("A").offset(10).first	
!
# Conjuctions	
Author.where(:name).contains("Emily").and.contains("Dickinson")	
Author.where(:name).starts_with("E").or(:pub_count).eq(1)	
!
# Nested Conjuctions	
Author.where(:name).contains("Emily").and(cdq(:pub_count).gt(100).or.lt(10)
)	
!
# Relationships	
Author.first.publications.offset(2).limit(1)	
cdq(emily_dickinson).publications.where(:type).eq('poetry')	
!
class Author < CDQManagedObject	
scope :prolific, where(:pub_count).gt(50)	
end
Core Data Query in Action
Curious about Core Data
and RubyMotion?
A book on this is available on
Leanpub, covers how to use the
Core Data stack with RubyMotion
and how to use libraries like
CoreDataQuery to make
developing Core Data-driven apps
easier.
RubyMotion
vs
Swift
• Entirely complementary: there’s a place for both
• I will be coding in Swift instead of Obj-C
• Not instead of Ruby(Motion)…
• Same base (LLVM), can use best tool for the job
in each case.
Which library?
• Live development: RubyMotionQuery + SugarCube
• View layout and styling: MotionKit
• Project structure: ProMotion
• Core Data: ruby-xcdm + Core Data Query (CDQ)!
• Testing, mocking: motion-stump!
• Anything else: try Sugarcube and BubbleWrap
Next Steps
• In the coming weeks I’ll be researching and writing about:
• Libraries that I didn’t cover in depth today such as MotionKit,
ProMotion, BubbleWrap and motion-stump.
• How to best handle heavyweight/data migrations in RubyMotion
(Core Data)
• Deconstructing the one bit of ‘magic’ in Core Data Query
• How to write a ruby gem and contribute to the RubyMotion
ecosystem.
Stefán Hafliðason
http://stefan.haflidason.com
@styrmis

More Related Content

What's hot

Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...Luciano Mammino
 
Reactive microservices with Micronaut - GR8Conf EU 2018
Reactive microservices with Micronaut - GR8Conf EU 2018Reactive microservices with Micronaut - GR8Conf EU 2018
Reactive microservices with Micronaut - GR8Conf EU 2018Alvaro Sanchez-Mariscal
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jscacois
 
There and back again: A story of a s
There and back again: A story of a sThere and back again: A story of a s
There and back again: A story of a sColin Harrington
 
Reactive microservices with Micronaut - Greach 2018
Reactive microservices with Micronaut - Greach 2018Reactive microservices with Micronaut - Greach 2018
Reactive microservices with Micronaut - Greach 2018Alvaro Sanchez-Mariscal
 
Release with confidence
Release with confidenceRelease with confidence
Release with confidenceJohn Congdon
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3Luciano Mammino
 
The Ring programming language version 1.6 book - Part 79 of 189
The Ring programming language version 1.6 book - Part 79 of 189The Ring programming language version 1.6 book - Part 79 of 189
The Ring programming language version 1.6 book - Part 79 of 189Mahmoud Samir Fayed
 
G*なクラウド 雲のかなたに ショートバージョン
G*なクラウド 雲のかなたに ショートバージョンG*なクラウド 雲のかなたに ショートバージョン
G*なクラウド 雲のかなたに ショートバージョンTsuyoshi Yamamoto
 
Callbacks and control flow in Node js
Callbacks and control flow in Node jsCallbacks and control flow in Node js
Callbacks and control flow in Node jsThomas Roch
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promisesAvoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promisesAnkit Agarwal
 
SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)Robert Swisher
 
Callbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascriptCallbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascriptŁukasz Kużyński
 
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with easeGDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with easeKAI CHU CHUNG
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsPiotr Pelczar
 
Hacking pokemon go [droidcon tel aviv 2016]
Hacking pokemon go [droidcon tel aviv 2016]Hacking pokemon go [droidcon tel aviv 2016]
Hacking pokemon go [droidcon tel aviv 2016]Guy Lis
 
Static sites with react
Static sites with reactStatic sites with react
Static sites with reactRobert Pearce
 

What's hot (20)

Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
Introducing Middy, Node.js middleware engine for AWS Lambda (FrontConf Munich...
 
Reactive microservices with Micronaut - GR8Conf EU 2018
Reactive microservices with Micronaut - GR8Conf EU 2018Reactive microservices with Micronaut - GR8Conf EU 2018
Reactive microservices with Micronaut - GR8Conf EU 2018
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
 
There and back again: A story of a s
There and back again: A story of a sThere and back again: A story of a s
There and back again: A story of a s
 
Reactive microservices with Micronaut - Greach 2018
Reactive microservices with Micronaut - Greach 2018Reactive microservices with Micronaut - Greach 2018
Reactive microservices with Micronaut - Greach 2018
 
Release with confidence
Release with confidenceRelease with confidence
Release with confidence
 
How to send gzipped requests with boto3
How to send gzipped requests with boto3How to send gzipped requests with boto3
How to send gzipped requests with boto3
 
The Ring programming language version 1.6 book - Part 79 of 189
The Ring programming language version 1.6 book - Part 79 of 189The Ring programming language version 1.6 book - Part 79 of 189
The Ring programming language version 1.6 book - Part 79 of 189
 
Varnish kann alles
Varnish kann allesVarnish kann alles
Varnish kann alles
 
G*なクラウド 雲のかなたに ショートバージョン
G*なクラウド 雲のかなたに ショートバージョンG*なクラウド 雲のかなたに ショートバージョン
G*なクラウド 雲のかなたに ショートバージョン
 
非同期javascriptの過去と未来
非同期javascriptの過去と未来非同期javascriptの過去と未来
非同期javascriptの過去と未来
 
Callbacks and control flow in Node js
Callbacks and control flow in Node jsCallbacks and control flow in Node js
Callbacks and control flow in Node js
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promisesAvoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promises
 
SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)SDPHP - Percona Toolkit (It's Basically Magic)
SDPHP - Percona Toolkit (It's Basically Magic)
 
Callbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascriptCallbacks, promises, generators - asynchronous javascript
Callbacks, promises, generators - asynchronous javascript
 
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with easeGDG Devfest 2019 - Build go kit microservices at kubernetes with ease
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
 
Hacking pokemon go [droidcon tel aviv 2016]
Hacking pokemon go [droidcon tel aviv 2016]Hacking pokemon go [droidcon tel aviv 2016]
Hacking pokemon go [droidcon tel aviv 2016]
 
Static sites with react
Static sites with reactStatic sites with react
Static sites with react
 
Alt.Net Presentation
Alt.Net PresentationAlt.Net Presentation
Alt.Net Presentation
 

Similar to Rapid App Development with RubyMotion

LambHack: A Vulnerable Serverless Application
LambHack: A Vulnerable Serverless ApplicationLambHack: A Vulnerable Serverless Application
LambHack: A Vulnerable Serverless ApplicationJames Wickett
 
Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Massimo Oliviero
 
Troubleshooting tips from docker support engineers
Troubleshooting tips from docker support engineersTroubleshooting tips from docker support engineers
Troubleshooting tips from docker support engineersDocker, Inc.
 
OpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignOpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignAltoros
 
Collaborative Workflow Development and Experimentation in the Digital Humanities
Collaborative Workflow Development and Experimentation in the Digital HumanitiesCollaborative Workflow Development and Experimentation in the Digital Humanities
Collaborative Workflow Development and Experimentation in the Digital Humanitiescneudecker
 
JavaScript Libraries: The Big Picture
JavaScript Libraries: The Big PictureJavaScript Libraries: The Big Picture
JavaScript Libraries: The Big PictureSimon Willison
 
LISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial HandoutsLISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial HandoutsTobias Oetiker
 
Play framework 2 : Peter Hilton
Play framework 2 : Peter HiltonPlay framework 2 : Peter Hilton
Play framework 2 : Peter HiltonJAX London
 
JavaFX - Next Generation Java UI
JavaFX - Next Generation Java UIJavaFX - Next Generation Java UI
JavaFX - Next Generation Java UIYoav Aharoni
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftChris Bailey
 
jQuery Mobile & PhoneGap
jQuery Mobile & PhoneGapjQuery Mobile & PhoneGap
jQuery Mobile & PhoneGapSwiip
 
Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015
Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015
Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015Windows Developer
 
Using Puppet - Real World Configuration Management
Using Puppet - Real World Configuration ManagementUsing Puppet - Real World Configuration Management
Using Puppet - Real World Configuration ManagementJames Turnbull
 
Beyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic AnalysisBeyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic AnalysisC4Media
 
Intravert Server side processing for Cassandra
Intravert Server side processing for CassandraIntravert Server side processing for Cassandra
Intravert Server side processing for CassandraEdward Capriolo
 
NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"
NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"
NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"DataStax Academy
 
Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek PROIDEA
 

Similar to Rapid App Development with RubyMotion (20)

Rails israel 2013
Rails israel 2013Rails israel 2013
Rails israel 2013
 
LambHack: A Vulnerable Serverless Application
LambHack: A Vulnerable Serverless ApplicationLambHack: A Vulnerable Serverless Application
LambHack: A Vulnerable Serverless Application
 
Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)Advanced iOS Debbuging (Reloaded)
Advanced iOS Debbuging (Reloaded)
 
Troubleshooting tips from docker support engineers
Troubleshooting tips from docker support engineersTroubleshooting tips from docker support engineers
Troubleshooting tips from docker support engineers
 
OpenWhisk: Event-driven Design
OpenWhisk: Event-driven DesignOpenWhisk: Event-driven Design
OpenWhisk: Event-driven Design
 
Collaborative Workflow Development and Experimentation in the Digital Humanities
Collaborative Workflow Development and Experimentation in the Digital HumanitiesCollaborative Workflow Development and Experimentation in the Digital Humanities
Collaborative Workflow Development and Experimentation in the Digital Humanities
 
JavaScript Libraries: The Big Picture
JavaScript Libraries: The Big PictureJavaScript Libraries: The Big Picture
JavaScript Libraries: The Big Picture
 
LISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial HandoutsLISA Qooxdoo Tutorial Handouts
LISA Qooxdoo Tutorial Handouts
 
Play framework 2 : Peter Hilton
Play framework 2 : Peter HiltonPlay framework 2 : Peter Hilton
Play framework 2 : Peter Hilton
 
JavaFX - Next Generation Java UI
JavaFX - Next Generation Java UIJavaFX - Next Generation Java UI
JavaFX - Next Generation Java UI
 
Docker In Bank Unrated
Docker In Bank UnratedDocker In Bank Unrated
Docker In Bank Unrated
 
FrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) SwiftFrenchKit 2017: Server(less) Swift
FrenchKit 2017: Server(less) Swift
 
Node azure
Node azureNode azure
Node azure
 
jQuery Mobile & PhoneGap
jQuery Mobile & PhoneGapjQuery Mobile & PhoneGap
jQuery Mobile & PhoneGap
 
Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015
Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015
Build 2016 - B880 - Top 6 Reasons to Move Your C++ Code to Visual Studio 2015
 
Using Puppet - Real World Configuration Management
Using Puppet - Real World Configuration ManagementUsing Puppet - Real World Configuration Management
Using Puppet - Real World Configuration Management
 
Beyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic AnalysisBeyond Breakpoints: A Tour of Dynamic Analysis
Beyond Breakpoints: A Tour of Dynamic Analysis
 
Intravert Server side processing for Cassandra
Intravert Server side processing for CassandraIntravert Server side processing for Cassandra
Intravert Server side processing for Cassandra
 
NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"
NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"
NYC* 2013 - "Advanced Data Processing: Beyond Queries and Slices"
 
Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek Docker Logging and analysing with Elastic Stack - Jakub Hajek
Docker Logging and analysing with Elastic Stack - Jakub Hajek
 

Recently uploaded

Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number SystemsJheuzeDellosa
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfjoe51371421
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxComplianceQuest1
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfkalichargn70th171
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyFrank van der Linden
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...soniya singh
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 

Recently uploaded (20)

Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
What is Binary Language? Computer Number Systems
What is Binary Language?  Computer Number SystemsWhat is Binary Language?  Computer Number Systems
What is Binary Language? Computer Number Systems
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
why an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdfwhy an Opensea Clone Script might be your perfect match.pdf
why an Opensea Clone Script might be your perfect match.pdf
 
Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
A Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docxA Secure and Reliable Document Management System is Essential.docx
A Secure and Reliable Document Management System is Essential.docx
 
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdfThe Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
The Essentials of Digital Experience Monitoring_ A Comprehensive Guide.pdf
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Engage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The UglyEngage Usergroup 2024 - The Good The Bad_The Ugly
Engage Usergroup 2024 - The Good The Bad_The Ugly
 
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
Russian Call Girls in Karol Bagh Aasnvi ➡️ 8264348440 💋📞 Independent Escort S...
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 

Rapid App Development with RubyMotion

  • 1. Rapid App Development with RubyMotion Stefán Hafliðason http://stefan.haflidason.com @styrmis External libraries that make development even easier
  • 2. • Promises increased developer productivity • Brings the flexibility of Ruby to iOS and OSX development • Bridges directly to Obj-C libraries: no intermediate glue code • A REPL for working with your app live! • Make tweaks quickly • Build whole views programmatically on the fly Why RubyMotion?
  • 3. RubyMotion + 3rd Party Libs • Stock RubyMotion makes life (generally) easier • Like Rails, there’s a healthy (and growing) ecosystem of libraries • These libraries can help speed up development even further • If the cost of experimenting is reduced, we’re more likely to try out new ideas, and that’s my goal.
  • 4. What are we looking for? • Non-Polluting! • Zero/Minimal pollution of our current namespace. • Minimal Magic! • Because when magic breaks, we’ll need to fix it. • Allows fallback to ‘plain’ RubyMotion • There when you need it, unobtrusive when you don’t.
  • 5. The Libraries • View management: Ruby Motion Query (RMQ), MotionKit • More Ruby-like view creation: ProMotion! • Core Data: ruby-xcdm + Core Data Query (CDQ) • Various helpers: BubbleWrap, SugarCube, MotionAwesome
  • 6. Ruby Motion Query • “It’s like jQuery for RubyMotion. Stylesheets, templates, events, and more” • Supercharges the REPL/console • Easy to add, access and modify views • Also: event binding, styling, animation and more.
  • 7. Layout Experimentation Quickly laying out 8 table views on the screen (iPad). Started in the console, then moved code into the app itself.  1  class  RootViewController  <  UIViewController    2      def  viewDidLoad    3          #  Start  by  laying  out  with  RMQ    4          @week  =  UITableView.alloc.init    5          rmq.append(@week).layout({  t:0,  l:0,  w:217,  h:704})    6      7          @monday  =  UITableView.alloc.init    8          rmq.append(@monday).layout({  t:0,  l:221,  w:200,  h:350})    9     10          @tuesday  =  UITableView.alloc.init   11          rmq.append(@tuesday).layout({  t:0,  l:422,  w:200,  h:350})   12     13          #  ...   14     15          @saturday  =  UITableView.alloc.init   16          rmq.append(@saturday).layout({  t:354,  l:422,  w:200,  h:350})   17     18          @sunday  =  UITableView.alloc.init   19          rmq.append(@sunday).layout({  t:354,  l:623,  w:200,  h:350})   20     21          #  ...   22      end   23  end
  • 8. Wiring it up Let’s give those tableviews a data source 1  class  TableViewDataSource   2      #  Implement  a  simple  data  source  delegate   3  end   4     5  rmq(UITableView).each  do  |tv|   6      tv.dataSource  =  TableViewDataSource.new   7  end
  • 9. Live Experimentation Trying out an inverted colour scheme: 1  (main)>  rmq(UITableView).each  do  |tv|   2  (main)>      tv.backgroundColor  =  rmq.color.black   3  (main)>      rmq(tv).find(UITableViewCell).each  do  |cell|   4  (main)>          cell.backgroundColor  =  rmq.color.from_hex("#333")   5  (main)>          cell.textColor  =  rmq.color.from_hex("#EEE")   6  (main)>      end   7  (main)>  end
  • 10. Exploring View Hierarchies  1  (main)>  rmq.log  :tree    2      3  ───  UIView    250313120    {l:  0,  t:  64,  w:  1024,  h:  704}    4          ├───  UITableView    172116992    {l:  0,  t:  0,  w:  217,  h:  704}    5          │        ├───  UITableViewWrapperView    250322496    {l:  0,  t:  0,  w:  217,  h:  704}    6          │        │        ├───  UITableViewCell    250561184    {l:  0,  t:  88,  w:  217,  h:  44}    7          │        │        │        ├───  UITableViewCellScrollV    250561584    {l:  0,  t:  0,  w:  217,  h:  44}    8          │        │        │        │        ├───  UITableViewCellContent    250562720    {l:  0,  t:  0,  w:  217,  h:  43}    9          │        │        │        │        │        ├───  UILabel    250563392    {l:  15,  t:  0,  w:  187,  h:  43}   10          │        │        │        │        ├───  _UITableViewCellSepara    250564720    {l:  15,  t:  43,  w:  202,  h:  1}   11          │        │        ├───  UITableViewCell    250552688    {l:  0,  t:  44,  w:  217,  h:  44}   12          │        │        │        ├───  UITableViewCellScrollV    250553088    {l:  0,  t:  0,  w:  217,  h:  44}   13          │        │        │        │        ├───  UITableViewCellContent    250554640    {l:  0,  t:  0,  w:  217,  h:  43}   14          │        │        │        │        │        ├───  UILabel    250555312    {l:  15,  t:  0,  w:  187,  h:  43}   15          │        │        │        │        ├───  _UITableViewCellSepara    250556592    {l:  15,  t:  43,  w:  202,  h:  1}   16          │        │        ├───  UITableViewCell    250531888    {l:  0,  t:  0,  w:  217,  h:  44}   17          │        │        │        ├───  UITableViewCellScrollV    250533056    {l:  0,  t:  0,  w:  217,  h:  44}   18          │        │        │        │        ├───  UITableViewCellContent    250533840    {l:  0,  t:  0,  w:  217,  h:  43}   19          │        │        │        │        │        ├───  UILabel    250538544    {l:  15,  t:  0,  w:  187,  h:  43}   20          │        │        │        │        ├───  _UITableViewCellSepara    250543888    {l:  15,  t:  43,  w:  202,  h:  1}
  • 11. Useful Helpers 1 # App! 2 ! 3 rmq.app.window! 4 rmq.app.delegate! 5 rmq.app.environment! 6 rmq.app.production?! 7 rmq.app.test?! 8 rmq.app.development?! 9 rmq.app.version! 10 rmq.app.name! 11 rmq.app.identifier! 12 rmq.app.resource_path! 13 rmq.app.document_path! 14 ! 15 1 # Device! 2 ! 3 rmq.device.screen! 4 rmq.device.width # screen width! 5 rmq.device.height # screen height! 6 rmq.device.ipad?! 7 rmq.device.iphone?! 8 rmq.device.four_inch?! 9 rmq.device.retina?! 10 ! 11 # return values are :unknown, :portrait,! 12 # :portrait_upside_down, :landscape_left,! 13 # :landscape_right, :face_up, :face_down! 14 rmq.device.orientation! 15 rmq.device.landscape?! 16 rmq.device.portrait? Why these are not easy to get at in the iOS SDK is beyond me…
  • 12. 1 class LoginLayout < MotionKit::Layout 2 include LoginStyles 3 4 def layout 5 add UIImageView, :logo do 6 frame [[0, 0], ['100%', :scale]] 7 end 8 9 add UIView, :button_container do 10 frame from_bottom(height: 50, width: '100%') 11 add UIButton, :login_button do 12 background_color superview.backgroundColor 13 frame [[ 10, 5 ], [ 50, parent.height - 10 ]] 14 end 15 end 16 17 add UIView, :inputs do 18 frame x: 0, y: 0, width: '100%', height: '100% - 50' 19 autoresizing_mask :pin_to_top, :flexible_height, :flexible_width 20 add UITextField, :username_input do 21 frame [[10, 10], ['100% - 10', :auto]] 22 end 23 add UITextField, :password_input do 24 frame below(:username_input, margin: 8) 25 end 26 end 27 end 28 end • Flexible DSL for view layouts • Simpler handling of device rotation • Support for constraints / Auto Layout • Build your own DSL on top
  • 13. ProMotion 1 class HelpScreen < PM::TableScreen 2 title "Table Screen" 3 4 def table_data 5 [{ 6 title: "Help", 7 cells: [ 8 { title: "About this app", action: :tapped_about }, 9 { title: "Log out", action: :log_out } 10 ] 11 }] 12 end 13 14 def tapped_about(args={}) 15 open AboutScreen 16 end 17 18 def log_out 19 # Log out! 20 end 21 end • Aims to remove as much boilerplate code as possible • More intuitive, Ruby-style view controller building • Built-in classes for common view types
  • 14. BubbleWrap • The first major extension library, contains a wide array of helpers: • Camera, JSON handling, notifications, key-value persistence, location API, message API, SMS, Timers… • An extremely easy to use HTTP library for working with remote APIs • And more!
  • 15. SugarCube: Sugar coating for verbose APIs 1  (main)>  tree   2      0:  .  UIWindow(#d282f80,  [[0.0,  0.0],  [768.0,  1024.0]])   3      1:  `-­‐-­‐  UILayoutContainerView(#9d23f70,  [[0.0,  0.0],  [768.0,  1024.0]])   4      2:          +-­‐-­‐  UINavigationTransitionView(#a28bf30,  [[0.0,  0.0],  [1024.0,  768.0]])   5      3:          |      `-­‐-­‐  UIViewControllerWrapperView(#a2c2b20,  [[0.0,  0.0],  [1024.0,  768.0]])   6      4:          |              `-­‐-­‐  UIView(#a2b23f0,  [[0.0,  64.0],  [1024.0,  704.0]])   7      5:          |                      +-­‐-­‐  UITableView(#aa7f200,  [[0.0,  0.0],  [217.0,  704.0]]) 1 (main)> a 4 # alias for 'adjust'! 2 => UIView(#a2b23f0, [[0.0, 64.0], [1024.0, 704.0]]), child of UIViewControllerWrapperView(#a2c2b20)! 3 (main)> d 100 # alias for 'down'! 4 [[0.0, 164.0], [1024.0, 704.0]]! 5 => UIView(#a2b23f0, [[0.0, 164.0], [1024.0, 704.0]]), child of UIViewControllerWrapperView(#a2c2b20)! 6 (main)> thinner 50! 7 [[0.0, 164.0], [974.0, 704.0]] Quick aliases for adjusting any view quickly, e.g. up, down, left, right, thinner, wider, taller, shorter…
  • 16. SugarCube: Sugar coating for verbose APIs 1 (main)> tree root! 2 0: . #<UINavigationController:0x9d243c0>! 3 1: -- #<RootViewController:0x9d24650>! 4 ! 5 => #<UINavigationController:0x9d243c0>! 6 (main)> a 1! 7 => #<RootViewController:0x9d24650>! 8 (main)> $sugarcube_view! 9 => #<RootViewController:0x9d24650>! 10 (main)> $sugarcube_view.any_public_method Works for view controllers too: Now when testing a particular method you have the option to simply invoke it directly.
  • 17. MotionAwesome 1  label(:check_square_o,  size:  18,  text:  @items[indexPath.row])  do  |label|   2      view  =  UIView.alloc.initWithFrame(cell.contentView.frame)   3      rmq(label).layout({  l:10,  t:10,  w:200,  h:25  })   4      view.addSubview(label)   5      cell.contentView.addSubview(view)   6  end
  • 18. Core Data and RubyMotion • No equivalent of Xcode’s visual data modeller • How do I define my data model?! • What about versioning?! • How will I handle migrations?
  • 19. What we need • Our data model (NSEntityDescriptions + NSRelationshipDescriptions forming our NSManagedObject) • A Core Data Stack (NSManagedObjectModel + NSPersistentStoreCoordinator + NSManagedObjectContext) • A workflow for versioning and migrating between versions
  • 20. Defining Our Data Model • We would normally do this in Xcode • Visual Editor for .xcdatamodel bundles • Integrated handling of versioning and custom migration code • Automatic lightweight (schema) migrations • How do we achieve this with RubyMotion?
  • 21. Options for RubyMotion • Handle everything programmatically (low level) • Use Xcode to work with .xcdatamodel files, copy in each time • Use a Ruby library for creating .xcdatamodel files
  • 22. Handling Everything Programmatically entity = NSEntityDescription.alloc.init entity.name = 'Task' entity.managedObjectClassName = 'Task' entity.properties = [ 'task_description', NSStringAttributeType, 'completed', NSBooleanAttributeType ].each_slice(2).map do |name, type| property = NSAttributeDescription.alloc.init property.name = name property.attributeType = type property.optional = false property end
  • 23. Handling Everything Programmatically entity = NSEntityDescription.alloc.init entity.name = 'Task' entity.managedObjectClassName = 'Task' entity.properties = [ 'task_description', NSStringAttributeType, 'completed', NSBooleanAttributeType ].each_slice(2).map do |name, type| property = NSAttributeDescription.alloc.init property.name = name property.attributeType = type property.optional = false property end Not all that bad, but we want to use .xcdatamodel files so that we can benefit from versioning, automatic schema migrations…
  • 24. .xcdatamodel files are just XML <?xml version="1.0" encoding="UTF-8" standalone="yes"?>! <model name="" userDefinedModelVersionIdentifier="001" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="2061" systemVersion="12D78" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">! <entity name="Article" syncable="YES">! <attribute name="title" optional="YES" attributeType="String" syncable="YES"/>! <relationship name="author" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="Author" inverseName="articles" inverseEntity="Article" syncable="YES"/>! </entity>! <entity name="Author" syncable="YES">! <attribute name="name" optional="YES" attributeType="String" syncable="YES"/>! <relationship name="articles" optional="YES" minCount="1" maxCount="1" deletionRule="Nullify" destinationEntity="Article" inverseName="author" inverseEntity="Author" syncable="YES"/>! </entity>! </model>
  • 25. Using a library to generate .xcdatamodel files (ruby-xcdm) 1 schema "001" do! 2 entity "Article" do! 3 string :body, optional: false! 4 integer32 :length! 5 boolean :published, default: false! 6 datetime :publishedAt, default: false! 7 string :title, optional: false! 8 ! 9 belongs_to :author! 10 end! 11 ! 12 entity "Author" do! 13 float :fee! 14 string :name, optional: false! 15 has_many :articles! 16 end! 17 end
  • 26. Workflow • Create schema file in schemas directory, e.g. schemas/001_initial.rb • Build the schema • Add a new schema version, e.g. 002_add_new_fields.rb • Rebuild the schema • That’s it!
  • 27. Workflow $ echo "gem 'ruby-xcdm', '0.0.5'" >> Gemfile $ bundle install $ rake schema:build Generating Data Model learn-xcdm Loading schemas/001_initial.rb Writing resources/learn-xcdm.xcdatamodeld/1.xcdatamodel/ contents $ rake # The default rake task is to run the app in the simulator (main)> mom = NSManagedObjectModel.mergedModelFromBundles(nil) => #<NSManagedObjectModel:0x8fa7690> (main)> mom.entities.count => 2 (main)> mom.entities.first.name => "Article" (main)> mom.entities.first.propertiesByName => {"body"=>#<NSAttributeDescription:0x8e5db30>, "title"=>#<NSAttributeDescription:0x8ea4770>}
  • 28. Advantages of using ruby- xcdm • No magic: generates XML from a schema • Schema versions are fully text-based and readable, making them well-suited to version control • Can compile our versions into .xcdatamodeld bundles, completely removing dependence on Xcode
  • 29. Basic Core Data Stack 1 model = NSManagedObjectModel.mergedModelFromBundles(nil) 2 3 store = NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(model) 4 store_path = File.join(NSHomeDirectory(), 'Documents', 'LearnXcdm.sqlite') 5 store_url = NSURL.fileURLWithPath(store_path) 6 7 options = { NSMigratePersistentStoresAutomaticallyOption => true, 8 NSInferMappingModelAutomaticallyOption => true } 9 10 error_ptr = Pointer.new(:object) 11 12 unless store.addPersistentStoreWithType(NSSQLiteStoreType, 13 configuration: nil, 14 URL: store_url, 15 options: options, 16 error: error_ptr) 17 raise "[ERROR] Failed to create persistent store: #{error_ptr[0].description}" 18 end 19 20 @context = NSManagedObjectContext.alloc.init 21 @context.persistentStoreCoordinator = store
  • 30. Core Data Query • From the developers of ruby-xcdm and RubyMotionQuery (RMQ) • Abstracts away much of the complexity of Core Data • All you need is your .xcdatamodeld bundle (that we just created using ruby-xcdm)
  • 31. Core Data Query in Action # app/models/task.rb class Task < CDQManagedObject end ! # app/app_delegate.rb class AppDelegate include CDQ ! def application(application, didFinishLaunchingWithOptions:launchOptions) cdq.setup true end end
  • 32. Core Data Query in Action (main)> Task.count => 0 (main)> t1 = Task.create(task_description: "Complete presentation") (main)> t2 = Task.create(task_description: "File tax return") (main)> cdq.save => true (main)> exit $ rake ... (main)> Task.count => 2 (main)> t1, t2 = Task.all.array (main)> t1.task_description => "Complete chapter" (main)> t2.task_description => "File tax return" (main)> t2.destroy => #<NSManagedObjectContext:0x914cbe0> (main)> cdq.save => true (main)> Task.count => 1
  • 33. Author.where(:name).eq("Emily") Author.where(:name).not_equal("Emily") Author.limit(1) Author.offset(10) Author.where(:name).contains("A").offset(10).first ! # Conjuctions Author.where(:name).contains("Emily").and.contains("Dickinson") Author.where(:name).starts_with("E").or(:pub_count).eq(1) ! # Nested Conjuctions Author.where(:name).contains("Emily").and(cdq(:pub_count).gt(100).or.lt(10) ) ! # Relationships Author.first.publications.offset(2).limit(1) cdq(emily_dickinson).publications.where(:type).eq('poetry') ! class Author < CDQManagedObject scope :prolific, where(:pub_count).gt(50) end Core Data Query in Action
  • 34. Curious about Core Data and RubyMotion? A book on this is available on Leanpub, covers how to use the Core Data stack with RubyMotion and how to use libraries like CoreDataQuery to make developing Core Data-driven apps easier.
  • 35. RubyMotion vs Swift • Entirely complementary: there’s a place for both • I will be coding in Swift instead of Obj-C • Not instead of Ruby(Motion)… • Same base (LLVM), can use best tool for the job in each case.
  • 36. Which library? • Live development: RubyMotionQuery + SugarCube • View layout and styling: MotionKit • Project structure: ProMotion • Core Data: ruby-xcdm + Core Data Query (CDQ)! • Testing, mocking: motion-stump! • Anything else: try Sugarcube and BubbleWrap
  • 37. Next Steps • In the coming weeks I’ll be researching and writing about: • Libraries that I didn’t cover in depth today such as MotionKit, ProMotion, BubbleWrap and motion-stump. • How to best handle heavyweight/data migrations in RubyMotion (Core Data) • Deconstructing the one bit of ‘magic’ in Core Data Query • How to write a ruby gem and contribute to the RubyMotion ecosystem. Stefán Hafliðason http://stefan.haflidason.com @styrmis