Wednesday, March 13, 2013

Cocoa Debugging Tip


I'm attending my local Ann Arbor CocoaHeads meeting tomorrow, and the topic is
Objective Tips.  So I thought I would share a tip.

What do you do when you get a crash due to an uncaught exception such as:

2013-03-13 13:30:10.186 Picross[43233:1303] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AppDelegate crash]: unrecognized selector sent to instance 0xc04de10'*** First throw call stack:
(0x355b012 0x32ffe7e 0x35e64bd 0x354abbc 0x354a94e 0x3313663 0x12f54 0x3f0153f 0x3f13014 0x3f042e8 0x3f04450 0x926b5e12 0x9269dcca)
libc++abi.dylib: terminate called throwing an exception

This can get really frustrating as you need to figure out where in your code it crashed.  Debugger to the rescue.  In the call stack, you can find the first "low" value.  This usually represents your code.  Then you just do a symbol lookup on that value.  Such as the following when using LLDB.
im loo -a 0x12f54
 This does a image lookup which gives a nice dump including the file and line number of the offending code:

Address: Picross[0x00012f54] (Picross.__TEXT.__text + 67540)
Summary: Picross`__57-[AppDelegate application:didFinishLaunchingWithOptions:]_block_invoke112 + 52 at AppDelegate.m:216

You can see from this dump that the offending code was at line 216 in the AppDelegate.m and was from a block where I was calling a selector that didn't exist.


I also find this GDB to LLDB guide to be a handy reference of the commands available in LLDB.

I hope this short quick tip was helpful.  Please feel free to follow me on twitter at @fivelakesstudio. I would love to hear about your experiences with the debugger or any tips you might have.


Monday, October 22, 2012

GameCenter Turn Based Matches


We have been looking and doing a major overall to Euchre HD.  In particular, we are looking at
leveraging GameCenter's turn based games.  Right now we use basic Game Center matchs, but there lots of good benefits with being able to leverage the turn based capabilities.  The two big benefits we want are:

  • Support for "live" and "non-live" matches (turn based)
  • Support for Timeouts
  • Improved Game Center match UI

A really good example to get started with Game Center Turn Based match's is Beginning Turn-Based Gaming with iOS 5 by Jacob Gundersen.  Some API's have been changed in iOS 6, but it is still a really good tutorial.

This was my first real experience working with the Turn Based API, and I wish I would have know the following going into the project.

Passing Turn To Yourself

It turns out that a match participant may "pass" the turn (baton) to themselves.  I had assumed that wouldn't be allowed, but it turns out the API does allow it, and it definitely simplified some of my game play logic.

The only catch is that other players won't be notified of the changes to the Game State when you pass the game baton to yourself.

Participant Timeout (new in iOS 6)

I thought that when a player timed out their participant matchOutcome would get set to GKTurnBasedMatchOutcomeTimeExpired.  However, it turns out you have to determine this condition yourself and set the appropriate matchOutcome for the participant.

Another interesting case is when a participant is in a matching state.  The timeout isn't applied against that unmatched participant.

Changes to Authentication

iOS 6 depreciated the old authenticateWithCompletionHandler and replaced it with GKLocalPlayer.localPlayer.authenticateHandler. They changed the block callback a bit, but other then that it looked equivalent. However, the authenticateHandler in iOS 6 won't present the login view if the user cancels it. I realize game center will auto lockout an app after 3 cancel attempts, but I'm talking about just 2 attempts. If they cancel the login, they have to leave the app and come back before Game Center will present the login even through the authenticateHandler is getting set again. I was able to workaround the issue by continuing to use the depreciated authenticateWithCompletionHandler.

The reason this is important for Euchre HD is that it requires Game Center for multi-player. The app tries to authenticate to game center on launch, but if the user cancels we don't ask them at launch again so they won't get nagged. What we do is show a Game Center Login button if they aren't logged in when they select multi-player.

Number of Players in a Match (Updated 11/1/2012)

One problem we have found is that if you have a variable number of players and then start a match with "auto-match", Game Center will start the match with the "minimum" number of players.  It does this even if the Game Center match-making UI is showing auto-match spaces for more then the minimum number of players.

Let me try and explain this a bit better. One thing we do in Euchre HD is to allow people to pick the number of human players they want in a match.  They can pick from 2 to 4 humans.  Euchre is of course a 4 person game, but we fill in the remaining spots with computer players.

Game Center provides a really nice interface for forming a multi-player game.  Here is an example, that shows a 2-4 player game.


The play can add or remove players players as long as they stay within the defined min/max limits.


However, if the player selects "Play Now" and "Auto-match" is selected a match will only be formed with the minimum number of players.  In the above example, even through 3 players are being shown as Auto-match, Game Center will only start a 2 player match.

Here is the example code showing how the request is created.

    GKMatchRequest *request = [[GKMatchRequest alloc] init];
    request.minPlayers = 2;   
    request.maxPlayers = 4;
    request.playersToInvite = playersToInvite;
    request.playerGroup = 0;
    request.defaultNumberOfPlayers = 4;
As anyone else encounter this issues?  Any good suggestions on how to handle it?  I would rather not write my own custom matchmaking interface.


Conclusion

Overall, turn based Game Center is a huge help in building multi-player games.  I was still surprised about how many special cases have to be handled and how much testing is needed.  I guess multi-player is just hard.  :)

Please feel free to follow me on twitter at @fivelakesstudio. I would love to hear about your experiences Game Center.  I hope this was helpful.

Thanks for reading and be sure to visit us at Five Lakes Studio.







Friday, August 31, 2012

NSMutableArray Weak References


I had the need to store non-retained objects to prevent a retain lock loop.  I found a couple of different ways to do this.  I started by trying to implement a derived version of NSMutableArray.  There is a nice example by Mike Ash on how to do implement a NSMutableArray.  It would be fairly easy to modify his example to remove the retain/release pieces.  However, I was a bit nervous when it came to testing the code and my changes.  While he has a test in place, the test wouldn't work if the objects weren't retained.

So I kept looking and found a great example on Stack Overflow by Mark Powell that takes advantage of Core Foundation's Mutable array.


I haven't done much with Core Foundation services, but I have to say that it's amazing how easy this was to do.  I modified the example above to work with ARC so there is a __bridge cast for the returned CGArrayCreateMutable. I put this along with my changes in a small class and header file which can be downloaded at Download NSArrayWeakReference Source.

I hope you found this helpful.

- Tod

Monday, August 27, 2012

Japanese App Store Withholding

I learned an interesting lesson this weekend.  I was reviewing our AppStore financial results and I noticed an interesting entry:



After a little bit of research, I learned Apple withholds a 20% tax on Japanese sales.  In order to eliminate this tax, you need to file some tax forms with the Japanese government and US government.   Apple helps with this process by providing and submitting the forms.

David Smith has a nice article on "Understanding Japanese App Store Withholding" that goes into more detail.

I filled out the forms this weekend (8/26/2012), and I will report back on when it takes effect.  I would love to hear about year experiences with this process.


Monday, August 13, 2012

Subversion to Git

A couple weeks ago Ken and I decided to make the plunge from Subversion to Git.  The main catalyst for this was some Hype generated files I wanted to check into Subversion.  Unfortunately, every time you generate the HTML for a Hype project it blows away the folder structure, which removes the .svn folder and gets subversion all confused.  I understand there are newer versions of Subversion that resolve that issue, but I decided to just do the switchover to Git given that's where it seems most people are going today.

Picking a Hosting Provider


Given Ken and I work together on projects we need a version control hosting provider. We currently use Beanstalk for subversion, and we have been very pleased with them.  However, I decided to take a look at the available options.  I took a serious look at GitHub and Bitbucket.  I opted to go with GibHub for our main git repository.  I really liked its interface and how it links your personal account to your corporate account.  Plus it has a big user following.  Several reviews I read also talked about GitHub being more performant and a step ahead of Bitbucket.  However, that info could be a bit dated by now.

We opted for GitHub's $25 per month plan for 10 private repositories.  However, I also created a Bitbucket account as they have free unlimited private repositories.  I'm using bitbucket to host paid for 3rd party assets, and I'm using GitHub for our source.

GUI Tools


While lots of people use the command line to manage Git, I prefer having some nice GUI tools for my day to day work.  There are several tools out there, but I ended up going with SourceTree.  It seems to be the most complete tool for managing subversion repositories.  I also used GitHub's tool for awhile to get up and started, but SourceTree feels like a more complete and powerful solution.

Migration


I decided not to do a full history migration from Subversion to Git.  I opted to just migrate the latest revision.  We are a small shop, and it sounded like more trouble then it was worth to preserve the entire history.

I still made a big mistake though.  I just took all our source and added it to Git, just like it was setup in subversion.  We have over 1.5 GB of source including game assets such as images, sounds, and puzzle data.  This is when I learned that GitHub doesn't recommend repositories over 1GB.

I learned firsthand how painfully slow it was to pull down a new large repository, and I was also worried that we would get flagged by GitHub for having too large of a repository.  I also found I would occasionally get an error downloading the new repository and would have to start over.

The solution was to breakup the old subversion repository into several smaller repositories and submodules.  I haven't had any issues with these smaller repositories.

Submodules


I decided to break up the old single subversion repository into several smaller repositories.  Each iOS app we make has its own GIT repository, and we use submobiles for shared components.


This is an example of the submodules used for Hashi, a new App we are working on.


What's nice about using submodules is that the parent project references an explicit revision of the submodule.  So if a submodule is updated in a different project, it won't break the current project.  This allows you the time to upgrade the project at your own connivence.

One thing we learned is that you have to be a little careful when adding a submodule to your project through SourceTree.  By default SourceTree was adding the user's login to the submodules URL. This of course causes issues for other people trying to work with the submodule.


You just need to remove the user's login from the URL.  If you forget, you can manually change the .git/config to reference the correct submodule URL.

Conclusion


It took me a weekend or two before I started to understand Git.  I'm still far from an expert, but I am getting more comfortable with it.  One of the sites that really helped was from Mark Lodato and his Visual Git Reference.  Stuart Ellis also has a really nice basic getting started guide to using Git.  And finally the must read Git Reference on Branching and Merging.

Please feel free to follow me on twitter at @fivelakesstudio. I would love to hear about your experiences on using or switching to Git. Let me know if you found this useful.

Thanks for reading and be sure to visit us at Five Lakes Studio.


Monday, July 2, 2012

Hype - How To Play

Ken and I have been struggling with the problem of how to teach people to play our games.  Most of our games are fairly niche, and we would like to broaden their appeal to people who might not know how to play.

We wanted something that would accomplish the following goals:

  • Quickly teach the players the basics of game play
  • Look integrated into the app
  • No download or streamed content
  • Measurable
  • We don't want to inflate the App size too much
  • Something we can create without too much custom programming

What we ended up with was a how to play integrated tutorial that was implemented with an imbedded WebView that plays back HTML 5 generated via Hype.  That was a mouth full.  Good thing we have a video showing it in action:





Hype


Using Hype, you can create HTML 5 web content with animations and interactive content.  The best part is it creates really small output as it just requires the art assets and some generated javascript to control the animations.


There are just a view basic types of object you can use in Hype such as Box, Text, Button, ...


I wish Hype had Arrows and other nicer callout objects build into it.  However between the art assets I already had and using Snagit for the other assets, I was able to put together what I needed.

I was actually able to simulate gameplay just by using these basic Hype elements and its Key framing capability.  It only took one evening to finish the Hype project.  I thought about using something like Camtasia to record a video of some of these parts as opposed to rolling the play animations by hand.  However, I wanted the output really small and it wasn't that hard to simulate the effects I needed.  However, given you can embed video in Hype it would be interesting to try it with embedded video clips.

HTML


You can easily generate the HTML assets for the Hype project.  It just produces a simple "main" html file and a folder containing the Javascript, Images, and other resources needed to run the project.

Given this is just HTML, you can even upload it to a website and interact with it directly in a browser:




We just took these generated assets and added them to our iOS project so they would be built into the resource bundle.  If you do add it to your project, be sure to include it as a folder reference so xcode will preserve the folder layout in the resource folder.  Otherwise, it will get flattened into a single folder which could cause issues.

Loading WebView From Resource


You load resource based files into the WebView just like you would load any local file.  Here is some example code that loads the above example:

    NSString     *howToPlayDevice = [FLUtil iPad] ? @"iPad" : @"";
    NSString     *resourcePath    = [[NSBundle mainBundle] resourcePath];
    NSString     *howToPlayPath   = [NSString stringWithFormat:@"%@/html/HowToPlay%@.html",
                                               resourcePath, howToPlayDevice];
    NSURL        *url             = [NSURL fileURLWithPath:howToPlayPath];
    NSURLRequest *requestObj      = [NSURLRequest requestWithURL:url];
    [self.howToPlayWebView loadRequest:requestObj];

One final tip, make sure the canvas in Hype is the same size as the WebView so it fits perfectly with no scaling or borders. That's all there is to it.  

Measurable


One of the requirements was for us to be able to measure the effectiveness of this effort through Flurry.  In order to do that, we need to be able to communicate from the HTML Webview into the Objective-C code.  

Alexandre Poirot has a nice article on How to Properly Call ObjectiveC From Javascript.  I didn't need to use his entire framework, but I used the basic concept to allow Hype's Javascript to be intercepted by the WebView's delegate.

In Hype, I setup a Javascript function to post to a special URL that can be intercepted by the shouldStartLoadWithRequest UIWebView delegate.  




I used a custom URL scheme called "howtoplayscene" and passed Hype's current scene name so I can tell what scene the user is viewing.  By doing this in an iframe that we create and then destroy it so the user doesn't see anything.  Plus, we can use the same HTML for the embedded WebView as well as a regular browser.  Although, there won't be any tracking when running in a regular browser.

Once the javascript is in place, each scene can then be configured to call the trackHowToPlayCreentScene function when it's loaded:




That's all there is to it on the Javascript side.   The Objective-C side is fairly straightforward:




With this in place, we will get custom events in Fluury that look something like:
  • HowToPlay.Scene.Goal
  • HowToPlay.Scene.Tool Bar
  • Show HowToPlay.Scene.Step 1
  • Show HowToPlay.Scene.Step 2
We will be able to track how many users make it through all they steps and which percentage of users drop off at any given point in time.  Hopefully, with this information we can help make the tutorial better and find where and if users are getting stuck.  We will also be able to correlate information such as the percentage of people that complete the tutorial that go on to purchase.

The other little trick I use, as seen above, is when the user complets the How To Play tutorial.  I have the HTML navigate to "http://www.fivelakesstudio.com/Five_Lakes_Studio/PicrossHD.html".  I picked that URL so when the user hits the done button they will be taken to the webpage for PicrossHD, when run from an external browser.  However, when run from within the App, we close the UIWebView.

Conclusion


This will go live soon, and we are excited to get this in the hands of our new users. I hope we can teach more people how to Play Picross HD and hopefully they will like it.

Please feel free to follow me on twitter at @fivelakesstudio. I would love to hear about your experiences on how to onboard people to your app. Let me know if you found this useful, and especially if you now understand how to play Picross HD.

Thanks for reading and be sure to visit us at Five Lakes Studio. I should mention that I also work for Techsmith, the makers of Snagit and Camtasia.

Monday, June 18, 2012

Engage Users In-App

Ken and I have been trying to figure out how to better engage and communicate with our users.   The AppStore sure doesn't make it easy.

We have tried to use GetSatisifaction and of course there is classic e-mail, but they both have the problem of discoverability.  We don't have a good spot in App to make those resources available without disrupting the user experience.  By forcing users to leave the app and visit our website to communicate with us, we averaged only a few contacts a month.

We also prompt for star ratings within the app and that has been helpful in getting reviews, and we appreciate and read the review feedback.  However, it's really frustrating when you can answer the persons question or problem with no way to actually get the answer to them.

I happened to find a nice solution to this problem.  The Appsfire App Booster SDK for iOS and Android is designed to help with in-app engagement.  It's still in Beta, but we have been using it for Picross HD, and its been great.  We now receive feedback a couple times a day from our users, and we are able to respond back to them directly.


Getting Started

First off, we carved out a little space in the UI to put in a messaging button and indicator.  Appsfire has several recommendations for this so it has enough visibility within your app.  We chose to put it at the top of our main puzzle screen to the right of our App name:

Message Button With Message Badge
They even include a little helper class that takes care of creating the badge for you.  We chose to do a custom integration because we have a game style UI.

We hooked up notifications for message changes so that badge can be updated correctly, and when the little message bubble is pressed we launch the Appsfire messaging interface.  That's all you have to do on the client side and you can start engaging your users.  I would be happy to share the code we use for this, just send me a tweet.

In-App Messaging

Appsfire includes a built-in messaging engine.  Your users can send you messages, and you can reply to them directly within the app.  



They have options for changing the color scheme, but the default color scheme worked well for us.

The UI is also supposed to be localized in several languages including German and Japanese.  However, we were have never able to confirm that it is indeed localized.  I can say that we have many Japanese users that have sent us feedback through Appsfire.



The e-mail field is optional.  We have found most users do provide an e-mail address.  When the feedback is sent, you will get an e-mail that allows you to respond either within the app or via an e-mail (assuming the user provided an e-mail address).



We usually try to answer the user via the "Notification Wall" aka in-app.  However, if the response is long or under special circumstances we will reply via e-mail.  For example, one user felt strongly about an issue and vowed to never launch the app again.  I wanted to thank the user for their feedback and reach out to them.  Doing it in-app wouldn't have been effective assuming they weren't going to open the app again so I used e-mail.  The user thanked me for reaching out to him and hopefully he gave us another chance.

What's New

Another feature we use is What's New.  The Appsfire server will automatically detect new App releases, and it can automatically notify your users of the update.



Tips and Tricks

We are planning to release a series of tips and tricks for our users.   So far we have released one tip, and it has been well received.  The Appsfire portal provides metrics on views and clicks for each message so you can measure their effectiveness.

We think this is a great way to expose users to features or capabilities of our app that they may not know about.   When creating custom message you can also specify the message in different languages such as German and Japanese.

Link to Web Content

In additional to static message, you can link to web content either displayed within the app or via the Safari app (on iOS).  We use this for inviting people to join your twitter account and are experimenting with other creative uses such as linking to a youtube help video.



App Promotion

App promotion is another option available via the Appsfire SDK.  We use it in Picross HD to promote Kento and vice-versa.


When the user clicks on an App promotion message they are taken directly to the AppStore.

Badge Updates on Launch Pad

A new feature they just added was the ability to update the badge on the Launch Pad without having to run the App.  This is a great way to help pull people back into the app.



Conclusion

I highly recommend the Appsfire App Booster SDK, and it is in Beta!!  It still has some rough edges, but even in its Beta form I consider it a must have for Picross HD because of how it allows us to connect with our customers and create a more engaging experience.  


Here are some of the improvements I hope they make as they continue to develop the SDK
  • The web portal for managing messages is a little rough around the edges.  
    • Setting the start and end dates for a message didn't work as expected.
    • It was challenging getting push badge notification to work and needed Appsfire support.  I expect this will get better.  It desperately needs debug tools.
    • Doesn't work behind some firewalls
  • The in-app messaging interface doesn't work behind some firewalls
  • Feature Request:  Only the welcome message allows you to provide "longer" text without having to use a web page.  It would be nice for tips and tricks to have a custom message with longer text.
  • Feature Request: I would love the ability to time message deliver based on days since installing the app (or other in-app usage).  Right now all messages are date/time delivered.

The team over at Appsfire is very professional and helpful, and I can't say enough nice things about them and their offering.

Please feel free to follow me on twitter at @fivelakesstudio.  I would love to hear about your experiences connecting and engaging with your users.    Let me know if you found this useful.

Thanks for reading and be sure to visit us at Five Lakes Studio.

- Tod