Swift Tutorial II

Ok so in the first tutorial we covered let, which is the keyword for defining constants.

let thisBeAConstant = 3.141

Now we are going to cover variables, which use the var keyword like so:

var thisVariable = time

Notice 2 things about Swift:

1) We don’t use ; at the end of a line.  That’s just weird 🙂

2) We don’t have to specify the type.  The type is inferred by whatever value you pass in, so:

var someString = “this is a string”

var someInteger = 5

So Swift is kinda smart.  Now let’s meet some old friends “Hao jiu bu juan”

ARRAYS

var energies = [“solar”“wind”“fossil”, “this is a mixed array”, 39]

var energies:String[] = [“solar”“wind”“this is a string array”]

And of course we can perform some basic operations such as:

READ

var item1 = energies[0]   // “solar”

INSERT

energies.insert(“hydro”, atIndex: 2)

MODIFY

energies[3] = “geothermal”

APPEND

energies.append(“nuclear”)

or

energies += “nuclear”

COMBINE

energies += [“biomass”“hamster”]

COUNT

var lengthofArray = energies.count

LOGIC

var arrayIsEmpty = energies.isEmpty

REMOVE

energies.removeAtIndex(3)
energies.removeLast()
energies.removeAll(keepCapacity: true)
MUTATE
If we used let energies, then our array is immutable, vs if we used var energies which means its mutable.
Easy as pie.
DICTIONARIES

var energies = [

    “Solar”“Thermal”,
    “Photovoltaic” : “Grid”,
    “Nuclear” : “Dangerous”
]
Here our keys are Solar, Photovoltaic & Nuclear.  These keys can be strings or numeric values such as integers.
Once again we can:
READ
let cheapestSolarEnergy = energies[“Solar”]
COUNT
var energiesCount = energies.count
MODIFY
energies[“Solar”] = “PV”
REMOVE
energies[“Nuclear”] = nil;
INSERT
energies[“Geo”] = “Thermal”
and of course the value of a key can be an array or another dictionary:

var typesOfEnergy =

[
    platforms[“Solar”]: [“Thermal”“PV”“GridTied”],
    platforms[“Wind”]: [“Autonomous”“GridTied”],
    platforms[“Nuclear”] : [“Fusion”“Fission”,“Meltdown”]
]
and we would read it:
var type1 = typesOfEnergy[“Solar”][0]
See you next time! 🙂

First SWIFT Tutorial ever! :-)

let is used for assigning constants whereas var is used for creating variables.

ie:

let  salute: Character = “Hey there…”

let everyone: Character = “Swift World”

var saluteEveryone = salute + everyone

saluteEveryone = saluteEveryone + “!”

Creating a simple UICollectionView in iOS

Steps

1) Create Master-Detail Application & Replace the MasterViewController

First we want to create a Master-Detail Application just because it sets up a Master-Detail relationship even though thats the first thing we are going to break :).  So go ahead and create a new project in XCode4 based on a Master-Detail Application type.  Use ARC, Storyboards and CoreData because we will use CoreData to store information.  Your storyboard should look like this:

Master-Detail Storyboard
Master-Detail Storyboard

Now select the Master scene until its highlighted in blue and delete it with the Delete key.  We simply replace it by dragging in a UICollectionViewController onto the storyboard in its place.  This places a UICollectionViewController scene with a small empty collectionview cell in the top left corner.  I made a few adjustments to mine and here is what it looks like but Ill go over those later:

UICollectionViewController Storyboard
UICollectionViewController Storyboard

The changes I made to it are the following:

a – Select the entire scene (again until its highlighted blue) and change its Class type from UICollectionViewController to MasterViewController.

b – Enlarged the UICollectionViewCell from 50×50 to 145×145 in the Dimension’s Inspector

Here are some clips of the Identity Inspector of the MasterViewController and Dimension’s Inspector of the UICollectionViewCell:

UICollectionViewController Identity Inspector
UICollectionViewController Identity Inspector
UICollectionViewCell Dimensions Inspector
UICollectionViewCell Dimensions Inspector

We did set the new UICollectionViewController to a new class, the MasterViewController class.  We must do the same with the UICollectionViewCell but we must create its class first.

