TweetFollow Us on Twitter

iPhone Productivity Applications, Part I

Volume Number: 25 (2009)
Issue Number: 03
Column Tag: iPhone

iPhone Productivity Applications, Part I

Developing applications that manage complex data

by Rich Warren

(Ed note: Parts I and II were actually printed out of order. Click here for Part II.)

Productivity Applications

My Last article, "Built for iPhone 2.0" described the three general categories of iPhone applications. Utilities provide a single view of the data with little or no interaction. Productivity applications manage and organize complex data. While immersive applications use the entire screen to provide a rich, interactive view. We then looked at the major tools used to build an iPhone application, and stepped through the development of a simple utility app.

This time, we will turn our attention to productivity. Productivity applications focus on managing and organizing complex data. Developers typically refrain from customizing the interface. Instead, they focus on creating a clean, streamlined solution that allows the user to move through the data in an unobtrusive and intuitive manner.

Note: productivity applications are not necessarily limited to traditional productivity-oriented tasks, like ToDo lists and calendars. Any application that focuses on data and organization may benefit from the productivity style. Mail is a perfect example of a productivity application. It allows you to manage a number of incoming and outgoing messages from a variety of different email accounts. Many third-party social networking applications also fall into this category. Even the iPod application is largely a data-driven, productivity app.

Productivity applications use three main tools to organize and interact with your data: UITabBarControllers, UINavigationControllers and UITableViews.

UITabBarControllers let the user easily switch between different tasks or different groups of data. For example, the clock application lets the user choose between the different time-related tasks: World Clock, Alarm, Stopwatch and Timer. The iPod app, on the other hand, lets the user select between different views of the data: Album, Artist, Genre, Song, Playlists, Videos, etc. Some applications even mix tasks and data groups. The App Store displays three data groups: features, categories and top 25, while letting the user access the search and updates tasks.

The tab bar can support any number of tab items. However, it will only show five tabs at a time. UITabBarController automatically manages any extra tab items by providing a more... button, and letting the user customize the items that appear on the tab bar.

Next, UINavigationController lets users move from general to more specific views. This controller maintains a stack of views. When a new view is added to the top of the stack, that view slides in from the right, while the old view slides off the left. When the top view is popped from the top of the stack, it slides back off the right, and the previous view returns from the left. This provides a very intuitive interface for moving through hierarchical data.

While both UITabBarController and UINavigationController manage navigation between views, UITableView handles the actual display of data. Tables are often used to present lists, for example, the list of email accounts, the list of mailboxes within a single account, or even the list of messages within a single mailbox. UITableView displays a single column of cells. Each cell within the table can be quite complex, combining images, icons and formatted text. But, in general, the pattern holds. Each entry represents a single selection from a list of similar items.

A productivity application typically combines some or all of these tools into a user interface that naturally matches the application's data. In general, UITabBarController provides the coarsest level of organization, defining our broadest groups. Within each group, UINavigationControllers allows the user to move from the most general to the most specific information, while UITableViews display the information available at each level of detail.

Note: Applications may have multiple UINavigationControllers or UITableViews; however, they should only have a single UITabBarController. Additionally, if you use a tab bar, then UITabBarController should always be the root controller for your application. You can add UINavigationControllers to a UITabBarController, but you should never add a UITabBarController to a UINavigationController.

Gas Tracker

For the rest of this article, we will build a simple productivity application. Given the state of our economy and the recent spike in gas prices, I wanted a tool that would help me track the amount of money I'm wasting on my car.

In many ways, this is an ideal iPhone application. A desktop version just wouldn't work. I'm nowhere near my computer when I fill up my tank. Sure, I've tried carrying a notepad in my car to do these calculations manually, but I always lose the pen under the seat. However, if I'm wearing pants, I have phone with me.

Additionally, the tasks are appropriate for a mobile application. Each interaction is short and simple - perfect for managing data on the go.

Creating the Project

