TweetFollow Us on Twitter

January 94 - OSL Components

OSL Components

Bo Klintberg

In this crazy, brave, new world of collaboration, Mr. Peabody might suddenly decide to use your application as a server when he's running his amazing new script. Let's have a look at what happens in an application written with MacApp 3 and the OSL Scripting Components source code framework.

PREPARING A SERVER APPLICATION

Let's begin with how easy it is to create a server application with the OSL Scripting Components package. The only thing that you need to do to create an skeleton server application that supports the Required, Core, Miscellaneous and OSLSC suites is to inherit your application class from TOSLServerApplication and to call IOSLServerApplication from your own TYourApplication::IYourApplication method.
pascal void TScriptServerApplication::IScriptServerApplication()
{
    this->IOSLServerApplication( 
        kScriptServerMainFileType, kScriptServerAppSignature);
}

THE SERVER SIDE

To describe and simplify the concept of an application that communicates in a collaborative environment, I decided to introduce the concept of communication side. As I see it, any application has two communication sides, which may or may not be actively used by the application. One side, the client side, is responsible for the sending of events to other applications and handling the reply coming back. The other side, the server side, is responsible for handling incoming events from other applications and returning a reply (see Figure 1). This figure shows that any application in fact has two communication sides: the Server side and the Client side, thus making it possible to handle incoming messages from client applications, as well as being able to send messages to server applications.

This means that if you want to build your own server (or recorder) application, then you should add a server side to your application; for the developer's convenience, the "normal" cases are already implemented in the OSL Scripting Components package. The following method in TOSLClientServerApplication, the abstract superclass from which TOSLServerApplication and TOSLRecorderApplication inherits, illustrates the main things that an installation of a server side should do.

pascal void TOSLClientServerApplication::DoAddServerSide()
{
    this->DoMakeServerSideNodes();   
    this->DoMakeOSLResolverManager();
    this->DoAddServerSideBehaviors();
}

The hooked-up clients manager

First, the DoMakeServerSideNodes method should create a manager for the client nodes that are going to hook up to this server. For example, the result of the DoMakeServerSideNodes method in the TOSLServerApplication class is that it will create a TServerSideManager, which, indeed, manages all hooked-up clients. Each new client that sends an Apple event to such a server application will be added to the TServerSideManager's list of clients.

The Apple events resolver

Second, the DoMakeOSLResolverManager method should create a resolving manager. The resolving manager controls the resolving process when the data received in the Apple event need to be interpreted.

The Apple events handler

Third, the DoAddServerSideBehaviors method should create any number of behaviors to actually dispatch the Apple events. I chose to create one behavior per event suite, thus ending up with adding four behaviors to the TOSLServerApplication: the TReqSuiteHandler (required suite), TCoreSuiteHandler (core suite), TMiscSuiteHandlerForServerSide (Miscellaneous suite) and the TSCSuiteHandlerForServerSide (OSLSC suite).
pascal void TOSLServerApplication::DoAddServerSideBehaviors()
{
    inherited::DoAddServerSideBehaviors();

    this->DoAddReqSuiteHandler();
    this->DoAddCoreSuiteHandler();
    this->DoAddMiscSuiteHandler();
    this->DoAddSCSuiteHandlerForServerSide();
}

By overriding the DoAddServerSideBehaviors method in your own TYourServerApplication class, you could add any number of suite-dispatching behaviors. You might, for example, want to dispatch any of the other standard suites like the Text suite and Database suite or dispatch the events of your own suite.

TRAPPING APPLE events

Now, let's look at what the application needs to know in order to work as an Apple events server.
  • The 'SIZE' resource must be set up properly
  • Each Apple event suite and its events must be defined
  • There must be dispatching in the code for each Apple event

The 'SIZE' resource

First, you must make sure that your application actually will be delivered an Apple event by the operating system. You do this by setting the highLevelEventAware flag in the 'SIZE' resource of your application. Now, all incoming Apple events can be dispatched in your application.

Suite RESOURCES