2)  Modify the MasterViewController class with the following in the .m file:

#import “MasterViewController.h”

#import “DetailViewController.h”

#import “MyCustomCell.h”

#import “AppDelegate.h”

static NSString *CellIdentifier = @”MyCustomCell”;

@interface MasterViewController ()

{

NSMutableArray *_objectChanges;

NSMutableArray *_sectionChanges;

}

@end

Now let’s go through the methods.  First the UICollectionView methods, which are quite similar to the UITableViewController methods:

#pragma mark – UICollectionView

– (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

{

id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];

return [sectionInfo numberOfObjects];

}

// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:

– (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

{

MyCustomCell *cell = (MyCustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];

NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];

[cell setImage:[UIImage imageWithData:[object valueForKey:@”photoImageData”]]];

return cell;

}

– (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

{

if ([[segue identifier] isEqualToString:@”showDetail”]) {

NSIndexPath *indexPath = [[self.collectionView indexPathsForSelectedItems] lastObject];

NSManagedObject *object = [[self fetchedResultsController] objectAtIndexPath:indexPath];

[[segue destinationViewController] setDetailItem:object];

}

}

The MyCustomCell Class is the one we will create in the next section.  For now we are simply filling in the cell’s image data with some fetched managed object which we will also create later.  A couple of more interesting tidbits for example; we gave our UICollectionViewCell a reuse identifier and we gave our segue an identifier as well.  We must make sure these identifiers also exist in the storyboard inspectors for the cell and segue respectively.

3) Create UICollectionViewCell Class & Connect it

Let’s go ahead and Create a New File in our project, base it off of Objective C Class.  Type in UICollectionViewCell as the subclass and name it MyCustomCell.  We are simply going to define a class for our UICollectionViewCell and once we are finished, we must go to Storyboard and set our cell to use this new class type.

Add a UIImageView property to the class so that your .h file looks like this:

#import <UIKit/UIKit.h>

@interface MyCustomCell : UICollectionViewCell{

IBOutlet UIImageView *imageView;

}

-(void)setImage:(UIImage *)image;

@end

and now implement the setter in your .m so that it looks like this:

#import “MyCustomCell.h”

@implementation MyCustomCell

-(void)setImage:(UIImage *)image{

[imageView setImage:image];

}

@end

4) Create the data model.  First let’s do the easiest part, which is creating the data model.  Select your project’s xcdatamodel file and create a new Entity, call it Snapshots if you’d like.  Now add 3 attributes to it and make them of this type:

Core Data Entity Data Model
Core Data Entity Data Model

Once that is done, we have a storage container for our data.  Let’s look at the code used by our app to access this store and manipulate it.

#pragma mark – Fetched results controller

– (NSFetchedResultsController *)fetchedResultsController

{

if (_fetchedResultsController != nil) {

return _fetchedResultsController;

}

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

// Edit the entity name as appropriate.

NSEntityDescription *entity = [NSEntityDescription entityForName:@”Snapshots” inManagedObjectContext:self.managedObjectContext];

[fetchRequest setEntity:entity];

// Set the batch size to a suitable number.

[fetchRequest setFetchBatchSize:20];

// Edit the sort key as appropriate.

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”photoName” ascending:NO];

NSArray *sortDescriptors = @[sortDescriptor];

[fetchRequest setSortDescriptors:sortDescriptors];

// Edit the section name key path and cache name if appropriate.

// nil for section name key path means “no sections”.

NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@”Master”];

aFetchedResultsController.delegate = self;

self.fetchedResultsController = aFetchedResultsController;

NSError *error = nil;

if (![self.fetchedResultsController performFetch:&error]) {

// Replace this implementation with code to handle the error appropriately.

// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

}

return _fetchedResultsController;

}

