Nick Harris

Unit Testing

leave a comment »

I started a new Xcode project this week to kick the tires of the new unit testing framework built into Xcode 5. I’ve long been an advocate of unit testing. Its great to see it being tightly integrated into Xcode and continuous integration. My impression so far has been very positive. Most of the hoops you needed to jump through in the past are now gone reducing the excuses to not unit test.

If you’re on the fence about unit testing, here’s a few thoughts to consider…

Code Design
Unit testing forces you to rethink how you design your code. I compare it to building a brick wall. Instead of focusing on the entire wall, you concentrate on just a single brick. The brick needs to be crafted well and tested to make sure it won’t fail and bring down the wall. Unit testing forces you to break down complex problems into manageable, understandable and testable pieces.

You’re Not Coding Alone
Most likely you’re working on a project with other developers (if you’re working by yourself you should consider your past self as another developer). Unit tests show other developers how you intended your code to be used. If someone needs to modify code you wrote, your tests will tell them if they are keeping with your intent.

Unit tests are also very handy when coding against web services. Your tests will alert you to changes in the request or response allowing you to adjust your code appropriately.

Reproducing Bugs
Unit testing also involves creating a mock environment for the tests to run against. The mock environment can help you reproduce real world bug reports. Its much easier to tweak a few parameters in a mock environment then to try and recreate a real world situation.

It may seem like you’re writing double the code. That’s because you are, but its well worth the effort.

Here are some unit testing resources from Apple:

Setting up Unit Testing and Continuos Integration
Sample Project 
WWDC Session 409 – Testing in Xcode 5 

Unit Test. You’ll thank yourself later.

Written by Nick Harris

January 7, 2014 at 1:11 am

Posted in Uncategorized

Introducing Bands

leave a comment »

When I was approached to write a book for the beginner’s series from Wrox, I decided I wanted to take a different approach then any of the other beginning iOS books I had bought.  I’ve written software in many languages and never found a beginners book that suited me. Instead I learned by creating something I could use. I decided to take that approach for the book.

“Beginning iOS Development – Building and Deploying iOS Applications” is now written, through edits and off to production. I believe it will be available in late January or early February.

The app the book builds is called Bands. It’s a simple app that would never win an Apple Design Award but instead walks readers through first step things like wiring up an IBAction to a button to advanced topics like using NSURLSession and completion handler blocks against the iTunes Search API.


Band list

Band details

Activity options

Map wide
 

Tracks tall

The chapter I’m most proud of is Chapter 2 “Introduction to Objective-C.” The book is aimed at devs who have written Java or C#. This chapter teaches Objective-C by comparing things like classes, methods and properties to similar code written in Java and C#. It also covers Manual Reference Counting, Automatic Reference Counting and Model-View-Controller. It’s the chapter (and honestly the book) I wish I had in 2008 when I started writing iOS apps.

Its been exhausting. The worthwhile experiences often are.

I hope readers learn as much reading this book as I learned writing it.

Written by Nick Harris

December 16, 2013 at 6:19 am

Posted in Uncategorized

Password Security Red Flag

leave a comment »

If you select “forgot password” from a service and they send you your old password in an email.

Written by Nick Harris

September 3, 2013 at 5:37 am

Posted in Uncategorized

Follow-up: Game Center Sandbox Invites

leave a comment »

In my previous post I laid out the issues I was having with the Game Center Sandbox sending and accepting game invitations.

In the comments you’ll see a link to an Apple Developer Forum thread with a handful of others who have been experiencing the same issue. I also had Game Center experts review the sample code with the only explanation being a bug with the Sandbox.

As of August 30th all seems to be working again. I’ve confirmed with my own code.

For a company that prides its self on things that “just work”, this is unacceptable. I’ve had to delay my app development 6 weeks for a service that I have no control over. Any other service provider would have been forced to at least publicly acknowledge the issue. Others would have been run out of business by developers moving to other solutions.

Perhaps this puts into perspective how Apple feels about the viability of Game Center.

Written by Nick Harris

September 3, 2013 at 2:26 am

Posted in Uncategorized

New Job Title: Author

leave a comment »

I start a new job today; I’m writing a book. Beginning iOS Programming for the Wrox line from Wiley Publishing.

Writing a book has been on my to-do list since college when I decided to minor in English to help balance my Engineering degree. I wasn’t expecting the opportunity until later in my career, but you know what they say about when opportunity knocks.

The truth though is that I’ve been working on the book for almost two months.

An Email