To prepare a suite's events for dispatching, you need to add two or three resource files, depending on which suite you want to be able to dispatch: a Suite Definition resource, a Command Constants resource, and an AEDispatch resource.

Note, however, that the OSL Scripting Components package already has defined everything you need if you are going to work with the Required, Core, Miscellaneous and OSLSC suites, both the resource files and the actual dispatching code inside the application.

The Suite Definition

A suite definition resource file contains all the constants used by this suite, including the suite ID, event IDs, additional properties and data types and more. If it's a standard suite, like the Required or Core suites, constants for these are already defined in the AppleEvent resource files supplied by Apple. In this case, for example, you won't have to create a RequiredSuite.r or CoreSuite.r resource file.

However, if you are implementing your own suite and your suite's name is, for example, MySuite, then you have to create a MySuite.r resource file, containing constants for the suite ID and the suite's event IDs (see figure 2). This figure shows the three different resource files you'll need to specify and dispatch your own Apple event suite. Note that the constants in the MySuite.r file also will be used if you are creating a client-type application that issues command from your suite.. In the case of the OSL Scripting Components suite, that resource file is named SCSuite.r, and it contains all the constants used by this suite. Remember, this file is not necessarily used only to compile your server application, but could be used to compile your client application as well, if you want that client to issue Apple events from the MySuite suite.

An example of the contents of a Suite Definition resource file: the OSL Scripting Components suite's SCSuite.r:

//***********************************************************
// OSL Scripting Components suite
//***********************************************************
#define kOSLScriptingComponentsSuiteEventClass      'SCec'          
#define kSCInformServerThatClientQuits              'SCis'  
#define kSCInformClientThatServerQuits              'SCic'  

The Command Constants

As you may know, MacApp's handling of incoming Apple events is normally to redirect the events so that they will end up in the application class' DoAppleCommand method, ready to be dispatched by you. This means that we must define the command constants that the server's application's DoAppleCommand method (or any of its installed behaviors DoAppleCommand methods) use to dispatch.

In the OSL Scripting Components package, all command constants that are a result of an incoming Apple event are placed in special file for the suite the event belongs to. For example, the command constants that are corresponding to incoming events from the Core suite are placed in the HandleCoreSuiteCommandID.r file.

#define cHandleAEClone             10100
#define cHandleAEClose              10101
#define cHandleAECountElements      10102
(more)
Note that all command constants for incoming Apple events begin with "cHandle" to point out that they are on an application's server side.

The Dispatch Table

To actually accomplish the redirection of an Apple event to an Apple command, we must map each event ID to a commandID in a resource of type 'aedt'-apple event dispatch table.

An example of such a resource from the CoreSuiteAEDispatch.r file is shown below, where the constants for the incoming Apple events of the Core suite are mapped to corresponding command constants.

// **********************************************************
// APPLE EVENT DISPATCHING - CORE SUITE
// **********************************************************
resource 'aedt' (kAECoreSuiteDispatchTable) {{          
    kAECoreSuite, kAEClone, cHandleAEClone;
    kAECoreSuite, kAEClose, cHandleAEClose;
    kAECoreSuite, kAECountElements, cHandleAECountElements;
    (more)
}};

DISPATCHING THE Suite

Normally, you'd expect to catch the Apple commands in the application's DoAppleCommand method. Well, that's perfectly OK to do, especially if you are going to handle just an event or two. But as soon as you realize that you probably have to support dozens of events from many different suites (including your own suites, too), you may want to look for other ways to do it.

In order to bring some system into this, I decided to work with events on a suite-level basis-that is, a suite and its events is a building block, which I would like to add or remove from the application. To accomplish this, I used the concept of behaviors that I can install into the application. Each behavior contains the handling of all the commands in one suite.

For example, the handling of the Core suite events are done in the behavior called TCoreSuiteHandler, located in the UHandleCoreSuiteCmds unit (see example code below).

