TweetFollow Us on Twitter

Your Attention, Please: Alerts and Sheets in Cocoa

Volume Number: 20 (2004)
Issue Number: 3
Column Tag: Programming

Mac OS X Programming Secrets

by Scott Knaster

Your Attention, Please: Alerts and Sheets in Cocoa

I have seen the light, and it turns out to be cocoa-colored. As you already know if you have read Dave Mark's Getting Started column, he and I spent a transformational week taking the Cocoa Bootcamp class at the Big Nerd Ranch. During this week, the other students and I were thrown into an ocean of Cocoa, but we didn't drown. Class instructor Aaron Hillegass' strategy is to take attendees through an enormous volume of material, and it works: you don't remember everything about everything, but a surprising amount sticks to your brain. The result is that the class is like a Cocoa Magic Decoder Ring. Now, I can look at Cocoa books, documentation, and code, and figure out what's going on - before, I couldn't.

As Dave mentions in his column, the class is held in rural Georgia, which is probably not the first place you think of when looking for a hotbed of Cocoa development. But upon arriving at the lodge, I figured out why this was such a good place for Cocoa training: one of the region's legendary characters is Chief William McIntosh. His face is all over the lodge, and figure 1 shows you what he looked like. McIntosh, Cocoa - get it?


Figure 1. Chief William McIntosh is an appropriate historical figure in the area where the Big Nerd Ranch holds its classes.

In this month's column, I'm going to start by talking a little about some observations and "aha" moments that happen as you're trying to figure out what Cocoa is all about. Then, I'll go through a gentle example of how you get something done in Cocoa: putting up an alert.

Drink the Cocoa

If you're an old-school programmer with experience writing Macintosh or Windows apps, several obstacles pop up immediately when you try to learn Cocoa. The roadblocks include:

  • Learning to read and think in Objective-C.
  • Learning to use and trust frameworks in general.
  • Figuring out how to use the Cocoa frameworks specifically.

Learning Objective-C is the first challenge to working in Cocoa. Although you can use Java with Cocoa, everything is designed first with Objective-C in mind. Objective-C and Cocoa together have some rules and conventions that will make you nuts at first, such as sending messages (which turn into method calls) using square brackets, formal parameter names (labels) in method calls, using the "NS" prefix for lots of symbols, including class names, constants, and plain functions, and no explicit "override" keyword . You get used to most of these, but they're a pain until you get used to them. There are also some important new concepts to learn when programming in Cocoa with Objective-C, such as delegates (customizing the way objects behave through a modern version of what used to be called hooks in the old Mac days), categories (a way to add methods and instance variables to a class), and nib files (the output of Interface Builder, consisting of encoded object instances that can be decoded and used by your running application).

If you've programmed with other object-oriented frameworks, you'll have an advantage in figuring out Cocoa. One of the trickiest parts is knowing which piece to use to solve a particular problem. The best way to gain this knowledge is through experience, which means it's hard when you're getting started.

How can you get around these obstacles? First, bootstrap yourself into Cocoa with a good class or book (or both). If you like learning by doing, Cocoa Bootcamp at the Big Nerd Ranch is a fantastic experience. Or, you can read and work through the book Cocoa Programming for Mac OS X, which is basically the portable version of the class. For those who like to learn by reading before jumping in and trying things, check out Cocoa in a Nutshell. The two books together form a fine combination.

As soon as you're to the point where you can look at Cocoa code in Objective-C and have a clue about what's going on, make sure you retain and build your skills by writing Cocoa code as often as possible. Fresh knowledge tends to evaporate if it's not used.

Rich Cocoa

Cocoa provides an amazing box of goodies for programmers to play with. Cocoa is so vast that if you printed out all the Cocoa APIs and laid them out end to end, you would have to walk a very long way indeed to read it all. This month and in future editions, we'll explore cool things you can do in Mac OS X using Cocoa, and I'll try to make sure we have fun while we're digging around. We'll start out this month with a gentle example - putting up an alert - that will also serve to introduce a little of the Cocoa way of doing things.

Before we write code, lets review a bit of user interface business. According to Apple's Aqua Human Interface Guidelines, Aqua supports three kinds of dialogs: modeless, document modal (also called sheets), and application modal. In this month's column, we're going to work with modal dialogs, which are also called alerts. We'll discuss both document modal and application modal alerts. We'll talk about how the recommended way to create alerts has changed recently in Cocoa, and what to do about it.

