PIXEL
DOCK

I like the smell of Swift in the morning…

iOS8 Share extension – completionHandler for loadItemForTypeIdentifier: is never called

Posted: | Author: | Filed under: iOS, Swift | Tags: , | 5 Comments »

Creating a Share Extension in iOS8 is really easy:

  1. In your app choose File > New > Target
  2. Choose Application Extension > Share Extension
  3. Enter a name for your Share Extension and press Finish

That’s all! Xcode creates everything you need for you and puts it in it’s own folder. You’ll find a ShareViewController that is a subclass of SLComposeServiceViewController. That class’ UI looks a lot like the SLComposeViewController that you’ve probably worked with when using iOS’s Social Framework (to share your app’s content to Twitter or Facebook).

In ShareViewController Xcode creates some stubs for you where you have add the code to actually share the content to your service. In my case I want to build an extension for Safari that allows the user to share the page’s URL on my service. So when the user selects my share extension from Safari I need to retrieve the page’s URL to send it to my server.

When the user starts my Share Extension and presses the “Post” button the method didSelectPost is called on the ShareViewController.

Let’s have a look at the stub method that Xcode created:

- (void)didSelectPost {
    // This is called after the user selects Post. 
    // Do the upload of contentText and/or NSExtensionContext attachments.
    
    // Inform the host that we're done, so it un-blocks its UI. 
    // Note: Alternatively you could call super's -didSelectPost, 
    // which will similarly complete the extension context.
    [self.extensionContext completeRequestReturningItems:@[] 
                                       completionHandler:nil];
}

So far so good. So I added the code to retrieve the page URL. Compared to the easiness of creating the whole extension, the simple task of retrieving the URL takes quite some effort:

- (void)didSelectPost {
    NSExtensionItem *item = self.extensionContext.inputItems.firstObject;
    NSItemProvider *itemProvider = item.attachments.firstObject;
    if ([itemProvider hasItemConformingToTypeIdentifier:@"public.url"]) {
        [itemProvider loadItemForTypeIdentifier:@"public.url"
                                        options:nil
                              completionHandler:^(NSURL *url, NSError *error) {
                                  NSString *urlString = url.absoluteString;
                                  // send url to server to share the link
                                  // BUT THIS BLOCK IS NEVER EXECUTED!!!
                              }];
    }
    [self.extensionContext completeRequestReturningItems:@[] 
                                       completionHandler:nil];
}

That’s a lot of code for just an URL. And the worst thing: It’s not working! The completion handler of the loadItemForTypeIdentifier:options:completionHandler: is never called.

As it turns out calling completeRequestReturningItems:completionHandler: at the bottom of the method causes this problem. In Apple’s documentation it says that you are supposed to call this method to ‘Tell the host app to complete the app extension request with an array of result items’. Calling this methods dismisses the ShareViewController and deallocates it. So the NSItemProvider that contains the URL is also destroyed before it can access the URL.

So the solution is quite simple: The call to completeRequestReturningItems:completionHandler: has to be done AFTER retrieving the URL and sending it to the server. So it has to go into the completionHandler block:

- (void)didSelectPost {
    NSExtensionItem *item = self.extensionContext.inputItems.firstObject;
    NSItemProvider *itemProvider = item.attachments.firstObject;
    if ([itemProvider hasItemConformingToTypeIdentifier:@"public.url"]) {
        [itemProvider loadItemForTypeIdentifier:@"public.url"
                                        options:nil
                              completionHandler:^(NSURL *url, NSError *error) {
                                  NSString *urlString = url.absoluteString;
                                  // send url to server to share the link
                                  [self.extensionContext completeRequestReturningItems:@[]         
                                                                     completionHandler:nil];
                              }];
    }
}

And now the URL is retrieved and can be posted to my server.

In case you need to do this in Swift, here is how that would look like:

override func didSelectPost() {
    if let item = extensionContext?.inputItems.first as? NSExtensionItem {
        if let itemProvider = item.attachments?.first as? NSItemProvider {
            if itemProvider.hasItemConformingToTypeIdentifier("public.url") {
                itemProvider.loadItemForTypeIdentifier("public.url", options: nil, completionHandler: { (url, error) -> Void in
                    if let shareURL = url as? NSURL {
                        // send url to server to share the link
                    }
                    self.extensionContext?.completeRequestReturningItems([], completionHandler:nil)
                })
            }
        }
    }
}

Strange looking UIAlertViews in iOS8

Posted: | Author: | Filed under: iOS | Tags: , | 4 Comments »

Do you have UIAlertViews that look strange under iOS8? The alert text is bold and clings to the top of the alert container?

