iOS, Tools, Xcode

Freeing the space used by Xcode with XcodeCleaner

With every release of Xcode it seems to eat more and more space. I was thrilled to find the XcodeCleaner project from Baye on Github.  This is a great project that allows you to tailor the space Xcode uses.  I’ve been able to free up an easy 12GB without any issues.

I highly recommend checking out the project on GitHub at XcodeCleaner. You can build the project from source or download from the macOS App Store for $0.99.
68747470733a2f2f7777772e6170706c652e636f6d2f6974756e65732f6c696e6b2f696d616765732f6c696e6b2d62616467652d61707073746f72652e706e67

screenshot

iOS, Tools, Xcode

Fastlane: Solving the WatchKit CFBundleVersion matching problem

Fastlane is one of the tools that a developer shouldn’t live without. At this point it must have saved me hundreds of hours.  As great as fastlane is out of the box it has a few rough edges when you introduce a WatchKit app.

Recently I added a WatchKit app into one of my projects and started getting the below error:

error: The value of CFBundleVersion in your WatchKit app’s Info.plist (262) does not match the value in your companion app’s Info.plist (263). These values are required to match.

In a nutshell, Apple requires that your WatchKit app and it’s companion app share the same version. If your fastlane build includes increment_build_number these will never match by default.

Solving this problem is pretty easy, although alittle more involved then just changing your versioning system like in your core app. Hopefully the below will help people Googling for this error in the future.

Step 1: Update your Watch App’s Versioning Configurations

The first thing you will need to do is update the versioning configuration for your Watch App’s target.

  1. In Xcode click on your Watch App’s target
  2. Go to the Build Settings tab
  3. Scroll (or search) for the Versioning section
  4. Update the “Current Project Version” to match your core app target’s version. It is important you get this correct the first time.
  5. Switch the “Versioning System” to “Apple Generic”

watchapp-versioning.png

With these changes in place fastlane increment your Watch App’s Current Project Version every time increment_build_number is called. This will work just like it does in your core app target.

Step 2: Update your Watch App’s Extension Configuration

Just like step 1, you will need to update the versioning configuration for your Watch App’s extension target.

  1. In Xcode click on your Watch App’s Extension target
  2. Go to the Build Settings tab
  3. Scroll (or search) for the Versioning section
  4. Update the “Current Project Version” to match your core app target’s version. It is important you get this correct the first time.
  5. Switch the “Versioning System” to “Apple Generic”

watchext-versioning.png

This again let’s fastlane increment your Watch App’s Extension when increment_build_number is called.

Step 3: Keeping your CFBundleVersion in sync

If you run your fastlane build process now you will notice you still get an error that your CFBundleVersion information does not match. Whereas fastlane is updating the Current Project Version in both your Watch App and Watch App Extension it doesn’t update their CFBundleVersion value.

The best way I’ve found to do this is to use PlistBuddy and update the CFBundleVersion as part of the fastlane build process. Below is a small convenience function created to manage the process of updating the build numbers.

For my workflow I’ve incorporated the setBuildNumberOnExtension helper function into my build method that is called as part of my deployment, testing, or CI process.

The same approach should work for all extensions. Even if you only have a “Today Extension” I’d recommend keeping the version numbers in sync with your core app. This will make life easier when you are debugging.

Check of the fastlane examples for more ideas on how to create extensions to improve your workflow.

iOS, Swift

Thinking about Memory: Converting UIImage to Data in Swift

How often do you convert a UIImage into a Data object? Seems like a relatively straight forward task, just use UIImageJPEGRepresentation and your done.

After doing this I started seeing memory spikes and leaks appear which got me thinking on how I can better profile different options for performing this conversion. If you want to follow along you can create your own Swift Playground using this gist.

Approaches

The first step was looking at the different ways you can convert a UIImage into Data. I settled on the following three approaches.

UIImageJPEGRepresentation

Out of all the options this is the most straightforward and widely used. If you look at the testing blocks later in the post you can see I’m simply inlined the UIImageJPEGRepresentation with the test suite compression ratio.

UIImageJPEGRepresentation within an Autorelease Pool

