TweetFollow Us on Twitter

Beginning REALbasic: Inside the Application

Volume Number: 24 (2008)
Issue Number: 09
Column Tag: REALBasic

Beginning REALbasic: Inside the Application

by Norman Palardy

REALbasic is a Rapid Application Development (RAD) tool from REALSoftware. In the last column we started designing the application we're working on in this getting started series. Since the last issue REALbasic 2008r3 has been released and we'll move up to using that as here are a number of bug fixes and improvements in it.

Making the Database

In this installment we're continuing to build the application for tracking the prices of stocks. Last time we laid out the basic interface and also looked at how listboxes work. We still have a lot of work to do though.

One of those things is making sure that we have a place to store the data we get long term so we can look at it again. For that we're going to use a database. REALbasic comes with a built in database and we can make great use of it. The built in database is based on SQLite.

One of the tricky things with databases is that when you deal with them, you use their language, usually Structured Query Language (SQL), to do many things. Finding data, or selecting data, is done using SQL. Creating the places to store data is done using SQL, and even deleting data is often done using SQL.

So when you deal with a database one thing to keep in mind is that REALbasic has no clue about SQL. It does not read it, write it or interpret it in any way. As far as REALbasic is concerned there's no practical difference between these two bits of code:

dim s1 as string = "Hello World"
dim s2 as string = "create table test ( column1 varchar(100) ) "

Both are strings that have contents. REALbasic makes no distinction about what the contents are. The thing to remember is if you have trouble getting something for the database to work, try it manually, which we'll discuss a little later. If that works make sure the string you create in REALbasic does exactly what you did manually.

First, start REALbasic and load up the project from last time so we can work with it

As we saw last time this default project is a fully functioning program. You could immediately run it by pressing the green Run button.

Let's consider the data that we'll want to gather to make this program work the way we want.

We'll want a list of stocks we're interested in.

We'll have the quotes that we grab each time for each stock of interest

We'll need to keep track of the source(s) we're going to read data from

Please note that the screen shots here use REALbasic Professional. If you are using REALbasic Standard, screens may appear differently in your version.

Let's add a database. For the purposes of creating it quickly and simply we'll use the menu Project > Add > Database > New REAL SQL Database. Name it Stocks.rsd - the rsd extension is the default for REAL SQL databases.

Create the database file in the same directory as the REALbasic project (so it's easy to locate).

This will let us use the built in database tools to edit the database and browse the date. However, as recommended by REAL Software in http://forums.realsoftware.com/viewtopic.php?t=4342 everything else will be done in REALbasic code. This is not only more flexible but it ultimately gives you more control. Your project should now look like the one in Figure 1.


Figure 1: After adding the database

Now let's add the tables we'll require. Double click on the database icon and you should see the database editor as in Figure 2.


Figure 2: The database editor

In an SQL database, data are stored in "tables" - rows and columns of data much like you might see in a spreadsheet. Each row in a table is one complete "record". While that's technically not the right term it's so commonly used that we'll use it as well (The correct term is actually "tuple" but it's not frequently used). Each record in a table has every column that exists in the table. However, in an SQL database, a column for a particular record can have a value, say "Socks", or not have had a value assigned. This special "no value is assigned value" is called Null. There are special rules in SQL about NULL and how it is handled. You can find more about this as http://www.sqlite.org.

Just keep in mind that a string that is set to "" is NOT the same as NULL. The string has a value, but the value is empty and NULL means "no value at all".

Onwards!

The first thing we'll do is create the table for the stocks we want to track. The only things we'll need in this table are the full name of the stock, and its trading symbol.

Add the table by clicking "Add Table". Change the name of it to "StocksOfInterest".

We'll also add the two columns, Name and Symbol. You'll notice that when you click Add Column that there are also several properties that can be added to each column.


Figure 3: Adding a column in the database editor

The Type property sets the kind of data that this column can hold. Strings should go in varchar or text columns as those are the same "types" in REALbasic and the database. If you click the Type you'll see there are several others as well. Integers in REALbasic should go in integer columns in the database, floating point values from REALbasic (single or double) should go in float or double columns, and so on.

The Primary Key check box indicates whether this column is what is known as the primary key for the table. This means that the value in this column will always be required (ie it cannot be NULL) and it will be unique (no duplicates). For what we're doing we don't need to set this.

