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

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.

def setBuildNumberOnExtension(build_number)
raise if build_number.nil?
puts "Setting Extension to build number #{build_number}"
sh("/usr/libexec/PlistBuddy -c 'Set CFBundleVersion #{build_number}' ../myWatchApp/Info.plist")
sh("/usr/libexec/PlistBuddy -c 'Set CFBundleVersion #{build_number}' ../myWatchAppExtension/Info.plist")
end

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.

desc "Create ipa"
lane :build do
increment_build_number
setBuildNumberOnExtension(
get_build_number
)
gym(scheme: "TestApp", workspace: "TestApp.xcworkspace", output_directory: "../../../Builds/TestApp", clean:true, silent: true, export_method: "enterprise")
end

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.

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

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.

Logging Exceptions to Google Analytics Using SwiftyBeaver

There has been an avalanche of Swift based logging frameworks lately.  After trying several, I found SwiftyBeaver was both flexible and simple enough to move all of my apps onto.  Similar to most of the other logging frameworks, SwifyBeaver is extensible. But, unlike many others, there is no real magic injected into the framework making it easy to read and hopefully maintain.  This is best demonstrated by how compacted it’s plugin system is.  You really only need to write you business specific information, the rest is handled for you.

I use Google Analytics for behavior tracking and basic exception tracking today.  Although all of my Google Analytics code is centralized exception reporting is handled explicitly.  With the move to SwiftyBeaver I wanted to see how reporting exceptions to Google Analytics could be handled automatically as part of my logging strategy.  To accomplish this I created the SwiftyBeaver Google Analytics Exception Logger, available here.  Just as the very long name indicates this plugin will automatically post error information to Google Analytics.

Before you start

The Google Analytics Exception plugin requires that you first install SwiftyBeaver and Google Analytics.  You can find information on how to do this below.

After both SwiftyBeaver and Google Analytics have been installed you need to copy the plugin into your project. Instructions for doing this are available here.  You’re now ready to start configuring logging in your project.

Creating your Logger

Creating a logger in your project is extremely simple. Typically you will want to add the below to the top of your AppDelegate.swift so you can use logging throughout your project.

import SwiftyBeaver
let logger = SwiftyBeaver.self

Adding the Google Analytics Logger Destination

Now that you have created your SwiftyBeaver logger you need to add destinations.  Without adding any destinations SwiftyBeaver wont actually do anything.  For this example I’m going to add two destinations. The first will be the built-in Console destination which simply writes to the Xcode console.

let console = ConsoleDestination()
logger.addDestination(console)

Next we’ll add the Google Analytics Exception Logger.  When creating the GABeaver plugin you must added your Google Analytics key.  This will be used when reporting Exceptions.  You can also specify the reporting threshold.  This parameter controls the minimum logging level that should be reported to Google Analytics as an exception. By default this is set to only report error levels or greater.  If you wanted to report warnings or higher you could simply provide a threshold of warning and the plugin will automatically send both warnings and errors.

Below illustrates how to add the Google Analytics Exception plugin with the default settings.

let gaErrorLogger = GABeaver(googleAnalyticsKey: "your GA Key")
logger.addDestination(gaErrorLogger)

Optional Configurations

By default only Error Log messages will be sent to Google Analytics.  You can change this by setting the Threshold property on the logger.

For example, you can record all message with a level of WARNING or great by doing the following:

gaErrorLogger.Threshold = SwiftyBeaver.Level.Warning

You can also configure the logger to write to the console each time a message is logged. To enable console logging, set the printToConsole property to true as shown below:

gaErrorLogger.printToConsole = true

More Information

Additional information is available on github at SwiftyBeaver-GA-Exception-Logger.

SwiftyBeaver

Hiding .DS_Store files on your desktop while still showing hidden files

Like most developers the first thing I do when I get a new mac or do a clean install is run the below terminal command to show all hidden files and folders.

defaults write com.app.Finder AppleShowAllFiles Yes
killall Finder

This is a necessary evil as now I see Dot files on my desktop. For me this is the annoying .DS_Store and .localized files.   The only way I’ve found to not show these on my desktop yet have hidden files enabled is to change their icon to transparent.  This is very much a hack but accomplishes it’s goal in only a few clicks.  Below is a step by step tutorial on how to get your clean desktop back.

Step 1: Getting the transparent image

This first thing you need to do is copy the below transparent image.  I’ve placed border around the image to make it easier to find.

transparent

To copy the image just right click on Safari and select the Copy Image  option as illustrated below.

CopyImage

Step 2: Get Info

The next step is to right mouse click on the .DS_Store file on your desktop and select “Get Info” as shown below.

getInfo

Step 3: Updating the icon

This opens a dialog with all of the information about your .DS_Store file.  You want to click on the document icon at the top left of the dialog and paste the image copied as part of step 1.

info-dialog

You will see the info dialog icon disappear.

step3-a

After closing the info dialog you will also notice that the .DS_Store icon disappears on your desktop.  Now you just need to repeat the process for each Dot file you wish to hide.

step3

Step 4: Hiding the icon text

Now that the icons are hidden, you will simply see the icon text.  I haven’t found a good way to remove the text but this is easy enough to hide.  If you simply drag the icons off screen, for example to the bottom right of your screen as shown below. Their text will be out of view and they will be out of sight out of mind.

