Monday, July 1, 2013

Newsletter

We have had more then 700,000 downloads of our apps since 2010, and we don't have any way to contact those people or know who they are outside of our apps.  For in-app engagement I highly recommend the Apps Fire Booster SDK which I did a blog post about awhile ago.  However, I think we need to be doing more to stay in touch with our users.  I talked with Jody Burgess who runs a strategic marketing company and she planted a bug in my ear about collecting e-mail addresses.

Newsletter Signup


I decided that collecting e-mail addresses would be a good idea, but I wanted to make sure we are adding value for that e-mail address and that we don't spam our users.  Ken and I decided to add a newsletter signup to Picross HD.

We added a simple signup process that will prompt users to signup for the Newsletter.  The signup offer will be shown after several launches of the App or upon request.


We are using a double opt in process that requires the user to ask to signup for the newsletter as shown above and via a confirmation e-mail.

Mailchimp


We are using mailchimp.com to manage the e-mail lists for us.  This helps us stay in compliance with different e-mail laws and manages all the opt-in and out stuff.  They also have a simple little api called ChimpKit on git hub.  I wish they had done a bit more work on it as it's just a very thin wrapper around their rest api, but it is still useful.

They also support localized e-mails, but I have had a hard time getting that setup correctly.  For this first pass, I'm just going to go in english, but I would like to support japanese better in the future as about half the Picross HD user base is japanese.

At the time of this writing, we haven't gone live with Mailchimp so I don't know how well they are going to perform until we get a few e-mail campaigns under our belt.

Privacy Policy


The one thing mailchimp doesn't really help with is in the creation of a Privacy Policy.  If you are collecting names and e-mail addresses you will need a privacy policy.  It cost about $650 to have one created along with a Terms of Use for our website and an End User License Agreement.

Free Puzzles


The main use of the newsletter will be to feature new free puzzles that can be played either via paper or via our games.  Here is an example of what the newsletter will look like:


We keep the e-mail really simple so that it has the best chance of being opened by various e-mail readers, but when you click on one of the puzzles it will open the puzzle in our puzzle view.

Puzzle Viewer


The puzzle view is interesting because it uses Javascript to give the user more options.  For example, if you have it open on an iOS device it will give you the option of installing Picross HD or if you have Picross HD already installed it will give you the option to play the puzzle directly in Picross HD.

If you are viewing it on a computer it gives you the option of download Picross HD through iTunes or print the puzzle.  You can even show the puzzle's solution so it's possible to play the puzzle with pen and paper.  We hope users will play it in Picross HD, but we want people to share our puzzles with their friends and have no barriers to playing our puzzles.


It was a little tricky to get it to work, just contact me if you would like me to share the Javascript code with you or you can view the page source to see it yourself.

The nice thing about the Puzzle view is we will also be able to do some social campaigns on Facebook and Twitter.  I plan on posting free puzzles to Facebook and Twitter which will bring people to the Puzzle viewer.  They will hopefully enjoy the puzzle and choose to signup for the newsletter or download Picross HD.

Cross Promote


In addition to Picross puzzles, we plan on making Hashi puzzles available through the newsletter.  We think people who like Picross puzzles will also probably like Hashi puzzles and this is a way to introduce them to Hashi puzzles and our Hashi app.

We will also use the newsletter to talk about upcoming releases and news we think our users may be interested in hearing about.

Conclusion


It's kind of amazing all the possibilities that open up just by having interested user's e-mails.  I'm excited to see how this goes over with our users and through our planned social campaigns.

Please feel free to follow me on twitter at @fivelakesstudio. I would love to hear about your app marketing experiences.  Also, feel free to signup now for our newsletter.  It may still be in a beta form, and it won't be able to open puzzles in Picross HD until the 3.4 version is available.  Other then that it should work fine.  :)


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



Monday, April 8, 2013

iOS In-App Purchases

This is hopefully a concise getting started with in-app purchases article. Five Lakes Studio has had a fair amount of success with leveraging in-app purchases, and I wanted to share some of the lessons we have learned along the way and also share some code to help make basic in-app purchases a little easier.  This article focuses on non-consumable purchases with built-in product (app) delivery.

Setting Up Your Account


In order to use in-app purchases you have to complete several steps.  A good guide to how to do this is  TN2259 Adding In-App Purchase to your iOS and Mac Applications.   I would recommend taking some time and reading it thoroughly.

One of the key things you need to do is make sure your app has a specific AppId such com.appleseedinc.MyGreatApp.   If your current AppId is using wildcards (".*") you will need to replace it with a fixed AppId and regenerate and reapply the provisioning profiles.  This is because in-app purchases can't be shared across multiple apps.

Creating Test Accounts


You will also need a test account.  These can be created using your iTunes Connect account.