pascal void TCoreSuiteHandler::DoAppleCommand(
                   CommandNumber aCmdNumber,
                   const AppleEvent& message,
                   const AppleEvent& reply)    // override
{
    switch (aCmdNumber)
    {
        case cHandleAEClone:
            this->DoHandleAECloneCommand(aCmdNumber,message,reply);
            break;
    
            case cHandleAEClose:
            this->DoHandleAECloseCommand(aCmdNumber,message,reply);
            break;
    
            case cHandleAECountElements:
            this->DoHandleAECountElementsCommand(aCmdNumber,message,
                reply);
            break;
     
        (more)
    
            default:
            inherited::DoAppleCommand(aCommandNumber, 
                            message, reply);
            break;
    }
} 

The DoHandleAECloneCommand, DoHandleAECloseCommand and DoHandleAECountElementsCommand methods in the code example above create, initialize and post a new instance of THandleAECloneCommand, THandleAECloseCommand and THandleAECountElementsCommand, respectively. These server-type command classes (they all inherit from TServerCommand, even though not direct descendants) are also physically located in the UHandleCoreSuiteCmds unit, together with the behavior (see Figure 3).

The server-type command

Each server-type command class has the following main responsibilities:
  1. The TServerCommand-subclass object reads the raw bytes in the event
  2. The TServerCommand hands over these bytes to the Toolbox's AEResolve function
  3. The TServerCommand subclass object asks the resolver manager for the newly resolved TOSLObjectResolver object.
  4. The TServerCommand-subclass object tells the TOSLObjectResolver its message
  5. The TServerCommand-subclass object writes the result back to the client
To be able to manage the complexity of these tasks and to simplify and clarify the responsibilities, I decided to divide the functionality into several subclasses:
  • TOSLServerCommand class
  • THandleAppleEventCommand class
  • THandleOSLObjectCommand class
  • the actual command to handle a specific Apple event, for example the THandleAEGetDataCommand class

TOSLServerCommand

This class is responsible for taking care of the type-casting of a TAppleEvent to a TOSLAppleEvent. The TOSLAppleEvent is a subclass of TAppleEvent with a more complete set of routines. The TOSLServerCommand also administrates the automatic registration of the calling client and the deregistering of the client when the communication closes.
class TOSLServerCommand : public TServerCommand
{
public :
// Creating and freeing:
    virtual pascal void InitializeFromAppleEvent(
                        CommandNumber itsCommandNumber,
                        TCommandHandler* itsContext,
                        Boolean canUndo,
                        Boolean causesChange,
                        TObject* objectToNotify,
                        const AppleEvent& itsMessage,
                        const AppleEvent& itsReply);

    virtual pascal void IOSLServerCommand(
                CommandNumber itsCommandNumber,
                        TCommandHandler* itsContext,
                                         Boolean canUndo,
                                         Boolean causesChange,
                                         TObject* objectToNotify);

// Action:
    virtual pascal void FreeTheMessage();               // override
    virtual pascal void Completed();                    // override
    
    virtual pascal void RegisterClient();   
    virtual pascal void UnregisterClient(); 
};

THandleAppleEventCommand

This class is responsible for the main structure of what's going to happen in any Apple event-type command.

The DoReadParameters method calls the GotRequiredParameters and the ReadParameters methods. GotRequiredParameters checks to see if all parameters that are required are OK, while the ReadParameters method is unimplemented (see subclasses).

The DoProcessing method identifies the need for additional processing, but is unimplemented in this class.

This class also identifies the concept of a result, fResultDesc, which can be written to the reply Apple event with the DoWriteReply method. The DoWriteReply method is unimplemented in this class.

class THandleAppleEventCommand : public TOSLServerCommand
{
public:
// Creating and freeing:
    virtual pascal void Initialize();   // override 
    virtual pascal void Free();         // override
    virtual pascal void DoIt();         // override

// Accessors and mutators:
    virtual pascal CDesc GetResultDesc();
    virtual pascal void SetResultDesc(const CDesc& theResultDesc);

// Action:
    virtual pascal void DoReadParameters();
    virtual pascal void ReadParameters();
    virtual pascal void GotRequiredParameters();

