PIXEL
DOCK

I like the smell of Swift in the morning…

Localizable.strings error: “The data couldn’t be read because it isn’t in the correct format”

Posted: | Author: | Filed under: Objective-C, Swift, Xcode | 4 Comments »

I added some new keys to my Localized.strings files:

"HOME_TEAM" = "Home team"
"AWAY_TEAM" = "Away team"

Then I got this error from Xcode:

Read failed: The data couldn't be read because it isn't in the correct format

It took me a minute but when I found what caused the error I had to smile. I simply forgot the semicolons at the end of each line:

"HOME_TEAM" = "Home team";
"AWAY_TEAM" = "Away team";

Those poor semicolons! Coding in Swift really makes you forget they even existed in the first place 😉

OCMock: signature declares ‘q’ but value is ‘i’

Posted: | Author: | Filed under: Objective-C | Tags: | No Comments »

When writing a test for a class using OCMock I ran into the following error message:

error: -[PXDItemsListView testListView] : failed: caught "NSInvalidArgumentException", "Return value does not match method signature; signature declares 'q' but value is 'i'."

The code that was causing this error was this:

OCMStub([self.dataSourceMock numberOfItems]).andReturn(4);

Which is a stub of the data source’s method:

- (NSInteger)numberOfItems;

And you do not get this error when you implement this method like this:

- (NSInteger)numberOfItems {
   return 4;
}

So why does the stubbing of this method create this error?

The reason is, that the implemented method does know it’s return type, so the 4 will automatically be typed to NSInteger. Which means that it will be of type int on 32-bit architectures and of type long on 64-bit architectures.

OCMock on the other hand has no idea what the return type of the 4 should be. It just sees the 4 and assumes that it is of type int. Regardless of the architecture. So when running this this on a 64-bit device you will get this error, because the stubbed method is supposed to return a long.

The fix for this is quite easy. You just have to be clear about the type of the value that your stub should return:

OCMStub([self.dataSourceMock numberOfTeasers]).andReturn((NSInteger)4);

Actually this is explicitly written down in the OCMock reference: “For methods that return primitive values it is important to use the right type of value.”

But, hey, who reads the f***ing manual, right?

iOS9 In-App Purchase bug when user cancels the purchase process

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

I just stumbled upon a bug that seems to be introduced in iOS9. When a user tries to buy a product via In-App Purchase and then cancels the process when he is asked if he really wants to buy the product a wrong error type is returned by Apple.

When the user presses “Cancel” in the UIAlertView the SKPaymentTransactionObserver method paymentQueue:updatedTransactions: gets called. The cancellation is handled like an error with the error type SKErrorPaymentCancelled. You can check for this error type because you don’t really want to show an error page when the user cancelled the purchase process. It’s not really an error but the user’s free will. So you only show an error message for the other error types:

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStateFailed:
                if (transaction.error.code == SKErrorPaymentCancelled) {
                   // just cancel the purchase process without
                   // showing an error message
                } else {
                   // show an error message
                }
                break;
            default:
                break;
        }
    }
}

So far, so good. Unfortunately under iOS9 when the user cancels the error type SKErrorUnknown is returned from Apple. That is unfortunate as it results in an error message being displayed when the user pressed “Cancel”. Somehow the correct error type SKErrorPaymentCancelled got lost in iOS9.

That is unfortunate, because we really don’t want to show the error message when the user cancels the purchase process. I do not see a solution for this problem as we cannot suppress all error messages of type SKErrorUnknown.

I filed a radar. Would be nice if Apple would fix that soon. Well, we know how seriously Apple treats radars, but hey, one can dream…

UPDATE: This is now fixed with the iOS9 release version

Use the Nil Coalescing Operator in Swift to assign default values to properties

Posted: | Author: | Filed under: iOS, Objective-C, Swift | No Comments »

One of the new operators that comes with Swift is the Nil Coalescing Operator. In the documentation it is explained as: The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil.

This operator already exited in C but for Objective-C developers it is kind of new (more about that at the bottom of this post).

With this operator assigning values to a property with a fallback to a default value becomes really concise:

Instead of doing this:

func setupBackgroundColor(color: UIColor?) {
   if (color != nil) {
      backgroundColor = color
   } else {
      backgroundColor = UIColor.whiteColor()
   }
}

You can simply do this:

func setupBackgroundColor(color: UIColor?) {
        backgroundColor = color ?? UIColor.whiteColor()
}

Pretty cool, isn’t it?

Actually something like this was already possible with Objective C:

- (void)setupBackgroundColor:(UIColor *)color {
   self.backgroundColor = color ? : [UIColor whiteColor];
}

How to show your iOS Share Extension only in Safari (or other browsers)

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

When you develop a Share Extension for your iOS App many times the Extension is not suitable for all places where it could be shown to the user. For example if your Share Extension can only share URLs it does not make sense to offer it to the user when he is using the Photos App.

This is why Apple implemented the NSExtensionActivationRule field in the Extension’s Info.plist. That’s quite straightforward and not exactly rocket science, but still many tutorials on Extension do not mention this and it took me a while to find out how this works, so I’ll share this with you.

So, when you create a new Extension Xcode creates a Info.plist in the ‘YOUR_EXTENSION/Supporting Files’ folder. In there you’ll find a Dictionary with the key NSExtension.

NSExtension

    NSExtensionAttributes
    
        NSExtensionActivationRule
        TRUEPREDICATE
    
    NSExtensionMainStoryboard
    MainInterface
    NSExtensionPointIdentifier
    com.apple.share-services