Our example app is going to create an alert that lets the user confirm whether to revert to factory settings. What's being reverted, and the actual reverting itself, are left as exercises for you and me to do later. Our application will create windows that look like Figure 2. The window includes four buttons: two that put up application modal dialogs, and two for document modal dialogs (sheets). There are two for each because we're demonstrating two different techniques for putting up alerts: one that was introduced in Mac OS X 10.3, and one that works in previous versions as well as 10.3.


Figure 2. This is what our document window will look like.

It's Time to Start

Let's start by creating the project in Xcode. Choose New Project, then Cocoa Document-based Application. Mine is named RedAlert. As usual with Cocoa projects, we'll start with the fun stuff by laying out the user interface and object connections in Interface Builder (IB). We double-click MyDocument.nib and IB opens. First, we'll build the document window and the buttons that will appear inside them. Click over to the Cocoa Windows palette (that's the fourth palette over, signified by a tiny picture of a window in the toolbar), and drag out a window. Then, click the second tab in the toolbar to go to the Controls palette, and drag out four buttons to make your window look like the one in Figure 2.

Now we need a way to make the buttons work. Our big plan is to have the buttons send messages to a controller object when the user pushes them. We're going to make a new class called AppController, which will be a subclass of NSObject. Click Classes in the MainMenu.nib window, then click NSObject. Choose Classes a Subclass NSObject to make the new class. (You can also right-click NSObject, or simply press return, to make a subclass.) Type a name for the new class: AppController.

NeXT, we have to get AppController objects ready to receive messages from the buttons. With AppController selected, choose Tools a Show Info, and make sure Attributes is selected on the popup menu at the top. We have four different buttons that are going to send messages to objects of this class, so we'll define four actions that controller objects can perform. Click Add and type in the four actions, one by one: appModalNew, appModalOld, docModalNew, docModalOld. The info inspector should look like the one in Figure 3.


Figure 3. AppController inspector shows the four actions we created.

We've now defined a template for objects of the class AppController. To actually work with such an object here in IB, we need to create one. Choose Classes a Instantiate AppController to make an AppController object that will be stored in the nib file.

Now it's time to wire this sucker up. IB's famous shortcut for connecting one object to another is to hold down the Control key while dragging between them. We're going to Control-drag from the object that will send the message (in this case, each of the buttons) to the object that will perform the action (the AppController). We hold down Control and start dragging from the first button to the MyDocument.nib window, which switches to the Instances tab as soon as we drag in, and we let go of the mouse button when we've drawn the line to the AppController object. After we connect the two objects, the inspector shows the possible actions in AppController that we can connect the button to (see Figure 4). Pick the appropriate action (such as appModalOld for the first button) and double-click to make the connection. We repeat this process for each of the four buttons.


Figure 4. Connect the buttons to the AppController by Control-dragging in Interface Builder.

We can use the inspector to tweak the settings of our objects. For example, we can prevent the window from having a resize box in the lower right corner by inspecting it, going to the Attributes page, and turning off the "Resize" setting.

Now we've built a window with four buttons, created the AppController class, made an instance of that class, and connected the buttons to actions in AppController. The nib file contains all the instances we built: the window and its buttons, along with the AppController. Before we leave IB, it will do one final favor for us. We go back to the MyDocument.nib window and click the Classes tab. Making sure AppController is selected, we choose Classes a Create Files for AppController. This generates skeleton header and implementation (.h and .m) files for our new class and adds them to the project. If only it would write all the code for us, we would be done. But for now, we still have to do that part ourselves. We'll bid adieu to our friend Interface Builder, save the nib file, and go over to Xcode.

Xcode Marks the Spot

In Xcode, we're going to flesh out the four alert-posting methods of AppController that we hooked up to the buttons in IB. The buttons on the left side of the window put up application modal alerts, while the two on the right produce document-modal sheet alerts. Let's talk about the app modal alerts first.

In versions of Mac OS X before 10.3, you put up an app modal alert by calling the function NSRunAlertPanel. Note that this is a plain old C function call - no messages sent to objects. NSRunAlertPanel takes parameters that let you specify its text and buttons, and returns an integer that indicates which button the user clicked. Here's the full syntax:

 int NSRunAlertPanel(NSString *title,    // main alert text
   NSString *msg,    // second line of alert text
   NSString *defaultButton,    // right button (usually 'OK')
   NSString *alternateButton, // left button ('Don't ...')
   NSString *otherButton, // middle button (Cancel)
   ...) // printf-style formatting for strings

