Swift excluding files from iCloud backup

Having your device automatically back-up to iCloud is one of the better services Apple provides.

Although not new, iCloud (introduced in June of 2011) back-up is one of the better services Apple has rolled out. If used right this removes a whole class of end user data lose fears.

However as an app developer you need to take this into consideration. Where it is privacy, size constraints, or other you need to be aware what you might be sending to iCloud.

You might wish to not back-up specific files. To do this you must set the file’s isExcludedFromBackup attribute to true. Below is a helper method to help get you started.

Forcing iCloud logout on your Mac

I think everyone, at least developers, have at least one iCloud account which is linked to an old email address.  To address this I’ve been migrating mine over to another domain.  The process is extremely easy, just visit appleid.apple.com and change your Apple ID email address.

Although easy, this can be a real pain as you will need to enter your new details 10x times per device.  Annoying but worth the effort to clean up your accounts.

I thought I was all set until I ran into an issue on macOS.  Since I didn’t sign out of iCloud on macOS before changing my Apple ID email it wouldn’t let me sign out.  I figured there had to be information on how to force a logout on Stackoverflow or the Apple forums.  But, after multiple searches I wasn’t able to find a solution that simply allowed me to sign out.  I decided to look if there was any tweaks to defaults which could help.

After a few attempts I found a solution. If you ever run into this problem, you can simply do the following:

  1. Open Terminal as a user with Administrator rights
  2. Type: defaults delete MobileMeAccounts
  3. Press enter to finish

You should now be able to go into System Preferences, iCloud and see the below iCloud login prompt.

after-signout

Check if GestureRecognizer added

One of my new projects dynamically generates a UI based on a DSL entered by the user. This leads to a host of defensive coding challenges since there is multiple code paths that can add controls and their associated gesture recognizers.

I found it helpful to add guard statements that stop recognizers from being added twice. The same approach is used for controls, but that is a different topic.

The below utility function can be used to check if the provided recognizer is already in the recognizer collection associated with an object.

The following code shows the utility function in action. All you have to do is incorporate containsGestureRecognizer into your guard statements.

Although really an edge case this was a fun bit of code to try out.

Removing CGPDFDocument Passwords

Working with PDFs on mobile is a pain. On the bright side at least Apple provides us CGPDFDocument

On the bright side Apple does provide CGPDFDocument. If you’re on Android you are left to 3rd party alternatives such as iText and PDFBox.

However examples on how to use CGPDFDocument are limited to the bare basics. On a recent project, I though I had a pretty basic requirement. Provide the user with the ability to remove the password for a protected PDF file. How hard could this be? Unfortunately I wasn’t able to find any examples on how to do this so decided to put my own together.

The most important thing you need to know about working with CGPDFDocument is it is immutable. This means you can’t just call unlockWithPassword and update a property to remove the password.

This means you will need to first unlock the PDF, then create an entirely new document. If you are working with large files you will need to be careful about the memory you are using. Instruments will be your best friend.

Let’s walk through an example. First we need to load a PDF file into a Data object as shown below.

Next you need to unlock your PDF using the existing password. In the example below we create a helper function called unlock that takes the Data from our PDF file and returns an unlocked CGPDFDocument. If the method is unable to either cast the Data object or unlock using the provided password a nil is returned.

We now have an unlocked CGPDFDocument object. Since this is immutable we need to use this as the source to create a new CGPDFDocument without a password. This is done by looping through the now unlocked CGPDFDocument and copying each CGPDFPage into a new CGPDFDocument. The copyPDFtoData helper function in the below example shows how the Core Graphics context is managed as part of the looping process. The end result is a Data object that is a copy of the PDF we started with, but without the password.

Keep in mind this isn’t an exact copy. The CGPDFDocument info and other meta data won’t be copied as part of this process. This can easily be added to the copyPDFtoData but was overkill for this example.

For convenience I’ve combined the above helper functions into a single class for easy maintenance and experimentation.

For more comprehensive PDF handling check out my PDFUtilities project.

Checking if device Passcode is enabled using Swift

If you spent any time building apps for the Enterprise or business space odds are you have turn into the requirement to check if the device has a passcode enabled.

Mobile Device Management (MDM) Solutions are full of tools that handle this for you.  More often I’m finding that organizations are opting for lighter weight solutions to simplify their deployments.