The interesting part is the String with the key NSExtensionActivationRule. By default Xcode sets its value to TRUEPREDICATE. That’s just for development purposes and makes sure that your Extension is shown as a sharing option everywhere. This has to be removed before you submit your app the the AppStore of your app will be rejected.

So in our case we can only share an URL so we want to make sure that it does not get added to the sharing options in Apps that do not offer an URL to share (like the Photos App).

So you have to change the type of the NSExtensionActivationRule field from String to Dictionary and add one or more keys that Apple provides to define what data types your Extension supports:

NSExtensionActivationSupportsAttachmentsWithMaxCount
NSExtensionActivationSupportsAttachmentsWithMinCount
NSExtensionActivationSupportsFileWithMaxCount
NSExtensionActivationSupportsImageWithMaxCount
NSExtensionActivationSupportsMovieWithMaxCount
NSExtensionActivationSupportsText
NSExtensionActivationSupportsWebURLWithMaxCount
NSExtensionActivationSupportsWebPageWithMaxCount

In our case we only support URLs so we need the NSExtensionActivationSupportsWebURLWithMaxCount key. So the NSExtension Dictionary should look like this:

NSExtension

    NSExtensionAttributes
    
        NSExtensionActivationRule
	
	    NSExtensionActivationSupportsWebURLWithMaxCount
	    1
	
    
    NSExtensionMainStoryboard
    MainInterface
    NSExtensionPointIdentifier
    com.apple.share-services

And now your Share Extension is not activated in the Photo App anymore. Only in apps that offer a URL will now show your Share Extension as one of the sharing options.

How to remove an option from a NS_OPTIONS bitmask

Posted: | Author: | Filed under: Objective-C | No Comments »

I really like the NS_OPTIONS macro. I think it is a neat and tidy way to store multiple boolean values in one bitmask.

So for example if you have a car object that stores the features a customer ordered for his new car you could define something like:

typedef NS_OPTIONS(NSInteger, Feature) {
    FeatureNavigationSystem = (1 << 0),
    FeatureHeatedSteeringWheel = (1 << 1),
    FeatureParkDistanceControl = (1 << 2),
    FeatureSunRoof = (1 << 3),
    FeatureConvertible = (1 << 4)
};

Now you can store the features that a customer selected in one NSInteger (or in this case 'Feature') value using the bitwise OR operator (|):

Feature selectedFeatures = FeatureNavigationSystem | FeatureParkDistanceControl;

To check if a customer has selected a certain feature you use the bitwise AND operarator (&):

if (selectedFeatures &= FeatureNavgiationSystem) {
    // user has selected navigation system
}

Now there might be a situation where you want to remove a feature from the bitmask of selected options. In my simple example that might be the case when a user selects the option FeatureConvertible. If the customers does that, you might want to make sure that the feature FeatureSunRoof is not selected (because obviously a convertible does not have a roof).

So you want to remove the option FeatureSunRoof from the selected options while leaving the rest of the selected options as they were. This is where our friend the bitwise NOT operator (~) comes into play:

selectedOptions &= ~FeatureSunRoof

This removes the FeatureSunRoof from the selectedFeatures bitmask.

How to get rid of the padding / insets in an UITextView

Posted: | Author: | Filed under: iOS | Tags: | 14 Comments »

Sometimes you want to display some text that contains an email address, an URL, a phone number, an address or a date. Of course you want to make these text parts interactive so that a user can tap on an URL and directly go to the linked webpage, or make a phone call when he taps on a phone number, etc.

Implementing that is really easy. You just use an UITextView and set dataDetectorTypes = .all. And voilá: the UITextView now automatically detects phone numbers, links, calendar events and addresses and makes them tappable. Really nice!

However, there is one caveat: UITextView adds some insets around the text it displays. So if you want to align an UITextView with another UI element (like an UILabel) and you set both elements to the same origin.x, you will see that the text of the UILabel and the text of the UITextView are not aligned. The text of the UITextView is shifted a bit to the right. If you try to align them horizontally you will notice that it is also shifted a bit to the bottom. When you add background color to the UITextView you see that it adds some padding around it’s text. That is nice, if your UITextView has a border or a background color, or you use it to display some editable or scrollable text, but in our case it is not ideal.

Luckily there is a solution for that. UITextView has a property called textContainerInset. The default value for this inset is (top = 8, left = 0, bottom = 8, right = 0). So setting this inset to UIEdgeInsetsZero should get rid of the top and the bottom padding.

textView.textContainerInset = .zero

Works as expected. But there is still some padding to the left and the right of the text. The solution to get rid of that is not as obvious as setting the insets to zero. UITextView uses a NSTextContainer object to define the area in which the text is displayed. And this NSTextContainer object has a property called lineFragmentPadding. This property defines the amount by which the text is inset within line fragment rectangles, or in other words: the left and right padding of the text. The default value for this is 5.0, so setting this to 0 removes the padding.

textView.textContainer.lineFragmentPadding = 0

So, now the text in the UITextView aligns beautifully with the text of the UILabel.

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.

Could not launch “APP_NAME”, process launch failed: Security

Posted: | Author: | Filed under: Xcode | Tags: , | No Comments »

After switching to Xcode 6 and the iOS8 SDK I was confronted with a strange error message when trying to test my app on a device. The app was built and deployed to the device but Xcode did not run it. Instead it showed an alert with the following error message:

Could not launch "APP_NAME", process launch failed: Security

The only thing you can do is to press “OK”, but the app still does not run. When you try to run it again from Xcode you just get the same alert.

The solution to this problem is quite simple:

  1. Run the App from Xcode
  2. When the alert shows, press “OK”
  3. Open the app on the device
  4. An alert will be shown, asking you if you want to trust this app. Press “Trust”.

Now you should be able to run the app from Xcode.