The Mandatory check box means this value, when you add a new row, MUST be provided and the addition of the row will fail if you do not provide it. In reality it means the column cannot be NULL (see? this NULL thing is really important!).

The Index check box specifies whether or not an "index" should be created. An index is a special data structure that a database uses to make access to data quicker. Often, primary keys are indexed and there may be reasons to index other columns; especially if you use them a lot to get data from the table. They can make a significant difference on very large tables. Again, for what we're doing, we can just ignore this.

The Default Value item is just that, the default that gets set if nothing is inserted for the column. It can be handy to make sure that a column always has at least some value.

The Length item usually only applies to the varchar or text types as they can be set to hold only X many characters (a length limit) or they can be set to hold an unlimited amount (up to the database limits) For what we're doing we'll leave this empty as well.

Now we have a table that can hold the stocks that we'll want to watch.

Click save to make sure your changes to the database design get saved. If you made a mistake, you can delete it and start over.

You'll also need to add a StockQuote table with the following columns: Symbol (type text), Price (type double) and quoteDateTime (type timestamp).

So now lets look at how we connect to this database, and insert rows to it and how we can use that to populate the listbox on our window.

In order to use the database in the program, we need to find the file that holds the database and connect to the database. We'll need a variable to refer to the database from anywhere in our program. There are lots of possible ways we could do these things. I'm going to show you one way you can use.

Close the Database editor window and open the App class in the project.

First, let's add a public property to the App class. Click on Add Property or use the Project > Add > Property menu item. Change the name to DB, and make the type REALSQLDatabase.

It should appear like the image in Figure 4


Figure 4: Adding a property to the App class

Now all we have to do is make it so this database variable points to the database file we just created. Again there are many ways to do this and I'm just going to illustrate one.

When an application starts up one of the first events that the application responds to is the "Open" event. This is the "Hey! I'm starting up" notification and the first opportunity you have as a programmer to do something.

Click on the "Event Handlers" disclosure triangle and you'll see there are other events as well.


Figure 5: The various events in the App class

Select the Open event and we'll add the following code to it:

dim fileTypeInst as FileType   
// create an instance of the Class
fileTypeInst = new FileType
// set up the "filter for what kind of file to select by 
fileTypeInst.Extensions = "rsd" 
// give it a name
fileTypeInst.Name = "REALSQLDatabase" 
// the Mac type and creator (only for OS X)
fileTypeInst.MacType = "RSdb" 
fileTypeInst.MacCreator = "RBv2"
  
// create a new instance of REALSQLDatabase Class
app.db = new REALSQLDatabase
// ask the user to select the database file
app.db.DatabaseFile = GetOpenFolderItem(fileTypeInst)   

There's no shortage of things going on in this little snippet. First we need to make it possible for use to ONLY select database files. So we set up a filter, known as a FileType.

The fist thing to do is create an "instance" of the class called "FileType".

In Object Oriented Programming (OOP) there are "Classes", which are a way of describing how something should work. Just like you say "Oh a car works like this by ... " and proceeding how internal combustion engines work, and how that eventually propels the car, a "Class" in OOP is a description of how things are supposed to work.

But your description of a car is not a car, just a description of one.

In order to get a car someone has to manufacture one and you get a NEW car.

In OOP programming you get a new instance using the New keyword.

So the first two lines:

dim fileTypeInst as FileType   
// create an instance of the Class
fileTypeInst = new FileType

say, "I'm going to create a thing and it's going to be of the type FileType. Now, create a new FileType thing and hold on to a reference to it in the variable I set aside for this purpose called fileTypeInst."

You'll notice a few things.

you create "instances"

you "refer" to instances

fileTypeInst is a reference to the instance created

yes it can be confusing :)

Suffice to say that we have created a new instance that we can set up to use as a filter for selecting the right file.

In order to complete the set up, we have to tell the filter what things to allow. Since OS X has adopted the use of extensions much like Windows, we set the extension AND the Mac Type and Creator. This filter will now only allow us to select REAL SQL Database files.

Then, because we are going to need a way to hold on to the reference to the database once we select it we need to create a new instance of the REALSQLDatabase class.

// create a new instance REASQLDatabase Class
app.db = new REALSQLDatabase

This does not create a new database. Rather, this creates a variable to hang on to the user's choice of database as long as the program runs. Now, we use the filter to ask the user to select the database that was created earlier. You did take my advice and put it next to the REAL basic project so you could find it, right?