The approach of checking for a passcode would easily become an anti-pattern in that the user at anytime can disable this feature.  I would recommend keeping this in mind and implement the check as only one part of a larger more comprehensive security strategy.

I would call the approach of checking for a passcode somewhat of an anti-pattern in that the user could disable this feature at anytime. If you implement this approach I would highly recommend that this is only one part of your security strategy.

Starting in iOS 8 you could finally check, without jailbreaking, if the device had a passcode set. All you have to do is create a new Keychain entry using the new kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly access option.

Below is an example of using the Keychain to test if the device passcode is enabled.

Then in iOS 9 Apple introduced a much better way to perform this check. In the same way you check if Touch ID is enabled you can check if the device has a passcode.

Below is an example on how you can do this using canEvaluatePolicy.

The below PasscodeUtils.swift combines the two approaches to allow for you to support this functionality back to iOS 8.

Simplifying using Keychain Access Groups

Keychain sharing is an easy and secure way to share information between apps on the same device. This is even more useful when you think about sharing information between an app and it’s extensions. In my opinion Keychain is just as easy as using NSUserDefaults but provides a greater level of security.

Although Keychain sharing is easy to use, I found the use of Access Groups or kSecAttrAccessGroup somewhat tricky.  There are a few different ways of working with Keychain Groups I thought it might be useful to go through the approach I adopted for others looking to implement Keychain sharing within their own apps.

First you will need to setup Keychain sharing in your app.  The below are some great tutorials to get you started.

Next, you will need to try to pass information between apps in your Keychain Group.  Here is where I ran into my problem.  At first I assumed that you simply set the kSecAttrAccessGroup value to the name of the Keychain Group you want to use.  No matter the KeyChain Group value I tried, it always returned a nil.

Why?  If you open your app’s entitlement file you can see why. Your keychain access group is a calculated value of your Keychain Group and your $(AppIdentifierPrefix).

This means if you set kSecAttrAccessGroup to your keychain access group of com.bencoding.awesome-group it would always return nil. You would need to add the App ID Prefix (also called Team ID) to your Keychain Group. In our example this would be something like ABC1234DEF.com.bencoding.awesome-group.

Getting your App ID Prefix from the developer portal isn’t the end of the world and the tutorials at the beginning of this article do a good job covering why this is necessary.

What is another “magic” string in your app?

If you are building enterprise apps which are often resigned or just hate using “magic” strings this approach leaves you looking for an alternative.

Another approach is to use the Keychain’s own meta data find the App ID Prefix .  All you need to do is create a keychain item and then read the returned kSecAttrAccessGroup value from the meta data.  Using the provided kSecAttrAccessGroup value you can parse the  App ID Prefix and default Keychain Access Group.  Removing (or at least reducing) the need for “magic” strings.

I’ve encapsulated this approach into the KeyChainAccessGroupHelper.swift shown at the end of this post.

This approach makes using Keychain sharing with any of the popular Keychain libraries easier.  Below demonstrates how to use KeyChainAccessGroupHelper with the KeychainAccess library.


The below gist shows the implementation details of KeyChainAccessGroupHelper.swift

KeyChainAccessGroupHelper is also used as part of the KeyStorage project.

Solving Appcelerator Studio’s “Invalid Request” when Compiling

I use Titanium for many of my projects and have found it a great framework. Just like with any tools every once and awhile you run into some interesting errors. I am often switching networks and every once and awhile I get the below error in the Appcelerator Studio console window.

[INFO] :   Alloy compiled in 9.88357s
[INFO] :   Alloy compiler completed successfully
[TRACE] :  offline build file ...
[TRACE] :  online 1
[TRACE] :  sending request ...
[TRACE] :  result from /build-verify=> {"success":false,"error":"invalid request","code":"com.appcelerator.security.invalid.session"}, err=null
[ERROR] :  invalid request

This is an interesting warning as it means the Appcelerator CLI no longer has valid session information.  Great for security, but can be confusing as the error is not resolved by logging in and out of Appcelerator Studio.

This is easily resolved by the following:

  1. Open terminal
  2. Type appc logout, this will logout the Appcelerator CLI
  3. Type appc login, then enter your Appcelerator credentials

If you are using the CLI most likely will never see the above but for those of us that like to use Appcelerator Studio might find this little trick helpful.