In order to test with the test account, you will need to logout of your current iTunes AppStore account.  You can do that in the "Settings" app under "iTunes & App Stores".


However, don't try to login with the test account through the Settings app.  The test account is only valid in the sandbox environment.  In order to use it, you enter it from within you app when you initiate the in-app purchase.  It will then ask you to login and then you can use the test account you just created.  Once you do this, you can logout of the test account through the Settings app.

Setting up In-App Purchases


You setup the items you want to be purchasable via iTunesConnect.  


This includes picking the type of in-app purchase, it's product id used to reference the purchase from within you app, pricing, and finally the actual text displayed to the user during the purchase process.

This article focuses on Non-consumable In-App Purchases.  These type of purchases only need to be purchased once by users. They do not expire or decrease with use and they can be restored.

Purchasable items need to be approved by apple, and they must include a screenshot.  When you define a new release of an app you can pick which new in-app purchases are available in the app.

It's App Time


Now it's time to start doing work in the app.  At this point, you should have your AppId provision profile set, a test account setup, and one or more non-consumable purchases configured in the App Store through iTunesConnect.  The StoreKit is used to communicate between your app and the AppStore.


The simplest type of in-app workflow to implement is for built in product delivery.  The workflow looks like:


The other workflow is a server based authentication workflow which is significantly more complicated and isn't a focus for this article.

I have made available a class called FLOStoreManager that helps manage the built in product deliver workflow and is part of the Five Lakes Studio Open Library project on GitHub.

FLSOpenLibrary


I'm happy to make FLOStoreManager available under the MIT license as part of the Five Lakes Studio Open Library on GitHub.  The FLOStoreManager class does most of the work to manage the in-app purchase process.   I suggest you download it and at least use it as an example.  

NOTE: The FLOStoreManager makes reference to featureId which is equivalent to to the App Store productIdentifier.

There are 3 main parts to the in product delivery process: Download, Present, and Purchase.

Downloading Purchasing Information


The in-app product definitions are stored on the App Store and must be download to the app before presenting or trying to purchase the item.  This is a download of the product information or meta-data such as product price.   Apple enforces this through the App review process so don't try to skip this step even if you think you can just embed all the meta-data in the app.  Apple still requires your to download and honor this information as it exists in iTunesConnect.

The product definitions contain information such as product description and price. In order to fetch the product definitions, the app needs to know the product ids in advance.  There is no method to query the product ids from the App Store.  This means the App needs to know the product ids by either hard coding them or by other means such as downloading them from a server.

The SKProductsRequest class is used to fetch the product definitions for a set of feature id's.  The FLOStoreManager wraps this in a handy method called startAsyncFetchOfProductForFeatureList.

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    NSSet *featureSet  = [[NSSet alloc] initWithArray:@[@"ProductId1", @"ProductId2"]];
    [[FLOStoreManager defaultManager] startAsyncFetchOfProductForFeatureList:featureSet];
}

This implementation also does some handy work for you automatically:
  • Auto retry on failed attempts
  • Sends an NSNotification (kFeatureListReady) when the feature list has been successfully retrieved.
  • If the feature list has already been retrieved and matches the given feature then it will only try and update the list once a day.   This is handy since it is called in applicationDidBecomeActive which may get called multiple times as users enter and leave the app.
  • Keeps track of the "registered" features and the product information associated with them