    virtual pascal void DoProcessing();
    virtual pascal void ReportError(OSErr error, long); // override

    virtual pascal void DoWriteReply();

private:
CDesc fResultDesc;
};

THandleOSLObjectCommand

This class is responsible for reading an Apple event containing an object specifier as a direct parameter-something which is quite useful when implementing the Core suite. Also, if it really reads a real object specifier, then that object specifier must be resolved. This facts implies that this class must support the initializing of the resolving process, too.

The reading of the incoming Apple event is done in the ReadDirectObject method. This method also finds out if it's a "real" object specifier (typeObjectSpecifier) or a null specifier (typeNull). If it's a null object specifier, then there's no need for additional resolving; the null value specifies that we found the object tree's root-the application.

If it needs to be resolved, the resolving is initiated by the Toolbox's AEResolve function, which is called from the ResolveObject method. This will initiate the resolving process, which is controlled by the TOSLResolverManager. When the resolving is ready, the DoAfterResolve method is called, which in turn calls the DoTheResolverAction. The DoTheResolverAction is responsible for applying the Apple event verb to the newly resolved resolver object.

class THandleOSLObjectCommand : public THandleAppleEventCommand
{
public:
// Creating and freeing:
    virtual pascal void Initialize();   // override
    virtual pascal void Free();         // override

// Accessors and mutators:
    virtual pascal Boolean GetCallResolveObject();
    virtual pascal DescType GetDirectObjectType();
    virtual pascal CDesc GetObjectSpecifier();
    virtual pascal TOSLObjectResolver* GetOSLObjectResolver();
    virtual pascal void SetCallResolveObject(
                                Boolean callResolveObject);
    virtual pascal void SetDirectObjectType(D
                                escType theDirectObjectType);
    virtual pascal void SetObjectSpecifier(
                            const CDesc& theObjectSpecifier);
    virtual pascal void SetOSLObjectResolver(
                    TOSLObjectResolver* theOSLObjectResolver);
    virtual pascal void SetShouldCallResolveObject(
                                DescType theDescType);
    
// Action:
    virtual pascal void DoProcessing();     // override
    virtual pascal void DoBeforeResolve();  
    virtual pascal void DoResolve();
    virtual pascal void DoAfterResolve();   

    virtual pascal void ResetBeforeResolve();
    virtual pascal void ResolveObject();
    virtual pascal void ResolveProperty();
    virtual pascal void CoerceResult();

    virtual pascal OSErr DoTheResolverAction(
                    TOSLObjectResolver* theOSLObjectResolver);

    // Read/Write:
    virtual pascal void ReadParameters();
    virtual pascal void ReadDirectObject();
    virtual pascal void DoWriteReply(); // override

private:
    Boolean fCallResolveObject;     // should we actually do resolve?
    DescType fDirectObjectType;     // either a null aedesc, AEList
                                    // or a obj specifier
    CDesc fObjectSpecifier;             
    TOSLObjectResolver* fOSLObjectResolver;
};

THandleAEGetDataCommand

Now, let's look at the interface of a typical server-type command from the Core suite, the THandleAEGetDataCommand, which inherits from THandleOSLObjectCommand.

The THandleAEGetDataCommand's main responsibility is to override the ReadParameters method to read an additional (and optional) parameter of the Get Data event: its keyAERequestedType parameter.

Another thing it does is to provide an override of the DoTheResolverAction method, which gives the TOSLObjectResolver object (a result of the resolving process) a DoGetData message.

The last thing it does is to coerce the result to the type requested in the keyAERequestedType parameter. This is done in the override of the CoerceResult method.

class THandleAEGetDataCommand : public THandleOSLObjectCommand
{
public:
// Create / free
    virtual pascal void Initialize();                   // override

// Access:
    virtual pascal DescType GetRequestedType();
    virtual pascal void SetRequestedType(
                            DescType theRequestedType);

// Reading:
    virtual pascal void ReadParameters();               // override
    virtual pascal DescType ReadRequestedType();

// Resolving
    virtual pascal OSErr DoTheResolverAction(
        TOSLObjectResolver* theOSLObjectResolver);      // override
    virtual pascal void CoerceResult();                 // override

private:
    DescType fRequestedType;
};

