TweetFollow Us on Twitter

Writing Cocoa Applications in Java

Volume Number: 19 (2003)
Issue Number: 12
Column Tag: Programming

Writing Cocoa Applications in Java

by Steve Klingsporn

Mac OS X is a great platform for software development. The combination of the Mac's elegant user experience, signature design and media tools, breakthroughs in mobile computing and UNIX-based underpinnings continues to lure developers away from other platforms. Apple has done a great deal of performance tuning in version 10.3 "Panther," optimizing the Carbon and Cocoa application frameworks while upgrading the underlying Mach kernel and BSD environment. Developers have a diverse choice of programming languages, from traditional assembly, C, and C++ to more dynamic languages like Objective-C, Java, JavaScript and Python. Apple ships Panther with "Xcode," its integrated development environment, which includes comprehensive documentation, sample code and the usual compilers and command-line tools. Xcode is an evolution of Project Builder, and adds neat features like code completion, fix and continue, predictive compilation, zero-link, distributed builds and a graphical debugger. Developers can choose between graphical tools like Xcode and Metrowerks CodeWarrior or more traditional command-line text editors and utilities. Apple rounds off its UNIX offerings with an X11 environment, which makes it extremely attractive to developers coming from Linux and FreeBSD camps. Apple is now the top vendor of high-volume UNIX systems, and Mac users can finally enjoy a truly remarkable, stable, and secure operating system.

Java has surpassed C and C++ as the world's most popular programming language, and is standard fare at most colleges and universities. Sun describes Java as "simple, object-oriented, distributed, interpreted, robust, secure, architecture neutral, portable, high-performance, multithreaded, and dynamic." Developers compile Java source into platform-independent byte code that executes identically across host platforms in a virtual machine (VM) environment. Sun's "Hotspot" VM dynamically compiles this byte code into native object code at runtime, and its performance often equals or exceeds that of compiled C and C++. Apple realized this when moving its WebObjects application server to Java, as did Netscape when it profiled the performance of its Java ("Rhino") and C-based ("SeaMonkey") JavaScript engines. The performance problems that plagued Java in the early days have been addressed, and in most cases, the only noticeable tradeoffs are slightly longer application launch times and the memory overhead incurred by the VM. This environment offers an additional level of stability and security that C and C++ can't match, protecting applications from crashing and unauthorized resource access through the use of exceptions and a policy-based security model. Java has taken off in the server space due to many of these advantages, where stability and security are critical. Server-side Java development is pure joy on Mac OS X, as you can run any of the popular J2EE-compliant application servers, most popular relational databases, and you can easily deploy on Mac OS X Server or any other Enterprise-class UNIX servers while developing on your PowerBook and listening to iTunes.

Apple ships Java 2 Standard Edition (J2SE) versions 1.3.1 and 1.4.1 with Panther, and these releases are available to developers still using Jaguar via the Software Update service. Apple includes a number of notable improvements and innovations in its J2SE implementations, including the sharing of commonly used classes across VM instances, and using the Cocoa framework as the basis for its AWT and Java2D implementations. The net result is arguably the best and most tightly integrated implementation of Java 2 available on any platform. In addition to the standard tools from Sun, Apple includes a "Jar Bundler" utility that assists in generating double-clickable applications from JAR files, and "JavaBrowser," a quick and useful class browser that allows the browsing of Java package hierarchies and their associated class definitions, source code and documentation without switching back and forth between Xcode and multiple pages in a web browser. These two applications live in /Developer/Applications/Java Tools, and are installed by the Xcode installer. Figure 1 depicts JavaBrowser displaying the source code for the java.util.StringTokenizer platform class.


Figure 1: JavaBrowser displaying the source code to the StringTokenizer class.

Cocoa Java

Cocoa is the evolution of the object-oriented application frameworks developed in the early 90's at NeXT, and was instrumental in the transition to Mac OS X at Apple. Developers who are interested in writing new applications for the Mac will probably choose Cocoa over Carbon because of its modern, object-oriented nature and award-winning rapid user interface design tool, "Interface Builder." With Interface Builder, you visually construct your application's user interface, dragging, dropping, configuring and tying components to "outlets" and "actions" which map directly to properties and methods implemented in your source code. Interface Builder generates "NIB files" containing frozen objects, which are packaged in directories that correspond to the various human languages your application is localized into. Organizing NIB files this way together with localized program strings gives developers a simple and powerful mechanism for providing their applications in many different languages and markets simultaneously.

