TweetFollow Us on Twitter

MacTech Magazine Article Archives

Volume Number: 23 (2007)
Issue Number: 07
Column Tag: The Patch Panel

AppleScript and Apple Remote Desktop

Automating an automation tool

by John C. Welch

Welcome

Ah, back again; into the breach, and this time, we look at two of my favorite tools, AppleScript and Apple Remote Desktop. I am not exaggerating in the least when I say that the day I found out that Apple Remote Desktop is scriptable, was the day that Apple Remote Desktop finally became, in my eyes, a 'real' sysadmin tool. Why? Well, the best sysadmins are lazy. We don't like doing monkeywork, especially repetitive monkeywork; hence our love for scripting. We also realize that the more manual a process is, the more prone to error it becomes. Even if the script is buggy, a script is going to do a task the same way every time. The more we can script repetitive tasks, the more time we have for work that requires a human, and the fewer errors said tasks will have.

The Basics

To look at AppleScript in Apple Remote Desktop, we first have to see what we can and cannot do with Apple Remote Desktop's scripting implementation. Once that's done, we can we can get to work. As it turns out, it's simpler to just categorize what you can't script, and then you know you can script everything else.

You can't directly set Apple Remote Desktop preferences, you can't authenticate new machines into Apple Remote Desktop, you can't script reports, and most computer properties are read—only directly via AppleScript. Note that you can set quite a few properties via the systemsetup command and other command line applications, and you can use AppleScript to do this, but Apple Remote Desktop's dictionary doesn't yet offer you a way to say, change the IP address of a computer.

So, what can you do then? Well, in three words: Tasks, Tasks, and Tasks. You can also modify computer lists, i.e. create them, add computers to them, etc. But the big thing is tasks. There are something like 23 separate tasks that you can run within Apple Remote Desktop, so even with the limitations, it's not useless at all. I have to give kudos to the Apple Remote Desktop team for giving us a more than usable dictionary in the first implementation of AppleScript in Apple Remote Desktop. It is much better than some of the other first implementations I've seen, like Keynote or Pages. It's not everything, but it's a great first step.

The Dictionary and Usage issues

