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
$111.78
Apple Inc.
-0.87
MSFT
$47.66
Microsoft Corpora
+0.14
GOOG
$516.35
Google Inc.
+5.25

MacTech Search:
Community Search:

Software Updates via MacUpdate

CleanApp 5.0.0 Beta 5 - Application dein...
CleanApp is an application deinstaller and archiver.... Your hard drive gets fuller day by day, but do you know why? CleanApp 5 provides you with insights how to reclaim disk space. There are... Read more
Monolingual 1.6.2 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. It requires a 64-bit capable Intel-based Mac and at least... Read more
NetShade 6.1 - Browse privately using an...
NetShade is an Internet security tool that conceals your IP address on the web. NetShade routes your Web connection through either a public anonymous proxy server, or one of NetShade's own dedicated... Read more
calibre 2.13 - Complete e-library manage...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Mellel 3.3.7 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
ScreenFlow 5.0.1 - Create screen recordi...
Save 10% with the exclusive MacUpdate coupon code: AFMacUpdate10 Buy now! ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your... Read more
Simon 4.0 - Monitor changes and crashes...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
BBEdit 11.0.2 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
ExpanDrive 4.2.1 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
Adobe After Effects CC 2014 13.2 - Creat...
After Effects CC 2014 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous After Effects customer). After Effects CS6 is still available... Read more

Latest Forum Discussions

See All

Make your own Tribez Figures (and More)...
Make your own Tribez Figures (and More) with Toyze Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
So Many Holiday iOS Sales Oh My Goodness...
The holiday season is in full-swing, which means a whole lot of iOS apps and games are going on sale. A bunch already have, in fact. Naturally this means we’re putting together a hand-picked list of the best discounts and sales we can find in order... | Read more »
It’s Bird vs. Bird in the New PvP Mode f...
It’s Bird vs. Bird in the New PvP Mode for Angry Birds Epic Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Telltale Games and Mojang Announce Minec...
Telltale Games and Mojang Announce Minecraft: Story Mode – A Telltale Games Series Posted by Jessica Fisher on December 19th, 2014 [ permalink ] | Read more »
WarChest and Splash Damage Annouce Their...
WarChest and Splash Damage Annouce Their New Game: Tempo Posted by Jessica Fisher on December 19th, 2014 [ permalink ] WarChest Ltd and Splash Damage Ltd are teaming up again to work | Read more »
BulkyPix Celebrates its 6th Anniversary...
BulkyPix Celebrates its 6th Anniversary with a Bunch of Free Games Posted by Jessica Fisher on December 19th, 2014 [ permalink ] BulkyPix has | Read more »
Indulge in Japanese cuisine in Cooking F...
Indulge in Japanese cuisine in Cooking Fever’s new sushi-themed update Posted by Simon Reed on December 19th, 2014 [ permalink ] Lithuanian developer Nordcurrent has yet again updated its restaurant simulat | Read more »
Badland Daydream Level Pack Arrives to C...
Badland Daydream Level Pack Arrives to Celebrate 20 Million Downloads Posted by Ellis Spice on December 19th, 2014 [ permalink ] | Read more »
Far Cry 4, Assassin’s Creed Unity, Desti...
Far Cry 4, Assassin’s Creed Unity, Destiny, and Beyond – AppSpy Takes a Look at AAA Companion Apps Posted by Rob Rich on December 19th, 2014 [ permalink ] These day | Read more »
A Bunch of Halfbrick Games Are Going Fre...
A Bunch of Halfbrick Games Are Going Free for the Holidays Posted by Ellis Spice on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

The Apple Store offering free next-day shippi...
The Apple Store is now offering free next-day shipping on all in stock items if ordered before 12/23/14 at 10:00am PT. Local store pickup is also available within an hour of ordering for any in stock... Read more
It’s 1992 Again At Sony Pictures, Except For...
Techcrunch’s John Biggs interviewed a Sony Pictures Entertainment (SPE) employee, who quite understandably wished to remain anonymous, regarding post-hack conditions in SPE’s L.A office, explaining “... Read more
Holiday sales this weekend: MacBook Pros for...
 B&H Photo has new MacBook Pros on sale for up to $300 off MSRP as part of their Holiday pricing. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1699... Read more
Holiday sales this weekend: MacBook Airs for...
B&H Photo has 2014 MacBook Airs on sale for up to $120 off MSRP, for a limited time, for the Thanksgiving/Christmas Holiday shopping season. Shipping is free, and B&H charges NY sales tax... Read more
Holiday sales this weekend: iMacs for up to $...
B&H Photo has 21″ and 27″ iMacs on sale for up to $200 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 1.4GHz... Read more
Holiday sales this weekend: Mac minis availab...
B&H Photo has new 2014 Mac minis on sale for up to $80 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $459 $40 off MSRP - 2.6GHz Mac mini: $629 $70 off MSRP... Read more
Holiday sales this weekend: Mac Pros for up t...
B&H Photo has Mac Pros on sale for up to $500 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2599, $400 off MSRP - 3.5GHz 6-core Mac Pro: $3499, $... Read more
Save up to $400 on MacBooks with Apple Certif...
The Apple Store has Apple Certified Refurbished 2014 MacBook Pros and MacBook Airs available for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
Save up to $300 on Macs, $30 on iPads with Ap...
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
iOS and Android OS Targeted by Man-in-the-Mid...
Cloud services security provider Akamai Technologies, Inc. has released, through the company’s Prolexic Security Engineering & Research Team (PLXsert), a new cybersecurity threat advisory. The... Read more

Jobs Board

*Apple* Store Leader Program (US) - Apple, I...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on experience, 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
*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* 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* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.