Back in late June I got an email from Mary James, an acquisition editor with Wiley wondering if I was interested in co-authoring a project based beginners book for iOS. My co-author, John May, had many more years experience then myself and had written five books previously. It seemed like the perfect opportunity so I took it.

I’m fairly certain my first email to John included the phrase “deer in headlights”. I had absolutely no idea what I was doing nor did I know how a book goes from idea to final print. John was a great mentor, but unfortunately that only lasted a couple of weeks. He got one of those offers you can’t refuse and had to drop working on the book.

I’m not one to give up an opportunity even if I have no idea what I’m doing, so I decided I would figure it out and do it myself.

Who. What. When. Why. How.

The first part is writing the book proposal. The best way to think of a book proposal is market research. Publishers want to know who’s going to buy it, what makes it unique, when you can deliver and why you should be the one to write it. How is up to the publisher.

Publishers already know most of those answers. What they really want is for you to do the research yourself so you know how to differentiate yourself from the crowd then show them how you’re going to do it. Personally I loved doing this research.

Map It

The next major part of the proposal is your complete Table of Contents. Obviously they know it will change as the book comes together, but it forces you as the author to basically write the book with just chapter titles and sub headers. You’ll also need to give estimated page counts as well as when you can deliver on each chapter. Essentially it becomes the roadmap to the final product for the publisher. It also helps you the author organize your thoughts so you can quickly execute should the proposal get approved.

Prove It

The last part, at least for first time authors with Wrox, is submitting a writing sample using the tools the publisher uses. Just like the Table of Contents, this benefits both the publisher and you the author. The publisher wants to see that you can write but also that you can produce within their parameters. You as the author get to learn the tools and figure out if you’re comfortable using them.

It’s a bit of a gauntlet, but with purpose. If you do well the publisher approves the proposal and you’re on your way.

Do It

That’s where I am. My proposal, Table of Contents and sample chapter on implementing Local Search in MapKit were approved last week.

Today I start writing.

Written by Nick Harris

August 26, 2013 at 6:24 am

Posted in Uncategorized

Game Center Sandbox – Troubleshooting Invites – Help Wanted!

with 2 comments

I’ve been working on integrating Game Center into my game for a few months now. The Game Kit framework is fairly straightforward. The hardest part I originally found was engineering my code to be server/client friendly. This was before the dev center went down. Since then, invitations in the Game Center Sandbox have become a nightmare.

To figure out what’s going on, I created a small sample app to demonstrate the issues I’m seeing.

If you’re a Game Center expert I’d love to get your opinion. Are the following issues with my code or are they issues with the Game Center Sandbox?

If you’re looking to learn Game Center you might find this little sample app helpful with getting started since it shows how to authenticate, get game center friends, send and accept invitations to a match as well as sending simple data to players.

The Sample AppYou can get the code here (*) (**)

demoappsuccess

The app is very simple. It authenticates the local user, gets the users Game Center friends and shows them in a table, then sends invites when the user taps a friends name. Once the match is established and everyone is connected, the players send a simple string of data to each other. All of the interaction is logged to the screen so you can see what’s happening in the code.

The Issues

Issue: Invites sent from one device never show on the other.

Game Center allows you to either invite specific friends to a match or use auto-match, which looks for other players waiting to join a match. I’ve opted not to use auto-match, instead inviting one specific friend at a time. Both use a GKMatchRequest, but I supply a playerID when creating the match.

The first player gets invited using findMatchForRequest:withCompletionHandler: (which I’ll refer to as findMatch). If the invitation is processed correctly, the app will get a GKMatch in the completion handler.

Subsequent players get invited using addPlayersToMatch:matchRequest:completionHandler: (which I will refer to as addPlayer).

In the past I’d occasionally see that invites from one device would never show on another. This issue seems to have been around for quite a while with a couple troubleshooting steps to make them show up again.

First, make sure you have Game Center notifications turned on the invitee device. The push notification for the invite will never show up if they’re turned off.

Second, use the Game Center app to log out of Game Center, then launch the Sample App and let it handle re-authenticating you. In the past I would need to do this every half hour or so while testing to get invitation push notifications working again.

These steps no longer work for me.

Instead, what I’ve found with the sample app is that repeatedly adding the player to the existing match will eventually get the invite through to the other device. Usually the findMatch call fails to get the invite through but the first addPlayer does work. Additional players often need two or more calls to addPlayer to get other invites out.

addplayer

This should all work the first time. I can’t figure out why I need to repeatedly add the same player to a match to get the invite to show on the other device.