THE RESOLVING PROCESS

Remember ResolveObject, the THandleOSLObjectCommand's method which calls AEResolve? Well, that's the entry point into the resolving process. From that point on, the resolving is managed by the resolving manager until the result, a TOSLObjectResolver object, is delivered back to the command.

The responsibility of the TOSLResolverManager is to manage the process of resolving. There should be only one TOSLResolverManager in an application, and the one provided by the framework is the one you should use-no subclassing is necessary. A typical resolving process goes like this:

First, AEResolve begins its internal resolving mechanism and calls the TOSLResolverManager:CallResolve static function, previously installed by the TOSLResolverManager::InstallAccessors method.

Second, CallResolve calls the TOSLResolverManager:ResolveIt function, which tries to find out which TOSLObjectResolver object the specifier contains. When this first round of resolving is ready, it sets the TOSLResolverManager's field fCurrentOSLObjectResolver to that newly found TOSLObjectResolver object.

Repeat the second step until all contained object resolvers are resolved.

Last, the server-type command retrieves the TOSLObjectResolver object in the TOSLResolverManager's field fCurrentOSLObjectResolver.

Resolver Objects

In the OSL Scripting Components framework, I'm using the concept of resolver objects. A resolver object is an object that is used by the resolving process to access any object's other contained objects (="elements") or any of its properties. Thus, the resolver object defines the properties and containing objects that can be accessed.

A resolver object communicates with its "real" object to get additional information-for example, a TOSLApplicationResolverObject communicates with its TOSLApplication object. What's more, the "real" object is responsible for actually creating the resolver object. This way, you can override the "real" object's class definition to create another resolver object-maybe a simple override of the original one with a couple of new properties and elements?

EXAMPLE: ADDING A NEW PROPERTY TO A WINDOW

Let's say that you want to add additional properties for your TYourWindow object (inherited from the TOSLWindow class) when it receives a Get Data Apple event. To do so, you have to make overrides of three classes: the TOSLWindowResolver class, the TOSLWindow class and TOSLApplication class.

Override TOSLWindowResolver to dispatch the new property

The first thing you need to do is to is to override the TOSLWindowResolver class. By using the TOSLWindowResolver class as a base class, you inherit the basic properties of a window according to the Core suite.

Then you have to override the DoGetData method of TOSLWindowResolver. You need to do this because you want the additional pColor property to be dispatched.

pascal OSErr TYourWindowResolver::DoGetData(CDesc& theData)
    {
    DescType propertyID = this->GetPropertyID();
    
    switch (propertyID)
    {
        case pColor:
            return this->DoGetData_ColorProperty(theData);
    
            default:
            return inherited::DoGetData(theData);
    }
}

The next thing you need to do is to add a new method in TYourWindowResolver that retrieves the data of that property. Getting the color property from your window object probably should be called DoGetData_ColorProperty. The example below shows you that in that method, you must ask the "real" window for its color property and then ask the "real" window object for its name.

pascal OSErr TYourWindowResolver::DoGetData_ColorProperty
        (CDesc& theData)
{
    this->FailThisObjectReference(); //is my "real" object valid?
    long aColor = fOSLWindow->OSL_GetData_Color(); 
                                    //color is long in this example
    theData.Create(aColor);         // create a long data descriptor
    return noErr;
}

Override TOSLWindow to provide a new accessor

You need to override the TOSLWindow class to be able to ask a window for its color. You could name it, for example, TYourWindow. The next thing you need to do is to is to create a new accessor method in TYourWindow that just returns the color of that object.
pascal long TYourWindow::OSL_GetData_Color()
{
    long theColor = this->GetColor();
    return theColor; // yes, color is a long in this example !
}

Override TOSLApplication to deliver a TYourWindowResolver

