TweetFollow Us on Twitter

The Road to Code: Writing Even Less Code

Volume Number: 24
Issue Number: 12
Column Tag: The Road to Code

The Road to Code: Writing Even Less Code

Introduction to Core Data

by Dave Dribin

Core Data

Our topic this month is Core Data, a technology introduced in Mac OS X 10.4 by Apple to help reduce time spent writing code. Interface Builder allows you to setup view classes with out writing any custom layout code. Cocoa bindings allow you tie together the view and the model without writing any custom controller code. What's left? The model. Core Data is an attempt to allow developers to write model classes without writing any code.

Core Data was introduced in Mac OS X 10.4 and heavily enhanced in OS X 10.5. In a nutshell, Core Data provides automatic object persistence and object lifecycle management. The phrase object persistence is a fancy way of saying "saving objects to a file," similar to archiving. Core Data provides more, though. It allows you to find and filter objects and provides integration with the user interface, such as automatic undo support. It can optionally store your objects to a single-user relational database for increased performance.

It is common to think of Core Data as an object-relational mapping (ORM) tool. ORM tools automatically persist objects to a relational database. ORM tools allow you to save, find, and update objects using SQL statements. SQL is the language of relational databases, and ORM tools typically generate the SQL select, update and insert statements for you. Some popular ORM tools in the Java world are Hibernate and Enterprise Java Beans (EJB), and Active Record is the ORM tool for Ruby on Rails.

However, there is one very important difference between most ORM tools and Core Data. Most ORM tools are designed for multi-user and high-concurrency environments, such as the backend of web application. Core Data is designed with the single-user desktop application in mind. You'll see some of the ramifications of this as you delve into the architecture and use cases of Core Data. For example, Core Data does not require use of a relational database.

A Bit of History

Before digging into the details of Core Data, it is useful to understand where it came from. It turns out that Apple has it's own web-based ORM tool as part of WebObjects. WebObjects is a complete web application framework. It started life way back in the early 1990s at NeXT written in Objective-C and was later rewritten in Java. When Apple acquired NeXT, mainly for Mac OS X, it also acquired WebObjects. Today, Apple uses WebObjects for its online store and the server side components of the iTunes Music Store. It is also available for free for developers to use and has quite a strong, albeit small, following.

WebObjects includes an ORM tool called Enterprise Objects Frameworks, or EOF. It allows automatic persistence of Java model objects to a relational database. As far as ORM tools go, EOF is quite powerful and feature filled. However, since it is now only available in Java, it is not something that can be used directly by Cocoa applications.

Apple used its experience with EOF to design Core Data. It took many of the concepts of EOF and pared them down for single-user applications. There is no direct link between EOF and Core Data, just a foundation of ORM experience. Unfortunately, not all of the features of EOF were ported to Core Data, so sometimes we are left wanting.

Terminology

Core Data introduces some new terminology, mainly borrowed terminology from the database world. Databases are modeled using the entity-relationship model, thus Core Data objects are also modeled using the entity-relationship model. But don't fret, most of these terms correlate to terms you already know.

In an entity-relationship model, a class is called an entity. And just classes contains instance variables, entities contain properties. There are two kinds of properties: attributes and relationships. Attributes are properties consisting of simple data types, such as strings, numbers, and dates. Relationships are properties that link together multiple entities. We won't be covering relationships in this article.

Okay, enough of the jibber jabber, let's get on to writing a Core Data application.

Core Data Version of Rectangles

We are going to convert the Rectangles application that we have built up over the last few months to a Core Data application. Using Core Data will allow us to remove almost all of our code. We can't quite get rid of all of it, but the reduction is still dramatic.

We're going to start off my creating a new project. Create a Core Data Document-based Application from the New Project window, as shown in Figure 1.


Figure 1: New Core Data project

This project looks similar to standard document-based applications, but it has an additional group in the Groups & Files list called Models, as shown in Figure 2. In that group is a file named MyDocument.xcdatamodel. This file extension is short for "Core Data data model" and represents the entity-relationship model for our application.


Figure 2: Groups and Files