Cocoa is written in the Objective-C language, which consists of a small number of extensions to ANSI C that enable object-oriented programming and dynamic runtime features. Objective-C is a more elegant and concise language than C++, lacks many of its drawbacks and pitfalls, and has a unique syntax for defining classes, categories, protocols and message passing between objects. Despite the power and simplicity of this language, C++ won the language war in the mid 90's, and Java's popular syntax more closely resembles C++, while borrowing many of Objective-C's features. James Gosling, Java's mastermind, credits Objective-C for inspiring many of the language's dynamic features, and today uses his PowerBook for most of his research and development work. While Objective-C may be the language that Apple would like to see developers using, it acknowledges Java's popularity and utilizes the "Java Bridge" technology the WebObjects team developed to facilitate building fully native Cocoa applications in pure Java. While you can continue to develop platform-independent Java applications using "Swing" and AWT, only Cocoa offers a true Aqua user experience and access to platform-specific features that Mac users have come to expect. Cocoa Java is a very attractive option for new and existing developers, leveraging the best aspects of these technologies without requiring the additional learning curve of mastering an unfamiliar programming language. You can freely mix Objective-C and Java code in your applications, and can design your classes so Cocoa-dependent code is separate from more generic code, which can continue to run unmodified on other host platforms.

Creating Your Project

To create a new Cocoa Java project, launch Xcode (in /Developer/Applications), and select "New Project..." from the "File" menu. Select "Cocoa Java Application," click "Next," and tell Xcode the name and location of your new project. If you are planning on developing a document-based application utilizing the NSDocument class, you may want to select "Cocoa Java Document-based Application" instead.


Figure 2: Using Xcode to create a new Cocoa Java project.

Xcode will create a new project directory for you based on its template for a Cocoa Java application, and will put some placeholder files into this directory, including a "MainMenu.nib" file for editing in Interface Builder, links to the AppKit, Cocoa and Foundation frameworks, and a "main.m" Objective-C stub file that is called only to launch your application, and is actually optional. Figure 3 shows what a new Cocoa Java project window should look like. The name of your project, of course, will be different from the one used in this article. If you are using Project Builder on Jaguar, things will be arranged differently. A considerable number of articles have been written about Cocoa development in Project Builder.


Figure 3: A new Cocoa Java application project window.

At this point, you will begin to follow the usual iterative Cocoa development patterns. Much has been written in previous articles and tutorials about this process, which generally consists of using Interface Builder to design classes in the "Classes" tab of your NIB file window, specifying instances of these classes in the "Instances" tab by dragging or manually creating them, and drawing out the connections between them. You then either auto-generate or edit your source code files, implementing the various member variables and methods associated with the outlets and actions you define in the "Classes" tab. You should keep all of your program strings in a text file called "Localizable.strings," and use NSBundle.localizedString to retrieve them instead of hard-coding them in your source. You can generate different language bundles for other localizations containing this file and any NIB files that have localization-specific changes. "MainMenu.nib" is the default NIB file that Xcode generates from its Cocoa Java project template, and should be double-clicked to launch Interface Builder when you are ready to begin specifying the menus and windows for your application. You can break your application's user interface up into multiple NIB files to decrease your application's launch time and provide your localizers with files that are easier to manage and edit.

Differences From Objective-C

The obvious difference from programming in Objective-C is that you will be using Java to implement your source files. The Cocoa AppKit and Foundation Kit frameworks are implemented in the com.apple.cocoa.application and com.apple.cocoa.foundation packages. You should import these packages in full, or at least the classes from them you plan on using, in each of your source files that depends on Cocoa. You are simply using Cocoa for your user interface and platform integration instead of AWT or JFC ("Swing"). Everything else more or less remains familiar, and you can use any of the tried and true Java platform classes you like mixed in with your use of Cocoa. You can add the paths to your class, source and documentation files for your application to JavaBrowser if you wish, and conveniently browse them in conjunction with the Java and Cocoa platform classes. You can even use "Ant" to build portions or the entirety of your project, though this will require you to manually write a "build.xml" file and do a bit of extra work. You will find that most of the Cocoa sample code on the web and most of the mailing lists, books and other resources listed at the end of this article are written in Objective-C, so a rudimentary understanding of the language and its message passing syntax will prove useful. Converting between Objective-C and Java will become second-nature as soon as you get the hang of its syntax peculiarities, and you will begin to appreciate the conventions Apple took in converting the API to Java, often using familiar interfaces instead of protocols and the like. In most cases, there is no penalty (or "toll") for subclassing or calling back and forth between the two languages across the Java Bridge, and Apple's documentation describes cases in which you should pay special attention to performance or memory management issues.