OK, let's get started. Open Xcode. Select File ... New Project.... This brings up the New Project window. Project templates are grouped into categories shown in the leftmost column. Select the iPhone OS Application group.

Apple provides a number of project templates, each pre-loaded with the initial framework for a particular style of application. Since we will use a tab bar to manage the lowest levels of our view hierarchy, we should select Tab Bar Application and press the Choose button. In the next panel, name the application GasTracker, select a path and press Save.


Select the Tab Bar Application Template

Like all Xcode templates, the Tab Bar Application provides a great deal of functionality before we even touch the code. Go ahead and build and run the application. As you see, it creates a tab view that allows us to toggle between two different views.


The code itself starts with the GasTrackerAppDelegate. As with most Cocoa applications, the app delegate manages many of the high-level details. Currently, it simply creates the tab bar controller when the application launches, then releases the controller and window when the application closes.

The delegate also acts as a UITabBarControllerDelegate. There are two commented-out methods for modifying the tab bar's behavior. tabBarController:didSelectViewController: is called whenever the user presses a button on the tab bar. The application also calls tabBarController:didEnd-CustomizingViewControllers: whenever the user edits the buttons on the toolbar. We won't use either of these methods in this application, but they may be useful in your own projects.

The only other class is FirstViewController. Like the app delegate, the initial implementation does not do much, but it has a number of method stubs we could use to customize our view's behavior. We will examine these in more detail later.

There are also two nib files. MainWindow.xib defines the application's Window and Tab Bar Controller. The tab bar's views can either be defined within the Tab Bar Controller, or placed in separate nib files. In this template, the first view is defined within the controller. Open the MainWindow.xib , and then open the Tab Bar Controller. Click on the First button. You will see the layout directly.

The second view is defined in its own nib, named (not surprisingly) SecondView.xib. Click on the tab bar's Second button, and you will see a grayed-out box in the center of the view, with a link to SecondView.xib. Double click the link, and the second view's nib will open automatically.

While the generated code demonstrates a few useful techniques, the initial files are not very useful. I think it's better to just start from a clean slate. First, let's handle the simple part. In Xcode, delete FirstViewController.h, FirstViewController.m, and SecondView.xib. Go ahead and move them to the trash when prompted.

Now for the trickier part. In Interface Builder, open MainWindow.xib, and click on the Tab Bar Controller from the Interface Builder Documents.


Interface Builder Documents

