SlideShare une entreprise Scribd logo
1  sur  33
   1
Build Your First App
With Core Data
A Step by Step Guide
UPDATEDfor StackMob
iOS SDK v1.2.0
   2
What is Core Data?
If you want to build anything beyond the most simplistic apps for
iOS, you’ll need a way to persist data. Luckily, Apple provides the
Core Data framework, a persistence layer that efficiently handles
the coordination of saving/updating/deleting data, as well as
maintaining data integrity.
With Core Data, you define the schema you want your data to
conform to, and afterwards create objects from that schema to
perform CRUD operations on. You don’t have to worry about what’s
going on at the actual database level, as this is all abstracted away.
Here at StackMob, we’ve placed an emphasis on the use of Core
Data in our iOS SDK. If you’re new to Core Data, have no fear!
This tutorial will get you started on the basics.
Part 1:
Learning Core Data
It’s worth noting that Core Data is not itself a database. By default, it sits on top of SQLite3, a lightweight
database baked into iOS. However, it can be configured to persist data to a document file or, in the case
of the StackMob SDK, a URL service that persists the data elsewhere.
Why Should I Learn Core Data?
Before Core Data, developers had to work directly with SQLite3 to save their data. This provided many
headaches; you had to write verbose SQL commands and handle the logic for CRUD operations. You
were also responsible for insuring the data you saved matched your schema. By the end of it all, you had
in essence written your own persistence layer! Core Data handles all the heavy lifting for you. As you
persist your data all logic is managed under the hood with optimal performance in mind.
Core Data has a little bit of a learning curve, which can dissuade developers who want to dive straight
into building to use it. However, taking the time to gain an understanding of the Core Data fundamentals
will pay large dividends in the long run, as it is a powerful tool that will enable you to build robust and
data-driven apps.
   3
We’ll name our project “MusicLabel” and name the class prefix “MLBL”.
Make sure you have “Use Core Data” selected. Doing so ensures the
project generates the configurations and boilerplate code necessary
to get started with Core Data. Uncheck “Include Unit Tests” since we
won’t be testing our application.
Lets build that out in a project:
Fire up Xcode and create a new project. Choose “Empty Application”
and click “Next”.
Lets Get Started
The NSManagedObjectModel is
where you define your schema
for Core Data. In your model,
you define the Entities, or classes
of data in your schema. Within
your Entities, you set their
Attributes, which are the details of your data. Finally, you
can link Entities together through Relationships.
For example, you could create a model with an entity
Label that defines a music label, an entity Artist
representing artists who are part of the label, and an
entity Album representing albums recorded by artists. Each entity
would have attributes representing details; all three entities would
be connected through relationships.
   4
For now lets concentrate on
the data model. Select the
“MusicLabel.xcdatamodeld” file,
and make sure the editor style is
set to table.
Add the following attributes
and corresponding types to the
“Label” entity:
Add another entity, “Artist”, and
add the following attributes:
Add one more entity, “Album”,
and add the following attributes:
founded
genre
name
hometown
title
released
Date
String
String
String
String
Date
Attribute
Attribute
Attribute
Type
Type
Type
Click on “Add Entity” at the
bottom of the Xcode window and
name it “Label”. With the entity
selected, click “Add Attribute”.
We’ll call this attribute “name”,
and set its type to “String”.
Switch the Editor Style to graph in the bottom right corner of the Xcode window.
   5
With the “Label” entity selected, click and hold “Add Attribute” until you see the option “Add Relationship”.
Select Add Relationship. Give it the name “artists”
and set the destination to the “Artist” entity. By
establishing this relationship, we’re effectively
saying “every Label has an Artist”. Our label can have
more than one artist, so we’ll make the relationship
a “To-Many Relationship”. Also, set the delete rule
to “Cascade”; this ensures that when an object is
deleted, its child objects will be deleted as well.
Now we’ll want to make an inverse relationship back to the parent.
Select the “Artist” entity and click “Add Relationship”. Name the
relationship “label” and set the destination to the “Label” entity.
Set the inverse to the “artists” relationship.
   6
“Label” to “Artist”
“Artist” to “Label”
“Artist” to “Album”
“Album” to “Artist”
artists
label
albums
artist
To-many
One-to-one
To-Many
One-to-one
Cascade
Nullify
Cascade
Nullify
Relationship Name Type Delete Rule
When you’re finished, your model should look like this:
Use these same steps to set relationships between the “Artist” and “Album” entities. Make a relationship
from the “Artist” entity to the “Album” entity titled “albums”. Make it a “To-Many Relationship” and set
the delete rule to “Cascade”. Make a relationship from the “Album” entity to the “Artist” entity with the
name “artist” and make the inverse be the the “albums” relationship.
Use this table for reference:
   7
Finally, select the “Label” entity, and click File > New > File.
Choose the “NSManagedObject subclass” template.
This generates Objective-C class files for the “Label” entity; you should see a “Label.h” file as well as a
“Label.m” file. Use the same steps to generate class files for the “Artist” and “Album” entities (make sure
to do it in the order Label > Artist > Album). With class files generated for each of the entities, our setup
is complete.
What’s Next?
So far, we’ve learned the basics of how Core Data works. We covered a high-
level overview of the subject and created an Xcode project with our own
NSManagedObjectModel. In part 2 of our tutorial we’ll take a more detailed look at
Core Data and implement CRUD operations.
   8
In this part of the tutorial we’ll delve a little deeper into Core Data,
and perform basic CRUD operations. If you’re not familiar with the
term, CRUD stands for Create, Read, Update and Delete; these are
the four fundamental operations performed on a database.
Part 2:
Diving into CRUD
NSManagedObjectContext & NSPersistentStoreCoordinator
The NSManagedObjectModel is only 1/3 of the Core Data picture. Let’s introduce the
NSManagedObjectContext. You can think of NSManagedObjectContext as a “scratch pad” where all
the changes happen. Here you create, read, update and delete objects from your database. None of
the changes you make are actually persisted until you call the “save” method on your managed object
context instance.
So when a call to save is made,
what happens next? Sitting
between the managed object
context and the database is the
NSPersistentStoreCoordinator.
The coordinator acts as an
interface to the database where
your data is being persisted. The
persistent store coordinator
grabs any objects that will be
saved from the managed object
context and verifies that names
and types are consistent with the
managed object model. It then
sends the objects on their way to
be persisted to the database.
   9
Creating an object
Open the “MLBLAppDelegate.m” file. In it, you’ll see methods to setup the context, model and persistent
store coordinator. Import the “Label.h”, “Artist.h”, and “Album.h” files. Add the following method:
- (void) create {
// Grab the context
NSManagedObjectContext *context = [self managedObjectContext];
// Grab the Label entity
Label *label = [NSEntityDescription insertNewObjectForEntityForName:@”Label” inManagedObjectCon	
text:context];
// Set label name
label.name = @”Diplomat Records”;
// Create a Date
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@”YYYY”];
NSDate *dateFounded = [dateFormatter dateFromString:@”2003”];
// Set the year founded for the label
label.founded = dateFounded;
// Set the label genre
label.genre = @”Rap/Hip-hop”;
// Insert the Artist entity
Artist *artist = [NSEntityDescription insertNewObjectForEntityForName:@”Artist”
inManagedObjectContext:context];
// Set the artist attributes
artist.name = @”Cam’Ron”;
artist.hometown = @”Harlem, NY”;
// Insert Album entity
Album *album = [NSEntityDescription insertNewObjectForEntityForName:@”Album”
inManagedObjectContext:context];
// Set album attributes
album.title = @”Come Home With Me”;
NSDate *releaseDate = [dateFormatter dateFromString:@”2002”];
album.released = releaseDate;
// Set relationships
[label addArtistsObject:artist];
[artist setLabel:label];
[artist addAlbumsObject:album];
[album setArtist:artist];
// Save everything
NSError *error = nil;
if ([context save:&error]) {
NSLog(@”The save was successful!”);
} else {
NSLog(@”The save wasn’t successful: %@”, [error userInfo]);
}
}
Get the Code from GitHub
   10
Make sure to call [self create] in the application:didFinishLaunchingWithOptions: method. Build your
project and run it. Check the output in the log navigator to see if it saved properly. You should see this output:
It worked? Great, now let’s walk
through reading data.
Reading an object
With Core Data, you perform
“fetch requests” to access saved
objects. These objects are
always returned as instances of
NSManagedObject. Check out
NSFetchRequest for more info.
Add the following method to the
“MLBLAppDelegate.m” file:
- (void) read {
NSManagedObjectContext *context = [self managedObjectContext];
// Construct a fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@”Label”
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
for (Label *label in fetchedObjects) {
// Log the label details
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@”YYYY”];
NSLog(@”%@, est. %@ (%@)”, label.name, [dateFormatter stringFromDate:label.founded], label.
genre);
NSLog(@”tArtists:”);
NSSet *artists = label.artists;
for (Artist *artist in artists) {
// Log the artist details
NSLog(@”tt%@ (%@)”, artist.name, artist.hometown);
NSLog(@”tttAlbums:”);
NSSet *albums = artist.albums;
for (Album *album in albums) {
// Log the album details
NSLog(@”tttt%@ (%@)”, album.title, [dateFormatter stringFromDate:album.re-
leased]);
}
}
}
}
Get the Code from GitHub
   11