– (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo

atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type

{

NSMutableDictionary *change = [NSMutableDictionary new];

switch(type) {

case NSFetchedResultsChangeInsert:

change[@(type)] = @(sectionIndex);

break;

case NSFetchedResultsChangeDelete:

change[@(type)] = @(sectionIndex);

break;

}

[_sectionChanges addObject:change];

}

– (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject

atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type

newIndexPath:(NSIndexPath *)newIndexPath

{

NSMutableDictionary *change = [NSMutableDictionary new];

switch(type)

{

case NSFetchedResultsChangeInsert:

change[@(type)] = newIndexPath;

break;

case NSFetchedResultsChangeDelete:

change[@(type)] = indexPath;

break;

case NSFetchedResultsChangeUpdate:

change[@(type)] = indexPath;

break;

case NSFetchedResultsChangeMove:

change[@(type)] = @[indexPath, newIndexPath];

break;

}

[_objectChanges addObject:change];

}

– (void)controllerDidChangeContent:(NSFetchedResultsController *)controller

{

if ([_sectionChanges count] > 0)

{

[self.collectionView performBatchUpdates:^{

for (NSDictionary *change in _sectionChanges)

{

[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {

NSFetchedResultsChangeType type = [key unsignedIntegerValue];

switch (type)

{

case NSFetchedResultsChangeInsert:

[self.collectionView insertSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];

break;

case NSFetchedResultsChangeDelete:

[self.collectionView deleteSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];

break;

case NSFetchedResultsChangeUpdate:

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:[obj unsignedIntegerValue]]];

break;

}

}];

}

} completion:nil];

}

if ([_objectChanges count] > 0 && [_sectionChanges count] == 0)

{

[self.collectionView performBatchUpdates:^{

for (NSDictionary *change in _objectChanges)

{

[change enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, id obj, BOOL *stop) {

NSFetchedResultsChangeType type = [key unsignedIntegerValue];

switch (type)

{

case NSFetchedResultsChangeInsert:

[self.collectionView insertItemsAtIndexPaths:@[obj]];

break;

case NSFetchedResultsChangeDelete:

[self.collectionView deleteItemsAtIndexPaths:@[obj]];

break;

case NSFetchedResultsChangeUpdate:

[self.collectionView reloadItemsAtIndexPaths:@[obj]];

break;

case NSFetchedResultsChangeMove:

[self.collectionView moveItemAtIndexPath:obj[0] toIndexPath:obj[1]];

break;

}

}];

}

} completion:nil];

}

[_sectionChanges removeAllObjects];

[_objectChanges removeAllObjects];

}

I know its long, but its pretty simple.  The first method, – (NSFetchedResultsController *)fetchedResultsController, basically opens up the store, fetches all entities by the name “Snapshots” and places them into a special object called NSFetchedResultsController.

The didChangeSection method is called when there is a change within a section.  We only have 1 section in our UICollectionView.

The didChangeObject method is called when a particular object is changed within our UICollectionView.

The controllerDidChangeContent actually manages the changes made.  Basically we update our two arrays, _sectionChanges and _objectChanges with each change in the data in order to keep our UICollectionView current.

5) Connect to Flickr API.  So what constitutes a change in those sections and objects?  There is an acronym that datastore managers use, CRUD, which basically says that everytime you create, read, update or delete you create a transaction.  Thats basically what we want to track (except for the read part :)).  So whenever we download a new photo to our datastore, update a photo or delete one, we trigger changes in objects and thus in sections.

We want to use the Flickr API to get images from the web and populate our collection view.  We are basically going to perform a fetch to Flickr API using our own key or identifier.  You must register for one at flickr.com/.

a – Get FlickrAPIKey and add it here to this string constant atop your .m file like so:

NSString *const FlickrAPIKey = @"YOURAPIKEYVALUE";

b – Add the loadFlickrPhotos method to fetch pics from the web.  So add this method:

– (void)loadFlickrPhotos{

// 1. Build your Flickr API request w/Flickr API key in FlickrAPIKey.h

NSString *urlString = [NSString stringWithFormat:@”http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=%@&tags=%@&per_page=10&format=json&nojsoncallback=1&#8243;, FlickrAPIKey, @”bayern”];

NSURL *url = [NSURL URLWithString:urlString];

// 2. Get URLResponse string & parse JSON to Foundation objects.

NSString *jsonString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

NSError *e = nil;

NSDictionary *results = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]options:NSJSONReadingMutableContainers error:&e];

photos = [[results objectForKey:@”photos”] objectForKey:@”photo”];