We're going to dive right in by working on our data model first. We want to create a rectangle entity with width and height attributes. If you double click on the data model, you will be presented with what is known as the Core Data modeler, as shown in Figure 3. We will design our data model graphically without writing any code or text.


Figure 3: Core Data modeler

Click the + button in the Entity pane to create a new entity. Use the detail view on the right side of the window to rename the entity to Rectangle, as shown in Figure 4.


Figure 4: Rectangle entity

Next, add a new attribute by making sure the Rectangle entity is still selected and click the + button in the Property pane. Rename the attribute to width, uncheck the Optional checkbox, change the Type to Float, and set the Default Value to 0. Now repeat this process to create a height attribute. The result should look like Figure 5.


Figure 5: Height and width attributes

In the bottom half of the window, you will see a graphical representation of our entity-relationship model, as shown in Figure 6. This graphical representation is called an entity-relationship diagram or ER diagram. It is common to model relational databases using an ER diagram, again showing the database roots of Core Data.


Figure 6: Rectangle ER diagram

With that, we're done with our data model. We can now save, load, and use Rectangle entities in our application. But, we haven't written any code, yet. How can you use an entity without any code? Core Data provides built-in classes to make this easy.

Core Data Class Architecture

When you created the Rectangle entity, you'll notice that one of the fields is named Class and, by default, is set to NSManagedObject. Every instance of a Core Data entity is represented by an instance of NSManagedObject. In a way, it is similar to how NSObject is the root class of all Objective-C classes. However, you do not need to subclass NSManagedObject to use it. So how do you access the properties? Using key-value coding.

When an NSManagedObject, or managed object for short, gets instantiated, it's initialized for a particular entity. The managed object reads your data model and allows you to access the attributes using the standard KVC valueForKey: and setValue:forKey: methods. For example, say we have an instance of NSManagedObject representing our Rectangle entity. You would access the width attribute like:

    NSManagedObject * rectangle = ...;
    NSNumber * width = [rectangle valueForKey:@"width"];
    NSLog(@"Width is: %f", [width floatValue]);

But for now, we do not need to use our entity from code. We will be using Cocoa bindings to connect managed objects to the user interface, which uses KVC.

Another important class in the Core Data architecture is NSMangedObjectContext, or managed object context. You use the managed object context to find, create, and delete managed objects. Every managed object instance is owned by the managed object context that created it. The managed object context also contains unsaved changes. Thus, when the user makes their edits, these changes occur only the managed object context. To save these changes to disk, you tell the managed object context to save.

The final important class in the Core Data architecture is NSPersistentStoreCoordinator, or persistent store coordinator. A persistent store coordinator links a managed object context to a specific file on disk, called a persistent store. There are three types of persistent stores that defines the internal file format on disk: XML, binary, and SQLite. The XML persistent store is nice because you can use any text editor to examine the contents. The binary persistent store is faster and more efficient, but it is a proprietary file format. The SQLite persistent store type is the most interesting.

SQLite is a full relational database system that is self-contained and serverless. Most relational database systems, like PostgreSQL, MySQL, or Oracle, require a separate server process to run in the background. A server-based relational database requires a lot of extra setup and can be a little difficult to maintain. SQLite on the other hand is linked in as part of your application and the database is stored in a single file on disk. You access the data using standard SQL queries. SQLite cannot handle highly concurrent multi-user applications like busy web applications, but it is perfect for a single user application, like an OS X application.

When Core Data uses a SQLite persistent store, it works very much like traditional ORM tools. It generates SQL statements to create, save, and find objects in the database. Core Data does not allow you to interact with SQLite directly, though. It hides all the SQL from the programmer.

The benefit of using a SQLite persistent store type is that it is much more efficient than the XML and binary types. Only the objects that you are using get loaded into memory. Thus if the user's data contains a large amount of entities, SQLite will only load the objects the user is using, saving memory. Also, finding and filtering managed objects are more efficient since Core Data can use SQL to find the objects. Databases are designed to perform searches very efficiently.