Make sure the Attributes Inspector is open (either through the Tools menu, or by clicking on the Inspector's Attributes tab). You should see a list of view controllers that have been added to the tab bar. Select First, and then click the minus button. It should be deleted from the list. Do the same for Second.


Now let's add our views to the tab bar. We can set both the name and the class (or at least the super class) in this table. Press the plus button. Name this view History, and set the class to Navigation Controller. Now add five more views. Name these MPG, Gas Price, Cost/Mile, Cost/Month and Cost/Day. Leave the classes set to View Controller.

Note: You should be a little careful with the button titles. If they are too long, the labels may appear cramped or even overlap. Also, the size shown in Interface Builder is different from the size in the actual app. Build and run the application to test the label's true size. According to my tests, Cost/Month is OK, but Cost Per Month was too long.


Now look at the Tab Bar Controller window again. Notice, if you click the button once, the window will display the view associated with that button. All the views are currently grayed out. Additionally, the Inspector will show the view controller settings for that view. Double click the button, and the inspector shows the settings for the tab bar item itself.

Building Controllers and Nibs

Before we can do any more work in Interface Builder, we need to create our nibs and controller classes. Let's start by adding the controllers. Back in Xcode, right click on the Classes folder in the Groups & Files view. Select Add ... New File.... In the New File window, make sure Cocoa Touch Classes is selected, and then double click on NSObject subclass. In the next screen, name it HistoryNavigationController.m and click Finish. Do the same for StatsViewController.m, but this time, select the UIViewController subclass.


Adding a UIViewController Subclass

NSObject may seem like a strange choice for our HistoryNavigationController, but we don't need the method stubs provided by the UIViewController template, and we're going to change the super class anyway. So, open HistoryViewController.h and make the changes shown below:

HistoryNavigationController.h

Initial interface for the HistoryNavigationController class.
#import <UIKit/UIKit.h>
@class Model;
@interface HistoryNavigationController : UINavigationController {
   IBOutlet Model *model;
}
@property (nonatomic, retain) Model *model;
@end

We've made two small changes. As we already indicated, we've changed the super class to UINavigationController. Second, we've added a Model property, including a forward declaration of the Model class, and a model instance variable. The model is also declared as an IBOutlet, which allows us to connect it to the controller using Interface Builder. What's Model? Hold that question; we'll get to that in a bit.

While we're here, take a closer look at the property definition. We are using both the nonatomic and the retain keywords. The first keyword is an easy choice. Most iPhone properties should be declared nonatomic. They typically do not need to be thread safe, and using nonatomic accessors improves performance.

The second keyword is more of a design choice. By default, properties are set to simply assign incoming values to the instance variable. That may be appropriate if you can guarantee that the object in question will be retained somewhere else in your application. In our case, the model object will be instantiated by our nib file.

In iPhone OS, the nib file autoreleases all objects it creates. However, if you connect an object to an IBOutlet, it will call setValue:forKey: which will use the appropriate setter method. For our model, it will use the setter defined by the property, so it will be retained. Additionally, if you don't provide an explicit setter, the object would be retained automatically.

Note: The rules defining how nibs retain objects differ from Mac OS X to iPhone OS. In both cases, Apple recommends that you manage these objects through an IBOutlet, and create an appropriate setter that explicitly retains the object. For more information, see the Resource Programming Guide, which can be found either in Xcode's help documentation or online at http://developer.apple.com.

Ok, let's look at the implementation. Open HistoryNavigationController.m. We need to make three small changes here. Import Model.h, synthesize the model property, and release the model as shown below.

HistoryNavigationController.m

Initial implementation for the HistoryNavigationController class.

#import "HistoryNavigationController.h"
#import "Model.h"
@implementation HistoryNavigationController
@synthesize model;
- (void)dealloc {
    [model release];
    [super dealloc];
}
@end

Now add the model property to the StatsViewController. It follows the same pattern: forward declaration, instance variable and property declaration in StatsViewController.h; import, synthesize and release in StatsViewController.m. I will leave the actual typing up to you.

While you're there, you'll probably notice that StatsViewController.m contains a number of method stubs. These handle a range of different events: custom initialization, rotation, and low memory warnings. We'll look at these settings in more detail in part two.

For now, let's build our nib. At this point, we'll just create a single nib for our stats view controller. Right click on the Resources folder and select Add ... New File.... Make sure User Interfaces is selected, and double-click View XIB. Name it StatsView.xib.

Open StatsView.xib. In the Document Window, select File's Owner. Open the Identity Inspector, and change the class to StatsViewController. Next, connect the File's Owner's view outlet to the View object in the Document Window. You can do this by right clicking on the File's Owner icon. A black and gray table will appear. Click in the circle at the end of the view row, then drag to the View icon. You can now save and close the nib file. We will edit the layouts later.


Connecting the File's Owner's view Outlet

Note: Nibs are traditionally placed in the Resources group; however, you do not have to place them there. The groups are only used within Xcode to organize and manage the source files. They have no relationship to the actual layout of the project's files on disk. In fact, you can create your own groups to further improve the project's organization. If you download the source code for this project from ftp.mactech.com, you will see that I've created a number of additional subgroups within the Classes group.

Ok, now let's link our tabs to these nib files. Make sure MainWindow.xib is open. Select the Tab Bar Controller icon in the Document Window. This will open the Tab Bar Controller view, if it isn't open already. The tab bar displays the six views that we just added. Single click on the History tab. The Attributes Inspector should be labeled as Navigation Controller Attributes. If not, try clicking on another tab, then single clicking on the History tab again.


The Nib Name attribute here would be for the Navigation Controller itself. We won't use a nib for this controller, so just leave this entry blank. We will eventually modify the navigation controller's layout directly in the Tab Bar Controller view. Switch to the Identity Inspector (either through the Tools menu, or by clicking on the Inspector's Identity tab), and change the class to HistoryNavigationController.



Now, single click on the MPG tab. This time, we should set the nib name to StatsView and set the class to StatsViewController. Repeat this for all the remaining tabs.



Now let's set the icons for each tab. History is the easiest. Make sure the Attribute Inspector is open. Double click on the history tab to bring up the Tab Bar Item Attributes. Change Identifier to History. This will set both the tab title and the icon to the built-in History tab item.


Note: Each of the built-in icons is automatically linked with a title. You cannot use the icon without using the corresponding title. If you want a custom title, you must use a custom icon.

For the other tabs, we will customize the icons and titles. Tab bar icons are simply 30-pixel by 30-pixel PNG image. Notice, however, that the image color does not matter. The tab bar uses the alpha value of each pixel to create a monochrome icon. It displays the alpha channel as blue in the tab bar, and as black in the More... list.

To create these icons, I find it easiest to use a paint program that supports transparencies. Draw the icon in black against a transparent background. You can even set the transparency of the ink to draw in grayscale. Then save the image as a PNG.

30 x 30 Icon Art, Black with a Semi-Transparent Gray Background


Once you have an icon, add it to your project. In Xcode, select Project ... Add to Project.... Navigate to the PNG file and click Add. In the next window, be sure to check Copy items into destination group's folder (if needed).

Now, back in Interface Builder. Double click on the tab in question. Identifier should say Custom. Select your icon's name from the Image combo box. Repeat for each tab.


OK, I know...I know...I've gone through a lot of Interface Builder commands rather quickly. Hopefully, I've given you enough pointers to get by, but a complete tutorial on Interface Builder is really beyond the scope of this article. If you have any questions or difficulties, check out Apple's Interface Builder User Guide. That can be found either in Xcode's developer documentation, or online at http://developer.apple.com.

M is for Model

Apple really pushes the Model View Control pattern (MVC to the cool kids). I won't go into a full description of MVC here. If you're not familiar with the concept, there are plenty of great explanations on the web. I'll just say that I agree with Apple. MVC is a good idea. [Ed. Note: For a review of MVC, see Dave Dribin's "Road to Code" in the August, 2008 issue]

We've already seen two of the three main players. We've just built a number of views and controllers. We've linked them together. All that with only a few lines of code.

But now things get a little weird. While Cocoa provides clear support for the controllers and views, models aren't so obvious, at least not on the iPhone. Here's the problem. Most of our controllers will need a connection to our model. Some of these controllers may be instantiated pragmatically, letting us pass the model to them directly, but many are automatically created through the nib files.

There are a number of ways to work around this problem. Many people simply instantiate their model object in the app delegate, and then add a property to the delegate. They can then access the property anywhere in their project, using the application singleton.

Using the UIApplication Singleton

Add your model to the application delegate as a property. You can access the application Singleton anywhere in your project. From the application, you can access the delegate, and from the delegate you reach your model.

UIApplication *app = [UIApplication sharedApplication];
id delegate = app.delegate;
id modelObject = [delegate model];

This works, and it is reasonably simple. However, there are a number of reasons I don't like it. First, singleton classes have many of the same problems as global variables. Second, we're programming against a particular application delegate's implementation, not the UIApplicationDelegate protocol. Third, from a pure Object Oriented standpoint, managing the model seems outside the application delegate's role. Finally (and most importantly), it just feels kludgy.

Alternatively, we could create a singleton object to explicitly manage our model. While this feels better in many ways, I still prefer to avoid Singletons where possible. Besides, it may be a bit heavy handed for this application. Though, I will admit, it may be the cleanest solution for some of the more-complicated projects.

If possible I'd like to connect the model the same way we connected the views and controllers, using Interface Builder. Fortunately, in this application we can create a single Model object in our MainWindow.xib, and connect it to all the relevant controllers. This may not always be possible. In more complex projects, you may need to pass an external object into a nib as a proxy object. These objects are then set when you load the nib file using NSBundle's loadNibNamed:owner:options: method. For more information, check out the NSBundle UIKit Additions, Resource Programming Guide and Interface Builder User Guide.

The first step is to build our model. Unfortunately, that is outside the scope of this article. Instead, you can simply grab the necessary code from ftp.mactech.com. You will need both the Model and the Entry classes. Add these to your project.

Note: For this project, our model knows how to automatically load any saved data when it is instantiated. It will also save itself whenever the user makes any changes. This greatly simplifies our controller and delegate code. Check out Model.m for more details.

Now let's add the object to our nib. Open MainWindow.xib. Drag an Object controller from the Library to the Document Window. In the Identity Inspector, set the Object's class to Model. Now we have to make our connections. Single click on each tab to select the tab's controller, then right click on the tab. Connect the tab's model outlet with the model object. That's it. We've linked our model, views and controllers.

Configuring Tab Customizability

As I said earlier, the tab control can only display up to five icons at a time. If you have more than five view controllers, the tab controller only displays four of them and a More... button. Clicking on the More... button brings up a table with all the additional controllers. The user can then select a controller from the table, or they can click on the edit button and customize the contents of the tab bar.

However, not all tab items are customizable. For example, the user cannot replace the More... button. In fact, UITabBarController maintains an array of customizable controls. If a controller is in that array, then it can be moved or removed from the tab bar. By default, all the tab bar's view controllers are automatically placed in the customizeableViewController array.

In our program the History view will act as the main interface for managing our data. It should always be available, therefore we must remove it from the customizeableViewController array. We could subclass UITabBarController, but that's probably overkill for this application. Instead, we can access the tab bar controller from within our application delegate.

Open GasTrackerAppDelegate.m, and modify the applicationDidFinishLaunching: method, as shown below:

applicationDidFinishLaunching:

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    
    // Add the tab bar controller's current view 
    // as a subview of the window
    [window addSubview:tabBarController.view];
   
      
    // Only the StatsViewControllers are customizeable
    NSMutableArray *customizeable = [[NSMutableArray alloc] init];
    for (id controller in tabBarController.customizableViewControllers) {
      
        if ([controller isKindOfClass: [StatsViewController class]]) {
            [customizeable addObject:controller];
        }
    }
   
    tabBarController.customizableViewControllers = customizeable;
    [customizeable release];
}