Replace [self create] with [self read] in the application:didFinishLaunchingWithOptions: method. Build
and run, and voila! A printout of the objects should appear in the log navigator.
- (void) update {
// Grab the context
NSManagedObjectContext *context = [self managedObjectContext];
// Perform fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@”Label”
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
// Date formatter comes in handy
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@”YYYY”];
// Grab the label
Label *label = [fetchedObjects objectAtIndex:0];
// Juelz Santana
Artist *juelz = [NSEntityDescription insertNewObjectForEntityForName:@”Artist” inManagedObject-
Context:context];
juelz.name = @”Juelz Santana”;
juelz.hometown = @”Harlem, NY”;
// Juelz Santana albums
Album *juelzAlbum = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb-
jectContext:context];
juelzAlbum.title = @”From Me to U”;
juelzAlbum.released = [dateFormatter dateFromString:@”2003”];
[juelzAlbum setArtist:juelz];
Album *juelzAlbum2 = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb-
jectContext:context];
juelzAlbum2.title = @”What The Game’s Been Missing!”;
juelzAlbum2.released = [dateFormatter dateFromString:@”2005”];
[juelzAlbum2 setArtist:juelz];
// Set relationships
[juelz addAlbums:[NSSet setWithObjects:juelzAlbum, juelzAlbum2, nil]];
// Jim Jones
Artist *jimmy = [NSEntityDescription insertNewObjectForEntityForName:@”Artist” inManagedObject-
Context:context];
jimmy.name = @”Jim Jones”;
jimmy.hometown = @”Harlem, NY”;
// Jim Jones albums
Album *jimmyAlbum = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb-
jectContext:context];
jimmyAlbum.title = @”On My Way to Church”;
jimmyAlbum.released = [dateFormatter dateFromString:@”2004”];
Updating an object
Let’s update our label and add a few more artists and albums. Add this method:
   12
[jimmyAlbum setArtist:jimmy];
Album *jimmyAlbum2 = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb-
jectContext:context];
jimmyAlbum2.title = @”Harlem: Diary of a Summer”;
jimmyAlbum2.released = [dateFormatter dateFromString:@”2005”];
[jimmyAlbum2 setArtist:jimmy];
Album *jimmyAlbum3 = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb-
jectContext:context];
jimmyAlbum3.title = @”Hustler’s P.O.M.E. (Product of My Environment)”;
jimmyAlbum3.released = [dateFormatter dateFromString:@”2006”];
[jimmyAlbum3 setArtist:jimmy];
// Set relationships
[jimmy addAlbums:[NSSet setWithObjects:jimmyAlbum, jimmyAlbum2, jimmyAlbum3, nil]];
// Freekey Zekey
Artist *freekey = [NSEntityDescription insertNewObjectForEntityForName:@”Artist” inManagedOb-
jectContext:context];
freekey.name = @”Freekey Zekey”;
freekey.hometown = @”Harlem, NY”;
Album *freekeyAlbum = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManaged-
ObjectContext:context];
freekeyAlbum.title = @”Book of Ezekiel”;
freekeyAlbum.released = [dateFormatter dateFromString:@”2007”];
[freekeyAlbum setArtist:freekey];
[freekey addAlbumsObject:freekeyAlbum];
// Set relationships
[label addArtists:[NSSet setWithObjects:juelz, jimmy, freekey, nil]];
// Save everything
if ([context save:&error]) {
NSLog(@”The save was successful!”);
} else {
NSLog(@”The save wasn’t successful: %@”, [error localizedDescription]);
}
}
Replace [self read] with [self update] in the application:didFinishLaunchingWithOptions: method. Build
and run, and check the log navigator for a successful save. Now let’s print them to the log again. Replace
[self update] with [self read]. Build and run, and you should see all the artists/albums under the label.
Run it a few more times and compare the log output each time. The order will vary almost every time.
Fetch requests return objects unordered, unless specified otherwise. To do so, you need to use NSSort-
Descriptor to organize your results. Use NSPredicate to grab specific objects.
Get the Code from GitHub
   13
Add the line [self delete] right before [self read] in the application:didFinishLaunchingWithOptions:
method. Build and run, and you should see Freekey Zekey disappear from the results. His album should
be gone too, as we set the delete rule for that relationship to “Cascade”.
What’s Next?
Now that you’ve learned the basics of Core Data, it’s time to start building a full-fledged
application on top of it. In part 3 of our tutorial, we’ll add a UI to what we’ve built so far.
Download the source code for this tutorial.
Deleting an object
Finally, we’ll delete an object. Let’s add this method:
- (void) delete {
// Grab the context
NSManagedObjectContext *context = [self managedObjectContext];
// We’re looking to grab an artist
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@”Artist”
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// We specify that we only want Freekey Zekey
NSPredicate *predicate = [NSPredicate predicateWithFormat:@”name == %@”, @”Freekey Zekey”];
[fetchRequest setPredicate:predicate];
NSError *error = nil;
NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];
// Grab the artist and delete
Artist *freekey = [fetchedObjects objectAtIndex:0];
[freekey.label removeArtistsObject:freekey];
// Save everything
if ([context save:&error]) {
NSLog(@”The save was successful!”);
} else {
NSLog(@”The save wasn’t successful: %@”, [error localizedDescription]);
}
}
Get the Code from GitHub
   14
Part 3:
Adding the User Interface
In this section, we’ll focus on the UI, or user interface, the layer
on top of our view controllers that allows a user to interact with
our data.
The setup of the UI is equally as important as Core Data. An app that functions perfectly won’t be
successful if its interface is not intuitive and aesthetically pleasing. Before touchscreen devices became
ubiquitous, the UI was sometimes cast as an afterthought. Now it’s virtually impossible to ignore.
Tablets and phones come with constraints in screen size, requiring much more consideration for the user
experience and flow of information through an app. Great UI design properly communicates to the user
the purpose of your application.
With that in mind, we’ll construct a simple user interface for our MusicLabel app, which will allow users
to create and delete entries into the database.
Create a storyboard
First, we’ll need to add a
storyboard to our project. Press
command + N and select “User
Interface” from the available
templates. Choose Storyboard,
and click next.
   15
Name the file “MainStoryboard”
and click create. In the
MusicLabel project, target select
the storyboard we just created
to be the Main Storyboard.
Create a tableview
Click on the storyboard and make
sure the “Utilities” pane is open.
Drag a table view controller
from the object library onto the
storyboard.
   16
With the prototype cell selected, navigate to the attributes inspector. Enter “Cell” as an identifier.
Press command + N, and from the
“Cocoa touch” templates, select
“Objective-C class”.
Name the file
“MLBLLabelViewController”
and make it a subclass of
UITableViewController.
Back in the storyboard,
select the table view
controller and navigate
to the identity inspector.
Change the custom class to
“MLBLLabelViewController”.
   17
