TweetFollow Us on Twitter

Client-Server Development

Volume Number: 14 (1998)
Issue Number: 11
Column Tag: Viewpoint

Client/Server Development based on the Apple Event Object Model

by Sascha Kratky and Christoph Reichenberger (UNI SOFTWARE PLUS)
Edited by Richard Clark

Using AEOM for the design and implementation of the VOODOO client/server system

The term "Client/Server System" has become one of most used terms in today's computing world. We use client applications practically every day to transfer incoming emails from mail servers, download files from FTP servers, or to browse HTML pages offered on web servers all over the world.

Developers have chosen the client/server architecture as a reasonable means to structure large software systems, in order to make them leaner, more efficient, and easier to maintain. The basis of every client/server system forms the protocol that the client and the server applications are using to communicate with each other. For some applications like file transfer, mail transfer or hypertext transfer, there are well-known standardized protocols (e. g. FTP, SMTP, or HTTP) which remove the burden of having to design a proprietary protocol from scratch. Other areas, such as software version control, lack standardized protocols.

This article deals with the design and implementation of a protocol for a client/server based software version control system. It starts with a discussion of the basic properties of client/server systems. It explains why we chose to design and implement the protocol for the VOODOO client/server system on top of the Apple Event Object Model. A section on the problems that the developer faces when implementing a client/server system on top of the Apple Event Object Model concludes the article.

Client/Server Systems

The term "Client/Server System" refers to a general software architecture which can be applied to a variety of different applications. Most discussions of client/server systems take for granted that we are aware of the advantages that arise from using a client/server-based system. It may be worthwhile to recall the encouragement that originally led to the widespread use of client/server systems. A main motivation for a client/server system is that it provides an elegant solution for applications that need concurrent access of many clients to the same data. This simultaneous access problem is sketched in Figure 1.

Figure 1. In a non client-server system, multiple clients may access the same resource concurrently.

In this scenario clients access a database concurrently on the file system level. The database is usually stored on a file server machine on the network and clients have to mount the database in order to access it. This results in the following shortcomings:

  • All clients make concurrent changes to parts of the database. To avoid modifying parts of the database that are currently in use by another client, the clients have to implement a file system-based locking mechanism. Nevertheless, an unexpectedly interupted client process may leave the database in an unstable state. The consequences are higher chances of database corruption and the loss of valuable data.
  • Every client application must have full knowledge of the external representation of the database. A change to this representation means that all client applications have to be upgraded as well.
  • Propagating changes one client has made to the database to the other clients currently working on the database is difficult and unreliable. Every client would have to constantly poll the files of the database for changes made by other users in the meanwhile.

Interestingly enough, many commercial systems that claim to be truly client-server-based still follow this "file-server" approach and therefore suffer from the previously-mentioned drawbacks.

In a real client/server-system the clients do not access the database directly but get the needed data by sending requests to a server application, which is the only process that directly accesses the database (Figure 2). Instead of mounting the remote database the clients connect to a server process running on a remote machine.

Figure 2. In a client-server system, a server mediates all accesses to the shared resource.

This avoids the above-mentioned shortcomings for the following reasons:

  • Only the server application makes changes to the files of the database. The server can schedule the incoming requests by the clients, such that the consistency of the data in the database is always assured. When a client process dies, the server can take care of undoing any unfinished transactions of that client.
  • Only the server process has to know the external representation of the database. Clients are unaffected by changes to the representation as long as the communication protocol remains unchanged.
  • The server can propagate changes from one client to the other clients, since it can keep track which client has retrieved what data and when this data has become obsolete.
  • The server can postpone housekeeping tasks until idle time, i. e. no client has sent a request to the server for some time. In VOODOO Server, the server uses delta storage to store all the versions space-efficiently. The server performs the calculation of these deltas during idle time, such that check-in operations for clients solely consist of transferring the file to the server.

These advantages make the client-server architecture well suited for the implementation of a true multi-user aware version control system where users can simultaneously check-in and check-out files.

Apple Events and the Apple Event Object Model

Apple events were introduced with System 7 as a means for applications to communicate with other applications in a standard way. Applications use Apple events to request information from other applications or to return information as result to these requests. An application can send an Apple event to another application on the same computer, an application on a remote computer, and even to itself.