dock-icon-hidden

Although this is a major hack, it does provide those of us that like to have a clean desktop some peace.

Results

Below shows the before and after results.

beforeafter

Wallpaper by Justin Maller.

Resetting the iOS Simulator

There seems to be several ways to reset or clear the iOS simulator ranging from pressing the “Reset Content and Settings..” button on the iOS simulator from deleting folder under ~/Library/Developer/CoreSimulator/DerivedData.

For me the easiest and faster approach for my workflow is to simply issue the below in terminal.

xcrun simctl erase all

Review : Xcode 4 Cookbook

Xcode

PACKT recently provided the opportunity for me to review the Xcode 4 Cookbook by Steven Daniel.  As primarily a Titanium developer I spend most of my time in Sublime Text or Titanium Studio so I thought it would provide a good opportunity to spend more time in Xcode. 

First, the book title is alittle misleading.  I found this book to be more about iOS development then Xcode itself.  The author does cover Xcode in detail, but always in the context of building or profiling an app.  This approach made it both easier to read and to remember.

The book starts with covering the basics and then moves into creating user interfaces.  Having built most of my UIs through code it I found the content on Interface Builder and Storyboards helpful.  The recipes are easy to follow and very detailed, perfect for beginners. 

My favorite section of the book is around instruments.  Again the author walks us through these recipes in a very detailed easy to follow way.  As a side note, you can use the same steps outlined in these recipes in your Titanium project. You just need to generate a full Xcode project using transport.py as detailed here.  Coming from a non Xcode background, the navigation and descriptions of the options available for profiling made Xcode’s sometimes challenging interface easy to understand.

The chapter on iCloud provided a great “getting started” guide with a clear easy to understand recipe showing on to implement Document based storage using NSUbiquitousKeyValueStore.  This was a good introduction to iCloud and very easy for someone new to iCloud to follow along.  I do wish the author would have gone further on this topic and provided more details on storing greater the 64k documents.

The recipe using CoreImage provides a nice introduction to the topic and discusses a few of the common available filters.  This recipe provided a nice transition from the earlier CoreGraphic recipe.  I do wish the author would have provides a multi filter example though.

Overall, I really enjoyed reading this book and found the recipes helpful.  The detail and direction provided by the author makes all of the recipes easy to follow and quick to perform.  If you are new to iOS development or Xcode this book provides the resources needed to tackle that learning curve quickly.

A Few Helpful Xcode Plugins

Xcode is one of those IDEs that you either love or hate ( sometimes both ).  Coming from a Visual Studio background it has taken awhile to get used to the Xcode workflow.  I’m still hopeful that someone will create a Resharper like tool for Xcode, but until then I wanted to share some of the plugins that I’ve found useful.

Completion Dictionary

This plugin provides enhanced auto completion.  I bounce between languages frequently and this makes remembering the specific syntax I’m thinking of much easier.

http://www.obdev.at/products/completion-dictionary/index.html

Code Pilot

Calling this Spotlight for for Xcode would be an understatement.  Once you get used to Code Pilot you will rarely leave the keyboard. In short navigation made easy.

http://codepilot.cc/

Accessorizer

This plugin seems to be the closest thing Xcode has to Resharper.  With 40+ code generation actions and a ton of help this is well worth the small purchase price.

http://www.kevincallahan.org/software/accessorizer.html

Color Sense

This visual plugin provides a color picker when working with UIColor ( and NSColor ).  I rarely use this plugin, but when I do it is a time saver.

https://github.com/omz/Colorsense-for-Xcode

KSImageNamed-Xcode

Can’t remember whether that image you just added to the project was called button-separator-left or button-left-separator? Now you don’t have to, because this will autocomplete your imageNamed: calls like you’d expect. Just type in [NSImage imageNamed: or [UIImage imageNamed: and all the images in your project will conveniently appear in the autocomplete menu.

Read more about this plugin on Kent Sutherlands blog.

On Github: https://github.com/ksuther/KSImageNamed-Xcode

XcodeColors

XcodeColors allows you to use colors in the Xcode debugging console.

https://github.com/robbiehanson/XcodeColors

HOStringSense

If you work with large strings in your code you will definitely want to check out this plugin.  You can enter your full string into this plugin’s UI and it will correctly escape and insert the string into your code.

https://github.com/holtwick/HOStringSense-for-Xcode

Bracket Matcher

This plugin automatically inserting paired message sending brackets. Maybe I’m missing something but shouldn’t Xcode do this for you by default?

https://github.com/ciaran/xcode-bracket-matcher

Fixins

I recently found a github project by Dave Keck that has several Xcode plugins are extremely useful.  My favorite is the CurrentLineHighligher but would recommend checking them all out.

  • CurrentLineHighlighter
  • DisableAnimations
  • FindFix
  • HideDistractions
  • InhibitTabNextPlaceholder
  • TabAcceptsCompletion

https://github.com/davekeck/Xcode-4-Fixins

MiniXCode

Just want to focus on the code? This plugin allows you to reduce the sometimes massive Xcode toolbars.

https://github.com/omz/MiniXcode

 

AppCode

A commenter (Nick) mentioned that JetBrains has a great IDE called AppCode available at http://www.jetbrains.com/objc.