7. Topics
• Rails API Versioning
• RestKit Installation
• Mapping Configuration
• Pulling and Pushing Data
• Offline Mode and Other Syncing
• Authentication
10. “ClipQueue”
• Helps with workflow automation.
• Detects content in the clipboard.
• Prompts to save it as a “Clip”, categorize it.
• Server processes it.
• Dispatches webhooks, sends emails, etc.
19. Rails JSON API
• Keep your API separate from your core app.
• Doing this efficiently requires thin controllers.
• Creating resources to represent actions that would
otherwise be custom controller actions helps, too.
20. class Clip < ActiveRecord::Base
attr_accessible :content, :created_at
end
Clip Model
22. class Api::V1::ClipsController < ApplicationController
load_and_authorize_resource :clip
respond_to :json
...
end
API Controller
23. class Api::V1::ClipsController < ApplicationController
load_and_authorize_resource :clip
respond_to :json
...
def index
respond_with @clips
end
...
end
API Controller Actions
24. class Api::V1::ClipsController < ApplicationController
load_and_authorize_resource :clip
respond_to :json
...
def show
respond_with @clip
end
...
end
API Controller Actions
25. class Api::V1::ClipsController < ApplicationController
load_and_authorize_resource :clip
respond_to :json
...
def create
@clip.save
redirect_to [:api, :v1, @clip]
end
...
end
API Controller Actions
28. Installation
• Use CocoaPods.
• This tutorial includes great installation steps for
new projects.
• https://github.com/RestKit/RKGist/blob/master/
TUTORIAL.md
30. Rails JSON API Versioning
• If your API changes, older apps will crash.
• Your ranking in the App Store will crash, too.
• Does anyone have a Gem they use for this?
• Maintaining old versions of your API is easy:
• Make a separate copy for each new version.
44. RESTKit ‘POST’ Lifecycle
• Issues HTTP POST request to URL.
• Rails creates the object and redirects to it.
• From here it’s the same as before:
• Receives XML/JSON response.
• Converts to NSDictionary + NSArray structure.
• Matches redirected URL to defined entity mapping.
• Maps response data to entity.
45. Clip *clip = (Clip *)[NSEntityDescription
insertNewObjectForEntityForName:@"Clip"
inManagedObjectContext:self.managedObjectContext];
clip.content = [[UIPasteboard generalPasteboard] string];
Creating a New Clip
49. Mass-Assignment
• Rails’ new mass-assignment defaults are good, but
they cause issues for us here.
• RESTKit sends the ‘id’ and ‘created_at’ attributes
across in the request.
• This triggers an exception in development by default,
because of this:
config.active_record.mass_assignment_sanitizer = :strict
51. Offline Mode
• The server is the authority, but is not always
available.
• We want to be able to work and save data locally even
in the absence of an Internet connection.
• We don’t want to lose our work.
• When a connection is available, we’d like our local
work to be pushed up to the server.
52. A VerySimple Example
• In this app, “Clips” are read-only.
• Don’t have to worry about resolving conflicting
changes from two sources.
• How simple a solution can you get away with?
53. A VerySimple Solution
• Try saving to the server.
• If that doesn’t work, just save it locally.
• It won’t have a “Clip ID”, but that doesn’t matter
for CoreData, even with relationships.
• When a connection becomes available, push it up.
54. A VerySimple Solution
• Try saving to the server.
• If that doesn’t work, just save it locally.
• It won’t have a “Clip ID”, but that doesn’t matter
for CoreData, even with relationships.
• When a connection becomes available:
• Push it up to the server.
• Claim our shiny new “Clip ID”.
55. A VerySimple Solution
• The only question is, how will the new ID get mapped
to the locally stored object?
• It can’t be matched by primary key ID, because it
didn’t have one locally.
• We’ll use a globally unique ID.
62. Sync Friendly Deletes
• An object exists locally, but not on the server.
• Was it created locally or deleted remotely?
• An object exists remotely, but not locally.
• Was it created remotely or deleted locally?
• Let’s look at a really easy way to deal with this.
63. class Clip < ActiveRecord::Base
...
scope :not_deleted, where('deleted_at IS NULL')
def destroy
self.update_column(:deleted_at, Time.zone.now)
end
end
Soft Delete
65. Added Bonus
• NSFetchedResultsController makes this an other UI
updates really pleasant to the user by default, even
when they’re triggered by results from the server.
68. Authentication
• Prompt for credentials.
• Store them in iOS Keychain.
• Delay loading until credentials are provided.
• Configure the credentials for RestKit.
• Implement data scoping on the server based on the
session.
73. Add User-Clips Relationship
• Add “user_id” attribute to Clip.
• Add “has_one :user” relationship to Clip.
• Add “has_many :clips” relationship to User.
74. class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
if user.persisted?
can :manage, :clip, {user_id: user.id}
end
end
end
HTTP Authentication
75. class Api::V1::ClipsController < ApplicationController
before_filter :authenticate_user!
load_and_authorize_resource :clip, through: current_user
...
end
Scoping Controller Results
76. What We’ve Covered
• Rails API Versioning
• RestKit Installation
• Mapping Configuration
• Pulling and Pushing Data
• Offline Mode and Other Syncing
• Authentication