for (NSDictionary *photo in photos) {

// 3.a Get title for e/ photo

NSString *title = [photo objectForKey:@”title”];

[photoNames addObject:(title.length > 0 ? title : @”Untitled”)];

// 3.b Construct URL for e/ photo.

NSString *photoURLString = [NSString stringWithFormat:@”http://farm%@.static.flickr.com/%@/%@_%@_s.jpg&#8221;, [photo objectForKey:@”farm”], [photo objectForKey:@”server”], [photo objectForKey:@”id”], [photo objectForKey:@”secret”]];

[photoURLs addObject:[NSURL URLWithString:photoURLString]];

}

// Process into CoreData

[self processCoreData];

}

It basically looks for Flickr photos of Bayern and stores the results in the photos ivar.  Now we must populate our CoreData db with these data.

c – Populate our CoreData model.  Add the processCoreData method to your MasterViewController.m like so:

-(void)processCoreData{

AppDelegate *myDelegate = [[UIApplication sharedApplication] delegate];

for (NSDictionary *photoDictionary in photos){

NSManagedObject *photoModel = [NSEntityDescription insertNewObjectForEntityForName:@”AFPhotoModel” inManagedObjectContext:myDelegate.managedObjectContext];

//[photoModel setValue:[photoDictionary valueForKey:@”rating”] forKey:@”photoRating”];

[photoModel setValue:[photoDictionary valueForKey:@”title”] forKey:@”photoName”];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{

//Build URL

NSString *photoURLString = [NSString stringWithFormat:@”http://farm%@.static.flickr.com/%@/%@_%@_s.jpg&#8221;, [photoDictionary objectForKey:@”farm”], [photoDictionary objectForKey:@”server”], [photoDictionary objectForKey:@”id”], [photoDictionary objectForKey:@”secret”]];

NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:photoURLString]];

dispatch_async(dispatch_get_main_queue(), ^{

[photoModel setValue:imageData forKey:@”photoImageData”];

});

});

}

}

Voila!  Run your app!

WWDC 2013 Videos of Interest

WWDC 2013 Videos of Interest

WWDC 2013 Videos Apple
WWDC 2013 Videos Apple

Introducing Text Kit

Advanced Text Layouts & Effects with TextKit

Building User Interfaces in iOS7

Custom Transitions Using View Controllers

Customizing Your App’s Appearance for iOS 7

Introduction to Sprite Kit

Designing Games with Sprite Kit

Getting Started with UIKit Dynamics

What’s New in iOS User Interface Design

Pretty much all the Whats New…

Apple’s iOS7 and the future of development

Sure iOS7 brings a lot of technological advances. But more importantly, it sets a new precedent in the Software Development industry. Here are a reasons:

1). An Elite development team. Many people complain about Apple shutting out developers by not opening up. It’s funny because their code is open, you just have to be willing to pay. Where disgruntled programmers see disappointment, I see a company filtering an elite team of outsourced developers to keep pushing the envelope where only the savviest survive.

2). An evermore demanding market niche. I downloaded ios7 on Monday like most devs. The first thing I noticed was Skype crashing and Testflight failing to install. Only a few days later, Skype’s community forums are flooded with complaints pressing for the company to update their app to iOS7. This may not seem like much, after all it’s just a beta, right? Why did all those devs update ahead of time anyway! Don’t forget this group of devs is growing everyday and at the end of the day their pressure is real! Many people are also paying the $99 dev fee just to have access to the betas. So everyday Apple’s niche market is growing in size and influential pressure. I can’t wait for iOSX! They’ll be releasing pre-pre-alpha candidates!

3). Indies are dying! Apple doesn’t really want indies. The days of making a simple game that scored you millions is over. Apple had a hand in this of course. They deliberately raised AppStore standards. WWDC is an expensive event and all these factors combine so that Apple can have, as mentioned before, an elite team of 24/7 developers that pose no company liability as employees but are at the same time manageable via quality controls they define and who are filtered to result in incorporated LLCs which are, unlike their freelance counterparts, accountable 🙂

How to Read iOS or Mac OS Programming Documentation

