TweetFollow Us on Twitter

Dec 01 REALbasic

Volume Number: 17 (2001)
Issue Number: 12
Column Tag: Declaring External Functions

by Joe Strout

Declaring External Functions in REALbasic

Harness the power of toolbox calls without resorting to C

Declaring Your Intentions

REALbasic’s extensive library floats on top of the sea of low-level functions that comprise the operating system like a cruise ship, protecting you from the dangers that lurk below. This is a two-sided coin; it frees you to concentrate on what you want to accomplish, rather than the details of how it’s done on this or that platform, but it also places limits on what you can do. When you hit one of those limits, and need to accomplish something not directly supported by the REALbasic library, then you’re going to have to get wet.

But even then, you have a choice. You could write a plug-in in C++, and link this plug-in into your REALbasic application [reference - Erik’s article on plugin dev]. That gives you full power to do pretty much anything, but it also requires quite a bit of learning, especially for developers not already familiar with C++. The other option is to use the Declare statement, which makes a function in REALbasic that links to a function in the operating system or a shared library. This is done entirely within REALbasic; no C++ is involved. To extend the ocean analogy a just bit further: writing a plug-in is like diving into the ocean untethered, while using a Declare is like hanging onto a life preserver on a rope — there are still restrictions on what you can do, but it’s also a lot easier to avoid drowning.

Though the Declare statement is used entirely within REALbasic, it’s still an advanced feature and a source of difficulty for many experienced REALbasic coders. This article will attempt to clarify and illustrate its use. After reading this article, you’ll be able to declare calls to classic MacOS, Carbon, or Win32 system routines, as well as to other shared libraries. This gives you the power to do a wide range of things not otherwise possible in REALbasic.

The Declare Statement: A Close Look

With no further ado, let’s jump right into an example. Listing 1 shows a REALbasic function that toggles the “modified” flag of a window. (Under Mac OS X, this sets or clears the little dot in the red Close widget in the title bar.) Since the functions involved are only available in MacOS 8.5 and higher, it also checks the system version before attempting to call them.

Listing 1: ToggleModified

ToggleModified

Toggle the “Modified” flag of the given REALbasic window.  This subroutine requires MacOS 8.5 or higher,
and does nothing under older versions of MacOS.  It will not compile as-is for 68k (these functions are 
not available to 68k apps).

Sub ToggleModified(win As Window)
   // Toggle the given window’s Modified flag
   Dim sysVersion, err As Integer

   #if TargetCarbon
   Declare Function IsWindowModified Lib “CarbonLib” (window 
         as WindowPtr) as Boolean
   Declare Function SetWindowModified Lib “CarbonLib” (window 
         as WindowPtr, modified as Boolean) as Integer
   #else
   Declare Function IsWindowModified Lib “WindowsLib” (window 
         as WindowPtr) as Boolean
   Declare Function SetWindowModified Lib “WindowsLib” (window 
         as WindowPtr, modified as Boolean) as Integer
   #endif

   // check the system version, since these functions are only available
   // in MacOS 8.5 and higher
   if System.Gestalt(“sysv”, sysVersion) and sysVersion >= 
            &h0850 then
      // system version is OK, so do the toggle
      err = SetWindowModified(win, not IsWindowModified(win))
    end if
  end if
end Sub

There are several things to notice in this example. First, if we want to compile for both “classic” Macintosh and for Carbon, we need two versions of each Declare — one linking against CarbonLib, and the other against the appropriate toolbox library (often InterfaceLib, but WindowsLib in this case). Next, built-in REALbasic functions generally do the right thing for any version of MacOS your code may be running on, but it can’t do so with a Declare; it’s your responsibility to make sure that the function you’re calling will be available. In this case, that means checking that the system version is at least 8.5. But once you’ve done all this, using the function you’ve declared is easy — just call it like any ordinary REALbasic method.

Let’s look at the Declare statements more closely. First, note that the declaration occurs within some other REALbasic method (subroutine or function). Just like a local variable, these declarations are local to where they’re declared; they will not be visible outside of this method. There is currently no way to make such a declaration global, though you could make a global wrapper function (as we’ve essentially done in this example).

The declaration always begins with the keyword “Declare” followed by either “Function” or “Sub”; REALbasic doesn’t actually care which you use, but by convention you use “Sub” when the C return type is void. Next is the name of the function, exactly as it appears in the external library. This is also the name used to invoke the function, unless you specify an “Alias” clause (which we’ll cover a bit later).