// ask the user to select the database file
app.db.DatabaseFile = GetOpenFolderItem(fileTypeInst)

This will cause a dialog to be shown where the user can select the database file. There are, as I mentioned, other means to do this in REALbasic and this is only one of them. We still have not actually opened the database though.

One of things to realize is that in the dialog that gets shown as user could press "Cancel" and not select a file. In that case the DatabaseFile property would be set to Nil - this is a lot like NULL in a database.

So we'll need to test for this case and do something appropriate. If it is NIL then we should do nothing and if it is not NIL then we can try opening the database so we can use it else where in the application.

But, how will the rest of the application know that we did or did not open the database?

We don't want to have to check to see if app.Db.DatabaseFile is nil all over. That is one way but maybe there's a better way? What if we just set the app.db back to nil (remember this means "no value assigned") when a person does not select a database file? This actually will end up being "better" as it will immediately cause obvious errors if we try to use it when it is this way.

You might think it's odd to WANT errors, but in this case it is because of one unique feature that the REAL SQL database has. You don't actually need a file for it to connect to. In this case it will create an in-memory database that will behave exactly like one on disk except that when your program quits everything is lost. That can be very useful for some things, but not for what we're doing - at least not at the moment.

So let's add the following to the very end of the open event

if app.db.DatabaseFile is Nil then
  app.db = nil
else
end if

So this will set app.db, our global property for referring to the database, to nil if the user does not select a file.

In the event they do select a file, we should try and connect and see if that succeeds. If not maybe we should set the global property to nil for the same reason.

Alter the code so it reads

if app.db.DatabaseFile is Nil then
  app.db = nil
else
  if app.db.Connect() <> true then
    app.db = nil
  end if
end if

This will connect to a database and hold on to the reference or leave the reference as NIL (and we can check for that in other places). Thus far, we've created the database, connected to it and now, how to put it to use?

Let's hand code in adding the data we had for Apple last time so we can see how that is done and also eventually how to get it back into the list box. What we'll do is make sure the table is empty and then add in the data for Apple, then we'll extract that data from the database and populate the listbox with that data.

In the App.open event let's add in the cleaning out of the database, but in a way that it's easy enough to remove later on (we will want to remove it later on). The altered code in the Open event should now look like this:

if app.db.DatabaseFile is Nil then
  app.db = nil
else
  if app.db.Connect() <> true then
    app.db = nil
  else
    CleanOutDB()
  end if
end if

If you try to run at this point the compiler will complain about CleanOutDB as it does not exist yet.

Add a global method to the App class called CleanOutDatabase using either the Add Method button or Project > Add > Method. Make its name CleanOutDB. It does not need to do anything just yet.

Save everything then choose run and give things a try. You should see the dialog we created. Select the database file. Quit and run again and try to select a different file. Run it once again and press cancel so no file is selected.

You won't see much obvious happen yet. However, all of the code you have added has really been doing what is intended. But how to tell?

Well, you can see things run and it does what we discussed. Don't trust me? OK, there is a better way. REALbasic has a built in debugger. This is very handy to help you see what is going on in your program as it runs.

In the Open event if you look at the editor to the very left of the lines of the program you should see small dashes. If you click on one it should turn into a red dot like that in figure 6. This little red dot indicates that you have a "break point" on this line.


Figure 6: Setting a break point in the program

When you run your program in the IDE, and the program reaches this line of code, it will stop, and show you that it is about to run the line like in Figure 7.


Figure 7: Encountering a break point in the program

Now you can see where you are in your program, you can look at the value of variables and slowly advance through the program using the various Step buttons. You can actually see what's going on and control it at your leisure.

There is a whole section on using the debugger in the documentation from REAL. Learn how to use it well and it can be a great asset to you in your programming adventures.

If you're still looking at the screen shown in Figure 7, choose Resume and the program will proceed normally.

Let's open the CleanOutDB method we just added.

In this method we're going to remove all the data from the one table that exists currently.

First we should check to see if the database was connected to at all.

We can accomplish that with this line:

if app.db is nil then return

Recall that we set the app.db variable to nil if the user did not select a database or if the database could not be connected to. What this line does is check to see if there is a usable database, and if not leaves this routine immediately.

