Category: iOS Toggle Comment Threads | Keyboard Shortcuts

These posts are syndicated from my iOS Dev Diary over at pinkstone.co.uk.

The format is slightly different with more code snippets and less explanations. I had originally designed it as a notebook in iOS Development – now you can follow along if you’re interested.

  • Jay Versluis 9:00 am on December 31, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to load a different storyboard depending on screen size in iOS 

    As much as I appreciate the idea of Auto Layout, I don’t think it likes me very much. In turn, I don’t like Auto Layout very much. The pain and hassle it takes to make several UI elements fit well on a 4 inch screen as well as a 3.5 inch screen regularly drives me crazy. On paper it’s a great idea – but in reality not fit for prime time.

    Screen Shot 2013-12-31 at 08.56.22

    It’s not a coincidence that we can choose completely different storyboards for iPads and iPhones. Let’s take the same approach when dealing with the two different iPhone screen sizes by loading different storyboards.

    Here’s how: First, finish one version of your storyboard. Next select it and head over to File – Duplicate. Give the second one an appropriate name, then tweak your UI elements so that they look nice on the other screen size.

    Now add this to your AppDelegate.m:

    - (UIStoryboard *)grabStoryboard {
    
        UIStoryboard *storyboard;
    
        // detect the height of our screen
        int height = [UIScreen mainScreen].bounds.size.height;
    
        if (height == 480) {
            storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
            // NSLog(@"Device has a 3.5inch Display.");
        } else {
            storyboard = [UIStoryboard storyboardWithName:@"Main-4in" bundle:nil];
            // NSLog(@"Device has a 4inch Display.");
        }
    
        return storyboard;
    }

    This method returns the following UIStoryboard file:

    • Main.storyboard on 3.5inch devices
    • Main-4in.storyboard on 4inch devices

    Now we need to make sure it gets loaded appropriately. We’ll take care of it in AppDelegate.m like so:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        UIStoryboard *storyboard = [self grabStoryboard];
    
        // show the storyboard
        self.window.rootViewController = [storyboard instantiateInitialViewController];
        [self.window makeKeyAndVisible];
    
        return YES;
    }

    This overrides the default storyboard that’s declared in your project settings. You can expand the above method by checking which iOS Version the device is running, and of course returning an iPad storyboard too.

    In your face, “Auto Layout”!





     
    • Charles Miller 2:23 pm on June 26, 2014 Permalink | Reply

      I am doing this with iOS7 and it seems to work fine with my iPhone 5 but not with my iPhone 4s. Any suggestions?

      • Jay Versluis 9:28 am on June 27, 2014 Permalink | Reply

        Are both phones starting in portrait mode? The height parameter might be reported as the shorter side of the iPhone and hence the pixel check would fail, regardless of the tallness of the screen. Just a thought, I’ve not checked this myself yet but it’s worth looking into.

  • Jay Versluis 6:03 am on December 31, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to dismiss the iOS Keyboard when the done button is pressed 

    It has puzzled many generations how to get rid of the keyboard when the user is done entering text into an iOS text field. There doesn’t seem to be an obvious action we can attach to make this happen.

    iOS Simulator Screen shot 31 Dec 2013 05.45.27

    That’s because rather than looking at the keyboard itself, we need to look at what actually brought it up in the first place: the UITextField. Even though the text field can trigger an action, we don’t want to touch that for dismissing the keyboard. All we need to do is to implement the following method from its delegate protocol:

    - (BOOL)textFieldShouldReturn:(UITextField *)textField {
        
        // done button was pressed - dismiss keyboard
        [textField resignFirstResponder];
        return YES;
    }
    

    This method is called whenever the return button is pressed. We return YES to agree, and resign the text field’s first responder status which takes the user focus away from it.

    Since this is a protocol we *should* really conform to it formally by declaring it in the header file. It appears to work without this step, but let’s play it by the rules here:

    @interface ViewController : UIViewController <UITextFieldDelegate>
    

    Note that you need to connect your text field’s delegate in Interface Builder to the class that’s implementing the above method (i.e. self), otherwise the keyboard won’t be dismissed. To do this, control-drag from the text field to the orange square at the bottom of your view controller and select “delegate”. You can verify this in the connections inspector:

    Screen Shot 2013-12-31 at 05.55.36





     
    • Josh 10:03 pm on May 8, 2014 Permalink | Reply

      I followed these exact steps and still the keyboard will not be dismissed. I’m using xcode 5.1.1.

      • Jay Versluis 1:15 pm on May 9, 2014 Permalink | Reply

        Hi Josh,

        it’s easy to overlook a step. Check out my screencast which demonstrates how this works in detail, also using Xcode 5.1.1: http://pinkstone.co.uk/how-to-dismiss-the-keyboard-from-a-uitextfield-in-ios/

        • Josh 4:08 pm on May 9, 2014 Permalink | Reply

          I just watched the video and looking forward to trying it out when I get home. Just as an aside, I noticed that when I select the text field for the purpose of assigning a delegate, the selected text field is highlighted pink, not blue like in your video. I’m not sure if this is a clue leading to the source of my problem, but I thought I’d mention it anyway. Thanks for the video!

          • Jay Versluis 4:18 pm on May 9, 2014 Permalink

            Great, glad you like it! Pink is strange indeed – I can’t make Xcode show anything in pink. Let me know what you find out, it’s intriguing ๐Ÿ˜‰

    • Josh 9:28 am on May 10, 2014 Permalink | Reply

      Good news. I followed along with your video and it worked! I then went back to my code that wasn’t working. I think the problem was that I didn’t wait for the view controller to be highlighted when assigning it as delegate for the text field. After doing that, the keyboard successfully dismissed. Thanks for your help!

      • Jay Versluis 2:48 pm on May 10, 2014 Permalink | Reply

        Excellent news – great to hear you got it working. Happy coding!

  • Jay Versluis 5:51 pm on December 30, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    Links to the latest Social Icons 

    InBug-60px-RFacebook, Twitter & Co. change their brandings every now and again, and every time I need them for a project I keep scrambling to find the official links. Well no more – here’s a list of them all:





     
  • Jay Versluis 12:21 pm on December 30, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to increase your Build Number automatically every time you build your Xcode Project 

    Xcode includes something rather magical called the Apple Generic Versioning Tool. With this tool you can increase your Build Number automatically every time you hit “Build and Run” in Xcode.

    Here’s how you can add it to your project. I’m using Xcode 5.1 here with a new Single View iOS project. But this will work just fine in other versions of Xcode.

    By default, Version and Build are set to 1.0 (in your app target, under the General tab). We want to leave Version alone and only increase Build.

    Screen Shot 2013-12-30 at 12.05.06

    Head over to the Build Settings tab and find the “Versioning” section. It’s easiest to just search for it. In here, set the Current Project Version field to your initial build – let’s say 1 for this example.

    Screen Shot 2013-12-30 at 12.10.10

    Now select the Build Phases tab. Click on the little plus icon in the top left corner and select New Run Script Build Phase. This will allow us to execute a small script every time we build.

    Screen Shot 2013-12-30 at 12.11.39

    Open the little triangle next to Run Script and underneath the Shell field, add this bit of code. You can even add multiple lines, it’s like a little text editor.

    &quot;${DEVELOPER_BIN_DIR}/agvtool&quot; next-version -all
    

    Here’s what it should look like:

    Screen Shot 2013-12-30 at 12.15.03

    And that’s it! Hit CMD+B a few times and head back to the General tab to see your Build Number magically increased. You can now retrieve this value from you main Bundle (explained in my previous article).

    Kudos to nekno who was kind enough to share this knowledge on Stackoverflow:





     
    • Isuru Nanayakkara (@IJNanayakkara) 5:56 am on March 31, 2014 Permalink | Reply

      As an avid Testflight user, the build number is something I always forget to set. Thanks to you that problem is no more ๐Ÿ™‚

      • Jay Versluis 1:25 pm on March 31, 2014 Permalink | Reply

        Glad to hear it helped you, Isuru ๐Ÿ˜‰

  • Jay Versluis 11:52 am on December 30, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to read you App Version and Build from the Main Bundle 

    The required Version and Build numbers you add to Xcode make it into your app’s Main Bundle. You can retrieve those values from the Main Bundle and use them in your app.

    Here’s how:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        // set version and build fields from bundle
        NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@&quot;CFBundleShortVersionString&quot;];
        NSString *build = [[[NSBundle mainBundle] infoDictionary] objectForKey:@&quot;CFBundleVersion&quot;];
        
        // display in your own labels
        self.versionLabel.text = version;
        self.buildLabel.text = build;
    
    }
    

    iTunes Connect reads both of these. Your “new version” is what’s displayed in the App Store, while the build is a more arbitrary thing you can set yourself.

    Note that both values are floats, and that you need to increase them with every version. If you decrease either of them, iTunes Connect will complain next time you submit a build.





     
  • Jay Versluis 10:12 am on December 30, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    What are the button indexes in a three-button UIAlertView 

    iOS Simulator Screen shot 30 Dec 2013 09.50.43

    The cancel button is always index 0, and all other buttons in the array start at index 1 counting up.

    For completion, here’s how to create it:

    - (IBAction)showAlert:(id)sender {
        
        // create an alert view with three buttons
        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:@&quot;Three Button Alert&quot; message:@&quot;This is an alert view with more than two buttons. Will it work?&quot; delegate:self cancelButtonTitle:@&quot;No Way (0)&quot; otherButtonTitles:@&quot;Perhaps (1)&quot;, @&quot;Definitely! (2)&quot;, nil];
        [alertView show];
    }
    

    And here’s how to react to it:

    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
        
        switch (buttonIndex) {
            case 0:
                NSLog(@&quot;That was button at index 0&quot;);
                break;
                
                case 1:
                NSLog(@&quot;That was button at index 1&quot;);
                break;
                
                case 2:
                NSLog(@&quot;That was button at index 2&quot;);
                break;
                
                case 3:
                NSLog(@&quot;We don&#039;t have a fourth button&quot;);
                break;
                
            default:
                break;
        }
    }
    

    This is a delegate method, so the reacting class needs to conform to the UIAlertView Protocol like so:

    @interface ViewController : UIViewController &lt;UIAlertViewDelegate&gt;
    




     
  • Jay Versluis 10:05 am on December 30, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to restore your single In-App Purchase in iOS 

    This is an addition to my previous article. To allow uses to restore their In-App Purchases you can expand on the previous article and include the following method in your Shop class. This needs to be public so it can be called from your main class:

    - (BOOL)restoreThePurchase {
        
        // verify/refresh receipt here (even though Apple doesn&#039;t say how)
        
        // restore the purchase
        [[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
        
        return YES;
    }
    

    Much like submitting a payment, this now submits a “restore request” to the app store, which in turn will get back to us via the observer delegate. Here’s the implementation from before (still in AppDelegate.m), with the addition of a fleshed out reaction to the restore:

    #pragma mark - StoreKit Observer
    
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
        
        for (SKPaymentTransaction *transaction in transactions) {
            switch (transaction.transactionState) {
                case SKPaymentTransactionStatePurchased: {
                    // user has purchased
                    [self saveTransactionReceipt:transaction];
                    [self unlockFullVersion];
                    // download content here if necessary
                    [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                    break;
                }
                    
                case SKPaymentTransactionStateFailed: {
                    // transaction didn&#039;t work
                    [self displayAlertViewWithMessage:@&quot;There was a problem with your purchase. Please try again later.&quot;];
                    break;
                }
                    
                case SKPaymentTransactionStateRestored: {
                    // purchase has been restored
                    [self displayAlertViewWithMessage:@&quot;Successfully restored your purchase&quot;];
                    [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                    break;
                }
                    
                    
                case SKPaymentTransactionStatePurchasing: {
                    // currently purchasing
                    break;
                }
                    
                default:
                    break;
            }
        }
    }
    

    We let the user know about a successful restore (or failure) via an alert view. Since I’m calling this multiple times it was easier to create a method for this:

    - (void)displayAlertViewWithMessage:(NSString *)message {
        
        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:nil message:message delegate:self cancelButtonTitle:@&quot;OK&quot; otherButtonTitles:nil, nil];
        [alertView show];
    }
    

    Note that you should validate the purchase before restoring it. The Apple documentation goes on a bit about receipt validation without describing a way to actually do it.





     
  • Jay Versluis 10:05 am on December 30, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to restore your single In-App Purchase in iOS 

    This is an addition to my previous article. To allow uses to restore their In-App Purchases you can expand on the previous article and include the following method in your Shop class. This needs to be public so it can be called from your main class:

    - (BOOL)restoreThePurchase {
        
        // verify/refresh receipt here (even though Apple doesn&#039;t say how)
        
        // restore the purchase
        [[SKPaymentQueue defaultQueue]restoreCompletedTransactions];
        
        return YES;
    }
    

    Much like submitting a payment, this now submits a “restore request” to the app store, which in turn will get back to us via the observer delegate. Here’s the implementation from before (still in AppDelegate.m), with the addition of a fleshed out reaction to the restore:

    #pragma mark - StoreKit Observer
    
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
        
        for (SKPaymentTransaction *transaction in transactions) {
            switch (transaction.transactionState) {
                case SKPaymentTransactionStatePurchased: {
                    // user has purchased
                    [self saveTransactionReceipt:transaction];
                    [self unlockFullVersion];
                    // download content here if necessary
                    [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                    break;
                }
                    
                case SKPaymentTransactionStateFailed: {
                    // transaction didn&#039;t work
                    [self displayAlertViewWithMessage:@&quot;There was a problem with your purchase. Please try again later.&quot;];
                    break;
                }
                    
                case SKPaymentTransactionStateRestored: {
                    // purchase has been restored
                    [self displayAlertViewWithMessage:@&quot;Successfully restored your purchase&quot;];
                    [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                    break;
                }
                    
                    
                case SKPaymentTransactionStatePurchasing: {
                    // currently purchasing
                    break;
                }
                    
                default:
                    break;
            }
        }
    }
    

    We let the user know about a successful restore (or failure) via an alert view. Since I’m calling this multiple times it was easier to create a method for this:

    - (void)displayAlertViewWithMessage:(NSString *)message {
        
        UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:nil message:message delegate:self cancelButtonTitle:@&quot;OK&quot; otherButtonTitles:nil, nil];
        [alertView show];
    }
    

    Note that you should validate the purchase before restoring it. The Apple documentation goes on a bit about receipt validation without describing a way to actually do it.





     
  • Jay Versluis 4:33 pm on December 29, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to create a single In-App Purchase in iOS 7 

    This “simple everyday” feature has caused me many headaches on many occasions. Implementing a simple unlockable feature isn’t as straightforward as it perhaps could be – and I’ll make it as simple as possible (so I won’t forget next time I need to add it to an app).

    In this example we’re going to have one single feature that is already setup in iTunes Connect. It’s a non-consumable feature, such as “The Full Version”. To keep all our methods together in one place we’ll create a new NSObject class called “Shop”. This is where we’ll do most of the coding. Later we’ll also add code to the AppDelegate class.

    This is a LONG article – grab a coffee and don’t get overwhelmed. It’ll take several tries to get comfortable with this matter (it’s not just you).

    Let’s start with our new Shop class. It’ll have two properties – these can be private:

    @property (nonatomic, strong) NSArray *currentProducts;
    @property (nonatomic, strong) SKProduct *mainProduct;
    

    One holds an array of product identifiers (reverse domain notation, matching those in iTunes Connect), the other holds our actual product. Here’s a custom initialiser for the first property – it creates an array with one item (so you can expand with more products later):

    - (NSArray *)currentProducts {
        
        // list of current product identifiers
        if (!_currentProducts) {
            _currentProducts = @[@&quot;com.yourapp.fullversion&quot;];
        }
        return _currentProducts;
    }
    

    Our Shop class needs a public method that can be called from the class that’s initiating the purchase. Here it is:

    - (BOOL)validateProductIdentifiers {
        
        // let&#039;s check if the products exist in the App Store
        SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithArray:self.currentProducts]];
        request.delegate = self;
        [request start];
        
        return YES;
    }
    

    It looks more complicated than it really is: we’re initiating an SKProductsRequest with our Product ID and call its start method. The App Store will come back to us in a moment by calling methods of the SKProductsRequestDelegate protocol.

    When we’re ready to initiate the purchase, create a new Shop instance and call this method. Use a property to hold your Shop instance, otherwise the instance is likely de-alloced before a response is received.

    The App Store will get back to us with the products title, description and price a bit later on. Note that for a response to be received, your device needs to be online. So before calling the above method, make sure you’ve checked for network connectivity – and if that’s not available, don’t call the method.

    Which reminds me: let’s make our Shop.h file react to the response by adding said protocol:

    @interface Shop : NSObject &lt;SKProductsRequestDelegate&gt;
    

    The SKProductsRequestDelegate Method

    The App Store calls this method when it’s ready – no matter if the product exists or not. All we have to do is implement it:

    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
        
        // invalid products
        for (NSString *invalidIIdentifier in response.invalidProductIdentifiers) {
            NSLog(@&quot;The following ID is invalid: %@&quot;, invalidIIdentifier);
        }
        
        // grab a reference for later
        self.mainProduct = [response.products objectAtIndex:0];
        
        // display UI - if user can make payments
        if ([SKPaymentQueue canMakePayments]) {
            // display store UI
            [self displayStoreUIwithProduct:[response.products objectAtIndex:0]];
        } else {
            // display can&#039;t buy UI
            [self cantBuyAnything];
        }
    }
    

    The App Store will give us the actual SKProduct which we’ll grab hold of in our self.mainProduct property. If the user can make payments, the Store UI is called giving the user a choice to buy the product. If in-app purchases are disabled, the user gets a different UI explaining the problem.

    We’ll also check if this product is invalid – which of course it shouldn’t be, but it’s good for testing. Typos can lurk everywhere.

    Store UI

    In our example we’ll have a UIAlertView that displays either a buying choice or an explanation as to why the user can’t buy anything. These methods are called by the above:

    - (void)displayStoreUIwithProduct:(SKProduct *)product {
        
        // display local currency
        NSNumberFormatter *formatter = [[NSNumberFormatter alloc]init];
        [formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
        [formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
        [formatter setLocale:product.priceLocale];
        NSString *price = [NSString stringWithFormat:@&quot;Buy for %@&quot;, [formatter stringFromNumber:product.price]];
        
        // a UIAlertView brings up the purchase option
        UIAlertView *storeUI = [[UIAlertView alloc]initWithTitle:product.localizedTitle message:product.localizedDescription delegate:self.delegate cancelButtonTitle:@&quot;Perhaps Later&quot; otherButtonTitles:price, nil];
        [storeUI show];
        
    }
    
    - (void)cantBuyAnything {
        
        // tell user that payments are switched off
        UIAlertView *noStoreAlert = [[UIAlertView alloc]initWithTitle:nil message:@&quot;You can unlock the full version, but your device does not allow in-app purchases at this time.&quot; delegate:self.delegate cancelButtonTitle:@&quot;I&#039;ll check my Settings&quot; otherButtonTitles:nil, nil];
        [noStoreAlert show];
    }
    

    The first method looks a bit difficult, but it’s all code from Apple’s Guidelines. We use a number formatter to display the localised price with correct currency symbol. Next we’ll bring up a UIAlertView with the title and description of our product – this comes directly from iTunes Connect. You can translate your product there with a localised version.

    The second method is called if in-app purchases are switched off on the device (under Settings – General – Restrictions).

    Making the Purchase

    Since we’re using an alert view here to react to the purchase, its delegate needs to react. In my example this happens in a different class – not the Shop class (hence the above alert views’ delegates are set to self.delegate rather than self). Therefore we need a second public method in our Shop class which our main class can call if the user decides to go ahead with the purchase. Here it is:

    - (BOOL)makeThePurchase {
        
        // take payment for our product
        SKPayment *payment = [SKPayment paymentWithProduct:self.mainProduct];
        [[SKPaymentQueue defaultQueue]addPayment:payment];
        
        return YES;
    }
    

    It’s short and sweet: create an SKPayment object with our product (glad we’ve saved it earlier), and add it to the SKPaymentQueue. In English: tell the App Store that our user wants to buy the product.

    Now App Store goes to work and will get back to us in a moment. It will post notifications about the outcome, to which we can listen by conforming to another protocol. But we can’t implement those methods in our Shop class because it could be called at any time – even long after a purchase. Imagine the old “connection goes dead” ploy right after a user hits the buy button. By the time he’s back online it may be decades later.

    App Store will remember that a payment was successful (or not) and will want to tell our app about it. So the only place we can put those delegate methods is in AppDelegate. The good news is we’re done with our Shop Class – just make sure those two public methods are declared in Shop.h:

    - (BOOL)validateProductIdentifiers;
    - (BOOL)makeThePurchase;
    

    Conforming to the SKTransactionObserver Protocol in AppDelegate

    I only touch AppDelegate in rare circumstances, and this is one of them. First let’s tell him that we want to conform to said protocol (in AppDelegate.h):

    @interface AppDelegate : UIResponder &lt;UIApplicationDelegate, SKPaymentTransactionObserver&gt;
    

    Next, in AppDelegate.m, implement the following observer methods. We need to do this so that AppDelegate will listen to notifications posted by the App Store and react accordingly. This is the part I thought was a bit sketchy and confusing in Apple’s In-App Purchase Programming Guide:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {   
        // StoreKit Observer
        [[SKPaymentQueue defaultQueue]addTransactionObserver:self];
    
        return YES;
    }
    

    The following method is called when the App Store gets back to us, and since AppDelegate is now the observer, we’ll listen to them at all times when our app is running:

    #pragma mark - StoreKit Observer
    
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
        
        for (SKPaymentTransaction *transaction in transactions) {
            switch (transaction.transactionState) {
                case SKPaymentTransactionStatePurchased: {
    
                    // user has purchased
                    [self saveTransactionReceipt:transaction];
                    [self unlockFullVersion];
    
                    // download content here if necessary
    
                    // let App Store know we&#039;re done
                    [[SKPaymentQueue defaultQueue]finishTransaction:transaction];
                    break;
                }
                    
                case SKPaymentTransactionStateFailed: {
                    // transaction didn&#039;t work
                    break;
                }
                    
                case SKPaymentTransactionStateRestored: {
                    // purchase has been restored
                    break;
                }
                    
                    
                case SKPaymentTransactionStatePurchasing: {
                    // currently purchasing
                    break;
                }
                    
                default:
                    break;
            }
        }
    }
    

    We use a for-in loop to react to every transaction in the queue. Conceivably users could have bought multiple products, that’s why – however unlikely in our case. Depending on the current transaction’s transactionState we can react accordingly. For simplicity I’ve only covered a successful purchase here, you get the idea with the comments in the other sections of the switch statement.

    In our “success” scenario, we need to unlock the full version (i.e. set a BOOL in NSUserDefaults or iCloud) and save the transaction receipt for later fraud detection and validation. If you’re offering downloadable content this is the time to download it.

    When all these things have finished, you need to tell the App Store that you’re done processing this transaction so that it is removed from the SKPaymentQueue.

    Save Receipt and Unlock Full Version

    For completion, here are two methods that save the receipt and saving a “fullVersion” BOOL in NSUserDefaults. Your app can check this next time a premium feature is used. If the user has bought your product, go ahead and make it available. If not set, display the Store UI:

    - (void)saveTransactionReceipt:(SKPaymentTransaction *)transaction {
        
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSString *receiptID = transaction.transactionIdentifier;
        NSArray *storedReceipts = [defaults arrayForKey:@&quot;receipts&quot;];
        
        if (!storedReceipts) {
            // store the first receipt
            [defaults setObject:@[receiptID] forKey:@&quot;receipts&quot;];
        } else {
            // add a receipt to the array
            NSArray *updatedReceipts = [storedReceipts arrayByAddingObject:receiptID];
            [defaults setObject:updatedReceipts forKey:@&quot;receipts&quot;];
        }
        
        [defaults synchronize];
    }
    
    - (void)unlockFullVersion {
        
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        [defaults setBool:YES forKey:@&quot;fullVersion&quot;];
        [defaults synchronize];
    }
    

    Note that the transactionidentifier string is only available in iOS 7 and later. iOS 6 and earlier can similarly save the full receipt object as data. The Apple Documentation has more on how to do this.

    Saving receipts isn’t strictly necessary, but if you do then you can check a valid purchase with the App Store sometime later.





     
  • Jay Versluis 2:47 pm on December 29, 2013 Permalink | Reply  
    Categories: iOS ( 222 )

    How to open the LinkedIn App from your own iOS App 

    Here’s a sample action that tries to open a LinkedIn Profile with the LinkedIn App. If it’s not installed, it opens a link to the profile in Safari. If that doesn’t work it gives a log message (please handle appropriately in your own code):

    - (IBAction)openLinkedIn:(id)sender {
        
        // open the LinkedIn App
        if (![[UIApplication sharedApplication] openURL:[NSURL URLWithString:@&quot;linkedin://#profile/35932112&quot;]]) {
            
            // opening the app didn&#039;t work - let&#039;s open Safari
            if (![[UIApplication sharedApplication] openURL:[NSURL URLWithString:@&quot;http://www.linkedin.com/company/pinkstone-pictures&quot;]]) {
                
                // nothing works - perhaps we&#039;re not onlye
                NSLog(@&quot;LinkedIn doesn&#039;t works. Punt.&quot;);
            }
        }
    }
    

    Note that currently you can only link to personal profiles, not to company profiles.

    To create a dynamic web button with follower count (or not), check out the LinkedIn Developer Portal and load the code into a small UIWebView.

    At the time of writing it appears that none of the LinkedIn provided plugins are actually working anywhere other than the preview. Looks like we’ll have to use a standard graphic instead until this is fixed.





     
c
compose new post
j
next post/next comment
k
previous post/previous comment
r
reply
e
edit
o
show/hide comments
t
go to top
l
go to login
h
show/hide help
shift + esc
cancel