The next part of the declaration is the library name. This is the fragment name as it appears to the Code Fragment Manager, not the file name as it appears in the Finder; and unlike most words in REALbasic, this name is case sensitive. Finding this name is not hard, but we’ll come back to it a bit later.

Next come the function parameters, and finally the return type. This is where things get interesting. The documentation or header files on which you’re basing your Declare probably specify the parameter and return types with C syntax, rather than REALbasic. It may say something like “char *” or “Str255” or something unpalatable. What to do?

The Right Type for the Job

Most of the work in creating a Declare statement is in finding the right conversions between the C types you see in the headers and documentation, and the REALbasic types that serve the same purpose. Fortunately you have this article, which contains the very handy Table 1. This shows you some of the most common C types, and the corresponding REALbasic types you should use in a Declare statement.

Table 1: C types and REALbasic
equivalents for Declare statements

C data type Declare type Variable Type
Boolean Boolean Boolean
long Integer, OSType, Color Integer, String, Color
int
UInt32
SInt32
void*
Ptr
(etc.)
OSType OSType (parameters only) String
ResType
DescType
short Short Integer
unsigned short
UInt16
SInt16
float Single Single
double Double Double
Str255 PString String
Str63
Str31
unsigned char *
char * CString (parameters only) String
WindowPtr WindowPtr Window
WindowRef (parameters only)
void* Ptr MemoryBlock
Ptr
(or pointer to data structure)

The first column of this table is the data type the function is expecting, as you might see it in the documentation or the C header file. The middle column shows the corresponding types you could use in the Declare statement. Finally, the last column shows the “natural” REALbasic types which most closely match the declared types; these are what you would pass (or get as a result) when calling the function.

REALbasic uses the type you specify in the Declare to figure out how much data to push onto the call stack when making the call; so the most important thing is to get the size right. If the function is expecting any 4-byte value, you can probably declare this as type Integer. If you declare it as Short instead, REALbasic (when compiling for 68k processors) will push only two bytes, the function will try to grab four, and you’ll almost certainly crash.

Many of the type choices are very straightforward; if the function wants a float, declare it as a Single and pass it a number. If the function is expecting a Pascal string, declare it as type PString, and when you invoke the function, give it any ordinary string. REALbasic will automatically convert its internal string representation into a Str255 when calling the function (but note: if the function expects a shorter string than that, it’s up to you to make sure your string isn’t too long before making the call). Or if the return type is a Pascal string, the returned value will be automatically converted into a normal REALbasic string.

With other types, it’s not so clear cut. Suppose the function expects a pointer. Should you declare this as type Integer, or type Ptr? The answer depends on the details. If it’s a function that allocates some data structure, returns you a pointer to it, and you simply pass it back to other functions without trying to peek at the data, then an Integer will do fine. REALbasic integers are four bytes, and a pointer is four bytes, so treating the pointer as if it were just a number will make life easiest for you. Most modern Apple toolbox calls, such as those in Carbon or QuickDraw 3D, are of this sort, since the underlying data structures are opaque.

If, however, the function is expecting you to allocate storage for some data before calling the function, then a bit more work is required. In this case, declare the parameter as type Ptr. Allocate the memory in the form of a REALbasic MemoryBlock, created with the NewMemoryBlock function (and be sure to make it the right size!). If the function is expecting the data structure to be initialized with some values, you can use the MemoryBlock methods to stuff appropriate values into it. Then, pass this MemoryBlock to the function. From the function’s point of view, it just gets a pointer to some already-allocated memory. It may stuff values into this area, which you can later read by using those MemoryBlock methods again.

Calling a Spade a Spade

Your Declare statement must name the shared library which implements the function. In the case of a Windows DLL, you simply use the file name. In the Mac world, we don’t rely on file names, which are too frequently changed to be reliable. So you must use the fragment name as seen by the Code Fragment Manager (CFM). Most of the MacOS toolbox is in the fragment “InterfaceLib” (or “CarbonLib” for Carbon/OS X apps). But suppose you’re calling some other library — for example, the “Quesa” high-level 3D graphics library. The CFM name of this library is “QuickDraw™ 3D” (because it’s a binary-compatible replacement for Apple’s QuickDraw 3D technology). If you don’t happen to have me around to tell you handy facts like this, how would you know?