Basically, this method creates a new array that only contains subclasses of StatsViewController, then it assigns that array to the UITabBarController's customizeableViewControllers property.

OK, a little bit of housecleaning left. Be sure to import StatsViewController.h at the top of GasTrackerAppDelegate.m.

#import "GasTrackerAppDelegate.h"
#import "StatsViewController.h"

Looking Forward:

So far, we've built the application's skeleton and set up the tab view and the model. The application compiles without any warnings. When you run the app, you can switch from tab to tab or customize the tab bar. Of course, the various views don't do much yet.

This project will continue in Part 2. In particular, we will focus on setting up the navigation controller and our table views. We will also add a view for entering data, and create custom classes for each of the stats views. Once that's done, we'll have a fully functional productivity application.


Rich Warren lives in Honolulu, Hawaii with his wife, Mika, daughter, Haruko, and his son, Kai. He is a software engineer, freelance writer and part time graduate student. When not playing on the beach, he is probably writing, coding or doing research on his MacBook Pro. You can reach Rich at rikiwarren@mac.com, check out his blog at http://freelancemadscience.blogspot.com/ or follow him at http://twitter.com/rikiwarren.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

iExplorer 4.1.10 - View and transfer fil...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
iExplorer 4.1.10 - View and transfer fil...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
Adobe InCopy CC 2018 13.0.1.207 - Create...
InCopy CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous InCopy customer). Adobe InCopy CC 2018, ideal for large team projects... Read more
Microsoft Office 2016 15.40 - Popular pr...
Microsoft Office 2016 - Unmistakably Office, designed for Mac. The new versions of Word, Excel, PowerPoint, Outlook and OneNote provide the best of both worlds for Mac users - the familiar Office... Read more
Adobe InDesign CC 2018 13.0.1.207 - Prof...
InDesign CC 2018 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous InDesign customer). Adobe InDesign CC 2018 is part of Creative Cloud.... Read more
Apple iOS 11.1.2 - The latest version of...
iOS 11 sets a new standard for what is already the world’s most advanced mobile operating system. It makes iPhone better than before. It makes iPad more capable than ever. And now it opens up both to... Read more
Slack 2.9.0 - Collaborative communicatio...
Slack is a collaborative communication app that simplifies real-time messaging, archiving, and search for modern working teams. Version 2.9.0: Slack now officially, and fully, supports Japanese.... Read more
iExplorer 4.1.9 - View and transfer file...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
PCalc 4.5.3 - Full-featured scientific c...
PCalc is a full-featured, scriptable scientific calculator with support for hexadecimal, octal, and binary calculations, as well as an RPN mode, programmable functions, and an extensive set of unit... Read more
iExplorer 4.1.9 - View and transfer file...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more