Out of the box UIImageJPEGRepresentation provides everything we need. In some cases I’ve found it holds onto memory after execution. To determine if wrapping UIImageJPEGRepresentation in a autoreleasepool has any benefit I created the convenience method UIImageToDataJPEG2. This simply wraps UIImageJPEGRepresentation into a autoreleasepool closure as shown below. We later use UIImageToDataJPEG2 within our tests.

Using the ImageIO Framework

The ImageIO framework gives us a lower level APIs for working with images. Typically ImageIO has better CPU performance than using UIKit and other approaches. NSHipster has a great article with details here. I was interested to see if there was a memory benefit as well. The below helper function wraps the ImageIO functions into an API similar to UIImageJPEGRepresentation.  This makes testing much easier. Keep in mind you’ll need to have image orientation yourself.  For this example we just use Top, Left. If you are implementing yourself you’ll want read the API documentation available here.

What about UIImagePNGRepresentation?

UIImagePNGRepresentation is great when you need the highest quality image. The side effect of this is it has a largest Data size and memory footprint. This disqualified UIImagePNGRepresentation as an option for these tests.

Testing Scenarios

For my scenarios it was important to understand how memory is impacted based on the following:

  • Number of executions, i.e. what is the memory impact for calling an approach on one or many images.
  • How the Compression ratio impacts memory usage.

Image quality is an important aspect of my projects, so the tests where performed using the compression ratios of 1.0 and 0.9.  These compression ratios where then run using 1, 2, 14, 20, and 50 executions.  These frequencies demonstrate when image caching and Autorelease Pool strategies start to impact results.

Testing Each Approach

I test each of the above mentioned approaches using the template outlined below.  See the gist of the details for each approach.

  1. At the top of the method a memory sample is taken
  2. The helper method for converting a UIImage to a Data object is called in a loop.
  3. To make sure we are measure the same resulting data across tests, we record the length of the first Data conversion.
  4. When the loop has completed the proper number of iterations the memory is again sampled and the delta is recorded.

There is some variability on how each approach is tested.

The implementation for each approach is slightly different, but the same iteration and compression ratios are used to keep the outcome as comparative as possible.  Below is an example the strategy used to test the JPEGRepresentation with Autorelease Pool approach.

Test Results

Below is the result broken down by iteration.

Results for 1 Iteration

uiimagetodata-1

Results for 2 Iterations

uiimagetodata-2

Results for 14 Iterationsuiimagetodata-14

Results for 20 Iterations

UIImageToData-20.png

Results for 50 Iterations

uiimagetodata-50

Conclusion

I am sure there is a ton of optimizations that could be made to bring these numbers down. Overall the usage of UIImageJPEGRepresentation wrapped within an Autorelease Pool looks to be the best approach.  There is more work to be done on why the compression ratio has an inconsistent impact, my guess is this is a result to caching within the test.

Although the ImageIO strategy was better in a single execution scenario I question if the proper handling of image orientation would reduce or eliminate any of your memory savings.

Caveats

There are more comprehensive approaches out there. This is just an experiment using Playgrounds and basic memory sampling.  It doesn’t take into account any memory spikes that happen outside of the two sampling points or any considerations around CPU utilization.

Resources

  • Gist of the Swift Playground is available here
iOS, Swift

Stopping Tap Gesture from bubbling to child controls

The default behavior is for your tap or other gestures to bubble up to their child controls. This avoids the need to add a recognizer on all of your child controls. But this isn’t always the behavior you are looking for. For example imagine you create the below modal view. You want to add a tap gesture recognizer to the view itself so when your user taps the grey area it closes the modal. But you don’t want the gesture to be triggered when a tap is made on the content UIView of the modal.

modal-example

First adding the UITapGestureRecognizer

For our use case the first thing we need to do is add a UITapGestureRecognizer to the root UIView of the UIViewController. This will close our UIViewController when the grey area is tapped.

Limiting the scope of the Gesture

So that the UITapGestureRecognizer isn’t triggered when a tap is made to the content UIView we simply need to add protocol method to restrict the tap to the view it is associated with.

Putting it all together

Below is the full code associated with the modal UIViewController. If you are not familiar with how to create a modal UIViewController I would recommend checking out Tim Sanders tutorial here.

iOS, Swift

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.

iOS, Swift

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.

iOS, Swift

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.