Apparently iOS8 does not tolerate setting the UIAlertView’s title to nil. When you set it to an empty string instead, the UIAlertView looks like it should.

So instead of doing this:

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil 
                                                message:@"Hello World!"
                                               delegate:nil
                                      cancelButtonTitle:nil
                                      otherButtonTitles:@"OK", nil];

Do this:

UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" 
                                                message:@"Hello World!"
                                               delegate:nil
                                      cancelButtonTitle:nil
                                      otherButtonTitles:@"OK", nil];

The funny thing is that with an UIAlertSheet the behavior is opposite: passing an empty string as title results in an empty title section on top of the UIActionSheet. Passing nil for the title fixes this.

Open settings directly from your app

Posted: | Author: | Filed under: iOS | Tags: , | 5 Comments »

Once upon a time it was possible to go to the settings right from your app using:

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs://"]];

That was quite useful when some feature in your app could not be used, because the user disabled something in his settings. In that case you could inform the user that he had to change his settings if he wanted to use that feature and offer to open the settings directly from your app.

Unfortunately Apple decided to not longer allow this after the introduction of iOS 5.1

Good news: This feature is back with iOS8! Apple has added a NSString constant to UIKit that you are supposed to use. To avoid crashes on iOS versions below iOS8 it is a good idea to check if the constant exists on the system version the device is running. You could check if the user is running iOS8 or higher but I prefer to check directly if the constant exits rather than checking the iOS version. Who knows if Apple will decide to remove this constant again in the future…

So, to open the settings, you can use the following snippet:

BOOL canOpenSettings = (&UIApplicationOpenSettingsURLString != NULL);
if (canOpenSettings) {
   NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
   [[UIApplication sharedApplication] openURL:url];
}

Swift:

if let settingsURL = NSURL(string: UIApplicationOpenSettingsURLString) {
    UIApplication.sharedApplication().openURL(settingsURL)
}

If your app has it’s own settings bundle, the settings will be opened showing your app’s settings. If your app does not have a setting bundle, the main settings page will be shown.

Unfortunately there does not seem to be a way to go directly to subpages of the settings (except your own settings bundle). If you’ve found a way to do that, please let me know in the comments!

Update: Linking to a subpage of the Setting seems to work again for system versions greater or equal than iOS8. It is not officially documented by Apple but it seems to work, although I not sure how reliable this is. It works on my iOS 9.1 iPhone but not in the Simulator. So I would not recommend to use this in production.

So, with caution, you can try this to open the Keyboard Settings:

if let settingsURL = NSURL(string: "prefs:root=General&path=Keyboard") {
    UIApplication.sharedApplication().openURL(settingsURL)
}

Or go even one level deeper:

if let settingsURL = NSURL(string: "prefs:root=General&path=Keyboard/KEYBOARDS") {
    UIApplication.sharedApplication().openURL(settingsURL)
}

To make these unofficial URLs work you might have to add prefs to the URL schemes of your project’s Info.plist file.

How to avoid the ugly flickering effect when using CSS transitions in iOS

Posted: | Author: | Filed under: CSS, HTML, iOS | 2 Comments »

Recently I used CSS transitions to animate some images inside an UIWebView. Everything worked fine and the transitions where really smooth. I used the translate transition to move the images that where inside a div HTML element:

To move the images I created a CSS class that contained the transition:

.move {
   -webkit-transform: translate(50px,100px); 
}

Then I would add the CSS Class to the

to move the div and the images contained in it.

document.getElementById('image-container').className = "move";

This worked as expected. However, when the images moved, there was an ugly flicker. It turned out that this is caused by a CSS property call “backface-visibility”. Normally this is used, when you do 3D CSS transitions (e.g. 3D rotations). “backface-visibility” determines whether the backside of a HTML element is visible, when it is not facing the screen. As I am not doing any 3D transitions it should not matter how this property is set. Wrong. Obviously in Webkit Browsers it does matter. To fix the ugly flickering you have to add the following to the CSS class:

.move {
   -webkit-transform: translate(50px,100px); 
   -webkit-backface-visibility: hidden;
}

And thats not enough. The flickering became less but it was still there. Only after I also set the “backface-visibility” property on the images themselves, the flickering went away completely:

.move {
   -webkit-transform: translate(50px,100px); 
   -webkit-backface-visibility: hidden;
}

.move img {
   -webkit-backface-visibility: hidden;
}

I don’t know if this is a bug in Webkit or if there is a logical explanation for this behavior, but setting “backface-visiblity” to “hidden” on all animated elements did the trick.

Dismiss keyboard in HTML form displayed in UIWebView inside modal view controller