Generally, you use the XML persistent store type during the early stages of development, because the file can be read with a text editor. However, SQLite is often the best case for a released application since it is more efficient. There are some subtle differences between the persistent store types, so if you do change persistent store types, be sure to thoroughly test after the changes. Also, if you change the type, your application will not be able to load previously saved files.

I admit that the Core Data architecture is somewhat complex. However, if you are writing a document-based application, your life is simplified. Apple provides an NSDocument subclass for document-based applications that use Core Data called NSPersistentDocument. The NSPersistentDocument takes care of setting up the persistent store coordinator and managed object context for you. When you save your document, it saves the context to the user's file. Since we chose a Core Data Document-based Application, our MyDocument class is automatically a NSPersistentDocument.

Configuring the Document Type

Let's now setup the document type for our application, so we can save and open files. Double-click on the Rectangles target to open the Info window for this target. You'll notice there are three document types already present, one for each of the different persistent store types. We'll use SQL type, so delete the other two. Change the Name to Rectangles Document and the extension to rectanglescd. We already used the rectangles extension in our previous, non-Core Data application, so we want to choose a different one to avoid a conflict.

Also change the Identifier to something unique. All of these changes are summarized in Figure 7.


Figure 7: Document types

Laying out the User Interface

We're now going to switch to Interface Builder to layout the interface and setup our Cocoa bindings. Remove the initial text label, and add a table view. Rename the columns to Width and Height. Drag number formatters on each of the columns and set their Style to Decimal. Now add two buttons underneath the table, one named Add the other named Remove. Setup the autosizing appropriately. The final result should look like Figure 8.


Figure 8: Initial window layout

Next, we're going to create an array controller for our Cocoa bindings. Drag an array controller from the Library to our xib document window, and rename it to Rectangles, as shown in Figure 9.


Figure 9: Array controller

Now setup the array controller's attributes to match Figure 10. You need to set the Mode to Entity, set the Entity Name to Rectangle, and check the Prepares Content checkbox. This puts the array controller into Core Data mode and will hold instances of NSManagedObject of the given entity name.


Figure 10: Array controller attributes

Our array controller needs a managed object context to fetch and edit managed objects. Remember that NSPersistentDocument sets up a context for each document. The File's Owner for the MyDocument.xib is our MyDocument subclass of NSPersistenDocument. We can bind the array controller's Managed Object Context to the managedObjectContext of File's Owner, as shown in Figure 11.


Figure 11: Array controller bindings

With our array controller in place, we can now bind the Width and Height columns. Just as we did for our non-Core Data application, bind the Width column to the Rectangles array controller. Set the Controller Key to arrangedObjects and the Model Key Path to width, as shown in Figure 12. Make similar bindings for the Height column, except using height as the Model Key Path.


Figure 12: Width column bindings

Finally, hookup the Add button to the Rectangles array controller's add: action and the Remove button to the remove: action. Bind the Add button's Enabled binding to the canAdd Controller Key and bind the Remove button's Enabled binding to the canRemove Controller Key.

At this point, we have a fully functional application. You should be able to add and remove rectangles from the table using the buttons, and you should be able to save and open documents. Furthermore, you will notice that you get dirty document and undo support. Now I'd like to remind you again that we haven't written any code. This is the promise of Core Data.

Of course our application is missing a few things from our non-Core Data version. First, the default width for new rectangles was 15 and the default height was 10. In our non-Core Data version, we created our own subclass of array controller and overrode the newObject method. We can certainly do that, but for now, I'm going to just change the default values in the data model, to keep with the "no code" theme.

The other major part missing is the area and perimeter table columns. Unfortunately, we are going to have to create some code to get these working. Because these are computed properties, we cannot add them in the data model. We're going to have to create a subclass of NSManagedObject for our Rectangle entity. Fortunately, Xcode can give us a head start. Make sure that your data model is selected in the Groups & Files list and click on File > New File to create a new file. Click on Managed Object Class in the New File window, as shown in Figure 13.


Figure 13: Generate managed object