The Apple Event Object Model (AEOM) enables developers to structure the scripting interface of their applications in an object-oriented way. The AEOM allows both the definitions of Apple events and Apple event Classes. The Apple event Classes are templates for Apple event Objects. Each object encapsulates some data that the user can access via the object's properties. Properties are named attributes of an object. For example, an object of class window would have its name, size, and position as properties.

Apple event objects can be hierarchically structured by making some Apple event objects elements of other objects. For example, an application object can have document objects as its elements. The documents themselves have window objects as elements. The resulting hierarchy of objects is called the containment hierarchy.

Apple events act on these objects just like messages do in objects oriented programs. Related classes and events can be grouped together into suites. The most commonly used objects and events are group together in the Apple event Core Suite. The Apple events defined in the Core Suite include Create Element, Delete, Get Data and Set Data. Just as a user can apply the Cut, Copy, and Paste commands to almost any data on the screen these commands should be implemented in virtually every Apple event object.

Apple events and Apple Event Objects also form the basis for the AppleScript programming language. AppleScript scripts enable the user to write down series of Apple events in a very high-level "natural" language. Users typically write scripts to automate everyday work or to combine the functionality of different applications to achieve a desired result. [Other applications, such as UserLand's Frontier can also generate Apple events to drive your application. - ed RC]

In order to be scriptable an application must contain an Apple Event Terminology Extension resource (a resource of type AETE), which defines the containment hierarchy as well as the mapping of objects and events to the vocabulary. AETE resources allow AppleScript to support multiple "dialects" such as English and Japanese.

The attempt to fully explain the Apple Event Object Model would be beyond the scope of this article. For more information on Apple events, the AEOM and AppleScript see Inside Macintosh: Inter-Application Communications and the AppleScript Language Guide.

Why should you use Apple events and the Apple Event Object Model?

Using Apple events and the Apple Event Object Model as the basis for a protocol has a number of advantages not only for clearness and extensibility reasons but also for your development and testing process:

  • Supporting the AEOM and Apple's Core Suite guarantees consistency with the scripting interface of other applications. When moving from one scriptable application to another the user will encounter already many familiar terms. So your server application is not only accessible for your clients but becomes a well-behaved citizen in the world of Open Scripting Architecture-compliant applications such as Frontier and FaceSpan.
  • You can test the server application by writing AppleScripts, which send Apple events to the server. Since you can save these scripts you are able to reproduce the same tests over and over again. These test scripts can grow into a handy test suite for your server application.
  • You can quickly build a GUI that accesses your server application using RAD tools like FaceSpan. Using FaceSpan you can implement an application completely in AppleScript while the FaceSpan environment offers Apple event classes for most user interface elements like Windows, Button and Lists. This makes it fairly easy to build a complete Mac-like client application with minimal effort on top of your AE-based client/server interface.
  • AEOM based protocols are quite flexible when it becomes necessary to extend the protocol. You can add a property to a class or add a completely new class which supports existing events. Rarely is it necessary to define a new Apple event, since many of the existing events can also act on objects of the new class. In contrast, protocols built without the Object Model often contain a myriad of verb-noun hybrid commands such as GetPreferences, SetPreferences, GetFolder, SetFolder, etc. The only way to extend such a protocol is by adding new Apple event definitions.
  • The transition from "procedural" Apple events to the AEOM can be compared with the transition from a procedural oriented language like C to an object oriented language like C++. This implies that many object oriented design methods like design patterns (1, 5) can also be applied to the design of your custom Apple Event Object Model making the design more flexible and easier to understand.

Designing your Custom AEOM

The following section guides you through the process of designing a client-server protocol based on the AEOM. We will use the protocol of our VOODOO Server application as an example here. Let us briefly explain VOODOO's functionality and data types in order to help you better understand the following description of the object hierarchy.

The VOODOO Server application is designed as an almost faceless server application, which exposes all its functionality through the Apple Event Object Model. Clients should be able to access all data stored in the server's database by sending requests to the server. Therefore, it was our goal that all elements of a VOODOO database should be modeled by means of Apple Event Objects. A VOODOO database holds collections of projects, parts and users. Users can log on to the server and open projects in the database. A project is further divided into different parts. A part represents a directory structure that is put under version control. Parts can also be shared among different projects. Figure 3 shows a VOODOO database for an example compiler project.

Figure 3. Structure of a VOODOO Server database.

The example database contains two projects: "Compiler" and "Linker". The project "Compiler" consists of the parts "Compiler" that stores all compiler specific files and a part "Toolslib" with common utility files. The part "Toolslib" is shared with the project "Linker", such that all changes made to "Toolslib" from the Compiler project are immediately visible in the project "Linker", too.

As mentioned, a part in the VOODOO database corresponds to a directory tree of the user's hard disk. The user can add files and folders to a part, fetch files from the part for modification and store a new versions to the database later. The VOODOO server takes care of storing different versions of files space efficiently using a combination of delta and compression algorithms. Each time a modification has been made to a part's item the server adds an event to the part's history. The event contains the date of the change, the user, the kind of change (e. g. added, revised, renamed, etc.), the items in the part that were changed and a comment. Besides that, the user can add a bookmark at an arbitrary point in time of the history to label milestones of the project.

It would be beyond the scope of this article to describe the full functionality of the VOODOO server. The server will form the basis for a new generation of version control tools to be released by UNI SOFTWARE PLUS in the near future but is also available for licensing to allow other companies to integrate version control functionality directly into their products. If you are interested in more details or want to download a free demo version, please visit <http://www.unisoft.co.at> or send an email to <voodoo@unisoft.co.at>.

Classes

The elements of a VOODOO database have been modeled as classes in the VOODOO suite. The inheritance hierarchy of these classes is sketched in Figure 4.

Figure 4. Inheritance hierarchy of the classes of the VOODOO AEOM.

All classes inherit from a common base class, which defines properties that are common to all the object classes. The directory tree of a part consists of files and folders. Therefore the base class of both is vItem, which represents a node in this tree. The part history contains both events and bookmarks. The common properties of these two classes have been extracted into a class entry which represents an entry in the part history.

Note that the names vItem, vFile, and vFolder were chosen to avoid name clashes with the terms item, file, folder which are the names of standard types in AppleScript.

The hierarchical relations of the objects can be seen in the containment hierarchy (Figure 5).

An object of class database forms the root of the containment hierarchy. The database contains user and project objects as elements. The recursive structure of directory trees is mirrored by the fact that the vFolder object can contain other vFolder objects.

Note that some object classes appear several times in the containment hierarchy. For example a vItem object contains all event objects that are valid for this item. This is a subset of the set of all the event objects that are valid for the whole part.

Figure 5. Containment hierarchy within the VOODOO AEOM.

Events

Once you have designed the inheritance and containment hierarchy for the classes, Apple event definitions can be added to your suite. Most of the events that you find you have to support are already defined in the Apple event Core Suite. As an example the following script shows how the Create Element event can be used to create files and folders in a part of the VOODOO database:

tell application "VOODOO Server"
   tell first database
      tell part "Compiler" of project "Compiler"
         make new vFolder at the end of it¨
            with properties {name:"FrontEnd"}
         make new vFile at the end of result¨
            with properties {name:"Scanner.c"}
      end tell
   end tell
end tell

To retrieve data from the database the Get Data and Count Elements Apple events can be used. The following script shows the use of these events.

tell application "VOODOO Server"
   tell project "Compiler" of first database
      - What parts does the project "Compiler" consist of?
      get name of every part
      tell part "Compiler"
- How many versions of the file "Scanner.c" have been stored?
         count every vVersion of vFile ¨
            "Scanner.c" in vFolder "FrontEnd"
      end tell
   end tell
end tell

When a user wants to store a new version of a file to a part, the data of this file has to be transferred to the server on the remote machine via Apple events. The following Apple events of the VOODOO Suite allow the transfer a particular version of a file to the database:

start store: announce a new store task
   start store  reference   - the vFile to be stored to
      [comment  string]     - comment describing new version
      [unlock  boolean]     - unlock vFile after store is finished (default:false)
   Result:   reference      - the newly created store task

The Apple event start store tells the server that the user wants to store data of a new version to a file. The comment parameter of the Apple event is used to initialize the comment of the event object that will be added to the part's history. The unlock parameter tells the server to unlock the file to be stored to, if the user previously locked it to prevent other users from making changes to it. The Apple event returns a reference to a store task as result, which is used later to store the actual data.

store: store data valid for the specified store task
   store  reference          - the store task the data is valid for
      data  anything         - the data block to be stored

The actual data of the version is transferred to the Server using one or several store Apple events (the data can be transferred in one block or in several chunks).

finish: finish the specified task
   finish  reference         - the task to finish

When all the data has been transferred to the server the client sends a finish Apple event to the store task to signal that the transfer is complete. The following example script illustrates the use of these Apple events:

tell application "VOODOO Server"
   tell first database
      tell part "Compiler" of project "Compiler"
         start store vFile "Scanner.c" in vFolder "FrontEnd"
         set theStoreTask to result
         store theStoreTask data "/* Scanner.c */"
         - store additional data ...
         finish theStoreTask
      end tell
   end tell
end tell
Using these Apple events many users can store data to the server at the same time. The server receives the versions, puts them into an incoming pool and takes care of storing these items space efficiently when there's nothing else to do.

Implementation Issues

Once you have designed your Object Model, you must implement the necessary handlers for resolving the Apple event object references and for processing the Apple events. This section gives some hints for implementing a server application and covers some implementation details that are particular for an AEOM based server application. The latter requires careful attention to identifying and maintaining multiple client/server connections.

First of all we want recommend you use an application framework that provides support for the AEOM in order to reduce your own implementation efforts. Metrowerks' PowerPlant is an example of an application framework that has extensive AEOM support. In PowerPlant, the AEOM can be implemented by adding classes to the framework using LModelObject as a base class and overriding methods to handle specific Apple events. PowerPlant already supports many of the events of the Core Suite. The exact details of implementing an AEOM in PowerPlant have been thoroughly described in (4).

Factor your Application

It is a good idea to factor your server application into domain-specific and communication-specific parts. This enables you to replace the communication layer of the server with a different protocol or to add support for other protocols. Figure 6 shows how this has been done in the VOODOO server application.

Figure 6. Structure of the VOODOO Server application.

The VOODOO Engine, which contains the code to access the data in the VOODOO database, is based on the ANSI C++ Standard Library only. The AEOM handling code is built on top of PowerPlant and calls the VOODOO engine to access the data in the VOODOO database. The very small user interface part of the VOODOO Server application also is based on PowerPlant.

Identifying Different PPC Sessions

An essential task of a server application is to keep track of all the connections of clients in order to be able to perform initializations when a new connection is established and cleanups when a connection is closed. Note that connections are not always closed explicitly by the client. The client process may crash and a server must be able to deal with this situation properly in order to ensure data integrity.

A TCP/IP based server has full control over its connections since it awaits and accepts new connections at a dedicated port. The server can easily keep track of all the connections that have been established by clients. When the client side unexpectedly closes the connection, an error is usually pending on the connection's socket on the server side.

With Apple events the situation is different. The Apple Event Manager does not explicitly inform the server application when a new session with a client is established. There is also no particular notification available when a session to a client application has been closed. Instead, the application receives all Apple events as high level events during normal event processing within the main event loop. The usual way to process an Apple event is to pass it to the Apple Event Manger function AEProcessAppleEvent, which then activates the appropriate Apple event Handler function.

However, the communication with Apple events is based on the PPC Toolbox. By using a combination of Apple Event Manger and PPC Toolbox calls, the developer can gain control over the server's session handling again. The following code examples illustrate how this can be done. The key idea is to wrap all the necessary Toolbox calls in a class PPCSession that represents a client's active connection to the server. For simplicity we use PowerPlant container classes in the following example code.

Class PPCSession

class PPCSession {
public:
   PPCSession(const AppleEvent& inAppleEvent);
   virtual PPCSessRefNum GetID();
   static PPCSession* FindSession(PPCSessRefNum inSessID);
   static void VerifySessions();
   virtual Boolean IsActive();
   virtual ~PPCSession();
protected:
   virtual void Closed();
   PPCSessRefNum                     mSessionID;
   PPCReadPBRec                      mPBRec;
   static TArray<PPCSession*>  sActiveSessions;
};

The class PPCSession can be used as is or as a base class for more elaborate session classes. The VOODOO Server overrides this class to add information for which user has logged on in this session and which projects the user has opened.

PPCSession::PPCSession

PPCSession::PPCSession(const AppleEvent& inAppleEvent)
{
   OSErr      err;
   Size         bogusSize;
   DescType   bogusType;
   TargetID   addr;
      
   err = ::AEGetAttributePtr(&inAppleEvent,
                  keyAddressAttr, typeTargetID,
                  &bogusType, &addr, sizeof(addr), &bogusSize);
   ThrowIfOSErr_(err);
   mSessionID = addr.sessionID;
   mPBRec.ioResult = 0;
   sActiveSessions.AddItem(this);
}

The constructor takes an Apple event as its only parameter and examines the address attribute of this Apple event. The address attribute contains the PPC session reference number of the underlying PPC session and the address of the machine on the AppleTalk network from which the Apple event was sent. The session reference number is stored in the instance variable mSessionID. The session object then inserts itself into the list of all active PPCSession objects. The instance variable mPBRec which is used by the IsActive method is initialized, too (see below).

PPCSession::GetID

PPCSessRefNum
PPCSession::GetID()
{
   return mSessionID;
}

The method GetID is an accessor function that returns the session reference number of the PPC session.

PPCSession:: FindSession

PPCSession*
PPCSession::FindSession(PPCSessRefNum inSessID)
{
   PPCSession* sess;
   TArrayIterator<PPCSession*> iterator(sActiveSessions);
   while (iterator.Next(sess)) {
      if (sess->GetID() == inSessID) {
         return sess;
      }
   }
   return nil;
}

The class method FindSession searches the list of all PPCSession objects for an object with the given Session Reference Number. The search is done using a PowerPlant iterator object.

PPCSession::VerifySessions

void
PPCSession::VerifySessions()
{
   PPCSession* sess;
   TArrayIterator<PPCSession*> iterator(sActiveSessions);
   while (iterator.Next(sess)) {
      if (!sess->IsActive()) {
         sess->Closed();
         delete sess;
      }
   }
}

PPCSession:: Closed

void
PPCSession::Closed()
{
   // should be overwritten!
}

VerifySessions is a class method that should be called periodically by the server application to verify that the server's connections are still intact. An ideal location for calling this method would probably be the idle handler of your application. The function iterates through the list of all PPCSession objects and deletes those objects whose underlying PPC session has been closed by the client side. Before the object is deleted, the message Closed is sent to the object. Derived classes of PPCSession can override the Closed method to perform cleanup tasks, when the connection has been unexpectedly closed. The VOODOO Server takes care of aborting any of the user's unfinished storage tasks, closing any of the user's opened projects, and logging off the user.

PPCSession::IsActive

Boolean
PPCSession::IsActive()
{
   if (mPBRec.ioResult == 0) {
      // test if session is still active 
      mPBRec.ioCompletion = nil;
      mPBRec.sessRefNum   = mSessionID;
      mPBRec.bufferLength = 0;
      mPBRec.bufferPtr    = nil;
      OSErr err = ::PPCReadAsync(&mPBRec);
      return (err == noErr);
   }
   else {
      // assume that session is active
      // if asynchronous call has not yet finished
      return (mPBRec.ioResult == 1);
   }
}

The actual check whether a PPC session is still intact is done inside the method IsActive. This function tries to read a zero length block from the PPC session, so no data is actually fetched, but an error message will be returned from the call when the session is no longer intact. The actual call of PPCRead is done asynchronously. Therefore the call's parameter block can not be declared as a local variable. For simplicity we made mPBRec an instance variable of the class PPCSession. If the asynchronous PPCRead call has not yet finished, it is assumed that the session is still active.

PPCSession::~PPCSession

PPCSession::~PPCSession()
{
   sActiveSessions.Remove(this);
   if (mPBRec.ioResult == 1) {
      // finishing asynchronous calls
      PPCEndPBRec pb;
      pb.ioCompletion = nil;
      pb.sessRefNum   = mSessionID;
      ::PPCEndSync(&pb);
   }
}

The classes' destructor removes the PPCSession object from the list of active sessions. If an asynchronous call is still pending for the session, this call is finished using the PPCEnd toolbox call. This is necessary since the parameter block mPBRec is not longer valid after the execution of the object's destructor.

MyAppleEventHandler

pascal OSErr
MyAppleEventHandler(
   const AppleEvent*   inAppleEvent,
   AppleEvent*            outAEReply,
   SInt32                     inRefCon)
{
   Size               bogusSize;
   DescType         bogusType;
   TargetID         addr;
   short            eventSource;
   PPCSession*      sess;

   err = ::AEGetAttributePtr(&inAppleEvent, keyAddressAttr,
           typeTargetID,
           &bogusType, &addr, sizeof(addr), &bogusSize);
   if (err != noErr) return err;
   sess = PPCSession::FindSession(addr.sessionID);
   if (sess == nil) {
      // a new session has been established
      sess = new PPCSession(inAppleEvent);
   }
   // continue with normal Apple event processing
}

The Apple event handler of your server application is the right place to create the session objects. The function MyAppleEventHandler sketches the necessary operations. The handler first examines the Apple event's address attribute to get the session reference number. If there is no PPCSession object valid for the session reference number, a new PPC session has been established by a client.

User Interaction

A server application usually runs unattended by the user on a remote machine. An Apple event based server application can thus not make use of the user interaction calls of the Apple Event Manager. The only way for the server application to interact with the user is to return error messages. The error message should contain both a unique error number and an error message string. For a further discussion of user interaction in Apple event based server applications see (6).

Concurrency

An Apple event server application can receive events from different client applications at the same time. While the server is busy handling an Apple event from one client, Apple events of other clients are delayed. It is important for the server to be as responsive to clients as possible, otherwise the server application becomes the bottleneck of the client/server system.

With Apple events the server has the possibility to suspend the handling of an Apple event using the AESuspendTheCurrenEvent toolbox call and continue processing other events. Execution of the suspended Apple event can be resumed later using the AESuspendTheCurrentEvent. Each suspended Apple event can then be assigned an own thread of execution. See (2) for details how to thread the execution of Apple events.

Conclusion

The Apple Event Object Model is an ideal choice when designing the protocol for a client-server-based application. This article discussed some issues you should consider when designing a protocol of a client/server system based on the AEOM. It showed the advantages of using Apple events and in particular the Apple Event Object Model. Thanks to the object-oriented nature of the AEOM the data provided by the server can be designed using an object-oriented approach. However, using Apple events as the communication basis for a client/server system also leads to some problems, especially what concerns the accurate control of connections. The paper finally presented a solution to gain the necessary control by using services of the PPC Toolbox.

Bibliography and References

  1. E. Gamma, R. Helm, R. Johnson, and J. Vlissides. Design Patterns,. Addison-Wesley, ISBN 0-201-63361-2, 1995
  2. Grant Neufeld. "Threading Apple Events". MacTech Magazine 12:4 (April 1996), pp. 33-40.
  3. Christoph Reichenberger. "Keeping Things Straight, Orthogonally" MacTech Magazine 12:6 (June 1996), pp. 61-70.
  4. Jeremy Roschelle. "Powering Up Apple Events in PowerPlant". MacTech Magazine 11:6 (June 1995), pp. 33-46.
  5. John Schettino. "The Tao of Design". MacTech Magazine 13:4 (April 1997), pp. 30-39.
  6. Cal Simone. "According To Script: User Interaction in Apple Event-Driven Applications". Develop, Issue 29 (May 1997), pp. 74-80.
  7. Cal Simone. "The AppleScript Scorecard Guidelines". MacTech Magazine 14:2 (February 1998), pp. 16-18.
  8. Cal Simone. "According To Script: An External Editing Apple Event Protocol". Develop in MacTech Magazine 13:5 (April 1997), pp. 30-39.

Sascha Kratky studied computer science at the Johannes Kepler University in Linz, Austria. After graduation he joined UNI SOFTWARE PLUS where he is working on the VOODOO project. You can reach Sascha by sending email to kratky@unisoft.co.at.

Christoph Reichenberger studied computer science at the Johannes Kepler University Linz, Austria. After that he worked there as an assistant professor for seven years and finalized his work with a PhD thesis on software configuration management. Then he joined UNI SOFTWARE PLUS where he is now managing the VOODOO product line which is an offspring of his former research work. He can be reached via email at chrei@unisoft.co.at. The home page of UNI SOFTWARE PLUS can be visited at http://www.unisoft.co.at.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.