The toughest part for me to get started was reading the Apple Documentation on iOS or MacOS. When I got into more APIs it got more complex. You need to understand how to read API or proprietary code documents in order to understand how to create a piece of code, connect to web services or debug changes in code.

You will very often see the term DEPRECATED, which means a particular method name is no longer used. This is very important so let’s take a look at Apple Docs first:

NSArray Class Reference
Santiapps. Marcio Valenzuela. Learn to program for iOS or MacOS

This tells us that the object of type NSArray has many methods that you can call on it. They may be Instance or Class methods but they are all listed here. If you want to know what an object can do, you ask it for its type and come to the Class Reference site.

Notice there are many tasks you can call on an NSArray:

5. NSArray Class Reference
Santiapps. Marcio Valenzuela. Learn to program for iOS or MacOS. Understand how to read NSArray Class Reference.

Notice the task with red text next to it that reads “Deprecated in…”. Like I said it says it was deprecated after a certain version of the OS. Now let’s suppose we want to know about a particular method we can use on this type of object, let’s look at initWithObjects, it only sounds natural that if we want to create an array, we would like to put objects in it:

6. NSArray Example
Santiapps. Marcio Valenzuela. Learn to program for iOS or MacOS. Understand how to read NSArray Class Reference.

This means that if you call our standard way of creating any object:

Object Class *objectPointer = Object Class alloc …

and then we insert that method signature -(id)initWithObjects:(id)firstObject…

we get this:

Object Class *objectPointer = Object Class alloc -(id)initWithObjects:(id)firstObject

we can start putting objects into our array object. Now let’s look at the correctly formatted code:

NSArray *myArray = [[NSArray alloc] initWithObjects:@”hi”, @”bye”, nil];

Notice we took out the -(id) at the beginning of the method signature because this forcibly has to return an NSArray object. And the reason we can add this method signature to the NSArray *myArray creation line is because we know that NSArray contains that method signature inside its Class file. We can make sure of this by using the Jump To Definition in Xcode!

Let’s do so for an example I just ran across. I had been working on a game using Cocos2d. This means I was using an API or library or third party code framework. These terms are usually interchangeable but they basically mean the same. When somebody or some company works on a bunch of code that helps you perform certain tasks. For example, Cocos2d creators worked on many Classes to create Cocos2d. One such class is CCAnimation which takes in frames and strings them together to create an animation. They recently released v.2.0 and this particular class changed some of its method signatures to include improvements I guess.

If I open my code in Xcode and try to run my old code, it crashes saying “Unrecognized Selector sent to instance”.

1. XCode Console debugging
Santiapps. Marcio Valenzuela. Learn to program for iOS or MacOS. Understand how to read NSArray Class Reference.

This means that instance of CCAnimation doesn’t recognize that old method or selector. Presumbale because they changed the old name to accommodate new structural changes. So I right click over the CCAnimation call in my code where the app crashed:

CCAnimation Definition
Santiapps. Marcio Valenzuela. Learn to program for iOS or MacOS. Understand how to read NSArray Class Reference.

And when I Jump To Definition I will be taken to that Class Reference for that Class:

3. CCAnimation Definition
Santiapps. Marcio Valenzuela. Learn to program for iOS or MacOS. Understand how to read NSArray Class Reference.

This gives me the Class Reference for CCAnimation, the one being used in my project, and tells me which methods the Class now implements. Now I just find the method which more closely matches my needs.

It’s important to learn how to go from a crash to the documentation which can help you fix that crash.

Apple Foxconn Labor

Much was commented this week about Apple’s China factories where American’s favorite phone and tablet computers are made.

Being from a developing country myself, I was surprised to see such a big deal made about this “differing” labor practice in such countries when compared to Developed Countries’ practices.

First, because when you compare absolute numbers instead of relative (%), statistics can scare or impress anyone. Take the $1.78 hourly wage and the board figure of $17 a month for example:

$1.78/hr x 40hrs a week x 4 weeks comes to about = $284.8/mo income.

$17 for room for what pretty much looked like my dorm room at University of Tampa, a private university.

$0.70/meal x 90 meals a month = $63/mo for board.

This leaves $204 = 72% of disposable income. My wife has about 50% disposable income (after room and board) and she is the Marketing Director for a private company in Honduras!