Latest Forum Discussions

See All

Mighty Battles guide - how to build a so...
Mighty Battles, the latest title from Hothead Games, is set to take the App Store by storm. The game puts a welcome twist on lane battlers, adding FPS elements to spice things up a bit. You'll collect cards to put your own military unit to gether,... | Read more »
Rules of Survival guide - how to be the...
The PUBG craze makes its way to mobile, with more and more battle royale games debuting on iOS and Android. Rules of Survival joins the ranks of mobile PUBG-likes, offering a classic battle royale experiences that doesn't vary too much from its... | Read more »
The best new games we played this week -...
The weekend is upon us friends, and it's time to take a look back and reflect on all of the wonderful games we've played over the past few days. This week was jam packed with new releases. There were some big, long awaited launches, some fun... | Read more »
Lineage II: Revolution guide - tips and...
At long last, Lineage II: Revolution has now come to western shores, bring Netmarble's sweeping MMORPG to mobile devices. It's an addictive, epic experience, but some of the systems in the game can be a bit overwhelming. Here are a few tips to help... | Read more »
A Boy and His Blob (Games)
A Boy and His Blob 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: | Read more »
Fight terrible monsters and collect epic...
Released on Western markets early last month, Dragon Project, created by Japanese developer COLOPL, brings epic monster hunting action to mobile for the very first time. Collect a huge array of weapons and armor, and join up with friends to fight... | Read more »
I Am The Hero (Games)
I Am The Hero 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: I Am The Hero is a pixel art, beat 'em up, fighting game that tells the story of a "Hero" with a glorious but mysterious past.... | Read more »
Kauldron (Music)
Kauldron 1.0 Device: iOS Universal Category: Music Price: $3.99, Version: 1.0 (iTunes) Description: Kauldron is our warmest sounding, punchiest synth yet! A completely new modeling technology, combined with carefully designed... | Read more »
Lineage II: Revolution is mobile’s bigge...
NCSoft’s hit fantasy MMORPG series has just made the leap to mobile with the help of Netmarble in Lineage II: Revolution. With over 1.5 million players having already pre-registered ahead of the game’s launch, Revolution hit the app stores... | Read more »
Swing skilfully in new physics-based pla...
Sometimes it’s the most difficult of obstacles that can be the most rewarding. One game hoping to prove this is OCMO, the new tough but fair platformer from developers Team Ocmo. Primed to set every speedrunner’s pulse racing, as an otherworldly... | Read more »