So, with the limitations in mind, how is the dictionary when you're working with it? Not bad at all. There are some issues, such as some really stilted class names. Every task class name ends with "task"; so when you're doing a lot of work, you get things like "set theTask to make new set network startup disk task..." It's not a huge issue, but it's tedious after a while, and you start thinking about old "Department of Redundancies Department" jokes. The other annoyance here is that you have to make the task then execute the task in a separate command. It would be much nicer to be able to just build and execute the task in one line. (Yes, I know, it should be theoretically possible, but the truth is, I've never had it work right in one statement.)

If you are doing a "copy items task" or "copy to me task", (I think that splitting this into two statements was kind of silly, but to not give them more similar names, i.e. "copy items to me task" is even sillier. Consistency is important, even to sysadmins.), and you want to display a list of the "standard" destinations, such as /Applications, et al., you get instead a list of the 4-character AppleScript codes for them. There are a number of reasons for this, but in any event, it means quite a bit more work if you want to put it in front of someone who is a newbie in the sysadmin or Apple arenas.

Speaking from a stricter POV, there are a lot of things in the Apple Remote Desktop dictionary that could have been done better, and hopefully, as items are added to it, those will be improved. But for now, what you have is not a sharp stick in the eye.

Scripting Apple Remote Desktop

So with all that in mind, let's look at what scripting Apple Remote Desktop is like. First, remember that other than list settings, everything you do is going to be a "build task/execute task" pair— a tedious process, but not evilly so.

One of the first things I've done, due to my admittedly abnormal workflow which involves far more system rebuilding than I'd like, is to simplify the setup of various computer lists that I know I'll use all the time. First, I create the lists:

tell application "Remote Desktop"
   set theComputerList to {"President's Office", "Communications",¬
"Print Shop", "Home", "Consulting", "Admin Macs", "Virex Updates"} --source list for computer lists repeat with x in theComputerList make new computer list with properties {name:x} --iterate -- through the list end repeat end tell

Six lines, but it sets up my standard lists rather nicely. Since a few of my Apple Remote Desktop scripts are designed to run against hardcoded lists, making sure these lists always have the same name is critical. (Yes, I know, hardcoding bad. However, there's a bug in the Apple Remote Desktop AppleScript implementation where you can't just get the selected list. You can get the selected computers IN the list, but not the selected list itself). Once that's done, we add computers to the list(s):

set theListofLists to {} 
--I like to make sure of things 

tell application "Remote Desktop"
	set theListofLists to name of every computer list 
	--get all existing lists
	set theDestinationLists to choose from list theListofLists with title¬
"Computer Lists" with prompt "Pick the computer list(s) you want the¬
selected computers to be members of" with multiple selections ¬
allowed --I have quite a few computers in multiple lists, so this lets me do -- that if I wish try --workaround for no choice if (length of theDestinationLists) ≠ 0 then --if there's no choice in -- choose list, this actually errors out silently if (length of theDestinationLists) = 1 then --if there's only -- one destination set theComputers to the selection --get the list of --selections if (length of theComputers) > 0 then --no empty -- selections (REMEMBER ABOUT ARD AND --THE CONSTANT SELECTION) set theDestination to item 1 of theDestinationLists --get the destination list name repeat with x in theComputers add x to computer list theDestination --add a -- computer to the list. We have to use the keyword because -- of how choose list works end repeat end if else --multiple destination lists set theComputers to the selection --get the list of -- selections if (length of theComputers) > 0 then --no empty -- selections (REMEMBER ABOUT ARD AND THE -- CONSTANT SELECTION) repeat with x in theDestinationLists --outer loop repeat with y in theComputers --inner loop add y to computer list x --add the --computer to the list end repeat end repeat end if end if end if end try end tell

This one is a bit more complicated, but only due to the logic in checking for 0, 1 or more than one computer or destination list selected. The reason I have "REMEMBER ABOUT ARD AND THE CONSTANT SELECTION" in many of the comments is that in Apple Remote Desktop, it's impossible to have no computers selected in any list. There is always at least one selected computer. I consider this a bug, you may not, but rather than assume Apple Remote Desktop will have this behavior until the end of time, I'm proactive about handling zeros just in case ARD gets fixed.

Now we have our lists set up. What's next? Well, we make sure that they're up to date. Now there're two ways to do this. The brute force way is to just select the computers and run all the updates. In ARD, since it doesn't yet support running Software Update in the GUI, you'd either have to run this via a Unix command, or via AppleScript:

tell application "Remote Desktop"
   set theScript to "softwareupdate -ia" --build the command for the
   -- Unix task
   set theTask to make new send unix command task with properties¬
{showing output:true, user:"root", script:theScript} --build the task set theClients to the selection if (length of theClients) = 0 then --no sense in whining, and with -- the way Apple Remote Desktop works, I'm not sure this condition -- is possible, so an empty selection fails silently else if (length of theClients) = 1 then try display dialog "By default, ARD always selects the topmost¬
computer in a list. If this is the computer you want to upgrade, click¬
the \"Okay\" button" --This is a warning I put in, because ARD *always* selects -- the first client in a computer list, so while that avoids --a problem with an empty selection, it can create a --problem if you don't notice that the wrong computer is --selected --I only ask this if a single computer is selected set theTaskResult to execute theTask on theClients --run the -- task, save the results end try else if (length of theClients) > 1 then set theTaskResult to execute theTask on theClients --run the -- task, save the results --I use an else if, because I don't want any default that would --run the task outside of the specific desired situation end if end tell

This is functionally quite simple. Build the Unix command, "softwareupdate —ia" to install all available updates, build the "send unix command task" that uses that command, then run it on the selected systems. Note, you could code this to run on every computer in a given list, etc. One thing to note here: Apple Remote Desktop lets you run remote Unix commands as root without additional authentication. If you can run Apple Remote Desktop, you can run commands on any client that copy of the Apple Remote Desktop administrator console has access too. This is convenient, but can be dangerous too, so be careful. Note that it's not limited to root. You can run this as any valid user.

Because Apple Remote Desktop always selects one computer, I put in a warning when you run it against one computer, just in case the default selection is a computer you didn't want to run this on. Also note that you can save the results of "the send unix command task". This comes in handy if you need to use those results in your script.

But what if we don't want to install every update blindly? Well, then we'd need to get a listing of the updates we want to install:

tell application "Remote Desktop"
   set theScript to "softwareupdate -l" --build the command for the
   -- Unix task
   set theTask to make new send unix command task with properties¬
{showing output:true, user:"root", script:theScript} --build the task set theClients to the selection if (length of theClients) = 0 then --no sense in whining, and with -- the way Apple Remote Desktop works, I'm not sure this condition -- is possible, so an empty selection fails silently else if (length of theClients) = 1 then try display dialog "By default, ARD always selects the topmost¬
computer in a list. If this is the computer you want to upgrade, click¬
the \"Okay\" button" --This is a warning I put in, because ARD *always* selects -- the first client in a computer list, so while that avoids --a problem with an empty selection, it can create a --problem if you don't notice that the wrong computer is --selected --I only ask this if a single computer is selected set theTaskResult to execute theTask on theClients --run the --task, save the results end try else if (length of theClients) > 1 then set theTaskResult to execute theTask on theClients --run the -- task, save the results --I use an else if, because I don't want any default that would --run the task outside of the specific desired situation end if end tell

The only real difference here is the Unix command used. Other than that, it's pretty much the same script. From there, I run the install commands manually, because they're rarely all the same for every computer in a list, however scripting that part would be fairly easy. But all of these require some manual intervention. What if you just want to do something without having to select anything? Like say, run a third party updater against a constant set of machines in a constant list?

For that we combine the power of folder actions with Apple Remote Desktop scripting:

on adding folder items to theFolder after receiving theAddedItems
   --tell application "Finder" to set theFolderName to name of 
   --theFolder
   repeat with x in theAddedItems
      set theFileInfo to info for x --get info for the downloading file(s)
      set theBaseSize to size of theFileInfo --get initial size
      delay 3 --wait 3 seconds
      set theFileInfo to info for x --get info again
      set theCompareSize to size of theFileInfo --get a newer size
      repeat while theCompareSize ≠ theBaseSize --if they don't equal,
      -- loop until they do
         set theBaseSize to theCompareSize --new base size
         delay 3 --wait three seconds
         set theFileInfo to info for x --get info
         set theCompareSize to size of theFileInfo --get a newer size
      end repeat --once the sizes match, the download is done
   end repeat
   tell application "Remote Desktop"
      set thePackageInstall to make new install package task with¬
properties {delegating to task server:false, encrypting:true,¬
packages:theAddedItems, stopping on error:false} set theTaskResult to execute thePackageInstall on computer list¬
"Virex Updates" display dialog (status of theTaskResult) giving up after 60 end tell end adding folder items to

The outer loop is a folder action that runs when a file or files are copied or added to a given folder. The repeat — delay loop is to work around a quirk in this folder action where it will sometimes start before the file(s) have finished copying. Once that's done, we have the Apple Remote Desktop-specific code. First, we build the install package text. I don't use an external task server, so that's set to false. I (of course) enable encryption for this task. By a happy coincidence, the way you enumerate the package(s) to be installed via this task is as a list. This is convenient, because the way this folder action deals with the files you are adding is as a list. So there's no need to change anything about the list of files you use in the folder action. You just pass it along to Apple Remote Desktop. We also set the task to not halt execution on error. Just because some of the targets have issues is no reason to stop everything. Once that's done, we run the task and save the result.

The result is a record, and we use the value of that record to pop a dialog that tells us what happened, so we don't have to watch Apple Remote Desktop. I mean, if we're going to stare at Apple Remote Desktop, why bother scripting it? I originally wrote this script for my Virex updates, hence the list name. Obviously, you can apply this to any list you choose.

So, you attach this script to a folder, and the next time you have to run an installer on that list, you just drop that installer in the folder. Barring it not running on one or more targets, that's it. No selecting, no watching Apple Remote Desktop. Drag, drop, and go on about your business. That's what I like. Let the software do the monkey work, I've better things to do.

Seriously though, when you start combining things like folder actions with ARD, that's when you start seeing the real convenience of the application. If you're building your own installer, just have the final step be to save it to the proper folder, and once the save is done, the install is happening automatically. Automation does not suck.

(Speaking of automation — a word on Automator. Pretty much anything you can do via AppleScript, you can build an Automator module for. I just don't happen to use Automator, but you could easily apply these scripts to Automator.)

What if you need to push out a new version of an application that is installed via drag and drop in /Applications? Q.E.D:

on adding folder items to theFolder after receiving theAddedItems --tell application "Finder" to set theFolderName to name of -- theFolder repeat with x in theAddedItems set theFileInfo to info for x --get info for the downloading file(s) set theBaseSize to size of theFileInfo --get initial size delay 3 --wait 3 seconds set theFileInfo to info for x --get info again set theCompareSize to size of theFileInfo --get a newer size repeat while theCompareSize ≠ theBaseSize --if they don't equal, -- loop until they do set theBaseSize to theCompareSize --new base size delay 3 --wait three seconds set theFileInfo to info for x --get info set theCompareSize to size of theFileInfo --get a newer size end repeat --once the sizes match, the download is done end repeat tell application "Remote Desktop" set theApplicationCopy to make new copy items task with¬
properties {conflict resolution:replace, copy items:theAddedItems,¬
encrypting:true, location:applications folder, ownership:destination¬
folder owner, stopping on error:false} set theTaskResult to execute theApplicationCopy on computer¬
list "Software Updates" display dialog (status of theTaskResult) giving up after 60 end tell end adding folder items to

There's really not a lot of difference between the installer script and the copy script. Again, we let the folder action handle passing the file(s) to be copied to the copy items task. Since it's going into /Applications, we set the destination to applications folder. We set the permissions so that the application is owned by the owner of /Applications, and we don't want it to halt on error.

Something a bit more complex

Now this is all good stuff, and I use these. But what about something a bit, oh, cooler? For example, while Apple Remote Desktop allows a client computer to signal the administrator, that pretty much requires you to have the Apple Remote Desktop menu item running. However, there are a few reasons why you wouldn't want this running. So then what? If you're in the room, say in a lab, then they can get your attention with ease. But what if you're in a different building? Email's an option, but only if you read it. How about if they could send you an email that would then be processed by the email client's scripting facility, and then tell Apple Remote Desktop to open an observe window to that client?

You'd have a neat set of scripts like the ones I toyed with a while ago. This requires the XMail OSAX from Lestang.org at http://www.lestang.org/rubrique9.html. There are a few pieces to this setup. First, we have the client piece:

property theSenderEmailAddress : "" --fill in here with the appropriate
-- origination email address. you could probably pull this from the
-- system if you want
property theSenderUserID : "" --email userid for the sender
property theSenderPassword : "" --email password for the sender
property theRecipientEmailAddress : "" --the account that gets the
-- help request emails
property theSMTPServer : "" --smtp server this application uses
set theSystemInfo to system info --grab various bits of info. 
--Feel free to modify for your needs
set theUserLongName to long user name of theSystemInfo
set theUserShortName to short user name of theSystemInfo
set theUserID to user ID of theSystemInfo as text
set theComputerName to computer name of theSystemInfo
set theUserHomeDir to home directory of theSystemInfo as text
set theIPAddress to IPv4 address of theSystemInfo
set theSystemVersion to system version of theSystemInfo
--have the user enter a short problem description
set theHelpRequest to text returned of (display dialog "Enter a¬
*short* description of the problem and click \"OK\"" default answer¬
"enter short description here") --build the email contents set theMessageContents to "User: " & theUserLongName & " " & "User Short Name: " & theUserShortName & "¬
" & "UserID: " & theUserID & "¬
" & "User Homedir Path: " & theUserHomeDir & "¬
" & "Computer Name: " & theComputerName & "¬
" & "Mac OS X Version: " & theSystemVersion & "¬
" & "System IP Address: " & theIPAddress & "¬
Needs help with: " & theHelpRequest --this uses the Xmail OSAX (in the bundle) to send a message. --It's a pretty sweet bit of work, and quite flexible. send mail to theRecipientEmailAddress from theSenderEmailAddress¬
subject "Student Help Request!" body theMessageContents username ¬
theSenderUserID password theSenderPassword authentication auto¬
port 587 priority 1 SMTP server theSMTPServer without ssl

Pretty simple. You manually set up a few properties and then copy this to all the clients. When they run it, it pulls in some basic information from system info, builds a message that is designed to be easily parsed, then sends it with the Xmail OSAX.

From there, we move to the next script, which is kicked off by a Microsoft Entourage Rule that uses the subject line "Student Help Request" in a mail rule to run our second script. I did get this working with Mail, but Mail's AppleScript implementation in Mac OS X 10.4 has some extreme issues, and required a third folder action script to get it to work. If anyone wants it, email me at jwelch@bynkii.com and I'll send it to you.

The script Entourage kicks off is:

property theMessageBodies : {}
property theLongUserName : ""
property theShortUserName : ""
property theUserId : ""
property theHomedirPath : ""
property theOSVersion : ""
property theIPAddress : ""
property theProblem : ""
property theComputerName : ""
--this is the entourage-specific part, and is how we get the message(s)
-- that set off the rule
tell application "Microsoft Entourage"
   set theMessageBodies to {}
   set theMessageList to current messages
   repeat with x in theMessageList --note that you're usually only
   -- going to have one message at a time. However, it's always a list 
   -- so we get more flexibility for the same amount of code
      set the end of theMessageBodies to the content of x
   end repeat
end tell
set oldDelims to AppleScript's text item delimiters --if you notice, I use
-- a lot of tabs in the email body. This is why
set AppleScript's text item delimiters to "   " --use tabs as the
-- delimiters
repeat with x in theMessageBodies
   set theMessage to every paragraph of x --list of paragraphs, one 
   --line per item
   
   set theLongUserNameParagraph to the first item of theMessage 
   --you can really do all these steps in one line per step
   set theLongUserName to the last text item of ¬
theLongUserNameParagraph --but I wanted to make it more clear for -- newbies set theShortUserNameParagraph to the second item of theMessage --this is where we grab the individual data bits for the alert set theShortUserName to the last text item of ¬
theShortUserNameParagraph --you could easily format the email --message so that it --already had the structure you want, and just dump all the --contents into the alert in one step set theUserIDParagraph to the third item of theMessage set theUserId to the last text item of theUserIDParagraph set theHomedirPathParagraph to the fourth item of theMessage set theHomedirPath to the last text item of ¬
theHomedirPathParagraph set theComputerNameParagraph to the fifth item of theMessage set theComputerName to the last text item of ¬
theComputerNameParagraph set theOSVersionParagraph to the sixth item of theMessage set theOSVersion to last text item of theOSVersionParagraph set theIPAddressParagraph to the seventh item of theMessage set theIPAddress to the last text item of theIPAddressParagraph set theProblemParagraph to the eighth item of theMessage set theProblem to the last text item of theProblemParagraph --display the alert message display alert theLongUserName & " is having a problem" message¬
"Computer: " & theComputerName & " IP Address: " & theIPAddress & " OS Version: " & theOSVersion & " Homedir Path: " & theHomedirPath & " UID: " & theUserId & " Problem: " & theProblem giving up after 45 tell application "Remote Desktop" --tell Apple Remote Desktop to -- pop the observe window observe computer theComputerName end tell end repeat set AppleScript's text item delimiters to oldDelims --reset your delims, -- cleanup is good tell application "Remote Desktop" to activate --bring Apple Remote -- Desktop to the front. Really, you don't need this

The email comes in and we tell Entourage to give us the content of the email. We then use the tabs in the email in conjunction with AppleScript's Text Item Delimiters, so we can pull the content we need out of the email with ease. From there, we fire off a dialog to the administrator telling them that a client has a problem, and then tell Apple Remote Desktop to open an observe session to that computer. Pretty spiffy.

Conclusion

I haven't even come close to the full potential of what you can do with Apple Remote Desktop's AppleScript implementation, but I hope I've given you some neat ideas that point you in the right direction.

Bibliography and References

While there aren't any books or articles that I used for this, there are some resources that are indispensable:

AppleScript and Apple Remote Desktop, are of course, from Apple Inc., and rather necessary to the process.

Script Debugger from Late Night Software, the only tool that lets you easily debug folder action scripts when running as folder actions. I'll say it again, I'd rather stop using AppleScript than stop using Script Debugger.

XMail from Jen-Baptiste Le Stang. A great OSAX that makes emailing from AppleScripts a snap.

The AppleScript-Users Mailing List. A phenomenal resource.


John Welch is Unix/Open Systems administrator for Kansas City Life Insurance http://www.kclife.com/, a columnist for Datamation http://itmanagement.earthweb.com/ columns/appleent/, and a contributor for InformationWeek http://informationweek.com/. He has almost twenty years of experience at making Macs work with other computer systems. John specializes in figuring out ways in which to make the Mac do what nobody thinks it can, showing that the Mac is a superior administrative platform, and teaching others how to use it in interesting, if sometimes frightening ways. He also does things that don't involve "computertry" on occasion, or at least that's the rumor.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

SketchUp 19.1.174 - Create 3D design con...
SketchUp is an easy-to-learn 3D modeling program that enables you to explore the world in 3D. With just a few simple tools, you can create 3D models of houses, sheds, decks, home additions,... Read more
ClamXav 3.0.12 - Virus checker based on...
ClamXav is a popular virus checker for OS X. Time to take control ClamXAV keeps threats at bay and puts you firmly in charge of your Mac’s security. Scan a specific file or your entire hard drive.... Read more
BetterTouchTool 3.151 - Customize multi-...
BetterTouchTool adds many new, fully customizable gestures to the Magic Mouse, Multi-Touch MacBook trackpad, and Magic Trackpad. These gestures are customizable: Magic Mouse: Pinch in / out (zoom)... Read more
FontExplorer X Pro 6.0.9 - Font manageme...
FontExplorer X Pro is optimized for professional use; it's the solution that gives you the power you need to manage all your fonts. Now you can more easily manage, activate and organize your... Read more
Dropbox 77.4.131 - Cloud backup and sync...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keeps them up-to-date between systems... Read more
DiskCatalogMaker 7.5.3 - Catalog your di...
DiskCatalogMaker is a simple disk management tool which catalogs disks. Simple, light-weight, and fast Finder-like intuitive look and feel Super-fast search algorithm Can compress catalog data for... Read more
Notion 1.0.7 - A unified workspace for m...
Notion is the unified workspace for modern teams. Notion Features: Integration with Slack Documents Wikis Tasks Note: This application contains in-app and/or external module purchases. Version 1.0... Read more
Microsoft Office 2016 16.16.12 - Popular...
Microsoft Office 2016 - Unmistakably Office, designed for Mac. The new versions of Word, Excel, PowerPoint, Outlook, and OneNote provide the best of both worlds for Mac users - the familiar Office... Read more
Little Snitch 4.4.2 - Alerts you about o...
Little Snitch gives you control over your private outgoing data. Track background activity As soon as your computer connects to the Internet, applications often have permission to send any... Read more
MainStage 3 3.4.3 - Live performance too...
Apple MainStage makes it easy to bring to the stage all the same instruments and effects that you love in your recording. Everything from the Sound Library and Smart Controls you're familiar with... Read more

Latest Forum Discussions

See All

Upcoming visual novel Arranged shines a...
If you’re in the market for a new type of visual novel designed to inform and make you think deeply about its subject matter, then Arranged by Kabuk Games could be exactly what you’re looking for. It’s a wholly unique take on marital traditions in... | Read more »
TEPPEN guide - The three best decks in T...
TEPPEN’s unique take on the collectible card game genre is exciting. It’s just over a week old, but that isn’t stopping lots of folks from speculating about the long-term viability of the game, as well as changes and additions that will happen over... | Read more »
Intergalactic puzzler Silly Memory serve...
Recently released matching puzzler Silly Memory is helping its fans with their intergalactic journeys this month with some very special offers on in-app purchases. In case you missed it, Silly Memory is the debut title of French based indie... | Read more »
TEPPEN guide - Tips and tricks for new p...
TEPPEN is a wild game that nobody asked for, but I’m sure glad it exists. Who would’ve thought that a CCG featuring Capcom characters could be so cool and weird? In case you’re not completely sure what TEPPEN is, make sure to check out our review... | Read more »
Dr. Mario World guide - Other games that...
We now live in a post-Dr. Mario World world, and I gotta say, things don’t feel too different. Nintendo continues to squirt out bad games on phones, causing all but the most stalwart fans of mobile games to question why they even bother... | Read more »
Strategy RPG Brown Dust introduces its b...
Epic turn-based RPG Brown Dust is set to turn 500 days old next week, and to celebrate, Neowiz has just unveiled its biggest and most exciting update yet, offering a host of new rewards, increased gacha rates, and a brand new feature that will... | Read more »
Dr. Mario World is yet another disappoin...
As soon as I booted up Dr. Mario World, I knew I wasn’t going to have fun with it. Nintendo’s record on phones thus far has been pretty spotty, with things trending downward as of late. [Read more] | Read more »
Retro Space Shooter P.3 is now available...
Shoot-em-ups tend to be a dime a dozen on the App Store, but every so often you come across one gem that aims to shake up the genre in a unique way. Developer Devjgame’s P.3 is the latest game seeking to do so this, working as a love letter to the... | Read more »
Void Tyrant guide - Guildins guide
I’ve still been putting a lot of time into Void Tyrant since it officially released last week, and it’s surprising how much stuff there is to uncover in such a simple-looking game. Just toray, I finished spending my Guildins on all available... | Read more »
Tactical RPG Brown Dust celebrates the s...
Neowiz is set to celebrate the summer by launching a 2-month long festival in its smash-hit RPG Brown Dust. The event kicks off today, and it’s divided into 4 parts, each of which will last two weeks. Brown Dust is all about collecting, upgrading,... | Read more »

Price Scanner via MacPrices.net

Clearance 12″ 1.2GHz MacBook on sale for $899...
Focus Camera has clearance 12″ 1.2GHz Space Gray MacBooks available for $899.99 shipped. That’s $400 off Apple’s original MSRP. Focus charges sales tax for NY & NJ residents only. Read more
Get a new 2019 13″ 2.4GHz 4-Core MacBook Pro...
B&H Photo has new 2019 13″ 2.4GHz MacBook Pros on sale for up to $150 off Apple’s MSRP. Overnight shipping is free to many addresses in the US: – 2019 13″ 2.4GHz/256GB 6-Core MacBook Pro Silver... Read more
AirPods with Wireless Charging Case now on sa...
Amazon has extended their Prime Day savings on Apple AirPods by offering AirPods with the Wireless Charging case for $169.99. That’s $30 off Apple’s MSRP, and it’s the cheapest price available for... Read more
New 2019 15″ MacBook Pros on sale for $200 of...
B&H Photo has the new 2019 15″ 6-Core and 8-Core MacBook Pros on sale for $200 off Apple’s MSRP. Overnight shipping is free to many addresses in the US: – 2019 15″ 2.6GHz 6-Core MacBook Pro Space... Read more
Amazon drops prices, now offers clearance 13″...
Amazon has new dropped prices on clearance 13″ 2.3GHz Dual-Core non-Touch Bar MacBook Pros by $200 off Apple’s original MSRP, with prices now available starting at $1099. Shipping is free. Be sure to... Read more
2018 15″ MacBook Pros now on sale for $500 of...
Amazon has dropped prices on select clearance 2018 15″ 6-Core MacBook Pros to $500 off Apple’s original MSRP. Prices now start at $1899 shipped: – 2018 15″ 2.2GHz Touch Bar MacBook Pro Silver: $1899.... Read more
Price drop! Clearance 12″ 1.2GHz Silver MacBo...
Amazon has dropped their price on the recently-discontinued 12″ 1.2GHz Silver MacBook to $849.99 shipped. That’s $450 off Apple’s original MSRP for this model, and it’s the cheapest price available... Read more
Apple’s 21″ 3.0GHz 4K iMac drops to only $936...
Abt Electronics has dropped their price on clearance, previous-generation 21″ 3.0GHz 4K iMacs to only $936 shipped. That’s $363 off Apple’s original MSRP, and it’s the cheapest price we’ve seen so... Read more
Amazon’s Prime Day savings on Apple 11″ iPad...
Amazon has new 2018 Apple 11″ iPad Pros in stock today and on sale for up to $250 off Apple’s MSRP as part of their Prime Day sale (but Prime membership is NOT required for these savings). These are... Read more
Prime Day Apple iPhone deal: $100 off all iPh...
Boost Mobile is offering Apple’s new 2018 iPhone Xr, iPhone Xs, and Xs Max for $100 off MSRP. Their discount reduces the cost of an Xs to $899 for the 64GB models and $999 for the 64GB Xs Max. Price... Read more

Jobs Board

*Apple* IOS Systems Engineer - Randstad (Uni...
Apple IOS Systems Engineer **job details:** + location:Irvine, CA + salary:$45 - $55 per hour + date posted:Tuesday, July 16, 2019 + job type:Temp to Perm + Read more
Business Development Manager, *Apple* Globa...
Business Development Manager, Apple Global Tampa, FL, US Requisition Number:73805 As a Global Apple Business Development Manager at Insight, you proactively Read more
*Apple* Systems Architect/Engineer, Vice Pre...
…its vision to be the world's most trusted financial group. **Summary:** Apple Systems Architect/Engineer with strong knowledge of products and services related to Read more
*Apple* Graders/Inspectors (Seasonal/Hourly/...
…requirements. #COVAentryleveljobs ## Minimum Qualifications Some knowledge of agricultural and/or the apple industry is helpful as well as the ability to comprehend, Read more
Best Buy *Apple* Computing Master - Best Bu...
**710003BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 000171-Winchester Road-Store **Job Description:** Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.