Get the Code from GitHub
Make the following changes to the MLBLLabelViewController.h:
Make the following changes to the MLBLLabelViewController.m:
#import <UIKit/UIKit.h>
#import “MLBLAppDelegate.h”
#import “Label.h”
@interface MLBLLabelViewController : UITableViewController
// An array to house all of our fetched Label objects
@property (strong, nonatomic) NSArray *labelArray;
@end
/* Make these changes to MLBLLabelViewController.M */
@implementation MLBLLabelViewController
@synthesize labelArray;
- (void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
/* Here we call the method to load the table data */
[self loadTableData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// We only need to return 1 for this table view
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// We’ll return the count of the objects in labelArray
return [labelArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)
indexPath
{
static NSString *CellIdentifier = @”Cell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
   18
Get the Code from GitHub
// Configure the cell...
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Grab the label
Label *label = [self.labelArray objectAtIndex:indexPath.row];
// Set the text of the cell to the label name
cell.textLabel.text = label.name;
return cell;
}
#pragma mark - Private methods
- (MLBLAppDelegate *)appDelegate {
return (MLBLAppDelegate *)[[UIApplication sharedApplication] delegate];
}
// This method executes a fetch request and reloads the table view.
- (void) loadTableData {
NSManagedObjectContext *context = [[self appDelegate] managedObjectContext];
// Construct a fetch request
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@”Label”
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// Add an NSSortDescriptor to sort the labels alphabetically
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”name”
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
self.labelArray = [context executeFetchRequest:fetchRequest error:&error];
[self.tableView reloadData];
}
@end
Build and run. Based on the data
we entered in the last tutorial,
you should see this:
   19
Get the Code from GitHub
Creating a Navigation Controller
Now lets add to our setup: with “MLBLLabelViewController” selected in the storyboard, choose Editor
> Embed In > Navigation Controller. A navigation controller is a standard UI element provided by Apple
to help guide the flow of an app. It includes logic for going back to a previous view controller as well as
transitions between screens. We’ve embedded “MLBLLabelViewController” as the root view controller
in our navigation controller.
Drag another table
view controller onto the
storyboard. Create a new file
“MLBLArtistViewController”
that subclasses
UITableViewController.
Change the custom class of the
new table view controller to
“MLBLArtistViewController”.
Give it a storyboard ID of
“ArtistViewController”
#import <UIKit/UIKit.h>
#import “MLBLAppDelegate.h”
#import “Label.h”
#import “Artist.h”
@interface MLBLArtistViewController : UITableViewController
// An array to house all of our fetched Artist objects
@property (strong, nonatomic) NSArray *artistArray;
//The id of the parent object
@property (strong, nonatomic) NSManagedObjectID *labelID;
@end
Make the following changes to the MLBLArtistViewController.h:
   20
Make the following changes to the MLBLArtistViewController.m:
/* Make these changes to MLBArtistViewController.m */
@implementation MLBLArtistViewController
@synthesize artistArray;
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadTableData];
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// We only need to return 1 for this table view
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// We’ll return the count of the objects in artistArray
return [artistArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)
indexPath
{
static NSString *CellIdentifier = @”Cell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
// Grab the artist
Artist *artist = [self.artistArray objectAtIndex:indexPath.row];
cell.textLabel.text = artist.name;
return cell;
}
#pragma mark - Private methods
- (MLBLAppDelegate *)appDelegate {
return (MLBLAppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void) loadTableData {
NSManagedObjectContext *context = [[self appDelegate] managedObjectContext];
// Construct a fetch request
   21
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@”Artist”
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@”label == %@”, [context
objectWithID:self.labelID]];
[fetchRequest setPredicate:predicate];
// Add an NSSortDescriptor to sort the labels alphabetically
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”name”
ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSError *error = nil;
self.artistArray = [context executeFetchRequest:fetchRequest error:&error];
[self.tableView reloadData];
}
@end
Add #import MLBLArtistViewController.h to the MLBLLabelViewController.m file. In the - (void)
tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method, make
the following changes:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MLBLArtistViewController *artistViewController = [self.storyboard
instantiateViewControllerWithIdentifier:@”ArtistViewController”];
// Grab the label
Label *label = [self.labelArray objectAtIndex:indexPath.row];
artistViewController.labelID = [label objectID];
[self.navigationController pushViewController:artistViewController animated:YES];
}
Build and run, and you should be able to view the artists that are associated with a label.
Next, do the same thing with albums. Create a file called “MLBLAlbumViewController” that subclasses
UITableViewController, and repeat the steps for “MLBLArtistViewController”.


Get the Code from GitHub
Get the Code from GitHub
   22
Hold the option key and click on MLBLLabelEntryViewController.h.
This will open the file side by side with the storyboard.
Creating an Input View
Now we’ll make input screens to insert data into our database.
Add these two lines of code to the -(void)viewDidLoad method in
MLBLLabelViewController.m:
UIBarButtonItem *item = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self
action:@selector(addItem)];
self.navigationItem.rightBarButtonItem = item;
Create a file called “MLBLLabelEntryViewController”, which
subclasses a UIViewController. Drag a view controller from the
object library onto the storyboard. Change the custom class to
“MLBLLabelEntryViewController”. Drag a text field onto the view
controller as well. In the attributes inspector you can customize the
text field; set the placeholder text to “Enter the Label Name”.
Hold the option key and click on MLBLLabelEntryViewController.h. This will open the file side by side
with the storyboard.
This establishes an outlet connection between the storyboard nib file and the view controller class.
Name the outlet “labelField” and click connect.
Add the following code to MLBLLabelEntryViewController.m:
   23
 Get the Code from GitHub
/* Make these changes to MLBLLabelEntryViewController.m */
#import “MLBLLabelEntryViewController.h”
#import “MLBLAppDelegate.h”
#import “Label.h”
@implementation MLBLLabelEntryViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// This will call our
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@”Add”
style:UIBarButtonItemStyleBordered target:self action:@selector(addLabel)];
self.navigationItem.rightBarButtonItem = item;
UIBarButtonItem *item2 = [[UIBarButtonItem alloc] initWithTitle:@”Cancel”
style:UIBarButtonItemStyleBordered target:self action:@selector(dismiss)];
self.navigationItem.leftBarButtonItem = item2;
}
#pragma mark - private methods
- (MLBLAppDelegate *)appDelegate {
return (MLBLAppDelegate *)[[UIApplication sharedApplication] delegate];
}
- (void) addLabel {
// Grab the context
NSManagedObjectContext *context = [[self appDelegate] managedObjectContext];
// Grab the Label entity
Label *label = [NSEntityDescription insertNewObjectForEntityForName:@”Label”
inManagedObjectContext:context];
// Set label name
label.name = self.labelField.text;
// Save everything
NSError *error = nil;
if ([context save:&error]) {
NSLog(@”The save was successful!”);
} else {
NSLog(@”The save wasn’t successful: %@”, [error userInfo]);
}
[self dismiss];
}
- (void) dismiss {
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
   24
 Get the Code from GitHub
In the MLBLLabelViewController.m file add #import “MLBLLabelEntryViewController.h”, add the
following code:
// Add this method for slide to delete
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleDelete;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)
editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source
NSManagedObjectContext *context = [[self appDelegate] managedObjectContext];
// Grab the label
Label *label = [self.labelArray objectAtIndex:indexPath.row];
[context deleteObject:[context objectWithID:[label objectID]]];
// Save everything
NSError *error = nil;
if ([context save:&error]) {
NSLog(@”The save was successful!”);
} else {
NSLog(@”The save wasn’t successful: %@”, [error userInfo]);
}
NSMutableArray *array = [self.labelArray mutableCopy];
[array removeObjectAtIndex:indexPath.row];
self.labelArray = array;
[tableView deleteRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
// Add this method under our private methods pragma mark
- (void) addItem {
MLBLLabelEntryViewController *labelEntryViewController = [self.storyboard
instantiateViewControllerWithIdentifier:@”LabelEntryViewController”];
UINavigationController *navigationController = [[UINavigationController alloc]
initWithRootViewController:labelEntryViewController];
[self presentViewController:navigationController animated:YES completion:nil];
}
   25
Build it and run, and you can now add Labels to the database. You’re also able to slide to delete Labels
from the table, and remove them from the database. You can easily expand on this by adding input views
for Artists and Albums as well.
What’s Next?
In this tutorial, we constructed a simple UI to access and display our data. In the next
installment of our series, we’ll utilize the StackMob SDK to enhance our app.
Download the source code for this tutorial.
   26
Part 4:
Connecting to StackMob
As we noted earlier, a requirement for anything beyond the
simplest app is the ability to persist data. Another trend we’re
witnessing is the proliferation of internet enabled apps that
have some sort of backend server logic.
The case for a backend
It’s very rare to find an app that doesn’t rely in some sort of fashion on a backend. Users come with the
expectation of sharing and interacting with their friends and having information delivered to them in a
manner tailored to their specific interests and/or location.
Some examples of these features include user creation and management, data indexing, push
notifications and geo location coordinates. Developers deliver these features by utilizing 3rd party API’s,
and ultimately, building a custom backend. Unfortunately, engineering a backend has a much steeper
learning curve than learning Core Data. You also have to manage and account for a whole host of other
potential problems: data security, scalability, etc.
Backend difficulties – and a solution
The configuration and
management of a backend
server is its own ordeal, entirely
separate from the app creation
process. Depending on the size
of your operation, it can range anywhere from a major nuisance to a project non-starter. Regardless
of size, the issue is relevant and real. Whether it’s an indie app maker, a startup developer or a large
enterprise organization, the prospect of building a backend is expensive in both time and resources.
This is where StackMob provides value. StackMob offers a robust end to end platform for everything
related to your app. Through StackMob you can house your data reliably and securely, and scale to
millions of users with minimal effort. 3rd party services, API versioning, custom server code and other
great features are available as modules in the StackMob Marketplace.
   27
Working with the StackMob SDK
Using the StackMob iOS SDK is simple and straightforward. Lets get started by incorporating it into
our MusicLabel project. If you haven’t done so already, head over to our dev center and signup for a
StackMob account. The getting started page will take you through the process of signing up, creating
an app and downloading and importing the iOS SDK. Name your app “musiclabel” and select iOS as a
platform. Clicking “Step 2” will create your app.
When you are finished importing
the SDK, navigate back to
your dashboard. You will see
information about your newly
created app, including public
and private keys for both your
development and production
environments.
   28
StackMob and Core Data
The StackMob iOS SDK is tightly integrated with Core Data. The Core Data Coding Practices outline
what to keep in mind when using the SDK.
In your Xcode project, navigate to your data model in the “MusicLabel.xcdatamodeld” file. In the Label
entity, add a string attribute titled “labelId”. Add “artistId” and “albumId” string attributes to the Artist
and Album entities, respectively.
Recreate your NSManagedObject subclass files. Click “Replace” when prompted.
   29
In “MLBLAppDelegate.m” make the following changes. Make sure to add your public key when initializing
the SMClient.
Get the Code from GitHub
#import <UIKit/UIKit.h>
#import “StackMob.h”
@class SMClient;
@class SMCoreDataStore;
@interface MLBLAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) NSManagedObjectModel *managedObjectModel;
// CODE UPDATE: Declare SMCoreDataStore instance
@property (strong, nonatomic) SMCoreDataStore *coreDataStore;
// CODE UPDATE: Declare SMClient instance
@property (strong, nonatomic) SMClient *client;
@end