Posted: | Author: | Filed under: HTML, iOS | Tags: , | 3 Comments »

Recently I encountered a problem with the keyboard while displaying a HTML form inside a UIWebView: The HTML form was a simple form to ask the login credentials from a user. Nothing more than 2 text input fields and a submit button. When the user tapped on an input field the iOS Keyboard came up, as it should. But then, when the user tapped the submit button, the keyboard stayed in placed and was not dismissed. The normal behaviour would be that the keyboard would be dismissed as soon as the textfield loses it’s focus. But somehow this was not happening.

I found several solutions on how to dismiss the keyboard programatically by either using javascript or calling resignFirstResponder on the UIWebView when the form was being submitted, but the keyboard would still not go away.

Then I found the solution to that problem. I turned out that the lingering keyboard was a UI design choice by Apple. The keyboard dismissal is disabled when you present your UIWebView using a modal ViewController with presentation style UIModalPresentationFormSheet!

The solution is quite simple: You have to override your ViewController’s disablesAutomaticKeyboardDismissal method:

- (BOOL)disablesAutomaticKeyboardDismissal {
    return NO;
}

After that the keyboard will be dismissed as soon as the input fields lose focus (or the form is being submitted).

“Collection was mutated while being enumerated” during CoreData operation on background thread

Posted: | Author: | Filed under: iOS, Objective-C | Tags: , , | 7 Comments »

During my current project I had to move a time consuming CoreData operation to a background thread to avoid a frozen UI. After reading some horror stories about using CoreData on a background thread I was actually surprised how straight forward it was. I used Apple’s sample code ThreadedCoreData as a guidance. When I tested my changes everything went smooth. Until I started to touch my app’s UI while the background process was running. Suddenly the app crashed and the following exception was thrown:

*** Terminating app due to uncaught exception 'NSGenericException',
reason: '*** Collection <__NSCFSet: 0x1f0ec200> was mutated while
being enumerated.'

The exception was being thrown on the background thread during a call to

[NSManagedObjectContext executeFetchRequest:error:]

What happened?

In Apple’s example code the CoreData operations happen inside the main method of a custom subclass of NSOperation. That subclass is initialized using a custom initWithData: method and then added to a NSOperationQueue.

When doing CoreData operations on a background thread it is important that the background thread uses it’s own instance of NSManagedObjectContext. And this NSManagedObjectContext has to be instantiated on the background thread.

And that was the problem.

In Apple’s example the NSManagedObjectContext for the background thread is instantiated in the custom initWithData: method of the custom ParseOperation class. But that method is called on the main thread when instantiating a new ParseOperation object before adding it to the NSOperationQueue. So the NSManagedObjectContext instance is created on the main thread!

The solution was quite simple:

After moving the code that instantiates the NSManagedObjectContext from the initWithData: method (called from the main thread) to the main method (called from the background thread), the problem vanished and the code run smoothly.

So, when using CoreData in a background thread make sure that the background thread has it’s own NSManagedObjectContext instance that was created on the background thread!

Code Sign Error: Provisioning Profile can’t be found.

Posted: | Author: | Filed under: iOS, Xcode | Tags: | 5 Comments »

Sometimes when you transfer your project to another computer and especially when you create a new developer certificate in the progress, you’ll run into difficulties. When you try to run your app on a device, Xcode will throw the following error:

Code Sign Error: Provisioning Profile '###ID###' can't be found.

The problem is that in your project’s project.pbxproj file the old provisioning profile identifiers are still lingering. You need to clean up that file so that Xcode can build your project without errors.

Here’s how you do that:

1. Close Xcode
2. Right click on your project’s .xcodeproj bundle to show it’s contents.
3. Open the .pbxproj file in a text editor of your choice (make a backup copy first if you feel paranoid)
4. Find all lines in that file that include the word PROVISIONING_PROFILE and delete them.
5. Open Xcode and clean your project
6. Build your project

80F04C2F14BDF2CD00851339 /* Ad Hoc */ = {
   isa = XCBuildConfiguration;
   buildSettings = {
      CODE_SIGN_IDENTITY = "iPhone Distribution: Caramelized GmbH";
      "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
      GCC_PRECOMPILE_PREFIX_HEADER = YES;
      GCC_PREFIX_HEADER = "CaramelReader/Caramelized-Prefix.pch";
      INFOPLIST_FILE = "CaramelReader/Caramelized-Info.plist";
      IPHONEOS_DEPLOYMENT_TARGET = 5.1;
      PRODUCT_NAME = Caramelized;
      ↓ DELETE THIS LINE! ↓
      PROVISIONING_PROFILE = "8B3033A4-0E28-42F8-80FC-70B6C3C473C7"; 
      ↑ DELETE THIS LINE! ↑
      WRAPPER_EXTENSION = app;
   };
   name = "Ad Hoc";
};