One key difference between Objective-C and Java Cocoa is that Apple has not provided Java equivalents for many of the I/O, string manipulation and other such classes in the Foundation Kit, including NSFileHandle, NSThread, NSValue and NSScanner, expecting developers to instead use standard Java platform counterparts like java.io.File, java.lang.Thread, java.lang.Object and java.util.StringTokenizer. Use java.lang.String instead of NSString; NSAttributedString and other Cocoa classes that take strings accept them. Java's network and file I/O classes are not only familiar to seasoned Java developers, but often offer more elegant solutions than their Objective-C counterparts. Java's platform advantages really start to become self-evident when using features like reflection, serialization, and remote method invocation (RMI), not to mention libraries like JDBC, which enables a Cocoa developer to easily write a vendor-neutral relational database application with a native Cocoa user experience in an afternoon. Any of the countless Java packages and libraries available can be leveraged by your Cocoa Java applications, saving you time and allowing you to concentrate on implementing application-specific functionality. This truly powerful combination of technologies should not be overlooked by anyone considering using Objective-C for their next killer Cocoa app.

Mixing Java and Cocoa

For the purposes of this article, we will be examining some of the code from "Aquataxx," a Cocoa Java implementation of the classic arcade strategy game, "Ataxx," shown in Figure 4. Aquataxx is a comprehensive example of using Cocoa features like brushed metal windows, drawers, sheets, bouncing Dock icons, text editing, tab views, user defaults, localized strings, drag and drop, sound, animation and drawing in custom NSView subclasses. In addition, it sports a game engine with impressive AI, networked game play and messaging, a standalone network "roster server" for finding other online players, and some advanced threading techniques, all written in portable, platform-agnostic Java. The result is a portable implementation of Ataxx with a challenging computer player, a rich, themable Cocoa user experience, network play and chat, and more. Writing the same game in Objective-C would have taken longer than the two weeks it took to write Aquataxx.


Figure 4: Aquataxx, a Cocoa Java game used as an example in this article.

Listing 1 depicts using Cocoa and Java platform classes together to implement the action method that is invoked by the "About Aquataxx" item in the "Aquataxx" application menu. Note that the method is public and a regular java.lang.Object is used for the sender parameter. We splice the Java version from java.lang.System into the version NSTextField and display the game's about sheet.

Listing 1:

A simple Cocoa "action" method, linked from the NIB file in Interface Builder.

/**
      Shows the about sheet 
      @param sender the sender of the message
   **/
   public void showAboutSheet(Object sender)
{        
      /* Set the version string, if need be */
      NSTextField versionField =    
         (NSTextField)mAboutPanel.contentView().
         viewWithTag(1);
         
      String versionString = versionField.stringValue();
   if (versionString.indexOf("{0}") > -1)
   {
      StringBuffer buffy = new 
         StringBuffer(AtaxxApplication.APPLICATION_VERSION);
         buffy.append(" - Java ");
         buffy.append(System.getProperty("java.version"));
         versionString = MessageFormat.format(versionString,
            new Object[] { buffy.toString() });
         versionField.setStringValue(versionString);
   }
        
/* Show the sheet */
   NSApplication.sharedApplication().beginSheet(
      mAboutPanel, mGameWindow, this, null, null);
}

Memory Management and Thread Safety

In most cases, you will not have to explicitly worry about memory management in your Cocoa Java applications. The usual warnings regarding hanging onto references and what not still apply, and Apple has a document that is referenced at the end of this article that explains the exceptions to these general rules. In some cases, to get around complicated interactions between Cocoa and Java objects, you will need to wrap objects in an NSArray; in others, you may have to catch possible NSExceptions that can be thrown by Cocoa object constructors. For the most part, the tricky retain and release memory management conventions used in Objective-C can be avoided; another one of Java's advantages improves the Cocoa development experience!