You need to override the NewOSLWindow method of the TOSLApplication class to give the resolving process your own window resolver instead of the standard TOSLWindowResolver otherwise supplied. Now you're ready to go.
pascal TOSLObjectResolver* TYourApplication::NewOSLWindowResolver(
                    TOSLWindow* theOSLWindow,
                    TOSLObjectResolver* theOSLObjectResolver)
{
if (theOSLWindow)   {
        TYourWindowResolver* aYourWindowResolver =
            new TYourWindowResolver;
        aYourWindowResolver>IYourWindowResolver(
            theOSLWindow,theOSLObjectResolver);
        return aYourWindowResolver;
    }
return NULL;
}
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Things 2.5.4 - Elegant personal task man...
Things is a task management solution that helps to organize your tasks in an elegant and intuitive way. Things combines powerful features with simplicity through the use of tags and its intelligent... Read more
NeoOffice 2014.10 - Mac-tailored, OpenOf...
NeoOffice is a complete office suite for OS X. With NeoOffice, users can view, edit, and save OpenOffice documents, PDF files, and most Microsoft Word, Excel, and PowerPoint documents. NeoOffice 3.x... Read more
iPhoto Library Manager 4.2 - Manage mult...
iPhoto Library Manager allows you to organize your photos among multiple iPhoto libraries, rather than having to store all of your photos in one giant library. You can browse the photos in all your... Read more
Web Snapper 3.3.8 - Capture entire Web p...
Web Snapper lets you capture Web pages exactly as they appear in your browser. You can send them to a file as images or vector-based, multi-page PDFs. It captures the whole Web page - eliminating the... Read more
TeamViewer 10.0.41404 - Establish remote...
TeamViewer gives you remote control of any computer or Mac over the Internet within seconds, or can be used for online meetings. Find out why more than 200 million users trust TeamViewer! Free for... Read more
Ableton Live 9.1.8 - Record music using...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
VOX 2.5 - Music player that supports man...
VOX is a beautiful music player that supports many filetypes. The beauty is in its simplicity, yet behind the minimal exterior lies a powerful music player with a ton of features & support for... Read more
OmniFocus 2.1.2 - GTD task manager with...
OmniFocus helps you manage your tasks the way that you want, freeing you to focus your attention on the things that matter to you most. Capturing tasks and ideas is always a keyboard shortcut away in... Read more
Adobe Flash Player 17.0.0.169 - Plug-in...
Adobe Flash Player is a cross-platform, browser-based application runtime that provides uncompromised viewing of expressive applications, content, and videos across browsers and operating systems.... Read more
iMazing 1.1.3 - Complete iOS device mana...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more

Chainsaw Warrior: Lords of the Night has...
It's time to put the Darkness back in its place now that Chainsaw Warrior: Lords of the Night has officially made it to iOS. | Read more »
A World of Ice and Fire Lets You Stalk 2...
George R. R. Martin’s A World of Ice and Fire, by Random House, is a mobile guide to the epic series. The new update gives you the Journeys map feture that will let you track the movements of 25 different characters. But don't worry, you can protect... | Read more »
Gameloft Announces Battle Odyssey, a New...
Battle Odyssey, Gameloft's newest puzzle RPG, is coming to the App Store next week. Set in the world of Pondera, you will need to control the power of the elements to defend the world from evil. You'll be able to entlist over 500 allies to aid you... | Read more »
Fusion - HDR Camera (Photography)
Fusion - HDR Camera 1.0.0 Device: iOS Universal Category: Photography Price: $1.99, Version: 1.0.0 (iTunes) Description: Fusion creates HDR (high dynamic range) photos by capturing different exposures and then combining them into one... | Read more »
Sago Mini Toolbox (Education)
Sago Mini Toolbox 1.1 Device: iOS Universal Category: Education Price: $2.99, Version: 1.1 (iTunes) Description: Come build with the Sago Mini friends! Use a wrench, try a saw, or hammer some nails. From sewing hand puppets to... | Read more »
You Should Probably Grab Hitman GO While...
Hitman GO is a surprisingly cool (yet also incredibly drastic) departure from the Hitman series. It's well worth playing for any puzzle game fans out there, and at the moment you can get your hands - or garrotte if you will - on it for a mere $0.99... | Read more »
IFTTT is Bringing Do Button and Do Note...
IFTTT has announced Do Button and Do Note for the Apple Watch. Do Button lets you make your own personalized button that can connect to things like your Google Drive, control the temperature in your home with Nest Thermostat, or turn the lights on... | Read more »
How Many Days, Hours, and Minutes Are Le...
Countdown, by Yves Tscherry, is now available on the App Store. The app keeps track of countdowns to your favorite things such as someones birthday or days till the New Year. You can display the time in seconds, minutes, hours, days, weeks, months,... | Read more »
The All-New Misfit 2.0 App is Available...
Misfit has just given their app a complete overhaul. Misfit 2.0 now has a brand new interface with a sleek design and is easier to navigate. You'll be able to sync your Misfit device and look up health and fitness information faster than ever before... | Read more »
Halo: Spartan Strike (Games)
Halo: Spartan Strike 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: Delve into 30 challenging missions through cities and jungles using a devastating arsenal of weapons, abilities and... | Read more »