Issue: inviteeResponseHandler doesn’t get called

Before calling either findMatch or addPlayer, the GKMatchRequest’s inviteeResponseHandler is set. Once the invitee either accepts or declines the invite, the handler gets fired so the inviter knows the fate of the invite. When the invitee accepts the invite, its able to get the match.

This use to fire every time I accepted or denied an invitation. Now it’s very hit or miss.

When it doesn’t fire, the invitee’s match delegate will eventually get a match:player:didChangeState: call saying the inviter has left the match. The inviter never gets any notifications. It’s interesting that the invitee sees this when its never called for the inviter connecting to the match.

inviterdisconnected

I’ve found that if I continue to re-invite the friend repeatedly it will eventually fire the correct callbacks and get all the players connected to the match.

When this process does finally complete, the devices are able to send data back and forth with no issue for the duration of the match.

repeatforsuccess

So…

Does anyone know if this is an issue with the Sandbox or is it my code? Does Apple review apps against the Sandbox or against the production servers?

If it’s my code I can fix it. If it’s the sandbox, how to I successfully test my app before submitting it for review?

(*) You’ll need to set the Bundle ID and Code Signing to one you’ve setup with iTunes Connect in order to test.

(**) GameCenterManager class is based on Kyle Richter book “Beginning iOS Game Center and Game Kit”.

Written by Nick Harris

August 20, 2013 at 7:03 am

Posted in Uncategorized

iOS Slide-out Navigation Code

Ken Yarmosh has a great post entitled New iOS Design Pattern: Slide-out Navigation in which he does a great job describing this new design pattern and how different applications are using it.

I personally like the approach so I decided to figure out how I would implement it.  I have no idea if any of the apps mentioned in Ken’s post actually do it this way, but it works.

Slide-out Navigation

The idea here is use the Application Delegate as the controller between the current content view of the application and the menu view.  When a view in the application needs to show the navigation menu, it can call a method on the App Delegate that handles all the work of showing and restoring the menu and content views.

The “illusion” of the content view sliding off to reveal the menu view is done by first grabbing a screenshot of the current content view and passing that off to the MenuViewController.  The MenuViewController has a UIImageView that it populates with that screenshot.  Its this UIImageView that acts as the content view overlay. It makes animation smooth using a single flat image rather then trying to animate an actual view back and forth.

AppDelegate

The AppDelegate acts as the central controller whenever any views need to show the menu view. It gets two new properties:

@property (strong, nonatomic) ContentViewController *contentViewController;

@property (strong, nonatomic) MenuViewController *menuViewController;

 

The contentViewController gets used as a temp holding place while the menuViewController is visible.  Having the two of them in the app delegate makes switching the window.rootViewController easier.  I do this in two new methods on the AppDelegate:

-(void)showSideMenu

{

// before swaping the views, we'll take a "screenshot" of the current view

// by rendering its CALayer into the an ImageContext then saving that off to a UIImage

CGSize viewSize = self.contentViewController.view.bounds.size;

UIGraphicsBeginImageContextWithOptions(viewSize, NO, 1.0);

[self.contentViewController.view.layer renderInContext:UIGraphicsGetCurrentContext()];

 

// Read the UIImage object

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

 

// pass this image off to the MenuViewController then swap it in as the rootViewController

self.menuViewController.screenShotImage = image;

self.window.rootViewController = self.menuViewController;

}

 

-(void)hideSideMenu

{

// all animation takes place elsewhere. When this gets called just swap the contentViewController in

self.window.rootViewController = self.contentViewController;

}

 

MenuViewController

The MenuViewController handles all the animation and touch gestures that make the slide-out navigation feel real.  It has two important properties:

@property (strong, nonatomic) IBOutlet UIImageView *screenShotImageView;

@property (strong, nonatomic) UIImage *screenShotImage;

 

Using the viewWillAppear:animated method we can reset the menu view, covering the entire view with the screenShotImageView whose image has been set using the screenshot taken by the app delegate of the currentContentViewController.  It’s then just animated to the right.

-(void)viewWillAppear:(BOOL)animated

