…wish me luck!
Here’s Funny or Die’s Mac Story, which features Apple products in place of Toy Story’s toys, the iPhone 5S playing the part of Buzz Lightyear, and a special guest appearance from a Microsoft product:
Update: Apple’s Perception Hurdle
And therein lies the perception hurdle that Apple faces: the fact that most of the improvements in the iPhone 5S are under the hood rather than clearly visible (and often “novelty”) changes, most notably the 64-bit A7 processor, which benchmarks beautifully:
I know, I know, it’s been over a fortnight since I last post a tutorial. Things have been a little bit crazy at work, but I plan to hope to use the next couple of days to get caught up, and then post a new tutorial next week.
In the last installment, I posted another installment of my iOS development tutorial series, iOS Fortnightly: SimpleWeather, a simple weather app that made use of the AFNetworking library to fetch the current weather conditions for any given city from Open Weather Map. If you’d followed the tutorial to the end, you’d have an app that looks like this when immediately after you launch it:
…and here’s what it looks like when displaying the current weather:
It’s nowhere near ready for the App Store, but in a few lines of code, it accomplishes a fair bit. If you’re new to Objective-C, it also covers some unfamiliar territory. This week’s set of articles will expand on the Simple Weather app. In this one, I’ll explain those parts of the code that might not be familiar to the novice iOS developer, namely Objective-C’s blocks and properties.
JSON Requests in AFNetworking
The meat of the SimpleWeather app is in the
Weather
class’ getCurrent:
method. It makes the call to Open Weather Map’s API, and it does so with this line:
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:weatherRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { weatherServiceResponse = (NSDictionary *)JSON; [self parseWeatherServiceResponse]; } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { weatherServiceResponse = @{}; } ];
AFJSONRequestOperation
is a class in the AFNetworking library for requesting and retrieving JSON data from a given URL. It has a single method, a class method called JSONRequestOperationWithRequest:success:failure:
. Here’s what its declaration looks like:
+ (AFJSONRequestOperation *)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest success:(void ( ^ ) ( NSURLRequest *request, NSURLResponse *response, id JSON ))success failure:(void ( ^ ) ( NSURLRequest *request, NSURLResponse *response, NSError *error , id JSON ))failure
JSONRequestOperationWithRequest:success:failure:
is a method that makes a request for some JSON from a given URL and:
- Does something if the requested JSON was successfully received
- Does something else if the requested JSON was not successfully received
Here are the method’s parameters:
Parameter Name | Description |
---|---|
urlRequest |
A NSURLRequest object representing our request for the current weather from Open Weather Map. |
success |
A function that should be executed if the operation was a success, meaning that the requested JSON object was successfully retrieved. |
failure |
A function that should be executed if the operation was a failure. This could mean that requested JSON object was not successfully retrieved, or that it was successfully retrieved, but couldn’t be parsed as JSON. |
The urlRequest
argument is pretty straightforward, but the success and failure arguments look more complicated. They’re block literals, and to talk about them, we need to talk about blocks.
A Very Quick Intro to Objective-C’s Blocks
If you’re reasonably well-versed in JavaScript, it’s quite likely that you’re familiar with anonymous functions. Here’s a quick and dirty example: an anonymous function that takes two numbers and returns their sum, stored inside a variable named doSomething
:
var doSomething = function(firstNumber, secondNumber) { return firstNumber + secondNumber; } alert(doSomething(2, 3));
In Ruby, there are a couple of types of anonymous functions: procs and blocks. Procs are the anonymous function type that you can bind to a local variable, which is close to the spirit of anonymous functions in JavaScript and Objective-C. Here’s the Ruby version of the anonymous function from the example above:
doSomething = Proc.new {|firstNumber, secondNumber| firstNumber + secondNumber} puts doSomething.call(2, 3)
And now, the Objective-C version:
int(^doSomething)(int, int) = ^(int firstNumber, int secondNumber) { return firstNumber + secondNumber; }; NSLog(@"%d", doSomething(2, 3));
It’s not all that different from the JavaScript and Ruby versions; it’s just that Objective-C requires explicit type information. Let’s take a look at the left side of the assignment first:
int(^doSomething)(int, int)
Here’s where cdecl comes in handy. It’s a site that translates declarations and casts from “C gibberish to English” and vice versa, comes in quite handy.
Enter int(^doSomething)(int, int)
into cdecl. cdecl will translate it as “declare doSomething
as block (int, int)
returning int
“. This in turn can be interpreted as “doSomething
is a variable containing an anonymous function that takes two int
s and returns an int
“.
Now let’s look at the right side of the assignment:
^(int firstNumber, int secondNumber) { return firstNumber + secondNumber; };
With the exception of the ^
character in front of the parameters, it looks like a like a standard function definition.
JSONRequestOperationWithRequest
‘s Block Parameters, success
and failure
Let’s look at JSONRequestOperationWithRequest
‘s success
parameter, which expects a block that should be executed if the JSON request operation is a success. Here’s its type:
(void ( ^ ) ( NSURLRequest *request, NSURLResponse *response, id JSON ))
This means that it expects a function that expects three parameters of type NSURLRequest *
, NSURLResponse *
, and id
, and doesn’t return anything. Here’s what we’ll pass to it:
^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) { weatherServiceResponse = (NSDictionary *)JSON; [self parseWeatherServiceResponse]; }
It does two things:
- It takes the retrieved JSON, casts it to
NSDictionary *
, and stores it in the instance variable (or, as they like to say in Objective-C, ivar)weatherServiceResponse
. Note that the block “knows” aboutweatherServiceResponse
- It calls the method
parseWeatherServiceResponse
, which extracts the data from the JSON retrieved from Open Weather Map and puts them into the instance variables behind the class’ properties.
Let’s now look at JSONRequestOperationWithRequest
‘s failure
parameter, which expects a block that should be executed if the JSON request operation is a failure. Here’s its type:
(void ( ^ ) ( NSURLRequest *request, NSURLResponse *response, NSError *error , id JSON ))
This means that it expects a function that expects four parameters of type NSURLRequest *
, NSURLResponse *
, NSError *
and id
, and doesn’t return anything. Here’s what we’ll pass to it:
^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) { weatherServiceResponse = @{}; }
Right now, we’re not worrying about what to do in the event of a failure, so for the moment, the failure
block simply sets weatherServiceResponse
to be an empty dictionary.
A Quick Look at Objective-C’s Properties and the Invisible Goodies that Come with Them
Consider the first line in the parseWeatherServiceResponse
method:
_cloudCover = [weatherServiceResponse[@"clouds"][@"all"] integerValue];
If you look around the class, you’ll find that the variable _cloudCover
hasn’t been declared anywhere. Yet Xcode hasn’t thrown up any error messages, and it colour-codes it as it would any declared variable. What’s going on here?
It turns out that there’s a little behind-the-scenes magic that takes place whenever a @property
is declared. If you look in Weather.h, you’ll see this declaration:
@property (nonatomic, readonly) NSInteger cloudCover;
Implicit in this declaration is this corresponding line in the class’ .m file:
@synthesize cloudCover = _cloudCover;
This line doesn’t actually appear in the .m file, but it’s effectively there thanks to a little compiler magic. @synthesize
itself does its own magic; it creates an invisible corresponding instance variable and invisible “getter” and “setter” methods. Implicit in the line above is this instance variable declaration:
NSInteger _cloudCover;
and these methods:
- (NSInteger)cloudCover { return _cloudCover; } - (void)setCloudCover:(BOOL)newValue { _cloudCover = newValue; }
Property and ivar Order
Speaking of properties and ivars, take a look at the order of the ivars in parseWeatherServiceResponse
, which lives in Weather.m:
- (void)parseWeatherServiceResponse { // clouds _cloudCover = [weatherServiceResponse[@"clouds"][@"all"] integerValue]; // coord _latitude = [weatherServiceResponse[@"coord"][@"lat"] doubleValue]; _longitude = [weatherServiceResponse[@"coord"][@"lon"] doubleValue]; // dt _reportTime = [NSDate dateWithTimeIntervalSince1970:[weatherServiceResponse[@"dt"] doubleValue]]; // main _humidity = [weatherServiceResponse[@"main"][@"humidity"] integerValue]; _pressure = [weatherServiceResponse[@"main"][@"pressure"] integerValue]; _tempCurrent = [Weather kelvinToCelsius:[weatherServiceResponse[@"main"][@"temp"] doubleValue]]; _tempMin = [Weather kelvinToCelsius:[weatherServiceResponse[@"main"][@"temp_min"] doubleValue]]; _tempMax = [Weather kelvinToCelsius:[weatherServiceResponse[@"main"][@"temp_max"] doubleValue]]; // name _city = weatherServiceResponse[@"name"]; // rain _rain3hours = [weatherServiceResponse[@"rain"][@"3h"] integerValue]; // snow _snow3hours = [weatherServiceResponse[@"snow"][@"3h"] integerValue]; // sys _country = weatherServiceResponse[@"sys"][@"country"]; _sunrise = [NSDate dateWithTimeIntervalSince1970:[weatherServiceResponse[@"sys"][@"sunrise"] doubleValue]]; _sunset = [NSDate dateWithTimeIntervalSince1970:[weatherServiceResponse[@"sys"][@"sunset"] doubleValue]]; // weather _conditions = weatherServiceResponse[@"weather"]; // wind _windDirection = [weatherServiceResponse[@"wind"][@"dir"] integerValue]; _windSpeed = [weatherServiceResponse[@"wind"][@"speed"] doubleValue]; }
In this method, I’m extracting data from the JSON objects returned by Open Weather Map, so they’re ordered from that perspective: by the keys of the top-level dictionaries in alphabetical order: clouds
, coord
, dt
, main
, name
, rain
, snow
, sys
, weather
, wind
. Those dictionaries’ elements are also listed in alphabetical order by key. The idea is to make things easy to find when programming with Open Weather Map’s API in mind.
The order of the corresponding @properties
exposed in Weather.h is different. Here, I also want things to be easy to find, but I also want to abstract away any details about the format in which Open Weather Map returns its info. Instead, I want an order that’s useful for when you just want the weather, so I’ve used comments to break the properties into three groups: place and time, qualitative, and quantitative.
// Properties // ========== // Place and time @property (nonatomic, copy, readonly) NSString *city; @property (nonatomic, copy, readonly) NSString *country; @property (nonatomic, readonly) CGFloat latitude; @property (nonatomic, readonly) CGFloat longitude; @property (nonatomic, copy, readonly) NSDate *reportTime; @property (nonatomic, copy, readonly) NSDate *sunrise; @property (nonatomic, copy, readonly) NSDate *sunset; // Qualitative @property (nonatomic, copy, readonly) NSArray *conditions; // Quantitative @property (nonatomic, readonly) NSInteger cloudCover; @property (nonatomic, readonly) NSInteger humidity; @property (nonatomic, readonly) NSInteger pressure; @property (nonatomic, readonly) NSInteger rain3hours; @property (nonatomic, readonly) NSInteger snow3hours; @property (nonatomic, readonly) CGFloat tempCurrent; @property (nonatomic, readonly) CGFloat tempMin; @property (nonatomic, readonly) CGFloat tempMax; @property (nonatomic, readonly) NSInteger windDirection; @property (nonatomic, readonly) CGFloat windSpeed;
As with the ordering in parseWeatherServiceResponse
, the objective is to make things easy to read and find, but this time, I’m doing so not from the perspective of a programmer trying to get data from the Open Weather Map API, but a programmer trying to get data from an instance of the Weather
class.
Previous Installments in the iOS Fortnightly Series
In case you missed them, here’s a list of the previous articles in this series:
My article from April 30th, Delusional CEO of Company Scrambling for Distant Third Place Says They’ll Be the “Absolute Leader” in Five Years, has been getting crazy hits since yesterday morning, thanks to its money quote:
“In five years, I see BlackBerry to be the absolute leader in mobile computing — that’s what we’re aiming for.”
Of course, we all had to keep in mind that as CEO, Heins’ job is to play the part of the cheerleader, boost confidence in his company and not say things like “O-M-G, we’re so dead.”
However, industry pundits have a different job: to read the tea leaves and call ’em as they see ’em. Here are some notable predictions that I saved in my bookmarks for a later date…
Jim Cramer, March 21 and 29, 2012: “I believe that in a few months from now, it’ll be clear that RIM did well”
On The Street on March 21, 2012, Jim “Mad Money / Bear Stearns is fine” Cramer, said that although RIM’s share price was dropping at the time, that it was the right time to buy:
- “RIM right now is the established player, not Apple.”
- “I believe that in a few months from now, it’ll be clear that RIM did well.”
A mere eight days later — March 29, 2012 — he’s singing a slightly different tune: “Things have passed [RIM] by, just the way Nokia got passed by”:
BerryReview, September 12, 2011: “The two main players would be Apple and RIM”
Why I Think RIM Will Succeed…The QNX Powered Comeback, published in BerryReview on September 12th, 2011, includes these gems:
- “I have been of the opinion for a while that Android and Android users have no tech identity beyond a very small rabid tech programming geek community. The average Android user doesn’t identify with the Android Platform nor does the Android Platform identify with the average user. I think Android users are the ones that are most likely to switch back to Blackberry. Android carries NO cache, no history, no track record and no loyalty in its users.“
- “Android development will slow in the next 12-24 months.“
- “Ice Cream Sandwich, ICS, will hit in the coming months, and, we generally know that it will not be anything revolutionary, just incremental.“
- “Whoever is first to this race will likely be the winner and really, the two main players would be Apple and RIM. Android is in no way remotely stable enough to get into this business and Microsoft’s OS is way too new and unpopular to enter the space. If RIM can get into this segment of the industry first, they have a strong chance of being the dominant force in the industry. QNX gives them that head start that Apple doesn’t have.“
IT World Canada, October 9, 2012: “RIM is NOT another Nortel”
10 Solid Reasons RIM Will Make a Comeback roots for the home team, which I can get behind. For the longest time, RIM was a source of Canadian pride, and one of the examples I’d cite in a speech I’d open with “Since Alexander Graham Bell, Canada has always punched above its weight class in communications technologies.”
However, when I first read this article, it lost me at its first reason for a RIM comeback: “Developers believe in BB10”. It turns out that these were developers working for RIM: “I personally know several developers who are still working for RIM and who are not the least bit interested in jumping ship.” That’s because you don’t jump ship before something you’ve worked on ships; you do it afterwards. Otherwise, you don’t have as good an answer when interviewing at your next gig and the “So what was the last project you worked on?” question comes up.
CNBC, November 26, 2012: RIM Could Rise on Wings of BlackBerry 10: Pro
The rationale in the article RIM Could Rise on Wings of BlackBerry 10: Pro comes from delusional National Bank analyst Kris Thompson: “”We don’t want to bet against the dollars flowing to the stock,” and “They have 80 million subscribers today. A lot of us are very loyal. Think about the business users that like to generate a lot of emails during the day: it’s very difficult to do that on a lot of the competing platforms with a virtual keyboard.”
He does emphasize that the investments that were leading to a rising stock price — one that he predicted could go as high as $20 (it did hit $17.90 on January 22nd) — are “speculative” and not based on “fundamentals”.
CIO, December 28, 2012: Why 2013 is RIM’s BlackBerry Year
Rob Enderle, the analyst whom Dell hired to help them with their efforts in taking on Apple with making an MP3 player and getting into music — an effort even more forgotten than Microsoft’s Zune — bet on the wrong horse again when he explained Why 2013 is RIM’s BlackBerry Year. His wrongness is best summed up in the second-last paragraph of the article:
I’ve had some time to talk to RIM about its upcoming platform, and it appears to address each one of these shortcomings with a vengeance. BlackBerry 10 is based on an OS that is used to operate machinery. RIM started with a business oriented core and then addressed consumer needs—as opposed to the more common approach of putting a business façade over a device that was targeted first at consumers.
Of course, the PC industry in which Rob plays (and often gets so, so wrong) got started by going consumer-first, then adding the business stuff later. They were either hobbyist machines like the Apple II and TRS-80, and even the original IBM PC was created by the dismissively-named “Entry Level Systems Division”, which hinted that it was something to tide you over until you got a big-boy computer like one of their mainframes or minis.
I was at the Toronto Mini Maker Faire earlier today, and managed to get a shot with this little fella, who was incredibly popular with the kids.
Also:
Shout out to @AccordionGuy – thanks for coming and playing a Star Wars theme. On an accordion. only at #MakerFaireTO :)
— Maker Faire Toronto (@MakerFaire_TO) September 22, 2013
This article also appears in The Adventures of Accordion Guy in the 21st Century.
Here’s a screenshot history of the default “home row” of icons in iOS, from version 1 all the way through version 7:
And since iOS now looks more like Windows Phone these days, one wag has speculated how far this evolution will continue in iOS 8:
Picture courtesy of “Tofutti Break”.
BarCamp Tampa is back, and I’m going to be there! BarCamp Tampa 2013 takes place this Saturday, September 28th in Tampa at USF’s College of Business (4202 East Fowler Avenue), with registration starting at 7:30 a.m. and the first session starting at 9:00 a.m.. Admission is free, but if you’d like to help sponsor the event, there are options for you as well — see the registration site for the details. If you haven’t signed up yet, do so now, before all the ticket slots run out!
Here’s a video that gives a quick run-down of what BarCamp is all about:
BarCamp is an “unconference”, which means that it’s a gathering that looks like a conference to the casual observer, but actually turns some of the basics of a conference upside-down. For starters, there’s no agenda until the morning of BarCamp. At the very start of the day, the agenda is blank, and it’s up to attendees who want to do a presentation to fill it. By filling in a spot on the agenda, you claim a room and a time slot. Non-presenting attendees can look at the schedule and choose which sessions they want to attend, and they are encourage to actively participate in the session, with questions, comments and points of information. Many presenters come in with a prepared topic and presentation, but impromptu ondes are welcome, and some of the best BarCamp presentations I’ve seen have been off-the-cuff, freewheeling discussions fuelled by the presenter’s knowledge and passion. As the BarCamp Tampa folks say on their site, “the day is what you make it, by choosing presentations to attend and deciding whether or not to present a topic.”
BarCamp topics are all over the map, but they’re usually of interest to people with an interest in technology, the internet, and community. If you’re a designers, developer, marketer, copy writers, SEO specialist, sysadmins or run a business with a web presence, you’re likely to find something of interest to you.
“The endearing quality of the unconference format is that you have options all day,” says BarCamp Tampa’s site, and it’s right. One of the most important rules of BarCamp is the Rule of Two Feet. If you’re in a session and it isn’t doing it for you, you are free — and even encouraged — to leave and check out another session. Or hang out in the hallway or other common areas, or visit the sponsor booths, or do anything else that interests you.
I’ll let the site do the talking here: “Barcamp is a community event, both in participation and in funding. It is made possible by the time and money donated by the crew of people running the event, the attendees and companies that support us. Thank you to all of you! Each year we have some fantastic companies that sponsor by donating time, money, facilities and supplies for the day. We’re also very lucky to get great support from attendees making donations in order to attend. We hope that everyone takes the time to check out the companies and people that support us!”
Once again, the details are:
- When: Saturday, September 28th, 2013
- Doors open/registration at 7:30 a.m.; first session starts at 9:00 a.m.
- Where: USF College of Business, 4202 East Fowler Avenue, Tampa
- How much: Free-as-in-beer. You can also pay if you’d like to help sponsor the event
See you there!
This article also appears in The Adventures of Accordion Guy in the 21st Century.