You don't have to pass values for all the parameters - some of them get default values, and others are optional. If you pass nil for defaultButton, the alert will use the localized version of "OK" for the button label. The other buttons are optional - if you pass nil, they don't appear. Passing nil for title gets the localized word for "alert".

Here's the code for putting up the alert using NSRunAlertPanel, which works with all versions of Mac OS X:

- (IBAction)appModalOld:(id)sender
{
    NSLog (@"appModalOld");
    int choice = NSRunAlertPanel 
(@"Do you want to revert to factory settings?", 
 @"If you revert to factory settings, you will lose any cool settings of 
    your own that you have made. (10.2 version)", 
 @"Revert", @"Don't Revert",@"Cancel");
    NSLog (@"NSRunAlertPanel returned %d", choice);    
}

When you call NSRunAlertPanel, you get some user keyboard shortcuts (also known as key bindings) automatically. If the user presses Return or Enter, that's the same as clicking the first button. Pressings Esc is a shortcut for clicking Cancel. If you have a button labeled "Don't Save", the user can type Command-D as a shortcut.

With the advent of Mac OS X 10.3, Cocoa added a groovy new objectve way to put up alerts: the class NSAlert. To use NSAlert, you create a new alert object, set it up by sending messages to it, then put it on the screen by calling its runModal method. Like NSRunAlertPanel (the pre-10.3 function we used above), runModal returns an integer that tells which button the user clicked. Here are some of the NSAlert methods you're likely to call when setting up an alert:

// Set the main text for the alert
 (void) setMessageText:(NSString *)messageText

// Set the secondary text for the alert
 (void) setInformativeText:(NSString *)infoText

// Choose Warning, Info, or Critical alert
 (void) setAlertStyle:(NSAlertStyle) style

// Add a button to the alert
 (NSButton *) addButtonWithTitle:(NSString *)aTitle

Calling addButtonWithTitle returns the button, although you'll often ignore the return value when you're creating the alert. You can call methods on the buttons to tweak their appearance and behavior. For example, you can call setKeyEquivalent to make a keyboard shortcut, or setImage to put a custom image on the button.

When you have the alert built to your specifications, you send it the runModal message and the alert appears. When the user dismisses the alert, runModal returns a value that tells you which button the user clicked. Here's how to put up the same alert as we did before, but using the 10.3 technique with NSAlert:

- (IBAction)appModalNew:(id)sender
{
    NSLog (@"appModalNew");
         // This requires 10.3. We could check
         // NSAppKitVersionNumber at this point to be sure
         // it's supported.

         // Create the alert object
    NSAlert *theAlert = [[NSAlert alloc] init];

   // Make the buttons.
   // For easiest localization, we could 
   // put these strings in a property list and
   // read them in.
   // Buttons are created from right to left.
   //
[theAlert setMessageText:@"Do you want to revert to factory settings?"];
[theAlert setInformativeText:@"If you revert to factory settings, you will lose 
   any cool settings of your own that you have made. (10.3 version)"];
      [theAlert setAlertStyle:NSWarningAlertStyle];
    [theAlert addButtonWithTitle:@"Revert"];
      [theAlert addButtonWithTitle:@"Don't Revert"];
    [theAlert addButtonWithTitle:@"Cancel"];
   // addButtonWithTitle actually returns the button object,
   // so you can mess with buttons using code like this:
   // NSButton *aButton = 
   //      [theAlert addButtonWithTitle:@"Revert"];
   //      [aButton setKeyEquivalent:@"r"];
  //      [aButton setBezelStyle:NSThickerSquareBezelStyle];
   //      [aButton setSound:mySound];
   //   etc.

   // Put up the alert and report the choice
int choice = [theAlert runModal];
    switch (choice)
    {
        case NSAlertFirstButtonReturn:  NSLog (@"NSAlertFirstButtonReturn"); break;
        case NSAlertSecondButtonReturn:  NSLog (@"NSAlertSecondButtonReturn"); break;
        case NSAlertThirdButtonReturn:  NSLog (@"NSAlertThirdButtonReturn"); break;
    }
    NSLog (@"NSAlert runModal: returned %d", choice);
   
    
    // This works too: "compatibility" method.
    // Note returned button values are different (-1, 0, 1 instead of 1000, 1001, 1002).
    
    /*
    theAlert = [NSAlert alertWithMessageText:@"Do you want to revert to factory setting?" 
                        defaultButton:@"Revert" 
                       alternateButton:@"Don't Revert" 
                          otherButton:@"Cancel"
               informativeTextWithFormat:@"If you revert to factory settings, you 
                  will lose any cool settings of your own that you have made. (10.3 version)"];
    choice = [theAlert runModal];
    NSLog (@"NSAlert runModal: returned %d", choice);
    */
    
}

Mac OS X 10.3 also provides a "compatibility" method that looks a lot like the NSRunAlertPanel function. There's an example of it commented out at the bottom of the last listing. This is implemented as a class method of NSAlert. Here's its declaration:

+ (NSAlert *)alertWithMessageText:(NSString *)messageTitle
       defaultButton:(NSString*)defaultButtonTitle
       alternateButton:(NSString *)alternateButtonTitle
       otherButton:(NSString*)otherButtonTitle
       informativeTextWithFormat:(NSString *)format, ...

There's other customizing you can do with NSAlert, including adding help (setShowsHelp) by using a delegate object. Check the documentation for NSAlert (and NSButton) to learn about more cool tweaks.

Changing the Sheets

Now we're going to write methods that put up document modal alerts, also called sheets. These are the cool eye-candy dialogs that slip out from window title bars, then roll back in when you're done. As with our previous examples, there are old-school and new-wave ways to do this. We'll show both.

For best compatibility with previous and current versions of OS X, use the function NSBeginAlertSheet, defined as follows:

void NSBeginAlertSheet(NSString *title, 
   NSString *defaultButton, // define three buttons
   NSString *alternateButton,   
   NSString *otherButton, 
   NSWindow *docWindow, // window the sheet goes with
   id modalDelegate, // sheet's delegate object
   SEL didEndSelector, // called after user finishes
   SEL didDismissSelector, // called when sheet is gone
   void *contextInfo, // passed to delegate as data
   NSString *msg, ...) 

The first few parameters are similar to those we used for app modal alerts earlier, but there's interesting new stuff too. We get to supply pointers to a couple of methods, one that gets called when the user clicks a button to end the alert, and the other that's summoned after the alert is actually closed. We can use these to validate data, record what happened, or for any other purpose. The following listing shows a barebones example of how to put up the document modal alert in our little sample:

- (IBAction)docModalOld:(id)sender
{
    NSLog (@"docModalOld");

   NSBeginAlertSheet (@"Do you want to revert to factory settings? (10.2 version)", // title
                     @"Revert", @"Don't Revert",@"Cancel", 
               [sender window], // sender is a button. We can
                           // get its window this way
                  nil, // delegate (owner of next two methods)
                  nil, // if this is a method selector,
                           // it's called when sheet is done
                  nil, // if this is a method selector,
                           // it's called when sheet is closed
                  nil, // this pointer is passed to the
                           // delegate when previous two 
                           // methods are called
                  @""); // message to display in sheet
                           // (Note: if this is nil, we get 
                           // a runtime error.)   
}

In this example, we're passing nil and so skipping the chance to use the didEndSelector and didDismissSelector hooks. If you want to use either of these methods, define them like this:

sheetDidEnd:(NSWindow *)sheet 
      returnCode:(int)returnCode 
      contextInfo:(void *)contextInfo;

sheetDidDismiss:(NSWindow *)sheet
      returnCode:(int)returnCode 
      contextInfo:(void *)contextInfo;

When you call NSBeginAlertSheet, pass a pointer to your methods as the appropriate parameter, like this:

@selector(sheetDidEnd:returnCode:contextInfo:)   and
@selector(sheetDidDismiss:returnCode:contextInfo:)

Cocoa will call your sheetDidEnd method when the user finishes with the sheet, and the sheetDidDismiss method after the sheet is actually gone. You can arrange to pass anything you want in the contextInfo parameter.

Just as with app modal alerts, the freshest Mac OS X 10.3 Cocoa provides the NSAlert class for you to create document modal alerts. You use it by sending messages to build an NSAlert object, then sending another message to actually show the alert. Here's the relevant code from our sample app:

- (IBAction)docModalNew:(id)sender
{
    NSLog (@"docModalNew");
   
   NSAlert *theAlert = [[[NSAlert alloc] init] autorelease];
      // set up buttons
   [theAlert addButtonWithTitle:@"Revert"];
   [theAlert addButtonWithTitle:@"Cancel"];
   [theAlert addButtonWithTitle:@"Don't Revert"];

      // set up text
   [theAlert setMessageText:@"Do you want to revert to factory settings?"];
   [theAlert setInformativeText:@"If you revert to factory settings, you will 
   lose any cool settings of your own that you have made. (10.3 version)"];

      // choose alert kind
   [theAlert setAlertStyle:NSWarningAlertStyle];
   
      // roll out the sheet!
      // We're not using a delegate or its hook. If we were,
      // we would define a method called 
      // alertDidEnd:returnCode:contextInfo  and
      // pass @selector(alertDidEnd:returnCode:contextInfo:)
      //    for didEndSelector and self for modalDelegate
   [theAlert beginSheetModalForWindow:[sender window] 
            modalDelegate:nil
            didEndSelector:nil 
            contextInfo: nil];
                     
}

This completes the basic framework of our sample app. Note that because we've created a document-based application, we can open as many windows as we want, and each one comes with sheet-displaying abilities. For fun, open a whole mess of them and drag them around. Whee!

As with all software, there are a jillion ways we could enhance our little app (such as by making it do some actual work instead of just demonstrating how to put up alerts). For example, we could take the English text out of the code and put it in a property list, which would be better for localization. We might try a customized sheet, which we can make with the beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo: method of NSApplication. Delve into the definitions and documentation for NSAlert, NSApplication, and NSButton for more exploration. Extending this example is a great way to get started playing around with Cocoa. You can download the Xcode project from http://www.papercar.com/mt/RedAlert.zip.

Until next month, have fun, stay alert, and please yell (scottk@mactech.com) if you are paying attention.


Scott Knaster attended Cocoa Bootcamp at Big Nerd Ranch (http://www.bignerdranch.com/classes/cocoa1.shtml). He did not go Cocoa Loco but he liked it very much. Scott counts the days until pitchers and catchers report.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Airmail 3.3.2 - Powerful, minimal email...
Airmail is an mail client with fast performance and intuitive interaction. Support for iCloud, MS Exchange, Gmail, Google Apps, IMAP, POP3, Yahoo!, AOL, Outlook.com, Live.com. Airmail was designed... Read more
Numi 3.15.1 - Menu-bar calculator suppor...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
Airmail 3.3.2 - Powerful, minimal email...
Airmail is an mail client with fast performance and intuitive interaction. Support for iCloud, MS Exchange, Gmail, Google Apps, IMAP, POP3, Yahoo!, AOL, Outlook.com, Live.com. Airmail was designed... Read more
Numi 3.15.1 - Menu-bar calculator suppor...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
TextSoap 8.4.1 - Automate tedious text d...
TextSoap can automatically remove unwanted characters, fix up messed up carriage returns, and do pretty much anything else that we can think of to text. Save time and effort. Be more productive. Stop... Read more
TextSoap 8.4.1 - Automate tedious text d...
TextSoap can automatically remove unwanted characters, fix up messed up carriage returns, and do pretty much anything else that we can think of to text. Save time and effort. Be more productive. Stop... Read more
Backblaze 4.3.0.44 - Online backup servi...
Backblaze is an online backup service designed from the ground-up for the Mac. With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
Numi 3.15 - Menu-bar calculator supports...
Numi is a calculator that magically combines calculations with text, and allows you to freely share your computations. Numi combines text editor and calculator Support plain English. For example, '5... Read more
EtreCheck 3.3.3 - 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
BusyContacts 1.1.8 - Fast, efficient con...
BusyContacts is a contact manager for OS X that makes creating, finding, and managing contacts faster and more efficient. It brings to contact management the same power, flexibility, and sharing... Read more

Latest Forum Discussions

See All

The best new games we played this week
We were quite busy this week. A bunch of big mobile games launched over the past few days, alongside a few teeny surprises. There're lots of quality games to load your phone with. We've gone and picked out five of our favorites for the week. [... | Read more »
Magikarp Jump beginner's guide
Magikarp Jump is a mystifying little game. Part Tamagotchi, part idle clicker, there's not a whole lot of video game there, per se, but for some reason we can't help coming back to it again and again. Your goal is to train up a little Magikarp to... | Read more »
Goat Simulator PAYDAY (Games)
Goat Simulator PAYDAY 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: ** IMPORTANT - SUPPORTED DEVICES **iPhone 4S, iPad 2, iPod Touch 5 or better Goat Simulator: Payday is the most... | Read more »
GRID Autosport delayed until autumn
Sorry mobile racing fans -- GRID Autosport has been delayed a few months. The game is now expected to launch this fall on iOS. Feral Interactive announced that they wanted more time to work on the game's UI and overall performance before launching... | Read more »
Zombie Gunship Survival Beginner's...
The much anticipated Zombie Gunship Survival is here. In this latest entry in the Zombie Gunship franchise, you're tasked with supporting ground troops and protecting your base from the zombie horde. There's a lot of rich base building fun, and... | Read more »
Mordheim: Warband Skirmish (Games)
Mordheim: Warband Skirmish 1.2.2 Device: iOS Universal Category: Games Price: $3.99, Version: 1.2.2 (iTunes) Description: Explore the ruins of the City of Mordheim, clash with other scavenging warbands and collect Wyrdstone -... | Read more »
Mordheim: Warband Skirmish brings tablet...
Legendary Games has just launched Mordheim: Warband Skirmish, a new turn-based action game for iOS and Android. | Read more »
Magikarp Jump splashes onto Android worl...
If you're tired ofPokémon GObut still want something to satisfy your mobilePokémon fix,Magikarp Jumpmay just do the trick. It's out now on Android devices the world over. While it looks like a simple arcade jumper, there's quite a bit more to it... | Read more »
Purrfectly charming open-world RPG Cat Q...
Cat Quest, an expansive open-world RPG from former Koei-Tecmo developers, got a new gameplay trailer today. The video showcases the combat and exploration features of this feline-themed RPG. Cat puns abound as you travel across a large map in a... | Read more »
Jaipur: A Card Game of Duels (Games)
Jaipur: A Card Game of Duels 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: ** WARNING: iPad 2, iPad Mini 1 & iPhone 4S are NOT compatible. ** *** Special Launch Price for a limited... | Read more »

Price Scanner via MacPrices.net

Memorial Day savings: 13-inch Touch Bar MacBo...
B&H Photo has the 2016 Apple 13″ Touch Bar MacBook Pros in stock today and on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 13″ 2.9GHz/512GB... Read more
Apple refurbished 13-inch MacBook Airs availa...
Apple has Certified Refurbished 2016 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.6GHz/8GB/128GB MacBook Air: $... Read more
Apple restocks refurbished 11-inch MacBook Ai...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models recently discontinued by Apple), available for up to $170 off original MSRP. An Apple one-year warranty is included with each... Read more
12-inch 1.2GHz Retina MacBooks on sale for up...
B&H has 12″ 1.2GHz Retina MacBooks on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: - 12″ 1.2GHz Space Gray Retina MacBook: $1449.99 $150 off... Read more
15-inch 2.7GHz Silver Touch Bar MacBook Pro o...
MacMall has the 15-inch 2.7GHz Silver Touch Bar MacBook Pro (MLW82LL/A) on sale for $2569 as part of their Memorial Day sale. Shipping is free. Their price is $230 off MSRP. Read more
Free Tread Wisely Mobile App Endorsed By Fath...
Just in time for the summer driving season, Cooper Tire & Rubber Company has announced the launch of a new Tread Wisely mobile app. Designed to promote tire and vehicle safety among teens and... Read more
Commercial Notebooks And Detachable Tablets W...
Worldwide shipments of personal computing devices (PCDs), comprised of traditional PCs (a combination of desktop, notebook, and workstations) and tablets (slates and detachables), are forecast to... Read more
Best value this Memorial Day weekend: Touch B...
Apple has Certified Refurbished 2016 15″ and 13″ MacBook Pros available for $230 to $420 off original MSRP. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2.6GHz... Read more
13-inch MacBook Airs on sale for up to $130 o...
Overstock.com has 13″ MacBook Airs on sale for up to $130 off MSRP including free shipping: - 13″ 1.6GHz/128GB MacBook Air (sku MMGF2LL/A): $869.99 $130 off MSRP - 13″ 1.6GHz/256GB MacBook Air (sku... Read more
2.8GHz Mac mini available for $973 with free...
Adorama has the 2.8GHz Mac mini available for $973, $16 off MSRP, including a free copy of Apple’s 3-Year AppleCare Protection Plan. Shipping is free, and Adorama charges sales tax in NY & NJ... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
Best Buy *Apple* Computing Master - Best Bu...
**509643BR** **Job Title:** Best Buy Apple Computing Master **Location Number:** 001482- Apple Valley-Store **Job Description:** **What does a Best Buy Apple Read more
*Apple* Media Products - Commerce Engineerin...
Apple Media Products - Commerce Engineering Manager Job Number: 57037480 Santa Clara Valley, California, United States Posted: Apr. 18, 2017 Weekly Hours: 40.00 Job Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.