Installing OS X El Capitan on Parallels VM

Want to install El Capitan, but not on your main machine?  This was really helpful for me in setting it up.

http://www.lostentropy.com/2015/06/10/installing-os-x-10-11-el-capitan-inside-parallels-desktop/

A cautionary tale about singletons and frameworks.

There is a pattern for creating singletons in Objective C.  It is extremely quick, thread-safe and allows only one instance of an object to be created.
+ (instancetype)sharedInstance {
    static dispatch_once_t onceToken;
    static NSObject *_sharedInstance;
    dispatch_once(&onceToken, ^{
        _sharedInstance =
            [[NSObject alloc] init];
    });
    return _sharedInstance;
}
dispatch_once_t is an 64 bit integer and  dispatch_once appears to be an OSAtomicCompareAndSwap64Barrier call to guard the execution of the block.

 

So, if init is made to always return nil and another private initializer is used, then only this class function will be used and there will only ever be one instance of _sharedInstance.

 

Turns out, there is a way to thwart this though.  onceToken will be added to each compilation unit that includes it.  Normally though if you have the same class linked into a program twice you will get a linker error.  There is one exception to this and that is XCTest.  When you build a unit test target for iOS, it is injected into the application that you are testing.  So if the same source code file is in your application target and your unit test target, there will be two onceToken variables.

 

At runtime, it is nondeterministic as to which is used, so you can end up creating multiple _sharedInstance objects.  So, don’t include files in your application AND your test targets.

 

Well, there is one more way to do this.  I’ve been building a static lib to support iOS7.  Now that the requirement to support it has been dropped and I only need to support iOS8 or later, I can use frameworks.  My project currently uses both.  I haven’t completed the transition yet.  It isn’t trivial and it hasn’t been a priority.  My application is using the framework and I’ve tested it with other applications as well.  However my unit tests are still using the static library.  Both the framework and the library are creating instances of onceToken.  When I run the unit tests, it injects my unit test code, along with the library, into the application that include the framework.  So when I run unit tests, dispatch_once is called twice.

 

That was fun tracking down.

UITextField and data caching

I discovered something new about iOS today. We’ll not entirely new, but new implications.

First a note about auto correction. When the user types something in to a UITextField they will get auto correction for any words that appear incorrect. As time goes by, some of those words will start to be suggested. For instance your name.

What is happening?
iOS stores words that are typed in root/Library/Keyboard/dynamic-text.dat This list is used in conjunction with the list that ships with iOS for autocorrection. You can turn off auto correction by chainging the UITextInputTraits for the object. However, even setting it to UITextAutocorrectionTypeNo will not keep text from being added to dynamic-text.dat. It will only prevent auto correction from suggesting changes. The only way to prevent the text from being written to the file is to set the secureTextEntry property to NO. The obvious drawback is that all text entered looks like you are typing in a password.

That just seems broken.  I filed radar 15912171 for this.

Also if you have entered your password into the wrong place, be sure to clear the keyboard cache to delete that file. (iPhone Settings -> General -> Reset -> Reset Keyboard Dictionary)

Using Xcode bots to sign your app.

Apple has great documentation about how to set up a bot to build your application.

If you want to resign your application so that it can be uploaded to TestFlight and used by beta testers, I found a great explanation of how to do that on this site. (http://matt.vlasach.com/xcode-bots-hosted-git-repositories-and-automated-testflight-builds/)

Basically, you create a new shared scheme, make sure your signing identity and provision are available to the bot and add a script to the post archive phase of that scheme.  That site does a good job of walking through the steps.

This will work unless you have some entitlements in your application. For instance push notifications, game center, in-app purchase, etc. In that case, you have to remove the entitlements from the app, update them for the new signature and resign the app with the entitlements. Then you can use PackageApplication to build the ipa. Basically after you verify the app but before you run PackageApplication, you want to do something like the following:

echo "get entitlements"
cd /tmp/Archive.xcarchive
BUNDLE_ID="com.company.application"
ENTITLEMENTS="enterprise.plist"
APS_ENVIRONMENT="production"
/usr/bin/codesign -d --entitlements ":${ENTITLEMENTS}" "${APP}"

echo "update entitlements - get-task-allow" /usr/libexec/PlistBuddy -c "Set :get-task-allow NO" "${ENTITLEMENTS}"

echo "update entitlements - application-identifier ${BUNDLE_ID}"
/usr/libexec/PlistBuddy -c "Set :application-identifier ${BUNDLE_ID}" "${ENTITLEMENTS}"

echo "update entitlements - aps-environment ${APS_ENVIRONMENT}"
/usr/libexec/PlistBuddy -c "Set :aps-environment ${APS_ENVIRONMENT}" "${ENTITLEMENTS}"

echo "resign with entitlements"
/usr/bin/codesign --force --preserve-metadata=identifier,resource-rules --sign "${SIGNING_IDENTITY}" --entitlements "${ENTITLEMENTS}" "${APP}"

Your BUNDLE_ID should match the application’s bundle ID, it needs to be added back into the plist. The APS_ENVIRONMENT variable will either be production or development. It should match the environment in your provisioning profile.

If you get it wrong, TestFlight will give you an error about APS environment doesn’t match signature.