Java developers are used to satisfying the needs of most modern applications, especially those that use network I/O, with multiple threads. Java encourages the use of multithreading, and Java threads (java.lang.Thread) on Mac OS X are implemented as native Mach threads, so they have excellent performance. One of the inherent pitfalls of multithreaded programming is thread safety, and many aspects of the Cocoa frameworks are not yet guaranteed to be thread safe, meaning you cannot safely call many aspects of Cocoa, including your user interface, from background threads such as one that blocks on incoming network I/O. Since network socket I/O blocks in J2SE 1.3.1, and holding up the main thread in which many aspects of Cocoa including your user interface run in can result in unacceptable performance, a mechanism is needed to enable background threads to tell the main thread to execute code that interacts with Cocoa. NSObject in Objective-C has a method, performSelectorInMainThread, but unfortunately this functionality is not available in Cocoa Java. To get around this, the most popular strategy is to subclass NSApplication and implement sendEvent to handle application-defined events that correspond to blocks of code that need to be called in the main thread. Code that is called from your sendEvent implementation is guaranteed run in the main thread. Failing to observe the fact that many aspects of Cocoa are not thread safe will result in unexpected, random crashes (not exceptions) that can be hard to reproduce and debug and appear to be in Cocoa's Objective-C code. These types of crash logs can be confusing to developers programming in pure Java, yet can often help in tracking down sections of code that should be run in the main thread. Listing 2 is the full source for AtaxxApplication, the custom NSApplication subclass used in Aquataxx to handle events posted by background threads. Listing 3 depicts an example of posting such an event. The full source for Aquataxx is referenced at the end of this article, and can be examined for further examples of using this technique to assure your application plays by proper threading rules.

Listing 2:

AtaxxApplication, an NSApplication subclass that handles application-defined events.

/* AtaxxApplication.java */

import com.apple.cocoa.foundation.*;
import com.apple.cocoa.application.*;

/**
   A NSApplication subclass to add the ability to respond to application-defined events.
 **/

public class AtaxxApplication
   extends NSApplication
{
   /** Application version string **/
   public static final String APPLICATION_VERSION = "1.8";
    
/* Application-defined event types */
   protected static final short NEW_MATCH_EVENT = 0;
   protected static final short SCROLL_CONVERSATION_EVENT = 1;
   protected static final short CONFIRM_CONNECTION_EVENT = 2;
   protected static final short UPDATE_ROSTER_EVENT = 3;
   protected static final short INCOMING_DROPPED_EVENT = 4;
    
/**
   Overridden to respond to custom events.
   @param event the event being processed.
 **/
   public void sendEvent(NSEvent event)
   {
      /* Only worry about application-defined events */
      if (event.type() == NSEvent.ApplicationDefined)
      {
         switch (event.subtype())
         {                
   /* Create a new match */
            case NEW_MATCH_EVENT:
            {
               AtaxxController.sharedController().
                  startNewMatch(event.data1(),
                     event.data2());
               break;
            }
                
   /* Scroll the conversation view */
            case SCROLL_CONVERSATION_EVENT:
            {
               AtaxxController.sharedController().
                  scrollConversationView();
               break;
            }
                    
   /* Confirm a connection request */
            case CONFIRM_CONNECTION_EVENT:
            {
               AtaxxController.sharedController().
                  confirmConnectionRequest();
               break;
            }
                    
   /* Update roster data source */
            case UPDATE_ROSTER_EVENT:
            {
               AtaxxController.sharedController().
                  updateRosterDataSource();
               break;
            }
                    
   /* Close the connection request sheet */
            case INCOMING_DROPPED_EVENT:
            {
               AtaxxController.sharedController().
                  closeAnySheet(null);
               break;
            }
         }
      }
      else
      {
         super.sendEvent(event);
      }
   }
}

Listing 3:

A wrapper method that posts an application-defined event.
      
      /**
         Posts an application-defined event to the current application.
         @param code the event code
         @param data1 the first parameter
         @param data2 the second parameter
         @param front post to the front of the queue?
      **/
      public void postApplicationDefinedEvent(short code,
                                                      int data1,
                                                      int data2,
                                                      boolean front)
      {
         NSEvent event = NSEvent.otherEvent(NSEvent.ApplicationDefined,
            new NSPoint(0, 0), 0, System.currentTimeMillis() / 1000.0, 0,
            null, code, data1, data2);
         NSApplication.sharedApplication().postEvent(event, front);
      }

Implementing Custom Views

One of the more rewarding aspects of Cocoa programming is implementing your own NSView subclasses. NSView is the base class in the Cocoa user interface class hierarchy, and all of the user interface components you see and interact with inevitably inherit from it. If one of Apple's standard views does not suit the type of data or interaction you are trying to represent, as is the case with the game board and player score views in Aquataxx, you should implement a custom NSView subclass. You subclass NSView in the "Classes" tab of your NIB file window in Interface Builder, and create a corresponding Java source file just as you would if you were using Objective-C. Listing 4 is the complete source code for AtaxxScoreView, a custom view that draws both players' names and scores and highlights the active player with an etched appearance that looks similar to iTunes. For a more comprehensive and exciting example of a custom view that handles events and uses optimized drawing and drag and drop, take a look at the source for AtaxxView, which draws the game board and is found in the full source code distribution. Cocoa user interface programming is a lot more fun, intuitive, and rewarding than using "Swing!"

Listing 4:

The complete source for AtaxxScoreView, a custom NSView subclass.
/* AtaxxScoreView.java */
import com.apple.cocoa.foundation.*;
import com.apple.cocoa.application.*;
/**
   A custom NSView subclass that displays an etched
   metallic scoreboard for two players, and highlights
   the player whose turn it is.
 **/
public class AtaxxScoreView
    extends NSView
{
   /* The colors for drawing the scoreboard */
   public static final NSColor FOREGROUND_COLOR =
      NSColor.colorWithCalibratedRGB(.20f, .20f, .20f, 1.0f);
   public static final NSColor BACKGROUND_COLOR = NSColor.whiteColor();
   public static final NSColor FOREGROUND_UP_COLOR = NSColor.blackColor();
   public static final NSColor BACKGROUND_UP_COLOR = NSColor.grayColor();
/* The (four) attributed score strings ((back, front) * 2) */
   private NSMutableAttributedString mScoreStrings[] = null;
    
/**
   Constructor
   @param frame the frame rectangle
 **/
   public AtaxxScoreView(NSRect frame)
   {
      super(frame);
      mScoreStrings = new NSMutableAttributedString[4];
   }
/**
   Tells the window server that we are not opaque (we are transparent).
   @returns false, as we are transparent.
 **/
   public boolean isOpaque()
   {
      return false;
   }
    
/**
   Draws the view
   @param rect the update rectangle
 **/
   public void drawRect(NSRect rect)
   {
      if (mScoreStrings != null)
      {
         NSMutableRect stencil = new NSMutableRect(bounds());
         stencil.insetRect(0.5f, 1.0f);
         stencil.setOrigin(new NSPoint(1.0f, 0.0f));
         NSGraphics.drawAttributedString(mScoreStrings[0], stencil);
         NSGraphics.drawAttributedString(mScoreStrings[2], stencil);
         stencil.setOrigin(new NSPoint(0.0f, 1.0f));
         NSGraphics.drawAttributedString(mScoreStrings[1], stencil);
         NSGraphics.drawAttributedString(mScoreStrings[3], stencil);
      }
   }
/**
   Creates the 4 attributed score strings, based on
   the current score strings and player that is up.
   @param player1 the string for the first player
   @param player2 the string for the second player
   @param up the player that is up (hilighted)
   (0 = none, 1 = player 1, 2 = player 2)
 **/
   public synchronized void update(String player1,
                                          String player2,
                                          int up)
   {
      /* For convenience in looping through the strings */
      String strings[] = { player1, player1, player2, player2 };
      NSMutableAttributedString newStrings[] =
         new NSMutableAttributedString[4];
                
      for (int i = 0; i < 4; i += 2)
      {
         /* Create the pair of attributed strings (foreground and background) */
         newStrings[i] = 
            new NSMutableAttributedString(strings[i]);
         newStrings[i + 1] = 
            new NSMutableAttributedString(strings[i + 1]);
   /* Create a range for the string pair */
         NSRange range = new NSRange(0, strings[i].length());
            
   /* Set the text alignment */
         NSMutableParagraphStyle paragraphStyle =
            new NSMutableParagraphStyle();
         paragraphStyle.setAlignment((i < 2) ? 
            NSText.LeftTextAlignment :
            NSText.RightTextAlignment);
         newStrings[i].addAttributeInRange(
            NSAttributedString.ParagraphStyleAttributeName,
            paragraphStyle, range);
         newStrings[i + 1].addAttributeInRange(
            NSAttributedString.ParagraphStyleAttributeName,
            paragraphStyle, range);
                
   /* Set the font to the system font, size 10 (localization?) */
         NSFont font = NSFont.systemFontOfSize(10);
         newStrings[i].addAttributeInRange(
            NSAttributedString.FontAttributeName,
            font, range);
         newStrings[i + 1].addAttributeInRange(
            NSAttributedString.FontAttributeName,
            font, range);
            
   /* Set the foreground and background colors */
         NSColor foregroundColor = FOREGROUND_COLOR;
         NSColor backgroundColor = BACKGROUND_COLOR;
         if ((i < 2 && up == 1) ||
            (i > 1 && up == 2))
         {
            foregroundColor = FOREGROUND_UP_COLOR;
            backgroundColor = BACKGROUND_UP_COLOR;
         }
         newStrings[i].addAttributeInRange(
            NSAttributedString.ForegroundColorAttributeName,
            backgroundColor, range);
         newStrings[i + 1].addAttributeInRange(
            NSAttributedString.ForegroundColorAttributeName,
            foregroundColor, range);
            
   /* Fix up the attributes, whatever that does */
         newStrings[i].fixAttributesInRange(range);
         newStrings[i + 1].fixAttributesInRange(range);
            
   /* Set the actual attributed score strings */
         mScoreStrings[i] = newStrings[i];
         mScoreStrings[i + 1] = newStrings[i + 1];
      }
      /* Redraw the view! */
      display();
   }
}