Price Scanner via MacPrices.net

Clearance 13-inch 2.6GHz Retina MacBook Pro a...
B&H Photo has clearance 2014 13″ 2.6GHz/128GB Retina MacBook Pros now available for $1099, or $200 off original MSRP. Shipping is free, and B&H charges NY sales tax only. Read more
Apple refurbished 2014 13-inch Retina MacBook...
The Apple Store has Apple Certified Refurbished 2014 13″ Retina MacBook Pros available for up to $400 off original MSRP, starting at $979. An Apple one-year warranty is included with each model, and... Read more
iMacs on sale for up to $205 off MSRP, NY tax...
B&H Photo has 21″ and 27″ iMacs on sale for up to $205 off MSRP including free shipping plus NY sales tax only: - 21″ 1.4GHz iMac: $1019 $80 off - 21″ 2.7GHz iMac: $1189 $110 off - 21″ 2.9GHz... Read more
Sale! 16GB iPhone 5S for $1 with service
Best Buy is offering 16GB iPhone 5Ss for $1.00 with 2-year activation at a participating cellular provider. Choose free home shipping and activation, or buy online and activate during in-store pickup... Read more
Apple refurbished 2014 MacBook Airs available...
The Apple Store has Apple Certified Refurbished 2014 MacBook Airs available starting at $679. An Apple one-year warranty is included with each MacBook, and shipping is free. These are currently the... Read more
27-inch 3.5GHz 5K iMac on sale for $2349, sav...
 Adorama has the 27″ 3.5GHz 5K iMac in stock today and on sale for $2349 including free shipping plus NY & NJ sales tax only. Their price is $150 off MSRP. For a limited time, Adorama will... Read more
Save up to $380 on an iMac with Apple refurbi...
The Apple Store has Apple Certified Refurbished iMacs available for up to $380 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 27″ 3.5GHz 5K iMac – $2119 $... Read more
iFixIt Teardown Awards 12-IInch Retina MacBoo...
iFixIt has posted its illustrated teardown of the new 12-inch MacBook Retina. They note that this new MacBook is less than half the thickness of the last Apple notebook called just “MacBook” back in... Read more
13-inch 2.5GHz MacBook Pro (refurbished) avai...
The Apple Store has Apple Certified Refurbished 13″ 2.5GHz MacBook Pros available for $829, or $270 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.... Read more
Faithful iPad 2 Gets A Second Career In Retir...
Finally, after four months’ transition, I handed my faithful old 2011 iPad 2 off to my wife at the end of March and switched whole-hog to using the iPad Air 2 I bought back in November. I’d found... Read more

Jobs Board

*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*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* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
*Apple* TV Live Streaming Frameworks Test En...
**Job Summary** Work and contribute towards the engineering of Apple 's state-of-the-art products involving video, audio, and graphics in Interactive Media Group (IMG) at Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.