If you have ResEdit handy, it can give you the answer. Drop the library onto ResEdit and open the “cfrg” (code fragment) resource. This gives you a list of code fragments — usually there’s only one — and the name you’re looking for is labeled “Member Name”. Copy that value, and paste it into your Declare statement as the library name.

You can also use PEF Viewer, a free little utility from Apple. It also lists the code fragments it finds, and even lets you peek into the code, data, and loader information for each. (The last can be handy when Carbonizing — if the shared library you’re using imports any symbols from anywhere other than CarbonLib, you probably can’t use it in a Carbon app.) The CFM name of the library is given right at the top, next to the disclosure triangle.

If you don’t have either of these tools handy, you can use REALbasic itself. Just launch REALbasic, and drag the shared library file into the project window. It will appear there with its CFM name, rather than its file name. If you’re curious, what you have there is the alternative interface to importing functions from shared libraries — useful if you need a large number of functions from a single shared library, and you want them to be global rather than local. Since you’re using Declares rather than importing the library, you can just make note of the name, then delete the library from your project.

TBFinder to the Rescue

Writing Declares has always been a bit of an arcane art. Even armed with the knowledge in this article, you’ll find yourself wondering if it’s really worth the trouble when you just need a quick Mac toolbox call. Fortunately, there is a tool which can take much of the guess-work out of writing a toolbox-related Declare.

TBFinder is an open-source REALbasic project coordinated by Fabian Lidman. First, you point it to your copy of Universal Headers — i.e., Apple’s toolbox interface files, which you can download from Apple’s Development Kits web site. Then you can just find the toolbox call you need by searching or browsing, and it writes the Declare statement for you. There are some caveats, but in many cases it’s as simple as that.

TBFinder’s search window — what you see when you launch the utility — is shown in Figure 1. If you don’t know the name of the call you’re looking for, click “Browse” to get a nice hierarchical list of the functions defined in all the interface files, then just double-click the one you want. If you do know the name of the function you need, type it in the edit field; if you happen to know the header in which it’s declared, you can check the “Look first in:” checkbox and choose the appropriate header from the pop-up menu. Then click Search.


Figure 1. TBFinder search window.

The result, assuming the function actually exists, is a window like that shown in Figure 2.


Figure 2. TBFinder result window.

TBFinder’s search window — what you see when you launch the utility — is shown in Figure 2. The key bit is the second section, where the REALbasic syntax is shown. You can either use the Copy command to move this text to the clipboard, or drag the little text-clipping icon directly into REALbasic. The other icons in the window are actually buttons that do things when you click on them: the upper left icon by the header file name reveals that header file in the Finder; the text file icon by the original C declaration opens the header file (careful — Apple’s headers are set to open in Macintosh Programmer’s Workshop); and the globe at upper right does a web search for the function name in Apple’s documentation.

At the bottom of the window, you’ll notice a handy note telling you whether or not the function appears to be Carbon-compatible. If it is, you can use it in a Carbon application by simply changing the library name to “CarbonLib”. In the case of a classic application, TBFinder correctly identifies the library for you, using a big internal look-up table which I fervently hope was generated automatically by parsing the various system libraries.

TBFinder also supports one other feature of the Declare that I not previously mentioned: the “Inline68K” block. Many Mac toolbox calls on 68k machines are not shared library calls at all, but rather direct jumps to system traps. The C headers give the necessary machine language glue with the xWORDINLINE macros; TBFinder converts this to RB syntax, and even appends the extra glue needed in some cases by RB itself. (Be sure to get the latest version of TBFinder; version 2.0, included on the REALbasic CD, does not do this correctly.)

Finally, note that while TBFinder is invaluable, it isn’t infallible. In particular, it takes its best guess at the appropriate parameter conversions, but these often require human judgment (see Table 1 again). Always double-check the parameter types TBFinder has chosen, and consider whether there are other choices that make more sense for you.

Some Examples

Now that we’ve covered all the basics, let’s look at some real examples. We already saw one in Listing 1, used to toggle the “modified” flag on a window. Listing 2 shows another example, a call to the toolbox routine ObscureCursor, which hides the cursor until the mouse is moved. As this routine takes no parameters and returns no result, the declaration is very straightforward.

Listing 2: ObscureCursor

ObscureCursor
Hide the cursor until the mouse is moved (MacOS only).