Conclusion

Java is an excellent choice for developing Cocoa applications. If you are a Java programmer, are new to Cocoa, or are not interested in learning Objective-C, you should give Cocoa Java a try. The popularity of the language and the sheer quantity of third-party libraries and tools provide Java programmers with an impressive array of pre-engineered solutions over those available in Objective-C. Apple continues to provide Java interfaces to the new features it adds to Cocoa. It's time for developers to take better advantage of this technology and provide Apple with the feedback they need to make Cocoa Java even better. The end result is more great applications for Mac OS X.

Resources

The following resources will help you get started in learning about Cocoa Java programming. Sun has a general Java language tutorial, Apple has a Cocoa Java tutorial, and there are several sample applications installed with Xcode that will prove to be good references. The Aquataxx game distribution and full source code referenced in this article is available, and Apple and The Omni Group have good Cocoa development mailing lists that can be browsed and searched at http://cocoa.mamasam.com. If you have questions that you cannot find the answers to, or would like to discuss Cocoa Java programming in general, you can contact the author at steve@buzzlabs.com.


Steve Klingsporn is an independent software developer living in Chicago with his cat, Sonya. He has an 11 year history of working at companies such as Apple, Netscape, and Sun. He is available for independent Mac OS X, Java and web development, and can be reached at steve@buzzlabs.com.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Seven Knights Idle Adventure drafts in a...
Seven Knights Idle Adventure is opening up more stages, passing the 15k mark, and players may find themselves in need of more help to clear these higher stages. Well, the cavalry has arrived with the introduction of the Legendary Hero Iris, as... | Read more »
AFK Arena celebrates five years of 100 m...
Lilith Games is quite the behemoth when it comes to mobile games, with Rise of Kingdom and Dislyte firmly planting them as a bit name. Also up there is AFK Arena, which is celebrating a double whammy of its 5th anniversary, as well as blazing past... | Read more »
Fallout Shelter pulls in ten times its u...
When the Fallout TV series was announced I, like I assume many others, assumed it was going to be an utter pile of garbage. Well, as we now know that couldn't be further from the truth. It was a smash hit, and this success has of course given the... | Read more »
Recruit two powerful-sounding students t...
I am a fan of anime, and I hear about a lot that comes through, but one that escaped my attention until now is A Certain Scientific Railgun T, and that name is very enticing. If it's new to you too, then players of Blue Archive can get a hands-on... | Read more »
Top Hat Studios unveils a new gameplay t...
There are a lot of big games coming that you might be excited about, but one of those I am most interested in is Athenian Rhapsody because it looks delightfully silly. The developers behind this project, the rather fancy-sounding Top Hat Studios,... | Read more »
Bound through time on the hunt for sneak...
Have you ever sat down and wondered what would happen if Dr Who and Sherlock Holmes went on an adventure? Well, besides probably being the best mash-up of English fiction, you'd get the Hidden Through Time series, and now Rogueside has announced... | Read more »
The secrets of Penacony might soon come...
Version 2.2 of Honkai: Star Rail is on the horizon and brings the culmination of the Penacony adventure after quite the escalation in the latest story quests. To help you through this new expansion is the introduction of two powerful new... | Read more »
The Legend of Heroes: Trails of Cold Ste...
I adore game series that have connecting lore and stories, which of course means the Legend of Heroes is very dear to me, Trails lore has been building for two decades. Excitedly, the next stage is upon us as Userjoy has announced the upcoming... | Read more »
Go from lowly lizard to wicked Wyvern in...
Do you like questing, and do you like dragons? If not then boy is this not the announcement for you, as Loongcheer Game has unveiled Quest Dragon: Idle Mobile Game. Yes, it is amazing Square Enix hasn’t sued them for copyright infringement, but... | Read more »
Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »

Price Scanner via MacPrices.net

Apple introduces the new M4-powered 11-inch a...
Today, Apple revealed the new 2024 M4 iPad Pro series, boasting a surprisingly thin and light design that pushes the boundaries of portability and performance. Offered in silver and space black... Read more
Apple introduces the new 2024 11-inch and 13-...
Apple has unveiled the revamped 11-inch and brand-new 13-inch iPad Air models, upgraded with the M2 chip. Marking the first time it’s offered in two sizes, the 11-inch iPad Air retains its super-... Read more
Apple discontinues 9th-gen iPad, drops prices...
With today’s introduction of the new 2024 iPad Airs and iPad Pros, Apple has (finally) discontinued the older 9th-generation iPad with a home button. In response, they also dropped prices on 10th-... Read more
Apple AirPods on sale for record-low prices t...
Best Buy has Apple AirPods on sale for record-low prices today starting at only $79. Buy online and choose free shipping or free local store pickup (if available). Sale price for online orders only,... Read more
13-inch M3 MacBook Airs on sale for $100 off...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices, along with Amazon’s, are the lowest currently available for new 13″... Read more
Amazon is offering a $100 discount on every 1...
Amazon has every configuration and color of Apple’s 13″ M3 MacBook Air on sale for $100 off MSRP, now starting at $999 shipped. Shipping is free: – 13″ MacBook Air (8GB RAM/256GB SSD): $999 $100 off... Read more
Sunday Sale: Take $150 off every 15-inch M3 M...
Amazon is now offering a $150 discount on every configuration and color of Apple’s M3-powered 15″ MacBook Airs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 15″ M3 MacBook... Read more
Apple’s 24-inch M3 iMacs are on sale for $150...
Amazon is offering a $150 discount on Apple’s new M3-powered 24″ iMacs. Prices start at $1149 for models with 8GB of RAM and 256GB of storage: – 24″ M3 iMac/8-core GPU/8GB/256GB: $1149.99, $150 off... Read more
Verizon has Apple AirPods on sale this weeken...
Verizon has Apple AirPods on sale for up to 31% off MSRP on their online store this weekend. Their prices are the lowest price available for AirPods from any Apple retailer. Verizon service is not... Read more
Apple has 15-inch M2 MacBook Airs available s...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs available starting at $1019 and ranging up to $300 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at Apple.... Read more

Jobs Board

Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
*Apple* App Developer - Datrose (United Stat...
…year experiencein programming and have computer knowledge with SWIFT. Job Responsibilites: Apple App Developer is expected to support essential tasks for the RxASL Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple 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.