{

[super viewWillAppear:animated];

 

// when the menu view appears, it will create the illusion that the other view has slide to the side

// what its actually doing is sliding the screenShotImage passed in off to the side

// to start this, we always want the image to be the entire screen, so set it there

[screenShotImageView setImage:self.screenShotImage];

[screenShotImageView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];

 

// now we'll animate it across to the right over 0.2 seconds with an Ease In and Out curve

// this uses blocks to do the animation. Inside the block the frame of the UIImageView has its

// x value changed to where it will end up with the animation is complete.

// this animation doesn't require any action when completed so the block is left empty

[UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{

[screenShotImageView setFrame:CGRectMake(265, 0, self.view.frame.size.width, self.view.frame.size.height)];

}

completion:^(BOOL finished){  }];

}

 

The easy part of the MenuViewController is if the user selects a new view.  Simply set the currentViewController on the AppDelegate with the new view, then call the slideThenHide method which animates the screenshot back over the entire screen completing the illusion.

-(IBAction)showLogoExpandingViewController

{

// this sets the currentViewController on the app_delegate to the expanding view controller

// then slides the screenshot back over

[app_delegate setContentViewController:[[LogoExpandingViewController alloc] initWithNibName:@"LogoExpandingViewController" bundle:nil]];

[self slideThenHide];

}

 

-(void) slideThenHide

{

// this animates the screenshot back to the left before telling the app delegate to swap out the MenuViewController

// it tells the app delegate using the completion block of the animation

[UIView animateWithDuration:0.15 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{

[screenShotImageView setFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];

}

completion:^(BOOL finished){ [app_delegate hideSideMenu]; }];

}

 

The more difficult part is detecting when the user interacts with the screenshot.  If they touch it, the end result should be the menu hiding and the content view becoming active again.  So it needs a UITapGestureRecognizer.

It also needs to be user interactive.  If they touch and drag the screenshot it should respond by moving. So it also needs a UIPanGestureRecognizer.

I wasn’t up to speed with dragging using Gestures so after a little searching I found this post by Soo How Ng which made it pretty simple:

- (void)viewDidLoad

{

[super viewDidLoad];

 

// create a UITapGestureRecognizer to detect when the screenshot recieves a single tap

tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapScreenShot:)];

[screenShotImageView addGestureRecognizer:tapGesture];

 

// create a UIPanGestureRecognizer to detect when the screenshot is touched and dragged

panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureMoveAround:)];

[panGesture setMaximumNumberOfTouches:2];

[panGesture setDelegate:self];

[screenShotImageView addGestureRecognizer:panGesture];

}

 

- (void)viewDidUnload

{

[super viewDidUnload];

 

// remove the gesture recognizers

[self.screenShotImageView removeGestureRecognizer:self.tapGesture];

[self.screenShotImageView removeGestureRecognizer:self.panGesture];

}

 

- (void)singleTapScreenShot:(UITapGestureRecognizer *)gestureRecognizer

{

// on a single tap of the screenshot, assume the user is done viewing the menu

// and call the slideThenHide function

[self slideThenHide];

}

 

/* The following is from http://blog.shoguniphicus.com/2011/06/15/working-with-uigesturerecognizers-uipangesturerecognizer-uipinchgesturerecognizer/ */

-(void)panGestureMoveAround:(UIPanGestureRecognizer *)gesture;

{

UIView *piece = [gesture view];

[self adjustAnchorPointForGestureRecognizer:gesture];

 

if ([gesture state] == UIGestureRecognizerStateBegan || [gesture state] == UIGestureRecognizerStateChanged) {

 

CGPoint translation = [gesture translationInView:[piece superview]];

 

// I edited this line so that the image view cannont move vertically

[piece setCenter:CGPointMake([piece center].x + translation.x, [piece center].y)];

[gesture setTranslation:CGPointZero inView:[piece superview]];

}

else if ([gesture state] == UIGestureRecognizerStateEnded)

[self slideThenHide];

}

 

- (void)adjustAnchorPointForGestureRecognizer:(UIGestureRecognizer *)gestureRecognizer {

if (gestureRecognizer.state == UIGestureRecognizerStateBegan) {

UIView *piece = gestureRecognizer.view;

CGPoint locationInView = [gestureRecognizer locationInView:piece];

CGPoint locationInSuperview = [gestureRecognizer locationInView:piece.superview];

 

piece.layer.anchorPoint = CGPointMake(locationInView.x / piece.bounds.size.width, locationInView.y / piece.bounds.size.height);

piece.center = locationInSuperview;

}

}

 

Sample

 

Source Code

https://bitbucket.org/nh129096/slideoutnavigationsample/src

 

Written by Nick Harris

February 5, 2012 at 3:53 am

Posted in Uncategorized

Follow

Get every new post delivered to your Inbox.

Join 480 other followers