Sub ObscureCursor()
   {
  // Declare it with a “Sys” prefix to avoid a name clash
   #if TargetCarbon
  Declare Sub SysObscureCursor Lib “CarbonLib” Alias 
            “ObscureCursor” ()
  #else
  Declare Sub SysObscureCursor Lib “InterfaceLib” Alias 
            “ObscureCursor” () Inline68K(“A856”)
  #endif
  
  // Call it!
  SysObscureCursor
end Sub

Note that in this example, I’ve chosen to name the REALbasic subroutine exactly like the toolbox routine. That means that when I declare the toolbox call within the subroutine, I’d have a name clash unless I use the “Alias” option. “Alias” allows me to declare the system routine as SysObscureCursor, avoiding thus avoiding the clash, and allowing all other users of this subroutine to simply call “ObscureCursor”.

Naturally, if you needed to use HideCursor/ShowCursor to hide the mouse even when it moves — say, because you’re making a game and have a custom cursor or no need for a cursor at all — then the declarations would be very similar. But let’s suppose you need to hide the cursor only within a rectangular region of the screen, perhaps because you’re doing some animation there, or simply because you want to demonstrate a Declare involving a MemoryBlock. The toolbox call for this is ShieldCursor. TBFinder gives the declaration as:

Declare Sub ShieldCursor Lib “InterfaceLib” (shieldRect as Ptr, offsetPt as Point) Inline68K(“A855”)

Apple’s documentation gives the first parameter as a Rect pointer, which TBFinder has rendered as type Ptr. From Table 1, you know that a MemoryBlock is needed for times like this. We use the MemoryBlock to manually construct a Rect structure, recalling that a Rect is just a set of four short integers representing top, left, bottom, and right

Dim m As MemoryBlock

m = NewMemoryBlock(8) // 8 bytes (four 2-byte ints)
m.Short(0) = r.top
m.Short(2) = r.left
m.Short(4) = r.top + r.height
m.Short(6) = r.left + r.width

We can then pass this as the first parameter to ShieldCursor. What about the second parameter, “offsetPt”? Again referring to Apple’s documentation, we find that this is the offset from the global coordinate system. In other words, it’s the top-left corner of the window containing the rectangle to which we’re referring. TBFinder has left this declared as “Point”, which is not a valid Declare parameter type. But since a Point is the same size as an Integer, we can fairly easily construct an integer that contains the information we need:

Dim point As Integer

// put pt.v into the high word, pt.h into the low word:
point = BitwiseAnd(self.top * 65536, &HFFFF0000)
point = BitwiseOr(point, BitwiseAnd(self.left, &H0000FFFF))

Then, we simply change the Declare statement so that the second parameter is of type Integer, and we’re ready to go. The full listing for the ShieldCursor function is shown in Listing 3.

Listing 3: ShieldCursor

ShieldCursor
Hide the cursor within the given rectangle control.

Sub ShieldCursor(r As RectControl)
     Declare Sub SysShieldCursor Lib “InterfaceLib” Alias “ShieldCursor” (shieldRect as Ptr, offsetPt 
     as Integer) Inline68K(“A855”)
  
  Dim m As MemoryBlock
  Dim point As Integer
  
  // construct the rect within which the cursor will be shielded
  m = NewMemoryBlock(8)  // 8 bytes (four 2-byte ints)
  m.Short(0) = r.top
  m.Short(2) = r.left
  m.Short(4) = r.top + r.height
  m.Short(6) = r.left + r.width
  
  // construct the point that converts global to local coordinates
  point = -self.top * 65536 - self.left   
                           // pt.v in the high word, pt.h in the low word
  
  // make the call
  SysShieldCursor m,point
End Sub

Let’s end with a Windows declaration. I know, this is a Mac programming magazine, but sometimes you need to deploy your app for that Other Platform — and the ability to do so easily is one of REALbasic’s strengths. However, with the Declare statement, you’re making a direct call to a toolbox or shared library routine; this is rarely portable. So the first thing you’ll need to do, is conditionalize your code so that Mac declares are included only in Mac builds, and Windows declares are included only in Windows builds. You can use either “#if TargetMacOS” or “#if TargetWin32” for this purpose.

Next, you’ll need information on the Windows API. Two good references are given at the end of this article.

Finally, you’ll need to construct the Declare statement by hand. TBFinder can’t help you here; but with this article on your shelf, you’re well-equipped for the task. So let’s dive into an example.

Windows applications can take command-line parameters, and these are often used to specify options or initial conditions for the application. REALbasic can get these one at a time via the OpenDocument event, but in many cases it’d be preferable to get the whole command line. A search on the MSDN Online Library quickly leads to the GetCommandLine function, declared in the documentation as:

LPTSTR GetCommandLine(VOID);

This function takes no parameters, making our lives easy in that respect. And the return type, it turns out, is a C string of either ASCII or UniCode characters. This is an important point: many Windows functions come in both ASCII and UniCode versions, as signified under “Requirements” with a note such as “Unicode: Implemented as Unicode and ANSI versions on all platforms.” When you see this, it means that the function name given is not the real function name — you need to append “A” for the ASCII version, or “W” for the UniCode version. So the actual function name, for our purposes, is GetCommandLineA.

The return type in this case is an ordinary C string. But, while REALbasic knows how to automatically convert its String type into a CString for use as a parameter, it can’t do the opposite conversion. So we need another approach. A C string is really just a pointer to some data, so we can declare the return type as “Ptr”, and use a MemoryBlock to get at the data. The result is shown in Listing 4.

Listing 4: GetCommandLine

GetCommandLine
Get the full Win32 command line, including executable name and parameters.

Function GetCommandLine()
  #If TargetWin32
  Dim m as MemoryBlock
  Declare Function GetCommandLineA Lib “KERNEL32.DLL” () as Ptr
  m = GetCommandLineA()
  Return m.cstring(0)   // get the C string to which m points
  #Else
  return “Command line available on Win32 only.”
  #EndIf
End Function

There is one other trick that can sometimes make Windows declares easier. If you can find a web site or other documentation with Declare statements for Visual Basic — such as the last reference given at the end of this article — then you can use those Declare statements in REALbasic almost verbatim. You’ll just need to make a few changes: the “ByVal” keyword can be omitted, and data types may have to be translated according to Table 1 (e.g., use “Integer” where VB says “Long”).

Conclusion

REALbasic tries to abstract away the details of the underlying operating system, so you don’t have to worry about whether you’re running on classic MacOS, OS X, or some flavor of Windows. But sometimes you need more control. You may need to talk directly with the OS, or you may need access to some other shared library (such as OpenGL, for example). In those cases, the Declare statement is invaluable.

The Declare statement allows you to do most of the same things you could do directly in C. There are some limitations; you can’t call a function that requires a callback, and you can’t directly invoke a function pointer (e.g., to call a mach-o entry point loaded via the CFBundle API). For those, you’ll still need to write a plug-in. But for most other needs, you can Declare your external calls, providing a neater, more integrated solution.

While Declare gives you nearly the power of C, it gives you nearly the complexity of C as well. Writing a Declare statement is error-prone, and if done incorrectly, can crash your program (and under Classic MacOS, the system as well). Deciphering the headers and documentation for the calls you’re making requires some understanding of how things work in C or Pascal. But, with the tips gained here and a bit of study and experimentation, there will soon be little you can’t do in REALbasic.

References

REALbasic Home Page
http://www.realsoftware.com/realbasic

TBFinder Home Page
http://homepage.mac.com/fabianl/development/

Apple’s Developer Documentation
http://developer.apple.com/techpubs/

Apple’s Software Development Kits
http://developer.apple.com/sdk/

Microsoft’s Official SDK Documentation
http://msdn.microsoft.com/library/

Unofficial Windows API Guide
http://www.vbapi.com/ref/index.html


Joe Strout is a neuroscientist-turned-software engineer currently working for REAL Software, Inc. You can reach him at joe@strout.net.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Victorious Knight (Games)
Victorious Knight 1.3 Device: iOS Universal Category: Games Price: $1.99, Version: 1.3 (iTunes) Description: New challenges awaits you! Experience fresh RPG experience with a unique combat mechanic, packed with high quality 3D... | Read more »
Agent Gumball - Roguelike Spy Game (Gam...
Agent Gumball - Roguelike Spy Game 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: Someone’s been spying on Gumball. What the what?! Two can play at that game! GO UNDERCOVERSneak past enemy... | Read more »
Runaway Toad (Games)
Runaway Toad 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: It ain’t easy bein’ green! Tap, hold, and swipe to help Toad hop to safety in this gorgeous new action game from the creators of... | Read more »
PsyCard (Games)
PsyCard 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: From the makers och Card City Nights, Progress To 100 and Ittle Dew PSYCARD is a minesweeper-like game set in a cozy cyberpunk... | Read more »
Sago Mini Robot Party (Education)
Sago Mini Robot Party 1.0 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0 (iTunes) Description: -- Children's Technology Review Editor's Choice -- | Read more »
How to get a high score in every level o...
Sky Charms is an adorable match three puzzler that provides a decent challenge thanks to its creative level design. It regularly presents something new, forcing you to think on your feet. [Read more] | Read more »
Apestorm: Full Bananas (Games)
Apestorm: Full Bananas 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: ***Launch sale – limited time only!*** Fugitive Apes have taken to the skies in search of revenge after humans have... | Read more »
How to create bigger words in Spellspire
Words have power. At least they do in Spellspire,a game about blasting out magical attacks by making words out of a jumble of letters. And it's a lot of fun. But if you want to be the best, you're going to have to think tactically when you start... | Read more »
Steel Media and DeePoon have partnered f...
Virtual reality is the next big thing, and 148Apps's publisher,Steel Media, wants to know what the hottest upcoming games are. [Read more] | Read more »
Airline Director 2 - Tycoon Game (Games...
Airline Director 2 - Tycoon Game 1.2.1 Device: iOS Universal Category: Games Price: $2.99, Version: 1.2.1 (iTunes) Description: Airline Director 2 is a management game set in the challenging field of commercial aviation. As the... | Read more »

Price Scanner via MacPrices.net

Aleratec Releases Mac Software Upgrade for 1...
California based Aleratec Inc., designer, developer and manufacturer of Portable Device Management (PDM) charge/sync products for mobile devices and professional-grade duplicators for hard disk... Read more
Sale! Amazon offers 27-inch iMac, 13-inch 2.9...
Amazon has the 27″ 3.2GHz 5K iMac and the 13″ 3.9GHz Retina MacBook Pro on sale for $300 off MSRP, each including free shipping, for a limited time: - 27″ 3.2GHz/1TB HD 5K iMac (model MK462LL/A): $... Read more
Apple refurbished 13-inch Retina MacBook Pros...
Apple has Certified Refurbished 13″ Retina MacBook Pros available for up to $270 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 13″ 2.7GHz... Read more
13-inch 2.7GHz/128GB Retina MacBook Pro on sa...
Take $200 off MSRP on the price of a new 13″ 2.7GHz/128GB Retina MacBook Pro (model MF839LL/A) at Amazon. Shipping is free: - 13″ 2.7GHz/128GB Retina MacBook Pro: $1099.99 $200 off MSRP Act now if... Read more
Apple refurbished clearance 15-inch Retina Ma...
Apple has Certified Refurbished 2014 15″ 2.2GHz Retina MacBook Pros available for $1609, $390 off original MSRP. Apple’s one-year warranty is included, and shipping is free. They have refurbished 15... Read more
27-inch 5K iMacs on sale for up to $150 off M...
B&H Photo has 27″ 5K iMacs on sale for up to $150 off MSRP including free shipping plus NY sales tax only: - 27″ 3.3GHz iMac 5K: $2199 $100 off MSRP - 27″ 3.2GHz/1TB Fusion iMac 5K: $1849.99 $150... Read more
What Does The Refreshed 12-Inch MacBook Tell...
A lot of commentators are complaining that Apple’s update of the 12-Inch MacBook last week is a bit of a damp squib. I don’t know what they were expecting, since it would be very unlike Apple to do a... Read more
Free Wittify Keyboard Now Available On The Ap...
A team of Harvard Business School students have announced that the Wittify Keyboard, a new app utility for iOS devices, is now available on the Apple App Store. The Wittify keyboard and application... Read more
Apple Reports First Year-Over-Year Quarterly...
Apple on TUesday announced financial results for its fiscal 2016 second quarter ending March 26, 2016. The Company posted quarterly revenue of $50.6 billion and quarterly net income of $10.5 billion... Read more
13-inch 2.7GHz Retina MacBook Pros on sale fo...
Take $130-$150 off MSRP on the price of a new 13″ 2.7GHz Retina MacBook Pro at Amazon. Shipping is free: - 13″ 2.7GHz/128GB Retina MacBook Pro: $1169 $130 off MSRP - 13″ 2.7GHz/256GB Retina MacBook... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
*Apple* Solutions Consultant - … (United Sta...
Job Summary As an Apple Solutions Consultant, you'll be the link between our future customers and our products. You'll showcase your entrepreneurial spirit as you Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.