Price Scanner via MacPrices.net

Best Apple iPad deals this weekend, up to $80...
Apple resellers are offering 9.7″ iPads and 10.5″ iPad Pros for up to $80 off MSRP this weekend as part of their early Holiday and Black Friday sales: Adorama is offering new 2017 9.7″ 32GB WiFi... Read more
Early Black Friday sale: Apple iMacs for up t...
B&H Photo has 27-inch iMacs in stock and on sale for up $130-$150 off MSRP including free shipping. B&H charges sales tax in NY & NJ only: – 27″ 3.8GHz iMac (MNED2LL/A): $2149 $150 off... Read more
Apple restocks refurbished Mac minis starting...
Apple has restocked Certified Refurbished Mac minis starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: – 1.4GHz Mac mini: $419 $80 off MSRP – 2.6GHz Mac... Read more
Save on 12″ MacBooks, Apple refurbished model...
Apple has Certified Refurbished 2017 12″ Retina MacBooks available for $200-$240 off the cost of new models. Apple will include a standard one-year warranty with each MacBook, and shipping is free.... Read more
Early Holiday sale: 12″ iPad Pros for up to $...
B&H Photo has 12″ iPad Pros on sale today for up to $130 off MSRP. Shipping is free, and B&H collects no sales tax outside NY & NJ: – 12″ 64GB WiFi iPad Pro: $749, save $50 – 12″ 256GB... Read more
Holiday sale prices on Apple 13″ MacBook Pros...
B&H Photo has 2017 13″ MacBook Pros in stock today and on sale for $100-$150 off MSRP, each including free shipping plus NY & NJ sales tax only: – 13-inch 2.3GHz/128GB Space Gray MacBook Pro... Read more
Sale: 13″ MacBook Airs starting at $899, $100...
B&H Photo has 2017 13″ MacBook Airs on sale today for $100 off MSRP including free shipping. B&H charges NY & NJ sales tax only: – 13″ 1.8GHz/128GB MacBook Air (MQD32LL/A): $899, $100 off... Read more
Week’s Best Deal on 13″ MacBook Pros: Apple r...
Apple has a full line of Apple Certified Refurbished 2017 13″ MacBook Pros available for $200-$300 off MSRP. A standard Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
Deal: 15″ 2.6GHz MacBook Pro for $1799 w/free...
B&H Photo has clearance 2016 15″ 2.6GHz Touch Bar MacBook Pros in stock today and available for $600 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 15″ 2.... Read more
Black Friday pricing on the 1.4GHz Mac mini....
MacMall has the 1.4GHz Mac mini on sale for $399 including free shipping. Their price is $100 off MSRP (20% off), and it’s the lowest price for available for this model from any reseller. MacMall’s... Read more

Jobs Board

Product Manager - *Apple* Pay on the *Appl...
Job Summary Apple is looking for a talented product manager to drive the expansion of Apple Pay on the Apple Online Store. This position includes a unique Read more
*Apple* Pro/Consumer Apps Support Engineer -...
…exemplify AppleCare's expert technical support paired with exceptional customer service for Apple 's software apps. This person is a problem solver, who understands Read more
Partner Marketing Manager, *Apple* Pay - Ap...
Job Summary The Apple Pay partner marketing team is looking for a Marketing Manager to develop and drive US programs. The right candidate will be passionate about Read more
*Apple* Solution Consultant - Apple (United...
# Apple Solution Consultant - Rochester, MN Job Number: 113037950 Rochester, MN, Minnesota, United States Posted: 19-Sep-2017 Weekly Hours: 40.00 **Job Summary** Are Read more
Sr. Experience Producer, Today at *Apple* -...
# Sr. Experience Producer, Today at Apple Job Number: 56495251 Santa Clara Valley, California, United States Posted: 23-Jun-2017 Weekly Hours: 40.00 **Job Summary** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.