Click Next as the Location and Target defaults should be correct. In the final screen, make sure that the Rectangles entity is selected as shown in Figure 14 and click Finish. This will create Rectangle.h and Rectangle.m files. You may want to move them in the Models group along with your data model file so you remember that they are managed objects.


Figure 14: Generate Rectangle class

If you look at the header, you'll see just a couple of @property declarations. There are no instance variables. Remember that the values are actually stored by the NSMangedObject superclass using KVC, so we don't need any instance variables in our subclass. These properties provide convenient, type-safe access to the entity's properties. The implementation file uses the @dynamic property keyword instead of the @synthesize. This is again because there are no instance variables.

Note that the width and height are of type NSNumber, even though we set them as Float in the data model. Core Data only deals with objects. If you want to get the actual float value, you need to use the floatValue method of NSNumber. Conversely, if you want to set the float value, you need wrap a float in an NSNumber:

    // Get the width as a float
    float width = [rectangle.width floatValue];
    // Set the width from a float
    rectangle.width = [NSNumber numberWithFloat:30.0];

Now that we have a real class, we can add our area and perimeter properties. Just as in our non-Core Data application, they are read-only. Listing 1 shows the full header with the property declarations:

Listing 1: Rectangle.h

#import <CoreData/CoreData.h>
@interface Rectangle :  NSManagedObject  
{
}
@property (retain) NSNumber * width;
@property (retain) NSNumber * height;
@property (readonly) float area;
@property (readonly) float perimeter;
@end

I used float here because it is easier to deal with primitive types when doing math. The implementation of these properties is similar to our non-Core Data application. We do have to convert the width and height to floats so we can perform math on them, though. The key-value observing methods are the same, however. Listing 2 shows the full implementation file.

Listing 2: Rectangle.m

#import "Rectangle.h"
@implementation Rectangle 
@dynamic width;
@dynamic height;
- (float)area
{
    return ([self.width floatValue] *
            [self.height floatValue]);
}
+ (NSSet *)keyPathsForValuesAffectingArea
{   
    return [NSSet setWithObjects:@"width", @"height", nil];
}
- (float)perimeter
{
    return ((2*[self.width floatValue]) +
             (2*[self.height floatValue]));
}
+ (NSSet *)keyPathsForValuesAffectingPerimeter
{
    return [NSSet setWithObjects:@"width", @"height", nil];
}
@end

And that's all the code we need. Now, we just need to add the remaining elements to the user interface.

Creating the Final User Interface

Switch to Interface Builder to complete the changes. Add two more columns to the table view and label them Area and Perimeter, respectively. Make sure they are marked as uneditable and add Decimal number formatters to both. Bind them to arrangedObjects.area and arrangedObjects.perimeter, respectively, of the Rectangles array controller. Figure 15 shows the bindings for the Area column.


Figure 15: Area column bindings

Add four labels at the bottom left side of the table for the Total Area: and Total Perimeter: fields. Two will be used for static text and two will be used for the actual values. For the two used for the values, add Decimal number formatters. The final window layout should look like Figure 16.


Figure 16: Final window layout

We are now going to setup the bindings using collection operators, just like we did in the non-Core Data application. Bind the total area value label to the Rectangles array controller. Set Controller Key to arrangedObjects and the Model Key Path to @sum.area. The result should look like Figure 17. Next, bind the total perimeter value field to the @sum.perimeter Model Key Path.


Figure 17: Total area bindings

We are now finished with our changes. You should be able to run the application and have the area and perimeter user interface elements work appropriately. Our application is nearly identical to our non-Core Data application, and all we had to do was write four methods for the area and perimeter. And this includes support for undo! Not to shabby.

Caveat Emptor

There is one subtle difference from our non-Core Data application. In our previous application, we store an array of rectangles in the document. This means that as we added rectangles, their order is preserved. Core Data uses sets, instead of arrays, which means they are unordered. But surely they have to show up in some order in the table. Unordered just means the order is not consistent. The list of rectangles may show up in a different order every time you load the file. Since the user can sort the table by clicking on one of the table columns, the easy way to fix the problem is to always sort the table by a column, by default. To truly remember the order of the items as they were entered is a very difficult Core Data problem, though. And even though setting up default column sorting is significantly easier, it is still beyond the scope of this article. For now, we'll just have to deal with that limitation.

