iPhone 3.0 SDK Screencast on Map Kit Shipping
Just finished up the Map Kit screencast.
Permalink Add A CommentNext iPhone Studio Class Announced
As with the book now that Apple shipped the iPhone 3.0 update the NDA is officially gone and the iPhone Studio can officially be 3.0 aware. In celebration we also announced the next iPhone Studio for Aug 25 to 28 in Denver.
Would love to see you there!
Permalink 2 Comments - Add YoursiPhone 3.0 shipping, that means the book is good to go!
With Apple shipping the iPhone 3.0 update the NDA is officially gone and we were able to ship an updated copy of the book. There are a ton of changes for 3.0 but here are some of the highlights.
- Core Data
- Map Kit
- iPod Library Access
- AFFoundation
- Core Audio
- Updates throughout for 3.0 API changes
The book's page over at pragprog.com has some new excerpts too in case you are still on the fence about purchasing you can see a bunch of the new stuff in these new excerpts.
We are only a few weeks (6 to 8) away from the dead tree version now, boy I can't wait!
Permalink 1 Comment - Add YoursOpen GL ES 2.0 Resources
With the announcement of the iPhone 3Gs came the availability (well at least when the hardware ships) of a programmable Open GL pipeline. Basically that means that just about everything changes for the new device. Not only is the new pipeline going to make for much faster apps, but its going to open up the door to tons of very cool stuff. For example Core Image (the tech behind the cool ripple transition in Dashboard) requires a programmable pipeline. No word on Core Image coming to an iPhone SDK near you but you don't have to be a clairvoyant prognosticator to see that coming.
Since everything out side the keynote is under the NDA (but only lasts till June 17) I can't really talk about the amazingly cool demos they have been showing on the new hardware. However here are some decent resources on Open GL ES 2.0 to start to get your mind wrapped around the new stuff.
Khronos spec page.
PowerVR's site.
GLSL spec (PDF).
I read Open GL ES 2.0 but I can't recommend it as a starting point. If you don't know Open GL already its a bit rough as a starting point. If you already know Open GL ES 1.1 then its a decent book.
If you are brand new to the whole thing you can read Jeff LaMarche's excellent series on OpenGL ES 1.1.
Permalink Add A CommentWWDC Keynote - iPhone SDK Book Changes
New laptops were cool yesterday but the rest of the content was stuff we already knew about. Until the new iPhone 3Gs announcement.
Looking at the announcement the most exciting thing for me was OpenGL ES 2.0. I'm so stoked about that I can hardly wait to get my new device.
On the book front there were only a couple of small things to add. The Compass needs a couple of pages as well as the new video integration. Apart from that the book is good to go so we are very very close to finishing up and getting the book into the final bits of work (indexing and other technical book stuff).
Permalink Add A CommentWWDC Keynote Line
Having a great time in line for WWDC09. I know its crazy to be up at 4:30 am to watch a keynote, but...
Here is the line in front of me.
And the line behind me.
WWDC
If you are going to be a WWDC, I'd love to meet you. Please don't hesitate to say hi, one of the best things about WWDC is getting to meet new people. Here is my ugly mug.
I will probably be up early on Monday waiting in line for the keynote. I'll be around in labs and the coding areas and spending a bit of time at the Core Plot shindig. Evenings I'll be at various parties.
Hope to meet you there!
Permalink 3 Comments - Add YoursNSUnknownKeyException
Over in the book's forums I've gotten several questions about 'setValue:forUndefinedKey:'. They usually take the form of something like this.
Hey my app crashes with the following error.
... * Terminating app due to uncaught exception 'NSUnknownKeyException', reason:
'[ setValue:forUndefinedKey:]:
this class is not key value coding-compliant for the key someKey.'
What this message is trying to tell you (and I promise once you solve it once it makes total sense) is that the instance of MyViewController does not have a property called 'someKey'.
You have a couple of ways to figure out what the problem is. My favorite is to set a symbolic breakpoint on -[NSObject setValue:forUndefinedKey:]. To do that on the GDB command line type 'b -[NSObject setValue:forUndefinedKey:]'. To do that in the debugger double click on the 'add symbolic breakpoint' entry and paste in '-[NSObject setValue:forUndefinedKey:]' without the quotes.
Run your program again and when it breaks at your breakpoint look up the stack trace and you will likely see that one of your nib files is being loaded (other things could be causing this issue but if you are using them then you probably don't need this blog post). This clues you in that you have a problem in one of your nib files. Here is the stack trace from the simple example I cooked up.
(gdb) bt #0 0x305bdcb6 in -[NSObject(NSKeyValueCoding) setValue:forUndefinedKey:] () #1 0x30529298 in _NSSetUsingKeyValueSetter () #2 0x30528ce5 in -[NSObject(NSKeyValueCoding) setValue:forKey:] () #3 0x30ab59b0 in -[UIRuntimeOutletConnection connect] () #4 0x3026230f in -[NSArray makeObjectsPerformSelector:] () #5 0x30ab44b2 in -[UINib instantiateWithOptions:owner:loadingResourcesFromBundle:] () #6 0x30ab64b3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:] () #7 0x30976757 in -[UIViewController _loadViewFromNibNamed:bundle:] () #8 0x30974e33 in -[UIViewController loadView] () #9 0x30974d19 in -[UIViewController view] () #10 0x00002889 in -[GimpyAppDelegate applicationDidFinishLaunching:] (self=0xd18280, _cmd=0x9026b944, application=0xd12c00) at /tmp/Gimpy/Classes/GimpyAppDelegate.m:21 #11 0x308f8802 in -[UIApplication _performInitializationWithURL:sourceBundleID:] () #12 0x30901935 in -[UIApplication _runWithURL:sourceBundleID:] () #13 0x308fec73 in -[UIApplication handleEvent:withNewEvent:] () #14 0x308faac2 in -[UIApplication sendEvent:] () #15 0x30901121 in _UIApplicationHandleEvent () #16 0x32054375 in PurpleEventCallback () #17 0x30245560 in CFRunLoopRunSpecific () #18 0x30244628 in CFRunLoopRunInMode () #19 0x308f904c in -[UIApplication _run] () #20 0x30901f2e in UIApplicationMain () #21 0x00002834 in main (argc=1, argv=0xbffff11c) at /tmp/Gimpy/main.m:14
Notice that in the stack trace on the 5th and 6th frames we are loading a nib file. You can find out which one in particular but going up the stack frame 6 frames and printing the first argument like this.
(gdb) up 6 #6 0x30ab64b3 in -[NSBundle(NSBundleAdditions) loadNibNamed:owner:options:] () (gdb) po *(id*)($ebp + 16) GimpyViewController
$ebp is the frame pointer and 16 bytes from the frame pointer is the first argument (this varies by hardware env, i was running in the simulator). So as you can see from this bit of GDB goodness our nib file of doom is 'GimpyViewController'. Open your file of doom and you should see something like this.
Notice the little yellow warning symbol in the bottom right hand side of the window. Double click on that and you should see something that looks like this.
If you double click on the error it will bring up a transparent window with the problematic connection highlighted in yellow. Delete that connection and you should be error free.
Now you might be asking 'how did that error get there'. In my case I added an IBOutlet to the GimpyViewController then made a connection in IB. Some time later I deleted the property that had the IBOutlet marker on it but never deleted the connection in IB. It also often happens that an IBOutlet adorned property is renamed without using the refactoring support built into Xcode.
You might also be wondering why this happens. As nib files are loaded they use KVC to make the connections that you specify. Which basically invokes a method like this [sourceObject setValue:destinationObject forKey:@"zippy"] where source object in this case is the GimpyViewController instance and the destination object is the text field that the view controller was connected to. When the setValue:forKey: method can't find the set method for the key (i.e. setZippy: in this case) it calls the setValue:forUndefinedKey: method giving the object one last chance to fix the error. If the object does not have a custom implementation of this method (i.e. NSObject's implementation is used) and error is thrown and you get the error message.
Hope this helps you get beyond this error message.
Permalink Add A CommentQuick Table View Weirdness
While working on a chapter of the iPhone book I came across an interesting feature/bug in table view.
Typically while adding content to a table view you don't want to call reloadData unless you absolutely have to because that pitches the whole stack of work that the table view did to get its drawing done. Instead you call one of insertRowsAtIndexPaths:withRowAnimation: or deleteRowsAtIndexPaths:withRowAnimation:. All this works fine if you start with at least one section. The example I was writing started with zero sections.
Adding a new object then calling insertRowsAtIndexPaths:withRowAnimation: leads to a really ugly error message. Like this;
2009-05-14 14:11:41.131 Conference[2716:20b] Serious application error. Exception was caught during ... change processing: *** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0) with userInfo (null) [Session started at 2009-05-14 14:11:41 -0600.] 2009-05-14 14:11:41.132 Conference[2716:20b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (0) beyond bounds (0)'
If on the other hand you call insertSections:withRowAnimations: for that initial insert, everything is a-ok.
Hope this helps someone running into the same issue.
Permalink 5 Comments - Add YoursPresenting Modal Views Bug
While continuing on the retooling of the iPhone book I ran into an irritating bug that has a very simple work around.
When you present a modal view controller via the presentModalViewController:animated: method the viewWillAppear: method gets called on the modal view controller before the view is actually loaded. This is a pain in the neck for me because I set the values for my GUI bits in this method. It was very irritating to see all my outlets being nil when they clearly should have been set to the connections I made in IB.
I found the bug by breaking in a much later method (one of the actions my modal VC implemented) and of course everything was set up as it should be (otherwise the action would not have been invoked). That led me to implement viewDidLoad and that is where I found that viewDidLoad was being called after viewWillAppear:
Fortunately this bug has a really simple work around. Before you present your view controller make it load its view by calling the view method. Here is some sample code.
- (IBAction)modal {
[self.second view];
[self presentModalViewController:self.second animated:YES];
}
Hope this helps someone avoid the hour or so it took me to track this down. I would expect this to be fixed in a upcoming release so this is not a long term need, but for your 2.2 apps it will hopefully help.
Permalink 7 Comments - Add YoursLife...
I have been so busy lately, its all been good but man what a crazy 3 or 4 weeks.
Right now I'm in the UK (some pics) getting ready for the NSConference put on by the great folks over at Mac Developer Network. I can't wait for the conference, its going to be a blast. But since I was coming all the way to the UK we figured that we should bring the whole family and make a trip of it. So everyone is here, we got cheap tickets by coming over on March 31 (2 weeks before the conference started). So now I'm running around doing all sorts of fun tourist stuff, nearly dying because of driving on the 'wrong' side of the road etc. We are finally starting to adjust to the time zone, but its gradual kids don't much get going to bed at what to their body is 1PM... It has been great fun so far though!
The week before this trip started I was at the Philly Emerging Technologies conference giving an 'Intro to iPhone Dev' talk. It was a blast and I meet some really smart folks there. Chariot and the other sponsors put on quite a show.
The week before that I was in Reston VA teaching the iPhone Studio. This was the first public class on the East Coast of the US and we had a great time. It was my first time back there in quite a while so it was great to catch up with some old friends.
The there was also the iPhone 3.0 beta, which is very exciting indeed but has caused me to go back over all my stuff for the iPhone Book. I am just about done with the 2.x tech review comments so I'll be starting in earnest on 3.0 work early next week.
Crazy but extremely fun times, if I am a bit quite here now you know why. In the mean time you can follow me on twitter.
Permalink 2 Comments - Add YoursIllustrator Work
Well I'm not happy with the colors but I am starting to feel comfortable with some of Illustrator after following through on my resolution
I basically followed this tutorial from Layers Magazine.
I'm not very good yet but its fun to try :)
iPhone OS 3.0 Beta
We are pouring over the docs preparing to rework the book for iPhone 3.0.
We are going to get 3.0 coverage into the book, however since its under NDA we are going to be quiet about the details.
I'm really excited about all the new stuff, especially Core Data (in the public slides so details are NDA but public facts are ok). It is going to be fantastic to work with CD on the iPhone, tons and tons easier than hard coding SQL into your code. Yet another reason to learn Cocoa :)
Sorry to be late with my thoughts and this post. Daniel and I have been swamped teaching class.
Permalink 9 Comments - Add YoursiVars vs Properties
While going over some of the errata on the book Chris, Daniel and I spent a bit of time talking about using '_'s in instance variables. It struck me during our conversation that I have not really seen much discussion on naming ivars vs naming properties. I figured I'd blog about it and get your thoughts.
This is how I am currently leaning because it clearly helps me keep in mind the ivar vs the property.
@interface Foo : NSObject {
NSString *_gak;
}
@property (copy) NSString *gak;
- (void)doSomething;
@end
What I don't like about this is that if I have an ivar that I want to make an outlet in IB (with IBOutlet) then I have an _ on the outlet name in IB. As Ashley points out in the comments though the current recommendation is to have a property for your outlets. Mostly I like that, but I'd also prefer sometimes that getting to an outlet be more difficult than calling a get method. As Brendan points out (also in the comments) though if you have a property you should place the IBOutlet tag on the property rather than the ivar. Semantically they are the same, practically though if you have your ivar and property named differently the ivar's name will show up in IB, which might not be what you want.
In the implementation I do this;
@synthesize gak = _gak;
- (void)doSomething {
NSLog(@"gak = %@", self.gak);
}
That keeps things nice and clean, I'm always using the get and set methods via the dot notation.
Thoughts? What do you do?
Permalink 12 Comments - Add YoursBonjour Network Server for iPhone
While helping my son build his first shipping iPhone app I was forced to dig into Bonjour. It has been quite a while since I did any Cocoa networking (distributed objects way back in the day) so I was nearly starting from scratch.
I started with the iPhone sample code for WiTap (search Xcode docs for it, there is not public URL AFAIK). A fine example as far as it goes, but I found it to be confusing because of the lack of commentary in the code. So I decided to start from scratch using WiTap as an example to look at but not copy the code from. I ended up copying a bit but more or less wrote a new server from scratch.
It was a ton of fun getting back into this. As with so many things on Cocoa once you peal back the skin you see so much more. The thing I really liked about this particular learning experience was getting deep into the delegation model that helps me not have to think too deeply about threads. It is really very nice and makes your life much easier. If you have not had the chance to look into this you should (this example or the URL loading code is a great place to start).
I have uploaded the example code here. Its licensed under the apache 2 license so do with it what you will. I will try to get it into a google code project at some point. I'll be discussing the code in this post so feel free to grab it and follow along.
The server we needed for BounceIt had to be simple to use both the API and the user scenarios it supported. To make it easy to use we choose Bonjour (no brainer) to handle the discovery and configuration. Once connected the peers send small packets back and forth so we also needed a way to limit the TCP buffer so it would send more often than a page full of data. To make the API easy to use we created a delegation protocol that gives the program the info in needs to support several different scenarios but remains simple to implement. We made all the methods required more from not being sure than out of necessity. Over time if its obvious that some of the methods are not really required they should be made optional.
The server has a relatively simple API. You create a new server using either initWithProtocol: or initWithDomainName:protocol:name:. Since we often ignore the name and domain, preferring to let Bonjour pick those for us, the first method is typically preferred. For the cases where you need to specify the domain or name use the second method. Keep in mind that the name is advisory and Bonjour treats this as a 'first come first served' request. In other words, if you ask for you server to be called "Bob's your Uncle" and there is another service already out there with that name you will be renamed by Bonjour to something it deems appropriate (most likely "Bob's your Uncle 2").
If the your app needs to know the actual name the service was published under you can get it from the server after it finishes starting.
After you create and init the server you set the delegate. The delegate of the server will get call backs when data arrives or when connections are made and lost.
Now that the server is setup and ready its time to send it the start: method. This is where the magic begins. If you have never messed with Bonjour it is really cool! Basically you register a service under the protocol string and the clients can look for that service using the same string. The client side does not need to know anything about the port numbers or underlying network protocol (udp, tcp etc). All we have to do from the client side is look up the protocol name using Bonjour and it pops out objects that can connect you to the server. Under the hood it is using dynamic DNS but you really don't need to know anything about that part of it (part of the beauty of Bonjour). The server takes care of looking for other services for you though so you don't have to mess with service discovery. The server sends it's delegate the serviceAdded:moreComing: method as services are discovered and serviceRemoved:moreComing: when services go away.
Before launching the Bonjour service though, the application needs a port. I know I just said 'hey you don't have to know the port number' but that was for the client side of the equation. The server must have a port to send data out and to receive info back. We just ask the OS for an open port and then use that to start up our service, the client configuration will be taken care of by Bonjour.
On to the start: method. First thing it does is create a CFSocket and configure it. The port number is set to zero which is how we ask the OS to choose a port for us. During the binding of the CFSocket to the native socket, the OS picks a port and assigns it to the native socket. We then ask the CFSocket for its address and from there we get the port. Kind of a pain in the neck because of the amount of code we have to write but fairly straightforward once you get your head wrapped around it. This stuff is easier on the Mac but the higher level NSPort class is not available on iPhone OS X.
Now that we have the port from the OS we can launch our Bonjour service. This is where the beauty of the Bonjour API's become evident. To launch the service we just create an instance of NSNetService, add it to the run loop, publish it and then set ourselves as the delegate. Four simple steps and we have a server started! Behind the scenes Bonjour is doing a ton of work with the dynamic DNS stuff. It lets us know what is happening by calling the delegate methods. The cool part is that all the behind the scenes stuff is going on in one or more threads and we did not have to do anything to manage them. The NSNetService and Bonjour takes care of all that for us and lets us know on the main thread through our delegate method implementations. It really is amazing when you let is sink in how easy it is to get network ability into your app. The server implements all the necessary Bonjour delegation API's and converts them into the server delegate method calls.
Now that the server is started we wait for notification via the server delegate methods. In the sample app each new server discovered is added to the list of available servers and displayed in a table view. When the user clicks on one of the entries the connection is made to the underlying socket. Now that the two services are connected the Bonjour service is stopped and the socket is used for further communication between the two services.
I tried to comment all the places in the code that were tricky to me but I'm sure there are places where I either did not document the code well or I got something wrong. Please let me know if you find something that does not make sense.
Happy networking :)
Permalink 3 Comments - Add Yours