In “MLBLAppDelegate.m”, we are going to initialize an SMClient instance as well as our declared
SMCoreDataStore instance. SMClient is the fundamental class for interacting with StackMob. Make sure
to add your public key when initializing the SMClient.
#import “MLBLAppDelegate.h”
#import “Label.h”
#import “Artist.h”
#import “Album.h”
@implementation MLBLAppDelegate
// Synthesize SMCoreDataStore
@synthesize coreDataStore _coreDataStore;
@synthesize managedObjectModel = _managedObjectModel;
// Synthesize SMClient
Changes to your code
The StackMob iOS SDK manages most of the Core Data stack, including initializing managed object
contexts and a persistent store coordinator. Let’s edit our codebase accordingly.
In the MLBLAppDelegate.h file, remove the managedObjectContext property. Remove the following
methods in MLBLAppDelegate.m as well:
• - (void)saveContext;
• - (NSURL *)applicationDocumentsDirectory;
• - (NSManagedObjectContext *)managedObjectContext;
• - (NSPersistentStoreCoordinator *)persistentStoreCoordinator;
To obtain an initialized, ready to go managed object context, SMCoreDataStore provides the
contextForCurrentThread method.Since we need to reference a local SMCoreDataStore instance to obtain
managed object contexts, declare one in your “MLBLAppDelegate.h” file like so:
   30
Get the Code from GitHub
The SDK provides helper methods for the traditional save: and executeFetchRequest:error: Core Data
calls. They are all designed for optimal performance by adhering to common Core Data patterns and
include asynchronous callback-based methods which are performed in the background.
Edit your create method from the previous tutorials to utilize the synchronous saveAndWait: call as well as
the asynchronous saveOnSuccess:onFailure: call. Your updated code should look like this:
@synthesize client = _client;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)
launchOptions
{
// Override point for customization after application launch.
self.client = [[SMClient alloc] initWithAPIVersion:@”0” publicKey:@”YOUR_PUBLIC_KEY”];
self.coreDataStore = [self.client coreDataStoreWithManagedObjectModel:self.
managedObjectModel];
/* The methods we use in the tutorial. */
[self create];
return YES;
}
// Application delegate methods remain the same. Delete the Core Data methods for
NSPersistantStoreCoordinator and NSManagedObjectContext.
...
// Returns the managed object model for the application.
// If the model doesn’t already exist, it is created from the application’s model.
- (NSManagedObjectModel *)managedObjectModel
{
if (_managedObjectModel != nil) {
return _managedObjectModel;
}
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@“MusicLabel” withExtension:@“momd”];
_managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
return _managedObjectModel;
}
@end
- (void) create {
// Grab the context
NSManagedObjectContext *context = [self.coreDataStore contextForCurrentThread];
// Grab the Label entity
Label *label = [NSEntityDescription insertNewObjectForEntityForName:@“Label”
inManagedObjectContext:context];
label.labelId = [label assignObjectId];
// Set label name
label.name = @“Diplomat Records”;
   31
Get the Code from GitHub
// Create a Date
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@“YYYY”];
NSDate *dateFounded = [dateFormatter dateFromString:@“2003”];
// Set the year founded for the label
label.founded = dateFounded;
// Set the label genre
label.genre = @“Rap/Hip-hop”;
// Insert the Artist entity
Artist *artist = [NSEntityDescription insertNewObjectForEntityForName:@“Artist”
inManagedObjectContext:context];
artist.artistId = [artist assignObjectId];
// Set the artist attributes
artist.name = @“Cam’Ron”;
artist.hometown = @“Harlem, NY”;
// Insert Album entity
Album *album = [NSEntityDescription insertNewObjectForEntityForName:@“Album”
inManagedObjectContext:context];
album.albumId = [album assignObjectId];
// Set album attributes
album.title = @“Come Home With Me”;
NSDate *releaseDate = [dateFormatter dateFromString:@“2002”];
album.released = releaseDate;
// CODE UPDATE: Use the synchronous save method saveAndWait:
NSError *error = nil;
if (![context saveAndWait:&error]) {
NSLog(@“The save wasn’t successful: %@”, [error userInfo]);
} else {
NSLog(@“Save successful”);
}
// Set relationships
[label addArtistsObject:artist];
[artist setLabel:label];
[artist addAlbumsObject:album];
[album setArtist:artist];
// CODE UPDATE: Use the asynchronous save method saveOnSuccess:onFailure:
[context saveOnSuccess:^{
NSLog(@“Successful relations update”);
} onFailure:^(NSError *error) {
NSLog(@“Unsuccessful relations update with error %@”, error);
}];
}
   32
Get the Code from GitHub
The key thing to remember is that any NSManagedObject must have an id assigned to it before it is
saved to StackMob. In “MLBLLabelEntryViewController.m” add the following line to the -(void) addLabel
method:
label.labelId = [label assignObjectId];
Go ahead and edit all the save:error and executeFetchRequest:error: calls throughout your codebase to
utilize the StackMob methods. In all your .m files you will want to assign your managedObjectContext
variables to [[[self appDelegate] coreDataStore] contextForCurrentThread];, which may require you to
add #import “StackMob.h” as well. More information on all the available methods to interact with Core
Data can be found in the NSManagedObjectContext+Concurrency reference.
Build and run, and check your dashboard. Under “Schema Configuration”, you should see the entities you
created, alongside the “user” schema.
- (void) delete {
// Grab the context
NSManagedObjectContext *context = [self.coreDataStore contextForCurrentThread];
// We’re looking to grab an artist
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@”Artist”
inManagedObjectContext:context];
[fetchRequest setEntity:entity];
// We specify that we only want Freekey Zekey
NSPredicate *predicate = [NSPredicate predicateWithFormat:@“name == %@”, @“Freekey Zekey”];
[fetchRequest setPredicate:predicate];
// CODE UPDATE: Nest the asynchronous save call in the asynchronous fetch call.
[context executeFetchRequest:fetchRequest onSuccess:^(NSArray *results) {
// Grab the artist and delete
Artist *freekey = [results objectAtIndex:0];
[freekey.label removeArtistsObject:freekey];
// Save everything
[context saveOnSuccess:^{
NSLog(@“The save was successful!”);
} onFailure:^(NSError *error) {
NSLog(@“The save wasn’t successful: %@”, [error localizedDescription]);
}];
} onFailure:^(NSError *error) {
NSLog(@“Error fetching: %@”, error);
}];
}
Let’s take a look at another updated CRUD method we’ve created, the delete method. Here, we see the
saveOnSuccess:onFailure: call wrapped in the callback of the executeFetchRequest:onSuccess:onFailure:
call. Edit your code to look like the following:
   33
There you have it! With a few steps and very little additional code, we’ve seamlessly integrated
StackMob into our Core Data stack.
What’s Next?
Be sure to check out the StackMob iOS SDK Reference for more information on using the
StackMob iOS SDK. Also, take a look at our iOS tutorials for helpful examples.
Download the source code for this tutorial. For more information, visit out blog.
Click on “Data Management” and you’ll be able to query the objects you’ve saved.

Contenu connexe

Dernier

Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditSkynet Technologies
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Scott Andery
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesThousandEyes
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfNeo4j
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityIES VE
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 