How to add a large amount of In-App Purchase items to iTunesConnect

Posted: | Author: | Filed under: iOS, Tools | Tags: , , | No Comments »

Adding In-App Purchase items to your Application in iTunesConnect by hand is a boring and time consuming task. When I realized that I’d have to add about 1000 items for my app I really did not want to do that manually. Luckily there is a tool for mass upload of In-App Purchase items. It is a bit hidden though.

When you log in to iTunesConnect and select “Manage your Apps” there is a section in the footer called Application Loader. That’s the tool you’ll need.

Download Application Loader and the Spreadsheet Example. With Application Loader you can select a text file that contains a list of the In-App Purchase items that you want to add to your app. It is a tab separated list that contains all the metadata that you’d normally add manually when adding an item in iTunesConnect. Have a look at the spreadsheet example to see what format the list should have.

To add the mandatory screenshots I found it easiest to put all the screenshots in one folder and name the screenshot according to the product ID. If your item’s product ID is com.yourdomain.product1 than the screenshot must be named com.yourdomain.product1.jpg.

If you have that text file all you have to do is select the file in Application Loader, select the screenshot folder, hit “next” a couple of times and then “deliver”.

Voilá, that’s it! Do not panic if your new In-App Purchase items do not show up in iTunesConnect right away. Sometimes it takes a couple of hours(!) before the show up.

A little category to get an NSDictionary from an NSString containing a plist

Posted: | Author: | Filed under: iOS, Objective-C | Tags: , , | 2 Comments »

I really like the plist format. Being able to transform a plist file into a NSDictionary with one line of code really appeals to the lazy guy in me. So when our backend developer asked me what my preferred format for server responses would be, I told him to send his responses in plist format. That’s when I came across a little problem: When accessing the response body of the request, all I got was a NSString. The string contained the plist, but it was still a string and not the usual XML structure that you normally get when you open a plist file.

I found a way to parse a NSString into a NSDictionary on StackOverflow (thanks to Peter N Lewis for his answer).

So I decided to write a little category to extend NSDictionary so that it can initialize a NSDictionary with a plist NSString:

@interface NSDictionary (DictionaryWithString)
   + (NSDictionary *)dictionaryWithString:(NSString *)string;
@end

#import "NSDictionary+DictionaryWithString.h"

@implementation NSDictionary (DictionaryWithString)

+ (NSDictionary *)dictionaryWithString:(NSString *)string {
   NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
   NSString *error;
   NSPropertyListFormat format;
   NSDictionary *dict = [NSPropertyListSerialization 
      propertyListFromData:data 
      mutabilityOption:NSPropertyListImmutable 
      format:&format 
      errorDescription:&error];
   if(!dict){
      NSLog(@"ERROR: could not parse NSString: %@",error);
      [error release];
   }
   return dict;
}

@end

Feel free to use it in your own projects.

Embed Javascript files into your iOS App

Posted: | Author: | Filed under: iOS, Xcode | Tags: , | 1 Comment »

When i tried to compile a Javascript file into my iOS App I ran into a strange problem. The javascript files just were not embedded. Whenever I tried to access them using this method:

[[NSBundle mainBundle] pathForResource:@"script.js" ofType:nil];

The method always returned nil. Somehow the javascript file just did not want to be embedded.

At first I could not find out what I was doing wrong, because embedding resources into an App is really easy. Just drag them into your resources folder and you’re done, right? Well, for most file types that’s true. However, with Javascript files things are a bit different:

When compiling the App the compiler treats Javascript files as script files. Meaning that it tries to interpret them instead of just including them into the App bundle uninterpreted.

Here’s how to make the compiler turn a blind eye on the javascript files and just include them into the App bundle without interpreting them:

1. In Xcode’s project navigator (left column) klick on the blue project icon.
2. Select the “Build Phases” tab in the main window
3. Open the “Compile Sources” section
4. Find your Javascript file in the list and remove it.
5. Open the “Copy Bundle Resources” section
6. Add your Javascript file to the list

And that’s it. Now the compiler treats your Javascript file like any other resource file and copies it into the App Bundle.

UPDATE:
Xcode 4.5 now creates a warning when you just add a javascript file to your project without removing it from the “Compile Sources” section:

warning: no rule to process file '*FILEPATH*' of type 
sourcecode.javascript for architecture armv7

So the problem is easier to spot now. You still have to fix it manually as I described above.