Also, this article just scratches the tip of the Core Data iceberg. There are many more advanced topics, such a relationship properties, threading techniques, and migration between different versions of your model. Apple's documentation has a lot of information on these topics.

Conclusion

Even though Core Data is a complicated subject with some limitations, it is still a hugely valuable asset to Mac OS X development. It can save you from writing a lot of your own model code, just as Cocoa bindings can save you from writing a lot of controller code. It won't eliminate code completely, but I urge you to at least investigate Core Data for your future applications.


Dave Dribin has been writing professional software for over eleven years. After five years programming embedded C in the telecom industry and a brief stint riding the Internet bubble, he decided to venture out on his own. Since 2001, he has been providing independent consulting services, and in 2006, he founded Bit Maki, Inc. Find out more at http://www.bitmaki.com/ and http://www.dribin.org/dave/.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Yasu 4.0.0 β - System maintenance app; p...
Yasu was created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks quickly within a... Read more
Skype 7.37.0.178 - Voice-over-internet p...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
EtreCheck 3.0.5 - For troubleshooting yo...
EtreCheck is an app that displays the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support Communities to... Read more
Amadeus Pro 2.3.1 - Multitrack sound rec...
Amadeus Pro lets you use your Mac computer for any audio-related task, such as live audio recording, digitizing tapes and records, converting between a variety of sound formats, etc. Thanks to its... Read more
NeoFinder 6.9.3 - Catalog your external...
NeoFinder (formerly CDFinder) rapidly organizes your data, either on external or internal disks, or any other volumes. It catalogs all your data, so you stay in control of your data archive or disk... Read more
WhatsApp 0.2.1880 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
Hazel 4.0.6 - Create rules for organizin...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a familiar... Read more
Apple iBooks Author 2.5 - Create and pub...
Apple iBooks Author helps you create and publish amazing Multi-Touch books for iPad. Now anyone can create stunning iBooks textbooks, cookbooks, history books, picture books, and more for iPad. All... Read more
MYStuff Pro 2.0.26 - $39.99
MYStuff Pro is the most flexible way to create detail-rich inventories for your home or small business. Add items to MYStuff by dragging and dropping existing information, uploading new images, or... Read more
MarsEdit 3.7.8 - Quick and convenient bl...
MarsEdit is a blog editor for OS X that makes editing your blog like writing email, with spell-checking, drafts, multiple windows, and even AppleScript support. It works with with most blog services... Read more

How to get past Vulture Island's tr...
Vulture Island is a colorful and quirky mish-mash of platforming and puzzles. It’s creative and fresh, but sometimes the game can throw a curveball at you, leaving you stuck as to how you should progress. These tips will help you explore smoothly... | Read more »
The new Clash of Kings is just for Weste...
If you’ve played the original Clash of Kings, you’ll probably recognise the city building, alliance forging and strategic battles in Clash of Kings: The West. What sets this version apart is that it’s tailor made for a Western audience and the... | Read more »
Frost - Survival card game (Games)
Frost - Survival card game 1.12.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.12.1 (iTunes) Description: *Warning: the game will work on iPhone 5C and above and iPad Pro / 4. Other devices are not supported* | Read more »
How to build and care for your team in D...
Before you hit the trail and become a dog sledding legend, there’s actually a fair bit of prep work to be done. In Dog Sled Saga, you’re not only racing, you’re also building and caring for a team of furry friends. There’s a lot to consider—... | Read more »
How to win every race in Dog Sled Saga
If I had to guess, I’d say Dog Sled Saga is the most adorable racing game on the App Store right now. It’s a dog sled racing sim full of adorable, loyal puppies. Just look at those fluffy little tails wagging. Behind that cute, pixelated facade is... | Read more »
Let the war games commence in Gunship Ba...
Buzz Lightyear famously said, “This isn’t flying, this is falling – with style!” In the case of Gunship Battle: Second War, though, this really is flying - with style! The flight simulator app from Joycity puts you in control of 20 faithfully... | Read more »
How to get a high score in Fired Up
Fired Up is Noodlecake Games’ high score chasing, firefighting adventure. You take control of a wayward firefighter who propels himself up the side of a highrise with blasts of water. Sound silly? It is. It’s also pretty difficult. You can’t... | Read more »
NBA 2K17 (Games)
NBA 2K17 1.0 Device: iOS iPhone Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Following the record-breaking launch of NBA 2K16, the NBA 2K franchise continues to stake its claim as the most authentic sports video... | Read more »
Dog Sled Saga (Games)
Dog Sled Saga 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: A game by Dan + Lisa As a rookie musher, foster a dogsledding team whose skills will grow if they're treated right. Week by... | Read more »
60 Seconds! Atomic Adventure (Games)
60 Seconds! Atomic Adventure 1.2 Device: iOS Universal Category: Games Price: $2.99, Version: 1.2 (iTunes) Description: 60 Seconds! is a dark comedy atomic adventure of scavenge and survival. Collect supplies and rescue your family... | Read more »