If the database was connected to we have to write the correct SQL to remove all the data from each table. The SQL command "delete" will delete things from a table. In its simplest form you simply use "delete from (tablename)". In our case this would be "delete from StocksOfInterest".

So lets write the REALbasic code that passes this command to the database.

dim sqlCmd as string = "delete from StocksOfInterest"

Recall that in REALbasic this is just a string and it means nothing special. It only has meaning when we tell the database to use it as a command. We do that by asking the database to "execute" it:

app.db.SqlExecute(sqlCmd)

This tells the database that we are connected to "run the SQL command that is in the variable sqlCMD".

How do we tell if things worked OK? Every database has several properties: error, errorCode and errorMessage. These properties can be checked to see if the previous operation succeeded. If everything worked properly, the error property will be false. If there was an error, the error property will be true.

If app.db.Error then
   msgbox "an error occurred " + format(app.db.ErrorCode,"-#") +_
          " " + app.db.ErrorMessage
end if

This displays a message box will the details of the error if things did not work, and does nothing if they did work.

Now we will want a way to add the data we have for Apple from last time. Since we will probably want to add data for quotes frequently we should make this a different method.

Create a new method called "AddDataForStock" that takes the stock symbol as one parameter, the stock price as another, and the date and time for the quote as the last one. Mine looks like that shown in Figure 8.


Figure 8: Definition of AddDataForStock

This method will be one that we'll use to add a quote for a given stock at a given date and time.

In the last installment, we just put the data directly into the listbox. This time, we'll alter that so the quote data goes into the database and then we retrieve it from the database to insert into the listbox.

For the moment, lets just pretend we are actually getting the quote from a service like Yahoo Finance or some other service and see how to get the data into the database.

In REALbasic there are at least two different ways you could add data to a database.

One is using raw SQL INSERT statements. While this gives you a great deal of control, it also means you have to take care of everything. Most SQL databases require you to double up quotes that are contained in values, and adding certain types of large binary chunks of data may be difficult using this method. You have to know what the specific database conventions are.

The second is using REALbasic's built-in DatabaseRecord class. This is substantially easier and only requires you to know how to use it and the specific database plugin does the rest of the grunt work; we'll use this second way for now.

First, you need to create a new instance of a DatabaseRecord. Then you add the specific values to it and then you add this record to the database. That code will look like:

  dim dbRec as DatabaseRecord   
  dbRec = new DatabaseRecord // create a databae record instance
  // add the data to it
  dbRec.Column("Symbol") = stockSymbol
  dbRec.DoubleColumn("Price") = stockPrice
  dbRec.DateColumn("quoteDateTime") = dateAndTimeForQuote
  // add the record to the database  
  db.InsertRecord("StockQuote", dbRec)
  
  // check for any errors
  if db.Error then
    msgbox "an error happened inserting the quote " + db.ErrorMessage
  end if 

This creates a new DatabaseRecord and sets the values. One thing to notice is that the addition of the values uses the names of the columns in the database table. The line:

dbRec.Column("Symbol") = stockSymbol  

can be interpreted as, "when you save this record the column called "Symbol" should be set to the value that is in stockSymbol. But this does not happen until you actually call InsertRecord.

At the end of the App.Open event, add in one line to add one quote for a stock like this:

  AddDataForStock "AAPL" , 169.73,  new Date

This will use the newly written method and pass in the symbol ("AAPL"), the stock price (169.73) and a date for the quote that is the current date and time.

Lets now see how to get this data out of the database and into our user interface. If you open the wStocks window and look in the Open event for the list box on that window you'll see:

  me.ColumnCount = 3
  me.HasHeading = true
  me.Heading(0) = "Symbol"
  me.Heading(1) = "Time"
  me.Heading(2) = "$"
  
  me.AddRow "AAPL" // add one symbol we're interested in watching
  dim newDate as new Date
  me.cell(me.LastIndex,1) = newDate.ShortDate + " " + newDate.ShortTime
  me.cell(me.LastIndex,2) = format(169.73,"$,#.00")

We're going to remove the last 4 lines and replace them with code that gets the data from the database. So lets first remove them and see what the new code will be to replace them:

dim rs as RecordSet 
// use SQL to get the data
rs = app.db.SQLSelect("select Symbol,  Price, quoteDateTime from stockquote")
  
