RubyMotion is great for quickly prototyping apps but it lacks the data modelling tools that Xcode provides. Luckily, using Core Data with RubyMotion can actually be easier and quicker with a little help from some 3rd party libraries.
2. Why RubyMotion?
• 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
3. Why Core Data?
• Optimised for low-memory/embedded (iOS)
devices
• Mature data access/persistence framework
• Also available on OSX
• Works with iCloud—free cloud syncing for your
app
4. Core Data is Difficult
• Provided boilerplate code unnecessarily complex
• An object graph that’s persisted to an SQLite
database
• Suggests relational access, which is not quite
the case
• Typical patterns for working with relational data
are not optimal here
5. RubyMotion is “Easy”
• Friendliness of Ruby
• An ARC equivalent is included
• Lots of work done to abstract complexity away
• More concepts similar to other OO languages
6. 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?
7. 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
8. 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?
9. 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
10. 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
11. 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
14. 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!
15. 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>}
16. 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
18. Core Data Query
• From the developers of ruby-xcdm
• Abstracts away much of the complexity of Core
Data
• All you need is your .xcdatamodeld bundle
19. 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
21. Core Data in Action
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
22. Takeaways
• Don’t be put off by the Xcode boilerplate: Core
Data doesn’t have to be that hard
• With CDQ, Core Data is arguably easier to use
with RubyMotion rather than harder
• XCDM, CDQ and RubyMotion Query (all by
Infinitered) are all worth taking a look at
23. Next Steps
• In the coming weeks I’ll be researching and writing
about:
• How to best handle heavyweight/data migrations
in RubyMotion
• Deconstructing the ‘magic’ in Core Data Query
• RubyMotion development best practices
Stefán Hafliðason
http://stefan.haflidason.com
@styrmis