I usually request the product information during applicationDidBecomeActive so it will be downloaded and available as soon as is possible so the purchasable items can be presented to users.

    Present Purchasable Items


    It's up to you to decide how to present the in-app purchases to your users.  However, the App Store uses UIAlert messages which you can not control other then through some text customization.  These customizations are done through iTunesConnect when defining the in-app purchase.

    Picross HD - Daily Puzzle Pack In-App Purchase
    You also shouldn't present purchases for items whose purchasing information hasn't been downloaded.  Apple can reject your app if you try to do that.

    One of the things you are going to want to know from the server is the price of the item.  This can vary based on the country and also can be changed in iTunesConnect.  FLOStoreManager has a handy routine to allow you to retrieve the localized price of the product formatted in a nice string.

    NSString *price = [[FLOStoreManager defaultManager] formattedPriceForFeatureId:@"ProductId1"];
    

    You will also want to take into account some other factors in the UI such as:
    • The act of purchasing an item is done asynchronously so you will probably want something to indicate when a purchasing is in progress,
    • You will need to handle the notifications when the purchase either completed or failed. 

    Purchase


    The SKPaymentQueue is used to initiate a purchase.   It's easy to initiate a purchase, but it does require some tricky handling of the payment queue.  FLOStoreManager tries to make this really easy.  You just call startAsyncPurchaseOfFeature to begin the purchase:

    [[FLOStoreManager defaultManager] startAsyncPurchaseOfFeature:@"ProductId1"];
    

    Once the request is completed, one of two NSNotifications will be sent:
    • kFeaturePurchased is sent if the purchase is successful. The notification object with be the featureId NSString
    • kFeaturePurchasedFailed is sent if the purchase failed.  The notification object with be the featureId NSString
    FLOStoreManager also keeps track of which product ids (features) are in the process of being purchased.  This is handy when updating or displaying a view and you need to know if something is being purchased.


    if( [[FLOStoreManager defaultManager] isFeatureBeingPurchased:@"ProductId1"] )
    {
        // The feature is in the process of being purchased
    }
    
    if( [[FLOStoreManager defaultManager] isAnyFeatureBeingPurchased] )
    {
        // Some feature is being purchased, we don't care which one
    }
    

    One of the limitations with FLOStoreManager is that it doesn't try to do purchase receipt validation. There is a vulnerability in iOS 5.1 and earlier related to in-app receipt validation.   It is not a simple topic to validate a receipt.  Apple has a handy article that talks through the "In-App Purchase Receipt Validation on iOS".  If people are willing to go through the hassle to hack my app to save a buck or two then so be it, I don't think they would be willing to pay for it anyway.  Perhaps some day they will change there ways.

    You will also need to handle the condition of a purchase request that gets completed after the user has left your app.  In order to handle this case, one of the first things the app should do is setup the  SKPaymentQueue in application:didFinishLaunchingWithOptions by adding an observer.  This needs to be done so the app can receive payment notifications from the AppStore.  This is all taken care of by FLOStoreManager just by getting the defaultManager.

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        [FLOStoreManager defaultManager];  // Allows us to receive AppStore notification
    }
    

    Once the user starts purchasing items, how do you track what has been purchased?

    Purchase Tracking


    It is up to the app to figure out how to store and keep track of purchased items.  The StoreKit does provide a method called restoreCompletedTransactions which is available as part of SKPaymentQueue  to restore non-consumable purchases.  However, it requires that the user provide their authentication to the Apple Store.  So it's usually called in response to an explicit action by the user to restore purchases.

    FLOStoreManager exposes the ability to restore purchases through its own restoreCompletedTransactions method.  Using  FLOStoreManager also has the added benefit of sending kFeaturePurchased notifications for items that where purchased and needed to be restored.  It will also send a kPurchaseRestoredCompleted notification after all items have been restored.

    The other big advantage of FLOStoreManager is it has a built in mechanism to keep track of non-consumable purchases.  It uses the built-in keychain to keep track of purchases.  The nice thing about the keychain is that it persists across deletes of the app.  You can also instantly query FLOStoreManager to see what has been purchased.

    if( [[FLOStoreManager defaultManager] isPurchased:@"ProductId1"] )
    {
        // The feature has been purchased
    }
    

    Conclusion


    Wow you made it this far.  This turned out to be a wee bit longer then expected, but I hope you found it useful.

    Please feel free to contribute to the FLS Open Library.  I wasn't planning on making this open source so it wasn't designed to be general use, but I think it provides a good start and example.  It also contains a lot more functionality then discussed here including an extension to handle consumable purchases.

    Please feel free to follow me on twitter at @fivelakesstudio. I would love to hear about your in-app purchase experiences.

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

    References


    https://github.com/FiveLakesStudio/FLSOpenLibraryIOS.git
    TN2259 Adding In-App Purchase to your iOS and Mac Applications
    In-App Purchase Programming Guide
    In-App Purchase Receipt Validation on iOS
    iPhone Tutorial – In-App Purchases By Mugunth Kumar


    Sunday, April 7, 2013

    Quick Tip: How to show source code in the blog

    I have been trying different solutions for showing source code in my blog.  This has been a little harder by the fact I'm trying to show Objective-C.  I have finally found just the trick. It's http://hilite.me/ by Alexander Kojevnikov.  It has a very simple copy/paste interface that just works.  You can also do some customizations and pick between may different styles.

    Using it is as simple as:
    1. Paste your source code
    2. Hit Highlight
    3. Copy and paste the HTML


    You can also customize the embedded css style information and pick the language.  I added a font size style to the css "font-size:small;" and here is what it looks like:

    - (void)alertViewCancel:(UIAlertView *)alertView
    {
        if( m_alertBlock != nil )
        {        
            m_alertBlock( kAlertViewCanceled );
            m_alertBlock = nil;        
        }    
    }
    

    The nice thing about this solution is that everything needed is embedded in the HTML block so there is no setup required on the blog itself.

    Please feel free to follow me on twitter at @fivelakesstudio. I would love to hear if this was useful and what you do to show source code on the web.

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

    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.