// go through any and all rows we get back and put them in the list
while rs <> nil and rs.eof = false
    me.AddRow ""
    me.Cell(me.LastIndex,0) = rs.Field("Symbol").StringValue
    me.Cell(me.LastIndex,1) = rs.Field("quoteDateTime").DateValue.SQLDateTime
    me.Cell(me.LastIndex,2) = rs.Field("Price").StringValue
    
    rs.MoveNext
  wend

So what does all this do? First, we declare a variable to refer to a recordset. A recordset is what a database query returns to us. They can have lots of data (rows), or none. Only if the query has an error do you get a recordset that is NIL.

After declaring the variable, we then use a SQL SELECT query to get the set of data back that we want. In this case it's all rows and columns from the table called StockQuote. However SQL is very powerful and can be used to bring back data from one or more tables, sums and all kinds of other information. A full discussion of SQL is well beyond the scope of these articles.

Once we run the query, if there were no errors we will have a non-NIL recordset. I prefer to use a while loop to traverse all the data. For every row in the data set we get back from the query, we want to add a row to the list box. And then we want to set the columns in the listbox to the various values in each of the columns of the data set.

Each row in a recordset has many fields and each of those fields can be referred to by name, as it is in the database, or by a numeric index. I prefer to use names as they are easier to follow. Note that the query asks for "Symbol, Price, quoteDateTime" and the code just uses those names. Each of the lines in the loop grabs the string value of the field and puts it in the cell of the specific row of the listbox, then it moves to the next row. This continues until all the rows are loaded into the list box.

Now, run your application a few times and see what happens!

Next time we'll see how to grab the quotes from a service like Yahoo and add them to the database.


Norman Palardy has worked with SQL databases since 1992, and has programmed in C, C++, Java, REALbasic and other languages on a wide variety of platforms. In his 15+ years of IT experience, Norman has developed innovative and award-winning applications for TransCanada Pipelines, Minerva Technologies (now XWave), Zymeta Corporation, and the dining and entertainment industry. He holds a BSc from the University of Calgary in Alberta. He's also a founder of the Association of REALbasic Professionals (http://www.arbp.org/) and currently works for REAL Software.

 
AAPL
$117.60
Apple Inc.
-1.03
MSFT
$47.47
Microsoft Corpora
-0.12
GOOG
$541.08
Google Inc.
+1.81

MacTech Search:
Community Search:

Software Updates via MacUpdate

MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
Carbon Copy Cloner 4.0.3 - Easy-to-use b...
Carbon Copy Cloner backups are better than ordinary backups. Suppose the unthinkable happens while you're under deadline to finish a project: your Mac is unresponsive and all you hear is an ominous,... Read more
ForeverSave 2.1.3 - Universal auto-save...
ForeverSave auto-saves all documents you're working on while simultaneously doing backup versioning in the background. Lost data can be quickly restored at any time. Losing data, caused by... Read more
Voila 3.8.1 - Capture, annotate, organiz...
Voila is a screen-capture, recording, and annotation tool that is a full-featured replacement for Mac's screen-capture and screen-recording capabilities. It has a large and robust set of editing,... Read more
SyncTwoFolders 2.0.6 - Syncs two user-sp...
SyncTwoFolders simply synchronizes two folders. It supports synchronization across mounted network drives and it is a possibility to run a simulation showing in a log what will be done. Please visit... Read more
Duplicate Annihilator 5.1.1 - Find and d...
Duplicate Annihilator takes on the time-consuming task of comparing the images in your iPhoto library using effective algorithms to make sure that no duplicate escapes. Duplicate Annihilator detects... Read more
HandBrake 0.10.0 - Versatile video encod...
HandBrake is a tool for converting video from nearly any format to a selection of modern, widely supported codecs. Supported Sources: VIDEO_TS folder, DVD image or real DVD (unencrypted -- CSS is... Read more

Latest Forum Discussions

See All

Tilt to Live Bundle Set to Arrive This T...
Tilt to Live Bundle Set to Arrive This Thanksgiving Posted by Ellis Spice on November 25th, 2014 [ permalink ] One Man Left has unveiled an upcoming Tilt to Live bundle, allowing players to get the series for a di | Read more »
BattleLore: Command (Entertainment)
BattleLore: Command 1.0 Device: iOS Universal Category: Entertainment Price: $9.99, Version: 1.0 (iTunes) Description: ***NOTE: Compatible with iPad 2/iPad mini, iPod touch 5 and up and iPhone 4S and up – WILL NOT RUN ON EARLIER... | Read more »
Weather Or Not Review
Weather Or Not Review By Jennifer Allen on November 25th, 2014 Our Rating: :: STYLISH WEATHER REPORTINGiPhone App - Designed for the iPhone, compatible with the iPad Check the weather quickly and conveniently with Weather or Not... | Read more »
The All-New Football Manager Handheld 20...
The All-New Football Manager Handheld 2015 is Available Now Posted by Jessica Fisher on November 25th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Six iOS Games to Get You Ready for Thank...
Image Source: Friends Wiki At this point in the month, you or at least a few people you know are probably getting ready to scramble around (or are already scrambling around) for Thanksgiving Dinner. It’s a hectic day of precise oven utilization, but... | Read more »
Call of Duty: Heroes: Tips, Tricks, and...
Hello Heroes: What’d we think of Call of Duty‘s take on Clash of Clans? Check out our Call of Duty: Heroes review to find out! Just downloaded Call of Duty: Heroes and need some handy tips and tricks on how to get ahead of the rest? As we often do,... | Read more »
Call of Duty: Heroes Review
Call of Duty: Heroes Review By Jennifer Allen on November 25th, 2014 Our Rating: :: CLASH OF FRANCHISESUniversal App - Designed for iPhone and iPad Mix Clash of Clans with Call of Duty, and this is what you get.   | Read more »
Slider Review
Slider Review By Jordan Minor on November 25th, 2014 Our Rating: :: SLIDE TO PLAYUniversal App - Designed for iPhone and iPad Slider has all the excitement of unlocking your phone screen.   | Read more »
oh my giraffe (Games)
oh my giraffe 1.0.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.0 (iTunes) Description: Eat fruits while being chased by lions. Cut the vines to send fruit plummeting onto the lions. Don't worry, your flexible... | Read more »
One of 2000’s Most Loves Adventure Games...
One of 2000’s Most Loves Adventure Games, The Longest Journey, has Come to iOS Posted by Jessica Fisher on November 25th, 2014 [ permalink ] | Read more »

Price Scanner via MacPrices.net

Early Black Friday MacBook Pro sale: 15-inch...
 Best Buy has posted early Black Friday prices on 15″ Retina MacBook Pros, with models on sale for $300 off MSRP on their online store for a limited time. Choose free local store pickup (if available... Read more
A9 Chips Already?
It’s barely more than a couple of months since Apple got the first A8 systems-on-chip into consumer hands, but rumor and news focus is already turning to the next-generation A9 SoC. Apple Daily... Read more
NewerTech Announces NuGuard KXs Impact X-Orbi...
NewerTech has announced updates to its family of Impact X-Orbing Screen Armor bringing military grade, triple layer protection to Apple’s new iPhone 6 and 6 Plus. Like all models in the NuGuard KXs... Read more
13-inch 1.4GHz MacBook Air on sale for $889,...
 B&H Photo has the 13″ 1.4GHz/128GB MacBook Air on sale for $889 including free shipping plus NY tax only. Their price is $110 off MSRP. B&H will also include free copies of Parallels Desktop... Read more
Save up to $300 on Macs and iPads with your A...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
Apple refurbished Mac Pros available for up t...
The Apple Store is offering Apple Certified Refurbished Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more
Jumptuit Launches One-Tap Windows 8.1 iTunes...
Jumptuit has launched Windows 8.1 support for One-Tap iTunes Sync. with which Windows 8.1 users can now easily sync their iTunes libraries with Microsoft OneDrive. Jumptuit provides easy access from... Read more
Apple restocks refurbished 13-inch 2014 Retin...
The Apple Store has restocked Apple Certified Refurbished 2014 13″ 2.6GHz Retina MacBook Pros for up to $230 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
CEA Study Finds More People Recycling Electro...
A new study by the Consumer Electronics Association (CEA) finds that electronics recycling receives the continued and growing support of consumers. According to the CEA,s Recycling and Reuse Study,... Read more
15″ 2.2GHz Retina MacBook Pro on sale for $17...
 B&H Photo has the 2014 15″ 2.2GHz Retina MacBook Pro on sale today for $1749. Shipping is free, and B&H charges NY sales tax only. B&H will also include free copies of Parallels Desktop... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.