When you add to this the fact that this kind of job is a temp job where these Chinese teens will remain for about 3 years to save up as well as gain experience and then move on to bigger and better things, the picture clears up considerably. It’s the equivalent of a job at McDonalds or Burger King! Who in America working at McD’s can tout 72% disposable income!?

Second, because thats how many of American’s consumer products get made: Walmart clothing from the Caribbean & Central America, Dunkin Donuts sugar from Asia, Starbucks Coffee from South America etc. the list goes on and on.

I’m not suggesting there is no room for improvement, but it’s no reason for alarm. If you want to be alarmed, check out factories in countries like Malaysia or Honduras where big companies with solid Social Responsibility Programs like Apple don’t go near!

My Cover Letter for a Job Application at Apple: You will be missed Steve

I believe the best Cover Letter I can write is based on my love for Apple.  I can’t tell you I have ever sat in line for days waiting for a product launch because I don’t have the luxury of living in the US and our “quasi-Apple” stores in Honduras suck to say the least.  However, I can say 3 things with great pride:

 

First, that I have been an Apple fan since the Apple IIe, simply because that’s around the time I was born and have recollection.  We used to spend summers in San Francisco at my uncle’s house.  He worked for United Airlines and had a simple house but not without his Apple computer.  My fondest memories of summers in San Fran were programming those cheesy loops used to make hearts and faces on the screen by printing out characters in a certain order; and the other, playing Where in the World is Carmen San Diego!  God I loved that game.

 

Second, that i have been shunned for being an Apple user and I enjoyed it.  Although I used my first Apple at my uncle’s house in the 80’s, I didn’t buy my own first Apple MacBook Pro until 2000, just before entering my MBA program.  As a business school, it was all about pragmatism, and compatibility issues and out dated versions of software were all but damning because everyone made fun of me.  A professor joked about me having paid a Coke’s worth for “that ugly thing” as he put it and still gotten change in return.  He joked about how its resale value was zero and how Apple would die just like betamax, since it was one of the case studies we worked on.  I remember how in reviewing that case study we were told how when 2 competing technologies tried to dominate the market, the winner was the one that was able to surround itself with accessories which made it indispensable.  And 10 years later Steve Jobs went ahead and did just that!  Id like to meet that professor again and show him my shiny new 2011 MBA, a market breakthrough product, not a follower.

 

And third, that I love Apple products, am a fan-boy, love converting people, believe our products are a work of Art and would buy my milk from Apple if you had a Milk Division.  I can appreciate the customer attention to detail Apple has.  Its not really customer service because a service almost implies something that is paid for and in return, because of the commitment acquired by the employee, he provides that service.  I live in a country where even franchise-trained personnel doesn’t provide good customer service.  This is my life 360 days of the year.  Then all of a sudden I go to Miami and rush to an Apple Store, not always for a product, just for the experience.  I love walking into a place and have someone walk up to me and genuinely ask me if there is anything they can help me with.  Even if I didn’t have anything in mind before I walked in, I think of something, anything, just for the “feeling of satisfaction” I get when they solve a problem for me or recommend something that ends up making my life easier or what not.  That truly is priceless.

 

I love converting people because when I pick up my old iPhone 4 and show it to someone, I take off the banged up protective case in order to show the brand-new product still intact.  I remember I used to buy Ford trucks and people would ask me why I preferred them over Chevy.  My answer always came back: “Because of the quality of sound the door makes when you close it”.  It just tells you its a quality truck.  The same thing happens when you hold an iPhone or iPad or Mac or accessory…it just feels good.  I can feel the quality and appreciate the workmanship that went into creating that “device”.  Yet its not just a device, and I can tell because when I sell an old model to get a new one, the buyer gets his iPhone with the original plastic cover sticker still on the front and back, or white sheet of paper that comes with a macbook pro.  I do it because I want that new Apple user to become a fan boy by having the same experience I did when I got it new!

 

You know what, even if I never get a job at Apple, I would just like Steve Jobs to get this cover letter.  It would be enough satisfaction for me to know that he received this letter as a compliment and a big Thank You for how you have affected the life of this one customer and how grandly I think of Apple and its people.