Dernier (20)

Manual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance AuditManual 508 Accessibility Compliance Audit
Manual 508 Accessibility Compliance Audit
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
Enhancing User Experience - Exploring the Latest Features of Tallyman Axis Lo...
 
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyesAssure Ecommerce and Retail Operations Uptime with ThousandEyes
Assure Ecommerce and Retail Operations Uptime with ThousandEyes
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
Decarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a realityDecarbonising Buildings: Making a net-zero built environment a reality
Decarbonising Buildings: Making a net-zero built environment a reality
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 

En vedette

Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Applitools
 

En vedette (20)

Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 

Technical eBook for iOS Developers

  • 1.    1 Build Your First App With Core Data A Step by Step Guide UPDATEDfor StackMob iOS SDK v1.2.0
  • 2.    2 What is Core Data? If you want to build anything beyond the most simplistic apps for iOS, you’ll need a way to persist data. Luckily, Apple provides the Core Data framework, a persistence layer that efficiently handles the coordination of saving/updating/deleting data, as well as maintaining data integrity. With Core Data, you define the schema you want your data to conform to, and afterwards create objects from that schema to perform CRUD operations on. You don’t have to worry about what’s going on at the actual database level, as this is all abstracted away. Here at StackMob, we’ve placed an emphasis on the use of Core Data in our iOS SDK. If you’re new to Core Data, have no fear! This tutorial will get you started on the basics. Part 1: Learning Core Data It’s worth noting that Core Data is not itself a database. By default, it sits on top of SQLite3, a lightweight database baked into iOS. However, it can be configured to persist data to a document file or, in the case of the StackMob SDK, a URL service that persists the data elsewhere. Why Should I Learn Core Data? Before Core Data, developers had to work directly with SQLite3 to save their data. This provided many headaches; you had to write verbose SQL commands and handle the logic for CRUD operations. You were also responsible for insuring the data you saved matched your schema. By the end of it all, you had in essence written your own persistence layer! Core Data handles all the heavy lifting for you. As you persist your data all logic is managed under the hood with optimal performance in mind. Core Data has a little bit of a learning curve, which can dissuade developers who want to dive straight into building to use it. However, taking the time to gain an understanding of the Core Data fundamentals will pay large dividends in the long run, as it is a powerful tool that will enable you to build robust and data-driven apps.
  • 3.    3 We’ll name our project “MusicLabel” and name the class prefix “MLBL”. Make sure you have “Use Core Data” selected. Doing so ensures the project generates the configurations and boilerplate code necessary to get started with Core Data. Uncheck “Include Unit Tests” since we won’t be testing our application. Lets build that out in a project: Fire up Xcode and create a new project. Choose “Empty Application” and click “Next”. Lets Get Started The NSManagedObjectModel is where you define your schema for Core Data. In your model, you define the Entities, or classes of data in your schema. Within your Entities, you set their Attributes, which are the details of your data. Finally, you can link Entities together through Relationships. For example, you could create a model with an entity Label that defines a music label, an entity Artist representing artists who are part of the label, and an entity Album representing albums recorded by artists. Each entity would have attributes representing details; all three entities would be connected through relationships.
  • 4.    4 For now lets concentrate on the data model. Select the “MusicLabel.xcdatamodeld” file, and make sure the editor style is set to table. Add the following attributes and corresponding types to the “Label” entity: Add another entity, “Artist”, and add the following attributes: Add one more entity, “Album”, and add the following attributes: founded genre name hometown title released Date String String String String Date Attribute Attribute Attribute Type Type Type Click on “Add Entity” at the bottom of the Xcode window and name it “Label”. With the entity selected, click “Add Attribute”. We’ll call this attribute “name”, and set its type to “String”. Switch the Editor Style to graph in the bottom right corner of the Xcode window.
  • 5.    5 With the “Label” entity selected, click and hold “Add Attribute” until you see the option “Add Relationship”. Select Add Relationship. Give it the name “artists” and set the destination to the “Artist” entity. By establishing this relationship, we’re effectively saying “every Label has an Artist”. Our label can have more than one artist, so we’ll make the relationship a “To-Many Relationship”. Also, set the delete rule to “Cascade”; this ensures that when an object is deleted, its child objects will be deleted as well. Now we’ll want to make an inverse relationship back to the parent. Select the “Artist” entity and click “Add Relationship”. Name the relationship “label” and set the destination to the “Label” entity. Set the inverse to the “artists” relationship.
  • 6.    6 “Label” to “Artist” “Artist” to “Label” “Artist” to “Album” “Album” to “Artist” artists label albums artist To-many One-to-one To-Many One-to-one Cascade Nullify Cascade Nullify Relationship Name Type Delete Rule When you’re finished, your model should look like this: Use these same steps to set relationships between the “Artist” and “Album” entities. Make a relationship from the “Artist” entity to the “Album” entity titled “albums”. Make it a “To-Many Relationship” and set the delete rule to “Cascade”. Make a relationship from the “Album” entity to the “Artist” entity with the name “artist” and make the inverse be the the “albums” relationship. Use this table for reference:
  • 7.    7 Finally, select the “Label” entity, and click File > New > File. Choose the “NSManagedObject subclass” template. This generates Objective-C class files for the “Label” entity; you should see a “Label.h” file as well as a “Label.m” file. Use the same steps to generate class files for the “Artist” and “Album” entities (make sure to do it in the order Label > Artist > Album). With class files generated for each of the entities, our setup is complete. What’s Next? So far, we’ve learned the basics of how Core Data works. We covered a high- level overview of the subject and created an Xcode project with our own NSManagedObjectModel. In part 2 of our tutorial we’ll take a more detailed look at Core Data and implement CRUD operations.
  • 8.    8 In this part of the tutorial we’ll delve a little deeper into Core Data, and perform basic CRUD operations. If you’re not familiar with the term, CRUD stands for Create, Read, Update and Delete; these are the four fundamental operations performed on a database. Part 2: Diving into CRUD NSManagedObjectContext & NSPersistentStoreCoordinator The NSManagedObjectModel is only 1/3 of the Core Data picture. Let’s introduce the NSManagedObjectContext. You can think of NSManagedObjectContext as a “scratch pad” where all the changes happen. Here you create, read, update and delete objects from your database. None of the changes you make are actually persisted until you call the “save” method on your managed object context instance. So when a call to save is made, what happens next? Sitting between the managed object context and the database is the NSPersistentStoreCoordinator. The coordinator acts as an interface to the database where your data is being persisted. The persistent store coordinator grabs any objects that will be saved from the managed object context and verifies that names and types are consistent with the managed object model. It then sends the objects on their way to be persisted to the database.
  • 9.    9 Creating an object Open the “MLBLAppDelegate.m” file. In it, you’ll see methods to setup the context, model and persistent store coordinator. Import the “Label.h”, “Artist.h”, and “Album.h” files. Add the following method: - (void) create { // Grab the context NSManagedObjectContext *context = [self managedObjectContext]; // Grab the Label entity Label *label = [NSEntityDescription insertNewObjectForEntityForName:@”Label” inManagedObjectCon text:context]; // Set label name label.name = @”Diplomat Records”; // Create a Date NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@”YYYY”]; NSDate *dateFounded = [dateFormatter dateFromString:@”2003”]; // Set the year founded for the label label.founded = dateFounded; // Set the label genre label.genre = @”Rap/Hip-hop”; // Insert the Artist entity Artist *artist = [NSEntityDescription insertNewObjectForEntityForName:@”Artist” inManagedObjectContext:context]; // Set the artist attributes artist.name = @”Cam’Ron”; artist.hometown = @”Harlem, NY”; // Insert Album entity Album *album = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedObjectContext:context]; // Set album attributes album.title = @”Come Home With Me”; NSDate *releaseDate = [dateFormatter dateFromString:@”2002”]; album.released = releaseDate; // Set relationships [label addArtistsObject:artist]; [artist setLabel:label]; [artist addAlbumsObject:album]; [album setArtist:artist]; // Save everything NSError *error = nil; if ([context save:&error]) { NSLog(@”The save was successful!”); } else { NSLog(@”The save wasn’t successful: %@”, [error userInfo]); } } Get the Code from GitHub
  • 10.    10 Make sure to call [self create] in the application:didFinishLaunchingWithOptions: method. Build your project and run it. Check the output in the log navigator to see if it saved properly. You should see this output: It worked? Great, now let’s walk through reading data. Reading an object With Core Data, you perform “fetch requests” to access saved objects. These objects are always returned as instances of NSManagedObject. Check out NSFetchRequest for more info. Add the following method to the “MLBLAppDelegate.m” file: - (void) read { NSManagedObjectContext *context = [self managedObjectContext]; // Construct a fetch request NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@”Label” inManagedObjectContext:context]; [fetchRequest setEntity:entity]; NSError *error = nil; NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error]; for (Label *label in fetchedObjects) { // Log the label details NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@”YYYY”]; NSLog(@”%@, est. %@ (%@)”, label.name, [dateFormatter stringFromDate:label.founded], label. genre); NSLog(@”tArtists:”); NSSet *artists = label.artists; for (Artist *artist in artists) { // Log the artist details NSLog(@”tt%@ (%@)”, artist.name, artist.hometown); NSLog(@”tttAlbums:”); NSSet *albums = artist.albums; for (Album *album in albums) { // Log the album details NSLog(@”tttt%@ (%@)”, album.title, [dateFormatter stringFromDate:album.re- leased]); } } } } Get the Code from GitHub
  • 11.    11 Replace [self create] with [self read] in the application:didFinishLaunchingWithOptions: method. Build and run, and voila! A printout of the objects should appear in the log navigator. - (void) update { // Grab the context NSManagedObjectContext *context = [self managedObjectContext]; // Perform fetch request NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@”Label” inManagedObjectContext:context]; [fetchRequest setEntity:entity]; NSError *error = nil; NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error]; // Date formatter comes in handy NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@”YYYY”]; // Grab the label Label *label = [fetchedObjects objectAtIndex:0]; // Juelz Santana Artist *juelz = [NSEntityDescription insertNewObjectForEntityForName:@”Artist” inManagedObject- Context:context]; juelz.name = @”Juelz Santana”; juelz.hometown = @”Harlem, NY”; // Juelz Santana albums Album *juelzAlbum = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb- jectContext:context]; juelzAlbum.title = @”From Me to U”; juelzAlbum.released = [dateFormatter dateFromString:@”2003”]; [juelzAlbum setArtist:juelz]; Album *juelzAlbum2 = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb- jectContext:context]; juelzAlbum2.title = @”What The Game’s Been Missing!”; juelzAlbum2.released = [dateFormatter dateFromString:@”2005”]; [juelzAlbum2 setArtist:juelz]; // Set relationships [juelz addAlbums:[NSSet setWithObjects:juelzAlbum, juelzAlbum2, nil]]; // Jim Jones Artist *jimmy = [NSEntityDescription insertNewObjectForEntityForName:@”Artist” inManagedObject- Context:context]; jimmy.name = @”Jim Jones”; jimmy.hometown = @”Harlem, NY”; // Jim Jones albums Album *jimmyAlbum = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb- jectContext:context]; jimmyAlbum.title = @”On My Way to Church”; jimmyAlbum.released = [dateFormatter dateFromString:@”2004”]; Updating an object Let’s update our label and add a few more artists and albums. Add this method:
  • 12.    12 [jimmyAlbum setArtist:jimmy]; Album *jimmyAlbum2 = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb- jectContext:context]; jimmyAlbum2.title = @”Harlem: Diary of a Summer”; jimmyAlbum2.released = [dateFormatter dateFromString:@”2005”]; [jimmyAlbum2 setArtist:jimmy]; Album *jimmyAlbum3 = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManagedOb- jectContext:context]; jimmyAlbum3.title = @”Hustler’s P.O.M.E. (Product of My Environment)”; jimmyAlbum3.released = [dateFormatter dateFromString:@”2006”]; [jimmyAlbum3 setArtist:jimmy]; // Set relationships [jimmy addAlbums:[NSSet setWithObjects:jimmyAlbum, jimmyAlbum2, jimmyAlbum3, nil]]; // Freekey Zekey Artist *freekey = [NSEntityDescription insertNewObjectForEntityForName:@”Artist” inManagedOb- jectContext:context]; freekey.name = @”Freekey Zekey”; freekey.hometown = @”Harlem, NY”; Album *freekeyAlbum = [NSEntityDescription insertNewObjectForEntityForName:@”Album” inManaged- ObjectContext:context]; freekeyAlbum.title = @”Book of Ezekiel”; freekeyAlbum.released = [dateFormatter dateFromString:@”2007”]; [freekeyAlbum setArtist:freekey]; [freekey addAlbumsObject:freekeyAlbum]; // Set relationships [label addArtists:[NSSet setWithObjects:juelz, jimmy, freekey, nil]]; // Save everything if ([context save:&error]) { NSLog(@”The save was successful!”); } else { NSLog(@”The save wasn’t successful: %@”, [error localizedDescription]); } } Replace [self read] with [self update] in the application:didFinishLaunchingWithOptions: method. Build and run, and check the log navigator for a successful save. Now let’s print them to the log again. Replace [self update] with [self read]. Build and run, and you should see all the artists/albums under the label. Run it a few more times and compare the log output each time. The order will vary almost every time. Fetch requests return objects unordered, unless specified otherwise. To do so, you need to use NSSort- Descriptor to organize your results. Use NSPredicate to grab specific objects. Get the Code from GitHub
  • 13.    13 Add the line [self delete] right before [self read] in the application:didFinishLaunchingWithOptions: method. Build and run, and you should see Freekey Zekey disappear from the results. His album should be gone too, as we set the delete rule for that relationship to “Cascade”. What’s Next? Now that you’ve learned the basics of Core Data, it’s time to start building a full-fledged application on top of it. In part 3 of our tutorial, we’ll add a UI to what we’ve built so far. Download the source code for this tutorial. Deleting an object Finally, we’ll delete an object. Let’s add this method: - (void) delete { // Grab the context NSManagedObjectContext *context = [self managedObjectContext]; // We’re looking to grab an artist NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@”Artist” inManagedObjectContext:context]; [fetchRequest setEntity:entity]; // We specify that we only want Freekey Zekey NSPredicate *predicate = [NSPredicate predicateWithFormat:@”name == %@”, @”Freekey Zekey”]; [fetchRequest setPredicate:predicate]; NSError *error = nil; NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error]; // Grab the artist and delete Artist *freekey = [fetchedObjects objectAtIndex:0]; [freekey.label removeArtistsObject:freekey]; // Save everything if ([context save:&error]) { NSLog(@”The save was successful!”); } else { NSLog(@”The save wasn’t successful: %@”, [error localizedDescription]); } } Get the Code from GitHub
  • 14.    14 Part 3: Adding the User Interface In this section, we’ll focus on the UI, or user interface, the layer on top of our view controllers that allows a user to interact with our data. The setup of the UI is equally as important as Core Data. An app that functions perfectly won’t be successful if its interface is not intuitive and aesthetically pleasing. Before touchscreen devices became ubiquitous, the UI was sometimes cast as an afterthought. Now it’s virtually impossible to ignore. Tablets and phones come with constraints in screen size, requiring much more consideration for the user experience and flow of information through an app. Great UI design properly communicates to the user the purpose of your application. With that in mind, we’ll construct a simple user interface for our MusicLabel app, which will allow users to create and delete entries into the database. Create a storyboard First, we’ll need to add a storyboard to our project. Press command + N and select “User Interface” from the available templates. Choose Storyboard, and click next.
  • 15.    15 Name the file “MainStoryboard” and click create. In the MusicLabel project, target select the storyboard we just created to be the Main Storyboard. Create a tableview Click on the storyboard and make sure the “Utilities” pane is open. Drag a table view controller from the object library onto the storyboard.
  • 16.    16 With the prototype cell selected, navigate to the attributes inspector. Enter “Cell” as an identifier. Press command + N, and from the “Cocoa touch” templates, select “Objective-C class”. Name the file “MLBLLabelViewController” and make it a subclass of UITableViewController. Back in the storyboard, select the table view controller and navigate to the identity inspector. Change the custom class to “MLBLLabelViewController”.
  • 17.    17 Get the Code from GitHub Make the following changes to the MLBLLabelViewController.h: Make the following changes to the MLBLLabelViewController.m: #import <UIKit/UIKit.h> #import “MLBLAppDelegate.h” #import “Label.h” @interface MLBLLabelViewController : UITableViewController // An array to house all of our fetched Label objects @property (strong, nonatomic) NSArray *labelArray; @end /* Make these changes to MLBLLabelViewController.M */ @implementation MLBLLabelViewController @synthesize labelArray; - (void) viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; /* Here we call the method to load the table data */ [self loadTableData]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // We only need to return 1 for this table view return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // We’ll return the count of the objects in labelArray return [labelArray count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath { static NSString *CellIdentifier = @”Cell”; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; }
  • 18.    18 Get the Code from GitHub // Configure the cell... cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // Grab the label Label *label = [self.labelArray objectAtIndex:indexPath.row]; // Set the text of the cell to the label name cell.textLabel.text = label.name; return cell; } #pragma mark - Private methods - (MLBLAppDelegate *)appDelegate { return (MLBLAppDelegate *)[[UIApplication sharedApplication] delegate]; } // This method executes a fetch request and reloads the table view. - (void) loadTableData { NSManagedObjectContext *context = [[self appDelegate] managedObjectContext]; // Construct a fetch request NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@”Label” inManagedObjectContext:context]; [fetchRequest setEntity:entity]; // Add an NSSortDescriptor to sort the labels alphabetically NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”name” ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; [fetchRequest setSortDescriptors:sortDescriptors]; NSError *error = nil; self.labelArray = [context executeFetchRequest:fetchRequest error:&error]; [self.tableView reloadData]; } @end Build and run. Based on the data we entered in the last tutorial, you should see this:
  • 19.    19 Get the Code from GitHub Creating a Navigation Controller Now lets add to our setup: with “MLBLLabelViewController” selected in the storyboard, choose Editor > Embed In > Navigation Controller. A navigation controller is a standard UI element provided by Apple to help guide the flow of an app. It includes logic for going back to a previous view controller as well as transitions between screens. We’ve embedded “MLBLLabelViewController” as the root view controller in our navigation controller. Drag another table view controller onto the storyboard. Create a new file “MLBLArtistViewController” that subclasses UITableViewController. Change the custom class of the new table view controller to “MLBLArtistViewController”. Give it a storyboard ID of “ArtistViewController” #import <UIKit/UIKit.h> #import “MLBLAppDelegate.h” #import “Label.h” #import “Artist.h” @interface MLBLArtistViewController : UITableViewController // An array to house all of our fetched Artist objects @property (strong, nonatomic) NSArray *artistArray; //The id of the parent object @property (strong, nonatomic) NSManagedObjectID *labelID; @end Make the following changes to the MLBLArtistViewController.h:
  • 20.    20 Make the following changes to the MLBLArtistViewController.m: /* Make these changes to MLBArtistViewController.m */ @implementation MLBLArtistViewController @synthesize artistArray; -(void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; [self loadTableData]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // We only need to return 1 for this table view return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // We’ll return the count of the objects in artistArray return [artistArray count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *) indexPath { static NSString *CellIdentifier = @”Cell”; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } // Configure the cell... cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; // Grab the artist Artist *artist = [self.artistArray objectAtIndex:indexPath.row]; cell.textLabel.text = artist.name; return cell; } #pragma mark - Private methods - (MLBLAppDelegate *)appDelegate { return (MLBLAppDelegate *)[[UIApplication sharedApplication] delegate]; } - (void) loadTableData { NSManagedObjectContext *context = [[self appDelegate] managedObjectContext]; // Construct a fetch request
  • 21.    21 NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@”Artist” inManagedObjectContext:context]; [fetchRequest setEntity:entity]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@”label == %@”, [context objectWithID:self.labelID]]; [fetchRequest setPredicate:predicate]; // Add an NSSortDescriptor to sort the labels alphabetically NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”name” ascending:YES]; NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; [fetchRequest setSortDescriptors:sortDescriptors]; NSError *error = nil; self.artistArray = [context executeFetchRequest:fetchRequest error:&error]; [self.tableView reloadData]; } @end Add #import MLBLArtistViewController.h to the MLBLLabelViewController.m file. In the - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath method, make the following changes: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { MLBLArtistViewController *artistViewController = [self.storyboard instantiateViewControllerWithIdentifier:@”ArtistViewController”]; // Grab the label Label *label = [self.labelArray objectAtIndex:indexPath.row]; artistViewController.labelID = [label objectID]; [self.navigationController pushViewController:artistViewController animated:YES]; } Build and run, and you should be able to view the artists that are associated with a label. Next, do the same thing with albums. Create a file called “MLBLAlbumViewController” that subclasses UITableViewController, and repeat the steps for “MLBLArtistViewController”.   Get the Code from GitHub Get the Code from GitHub
  • 22.    22 Hold the option key and click on MLBLLabelEntryViewController.h. This will open the file side by side with the storyboard. Creating an Input View Now we’ll make input screens to insert data into our database. Add these two lines of code to the -(void)viewDidLoad method in MLBLLabelViewController.m: UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(addItem)]; self.navigationItem.rightBarButtonItem = item; Create a file called “MLBLLabelEntryViewController”, which subclasses a UIViewController. Drag a view controller from the object library onto the storyboard. Change the custom class to “MLBLLabelEntryViewController”. Drag a text field onto the view controller as well. In the attributes inspector you can customize the text field; set the placeholder text to “Enter the Label Name”. Hold the option key and click on MLBLLabelEntryViewController.h. This will open the file side by side with the storyboard. This establishes an outlet connection between the storyboard nib file and the view controller class. Name the outlet “labelField” and click connect. Add the following code to MLBLLabelEntryViewController.m:
  • 23.    23  Get the Code from GitHub /* Make these changes to MLBLLabelEntryViewController.m */ #import “MLBLLabelEntryViewController.h” #import “MLBLAppDelegate.h” #import “Label.h” @implementation MLBLLabelEntryViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // This will call our UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@”Add” style:UIBarButtonItemStyleBordered target:self action:@selector(addLabel)]; self.navigationItem.rightBarButtonItem = item; UIBarButtonItem *item2 = [[UIBarButtonItem alloc] initWithTitle:@”Cancel” style:UIBarButtonItemStyleBordered target:self action:@selector(dismiss)]; self.navigationItem.leftBarButtonItem = item2; } #pragma mark - private methods - (MLBLAppDelegate *)appDelegate { return (MLBLAppDelegate *)[[UIApplication sharedApplication] delegate]; } - (void) addLabel { // Grab the context NSManagedObjectContext *context = [[self appDelegate] managedObjectContext]; // Grab the Label entity Label *label = [NSEntityDescription insertNewObjectForEntityForName:@”Label” inManagedObjectContext:context]; // Set label name label.name = self.labelField.text; // Save everything NSError *error = nil; if ([context save:&error]) { NSLog(@”The save was successful!”); } else { NSLog(@”The save wasn’t successful: %@”, [error userInfo]); } [self dismiss]; } - (void) dismiss { [self dismissViewControllerAnimated:YES completion:nil]; } @end
  • 24.    24  Get the Code from GitHub In the MLBLLabelViewController.m file add #import “MLBLLabelEntryViewController.h”, add the following code: // Add this method for slide to delete -(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewCellEditingStyleDelete; } // Override to support editing the table view. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle) editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the row from the data source NSManagedObjectContext *context = [[self appDelegate] managedObjectContext]; // Grab the label Label *label = [self.labelArray objectAtIndex:indexPath.row]; [context deleteObject:[context objectWithID:[label objectID]]]; // Save everything NSError *error = nil; if ([context save:&error]) { NSLog(@”The save was successful!”); } else { NSLog(@”The save wasn’t successful: %@”, [error userInfo]); } NSMutableArray *array = [self.labelArray mutableCopy]; [array removeObjectAtIndex:indexPath.row]; self.labelArray = array; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } } // Add this method under our private methods pragma mark - (void) addItem { MLBLLabelEntryViewController *labelEntryViewController = [self.storyboard instantiateViewControllerWithIdentifier:@”LabelEntryViewController”]; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:labelEntryViewController]; [self presentViewController:navigationController animated:YES completion:nil]; }
  • 25.    25 Build it and run, and you can now add Labels to the database. You’re also able to slide to delete Labels from the table, and remove them from the database. You can easily expand on this by adding input views for Artists and Albums as well. What’s Next? In this tutorial, we constructed a simple UI to access and display our data. In the next installment of our series, we’ll utilize the StackMob SDK to enhance our app. Download the source code for this tutorial.
  • 26.    26 Part 4: Connecting to StackMob As we noted earlier, a requirement for anything beyond the simplest app is the ability to persist data. Another trend we’re witnessing is the proliferation of internet enabled apps that have some sort of backend server logic. The case for a backend It’s very rare to find an app that doesn’t rely in some sort of fashion on a backend. Users come with the expectation of sharing and interacting with their friends and having information delivered to them in a manner tailored to their specific interests and/or location. Some examples of these features include user creation and management, data indexing, push notifications and geo location coordinates. Developers deliver these features by utilizing 3rd party API’s, and ultimately, building a custom backend. Unfortunately, engineering a backend has a much steeper learning curve than learning Core Data. You also have to manage and account for a whole host of other potential problems: data security, scalability, etc. Backend difficulties – and a solution The configuration and management of a backend server is its own ordeal, entirely separate from the app creation process. Depending on the size of your operation, it can range anywhere from a major nuisance to a project non-starter. Regardless of size, the issue is relevant and real. Whether it’s an indie app maker, a startup developer or a large enterprise organization, the prospect of building a backend is expensive in both time and resources. This is where StackMob provides value. StackMob offers a robust end to end platform for everything related to your app. Through StackMob you can house your data reliably and securely, and scale to millions of users with minimal effort. 3rd party services, API versioning, custom server code and other great features are available as modules in the StackMob Marketplace.
  • 27.    27 Working with the StackMob SDK Using the StackMob iOS SDK is simple and straightforward. Lets get started by incorporating it into our MusicLabel project. If you haven’t done so already, head over to our dev center and signup for a StackMob account. The getting started page will take you through the process of signing up, creating an app and downloading and importing the iOS SDK. Name your app “musiclabel” and select iOS as a platform. Clicking “Step 2” will create your app. When you are finished importing the SDK, navigate back to your dashboard. You will see information about your newly created app, including public and private keys for both your development and production environments.
  • 28.    28 StackMob and Core Data The StackMob iOS SDK is tightly integrated with Core Data. The Core Data Coding Practices outline what to keep in mind when using the SDK. In your Xcode project, navigate to your data model in the “MusicLabel.xcdatamodeld” file. In the Label entity, add a string attribute titled “labelId”. Add “artistId” and “albumId” string attributes to the Artist and Album entities, respectively. Recreate your NSManagedObject subclass files. Click “Replace” when prompted.
  • 29.    29 In “MLBLAppDelegate.m” make the following changes. Make sure to add your public key when initializing the SMClient. Get the Code from GitHub #import <UIKit/UIKit.h> #import “StackMob.h” @class SMClient; @class SMCoreDataStore; @interface MLBLAppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) NSManagedObjectModel *managedObjectModel; // CODE UPDATE: Declare SMCoreDataStore instance @property (strong, nonatomic) SMCoreDataStore *coreDataStore; // CODE UPDATE: Declare SMClient instance @property (strong, nonatomic) SMClient *client; @end  In “MLBLAppDelegate.m”, we are going to initialize an SMClient instance as well as our declared SMCoreDataStore instance. SMClient is the fundamental class for interacting with StackMob. Make sure to add your public key when initializing the SMClient. #import “MLBLAppDelegate.h” #import “Label.h” #import “Artist.h” #import “Album.h” @implementation MLBLAppDelegate // Synthesize SMCoreDataStore @synthesize coreDataStore _coreDataStore; @synthesize managedObjectModel = _managedObjectModel; // Synthesize SMClient Changes to your code The StackMob iOS SDK manages most of the Core Data stack, including initializing managed object contexts and a persistent store coordinator. Let’s edit our codebase accordingly. In the MLBLAppDelegate.h file, remove the managedObjectContext property. Remove the following methods in MLBLAppDelegate.m as well: • - (void)saveContext; • - (NSURL *)applicationDocumentsDirectory; • - (NSManagedObjectContext *)managedObjectContext; • - (NSPersistentStoreCoordinator *)persistentStoreCoordinator; To obtain an initialized, ready to go managed object context, SMCoreDataStore provides the contextForCurrentThread method.Since we need to reference a local SMCoreDataStore instance to obtain managed object contexts, declare one in your “MLBLAppDelegate.h” file like so:
  • 30.    30 Get the Code from GitHub The SDK provides helper methods for the traditional save: and executeFetchRequest:error: Core Data calls. They are all designed for optimal performance by adhering to common Core Data patterns and include asynchronous callback-based methods which are performed in the background. Edit your create method from the previous tutorials to utilize the synchronous saveAndWait: call as well as the asynchronous saveOnSuccess:onFailure: call. Your updated code should look like this: @synthesize client = _client; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions { // Override point for customization after application launch. self.client = [[SMClient alloc] initWithAPIVersion:@”0” publicKey:@”YOUR_PUBLIC_KEY”]; self.coreDataStore = [self.client coreDataStoreWithManagedObjectModel:self. managedObjectModel]; /* The methods we use in the tutorial. */ [self create]; return YES; } // Application delegate methods remain the same. Delete the Core Data methods for NSPersistantStoreCoordinator and NSManagedObjectContext. ... // Returns the managed object model for the application. // If the model doesn’t already exist, it is created from the application’s model. - (NSManagedObjectModel *)managedObjectModel { if (_managedObjectModel != nil) { return _managedObjectModel; } NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@“MusicLabel” withExtension:@“momd”]; _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; return _managedObjectModel; } @end - (void) create { // Grab the context NSManagedObjectContext *context = [self.coreDataStore contextForCurrentThread]; // Grab the Label entity Label *label = [NSEntityDescription insertNewObjectForEntityForName:@“Label” inManagedObjectContext:context]; label.labelId = [label assignObjectId]; // Set label name label.name = @“Diplomat Records”;
  • 31.    31 Get the Code from GitHub // Create a Date NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat:@“YYYY”]; NSDate *dateFounded = [dateFormatter dateFromString:@“2003”]; // Set the year founded for the label label.founded = dateFounded; // Set the label genre label.genre = @“Rap/Hip-hop”; // Insert the Artist entity Artist *artist = [NSEntityDescription insertNewObjectForEntityForName:@“Artist” inManagedObjectContext:context]; artist.artistId = [artist assignObjectId]; // Set the artist attributes artist.name = @“Cam’Ron”; artist.hometown = @“Harlem, NY”; // Insert Album entity Album *album = [NSEntityDescription insertNewObjectForEntityForName:@“Album” inManagedObjectContext:context]; album.albumId = [album assignObjectId]; // Set album attributes album.title = @“Come Home With Me”; NSDate *releaseDate = [dateFormatter dateFromString:@“2002”]; album.released = releaseDate; // CODE UPDATE: Use the synchronous save method saveAndWait: NSError *error = nil; if (![context saveAndWait:&error]) { NSLog(@“The save wasn’t successful: %@”, [error userInfo]); } else { NSLog(@“Save successful”); } // Set relationships [label addArtistsObject:artist]; [artist setLabel:label]; [artist addAlbumsObject:album]; [album setArtist:artist]; // CODE UPDATE: Use the asynchronous save method saveOnSuccess:onFailure: [context saveOnSuccess:^{ NSLog(@“Successful relations update”); } onFailure:^(NSError *error) { NSLog(@“Unsuccessful relations update with error %@”, error); }]; }
  • 32.    32 Get the Code from GitHub The key thing to remember is that any NSManagedObject must have an id assigned to it before it is saved to StackMob. In “MLBLLabelEntryViewController.m” add the following line to the -(void) addLabel method: label.labelId = [label assignObjectId]; Go ahead and edit all the save:error and executeFetchRequest:error: calls throughout your codebase to utilize the StackMob methods. In all your .m files you will want to assign your managedObjectContext variables to [[[self appDelegate] coreDataStore] contextForCurrentThread];, which may require you to add #import “StackMob.h” as well. More information on all the available methods to interact with Core Data can be found in the NSManagedObjectContext+Concurrency reference. Build and run, and check your dashboard. Under “Schema Configuration”, you should see the entities you created, alongside the “user” schema. - (void) delete { // Grab the context NSManagedObjectContext *context = [self.coreDataStore contextForCurrentThread]; // We’re looking to grab an artist NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@”Artist” inManagedObjectContext:context]; [fetchRequest setEntity:entity]; // We specify that we only want Freekey Zekey NSPredicate *predicate = [NSPredicate predicateWithFormat:@“name == %@”, @“Freekey Zekey”]; [fetchRequest setPredicate:predicate]; // CODE UPDATE: Nest the asynchronous save call in the asynchronous fetch call. [context executeFetchRequest:fetchRequest onSuccess:^(NSArray *results) { // Grab the artist and delete Artist *freekey = [results objectAtIndex:0]; [freekey.label removeArtistsObject:freekey]; // Save everything [context saveOnSuccess:^{ NSLog(@“The save was successful!”); } onFailure:^(NSError *error) { NSLog(@“The save wasn’t successful: %@”, [error localizedDescription]); }]; } onFailure:^(NSError *error) { NSLog(@“Error fetching: %@”, error); }]; } Let’s take a look at another updated CRUD method we’ve created, the delete method. Here, we see the saveOnSuccess:onFailure: call wrapped in the callback of the executeFetchRequest:onSuccess:onFailure: call. Edit your code to look like the following:
  • 33.    33 There you have it! With a few steps and very little additional code, we’ve seamlessly integrated StackMob into our Core Data stack. What’s Next? Be sure to check out the StackMob iOS SDK Reference for more information on using the StackMob iOS SDK. Also, take a look at our iOS tutorials for helpful examples. Download the source code for this tutorial. For more information, visit out blog. Click on “Data Management” and you’ll be able to query the objects you’ve saved.