Price Scanner via MacPrices.net

21-inch iMacs on sale for up to $120 off MSRP
B&H Photo has 21″ iMacs on sale for up to $120 off MSRP including free shipping plus NY sales tax only: - 21″ 3.1GHz iMac 4K: $1379 $120 off MSRP - 21″ 2.8GHz iMac: $1199.99 $100 off MSRP - 21″ 1... Read more
13-inch 2.7GHz/256GB Retina MacBook Pro on sa...
Amazon.com has the 13″ 2.7GHz/256GB Retina Apple MacBook Pro on sale for $151 off MSRP including free shipping: - 13″ 2.7GHz/256GB Retina MacBook Pro (sku MF840LL/A): $1348 $151 off MSRP Read more
Apple TVs on sale for up to $50 off MSRP
Best Buy has 32GB and 64GB Apple TVs on sale for $40-$50 off MSRP on their online store. Choose free shipping or free local store pickup (if available). Sale prices for online orders only, in-store... Read more
Apple refurbished 13-inch Retina MacBook Pros...
Apple has Certified Refurbished 13″ Retina MacBook Pros available for up to $270 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 13″ 2.7GHz... Read more
Duplicate Sweeper Free On Mac App Store For O...
To celebrate the launch of Apple’s latest macOS Sierra, Stafford, United Kingdom based Wide Angle Software has announced that its duplicate file finder software, Duplicate Sweeper, is now available... Read more
13-inch Retina MacBook Pros on sale for up to...
B&H Photo has 13″ Retina Apple MacBook Pros on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro: $1174.99 $125 off MSRP - 13... Read more
Evidence Surfaces Pointing To New A10X Chip F...
Citing a job description for a Project Lead position at Apple’s Austin, Texas engineering labs, Motley Fool’s Ashraf Eassa deduces that development is progressing well on Apple’s next-generation in-... Read more
Check Print’R for macOS Allows Anyone to Easi...
Delaware-based Match Software has announced the release and immediate availability of Check Print’R 3.21, an important update to their easy-to-use check printing application for macOS. Check Print’R... Read more
Apple refurbished 11-inch MacBook Airs availa...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models), available for up to $170 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Apple refurbished 15-inch Retina MacBook Pros...
Apple has Certified Refurbished 2015 15″ Retina MacBook Pros available for up to $380 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2... Read more

Jobs Board

Sr. *Apple* Mac Engineer - Net2Source Inc....
…staffing, training and technology. We have following position open with our client. Sr. Apple Mac Engineer6+ Months CTH Start date : 19th Sept Travelling Job If Read more
*Apple* Retail - Multiple Positions-Norfolk,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
Lead *Apple* Solutions Consultant - Apple (...
# Lead Apple Solutions Consultant Job Number: 51829230 Detroit, Michigan, United States Posted: Sep. 19, 2016 Weekly Hours: 40.00 **Job Summary** The Lead ASC is an Read more
US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.