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
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!
And now for something completely different: After updating my OS to Mountain Lion, one thing was driving me nuts:
I just could not drag any window (Chrome, Finder etc.) from my MacBook Pro’s display to my secondary monitor (which I arranged on top of my MacBook Pro monitor). It just was blocked by the menu bar.
I took me a while to figure it out but apparently you have to show a little persistence: Just keep on dragging! At first the window is stuck below the menu bar but then just as the mouse is about to reach to top of the secondary monitor, the window jumps to the secondary monitor as well.
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";
};
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.
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.
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:
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.
Recently I had to use NSDecimalNumber objects to avoid the rounding errors that come with float values. While comparing two float values is as easy as it gets comparing two NSDecimalNumbers works a bit different:
NSDecimalNumber is a subclass of NSNumber so you have to use NSNumber’s compare: method to compare the values:
NSDecimalNumber *number1 = [NSDecimalNumber numberWithFloat:0.1];
NSDecimalNumber *number2 = [NSDecimalNumber numberWithFloat:0.2];
// check if number1 is less than number2
if ([number1 compare:number2] == NSOrderedAscending {
NSLog(@"number1 is less than number2")
}
When I started to play around with CoreData for the first time, I came across a strange error. The error description said “The model used to open the store is incompatible with the one used to create the store”. At first I just couldn’t figure out what the problem was. As soon as I tried to access the NSManagedObjectContext the app crashed.
The solution is really simple: Whenever you change something your CoreData data model, you have to delete the old compiled app from the simulator or device. CoreData uses SQLite3 which is nothing more than a file in your app’s filesystem. If you do not delete the old version of your app the file remains in the file system and does not reflect the changes that you made in the data model. The SQLite file will only be deleted when you delete the whole app.
So the golden rule is: Whenever you change something in the data model of your CoreData driven App, make sure to delete the old version of the app before compiling and running the new one.
When you use the new iOS 4 UIView animations with blocks you will notice that the animations will lock user interaction while they are running. With really short animations this might be uncritical but it is surely unacceptable when a user cannot scroll a UIScrollView because another UIView is being animated with a longer duration like 2 seconds.
The reason for this “frozen UI problem” lies in the default value for the options mask of UIView’s animateWithDuration: methods. Apparently the option UIViewAnimationOptionAllowUserInteraction is not part of the default options mask. Unfortunately only one of the three animateWithDuration: methods allows to set the animation options. So to avoid the freezing of the user interface use animateWithDuration:delay:options:animations:completion: like in this example where a view is faded out over the duration of two seconds without blocking the user interface:
Sometimes you need to know the height of a html document that is loaded into a UIWebView. For example if you want to set the height of the UIWebView to the height of its content.
The logical way would be this:
1. add a Javascript function to the HTML that returns the height of the document.
2. add the call to this Javascript function to the UIWebViewDelegate’s method webViewDidFinishLoad:
This sounds easy, but if you look at the results you’ll realize that the values for the document height are not correct and pretty random.
The problem is: webViewDidFinishLoad: get’s called when the HTML is fully loaded BUT it still has to be fully rendered before you can determine its height!
So the call comes too early. You could delay the call but that’s not the way to go here as this is still unreliable and you want the height as soon as possible.
The solution is to revert the process. Instead of Objective-C asking the Javascript for the height, have the Javascript call Objective-C as soon as it knows the height of the document.
Here’s how to do it:
1. Add a Javascript function to the HTML document
Add this function either to the head or the body of your HTML
This function gets called as soon as the HTML document is fully rendered. It puts the height into an URL and sends a request with this URL.
If you are asking yourself why I use “ready://” instead of “http://”: I do this because sending a request is the only way, how the Javascript inside a UIWebView can send messages to the UIWebViewDelegate. So this is not a “real” HTTP Request. Instead you can use the URL scheme to make things easier on the Objective-C part (as you will see in step 2).
2. Intercept the request in the UIWebView’s delegate
A UIWebViewDelegate has a method, that get’s called everytime the HTML inside the UIWebView sends a request. Here’s what to do in this method:
Here you can see why I used the custom URL scheme “ready”. It makes it easy to identify my request. The URL that the javascript requested has the format “ready://1200” meaning that the HTML content is 1200px high. So I use the scheme “ready” to identify my request and the “host” part of the URL to sent the actual height. Then it’s easy to set the height of the UIWebView to the height of the HTML document’s content.
Return NO to stop the UIWebView from trying to load the request. Don’t forget to return YES for all other requests or the HTML content won’t even get loaded into the UIWebView in the first place.