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.

macOS, OSX, Tools

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

Titanium, Tools

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.

iOS, Swift, Tools

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

OSX, Tools

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.