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.

Xcode

Plugin to help fight the DerivedData Junk Drawer

It just takes creating a few example projects and suddenly you have a ton of simulator folders to deal with. Over time this becomes a junk drawer of simulator references to experiments and samples.

Toshihiro Morimoto (dealforest) has released the Cichlid Xcode plugin to help combat this.  It is super simple to use and simply deletes the DerivedData associated with your project when you run Product-> Clean.  I’ve been using it for the last week and it simply works.

Give it a try if you are looking for an easy way to clean the ~/Library/Developer/Xcode/DerivedData folders associated with your project.

Learn more on Github : https://github.com/dealforest/Cichlid

banner

iOS, Tools

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
Android, Titanium

Android orientation locking for Titanium

Locking the orientation of a Titanium Android app if fairly straightforward after you’ve done it once or twice.  I thought it would be helpful to outline the steps that I follow to lock several of my applications in a portrait orientation.

To start with you first must build your Titanium Android app.  It doesn’t matter if you have any functionality in your project yet.  You just need to make sure an AndroidManifest.xml file is created in your project’s build/android folder.  Once you have done a build, you will need to open your AndroidManifest.xml and look for the android:name property of your launch activity.  You can see an example of this in the below snippet found in our AndroidTest AndroidManifest.xml. Look for the .AndroidtestActivity line.  This is the line and pattern you will want to look for in your project’s AndroidManifest.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
	<application android:icon="@drawable/appicon" 
           android:label="AndroidTest" 
           android:name="AndroidtestApplication" 
           android:debuggable="false" 
           android:theme="@style/Theme.AppCompat">
		<activity 
                 android:name=".AndroidtestActivity" 
                 android:label="@string/app_name" 
                 android:theme="@style/Theme.Titanium"       
                 android:configChanges="keyboardHidden|orientation|screenSize">
		   <intent-filter>
		     <action android:name="android.intent.action.MAIN"/>
		     <category android:name="android.intent.category.LAUNCHER"/>
		    </intent-filter>
		</activity>
	</application>
</manifest>

You can see the naming convention is a period then your project name with the first letter capitalized followed by Activity.  In our example project this is .AndroidtestActivity. This is the pattern you will want to look for in your AndroidManifest.xml.

Once you have the activity name of your launch activity you will want to replace the default Android configuration with a new template.

First open your project’s tiapp.xml and locate the below default Android entry.

<android xmlns:android="http://schemas.android.com/apk/res/android"/>

Replace the default information with the below template.

    <android xmlns:android="http://schemas.android.com/apk/res/android">
        <manifest>
            <application>
                <activity
                    android:configChanges="keyboardHidden|orientation|screenSize"
                    android:screenOrientation="portrait"
                    android:name="[replace me]">
                    <intent-filter>
                        <action android:name="android.intent.action.MAIN"/>
                        <category android:name="android.intent.category.LAUNCHER"/>
                    </intent-filter>
                </activity>
                <activity
                    android:configChanges="keyboardHidden|orientation|screenSize"
                    android:name="org.appcelerator.titanium.TiActivity" 
                    android:screenOrientation="portrait"/>
                <activity
                    android:configChanges="keyboardHidden|orientation|screenSize"
                    android:name="org.appcelerator.titanium.TiTranslucentActivity"
                    android:screenOrientation="portrait" android:theme="@style/Theme.AppCompat.Translucent"/>
            </application>
        </manifest>
    </android>

Next replace the [replace me] placeholder with the name of the launch activity you found earlier.  The below example shows this replacement being done for our AndroidTest app.  Please remember to remove the brackets when making the replacement.

    <android xmlns:android="http://schemas.android.com/apk/res/android">
        <manifest>
            <application>
                <activity
                    android:configChanges="keyboardHidden|orientation|screenSize"
                    android:screenOrientation="portrait"
                    android:name=".AndroidtestActivity">
                    <intent-filter>
                        <action android:name="android.intent.action.MAIN"/>
                        <category android:name="android.intent.category.LAUNCHER"/>
                    </intent-filter>
                </activity>
                <activity
                    android:configChanges="keyboardHidden|orientation|screenSize"
                    android:name="org.appcelerator.titanium.TiActivity" 
                    android:screenOrientation="portrait"/>
                <activity
                    android:configChanges="keyboardHidden|orientation|screenSize"
                    android:name="org.appcelerator.titanium.TiTranslucentActivity"
                    android:screenOrientation="portrait" android:theme="@style/Theme.AppCompat.Translucent"/>
            </application>
        </manifest>
    </android>

Next you will need to tell Alloy to only create Windows with a portrait orientation.  This is super easy and can be done by adding one class to your app.tss as shown below.  Your app.tss can be located in your app/styles folder.  If your project doesn’t already contain an app.tss you can create one by simply creating a file called app.tss in your project’s style folder.  To learn more about global styles please read the documentation available here.  The following snippet creates a global style for the windows in your project setting the orientation to portrait.

"Window" : {
	orientationModes :[
		Ti.UI.PORTRAIT
	]
}
FAQ:
  • Q: Why lock at the Android Activity level, isn’t adding the Alloy style enough?
  • A: Unfortunately no. The Alloy style is applied after the window is created. So if the device is held in a landscape orientation you will see the window open and turn. Locking at the Android Activity level stops this behavior.
  • Q: Does this mean all of my Ti.UI.Window object will be locked in portrait?
  • A: Yes, this locks both at the Android Activity and Titanium object levels.
  • Q: Can I later change the orientation by updating my Ti.UI.Window orientation?
  • A: No, orientation is locked at the Android Activity level as well.
iOS, Swift

Using IANA Time Zones with NSTimeZone

As part of a recent project I’ve been spending more time then I’d like with time zones.  In my case a variety of dates and time zone details are provided by a remote service. Having spent the last few years solving this type of problem with moment.js I was excited to see that NSTimeZone contained many of the same features.

The time zone information is provided in the IANA time zone database format.  I was really existed to see that you can create a new NSTimeZone using an IANA time zone name.  Below is an example using the Kuala Lumpur time zone.

let timeZone = NSTimeZone(name: "Asia/Kuala_Lumpur")
//What is the abbreviation
timeZone?.abbreviation
//Get the description with everything
timeZone?.description

So my first experiment was to parse the IANA time zones into a Swift String Array so that I could write a few Playground tests.  For this blog post we will show a few of the time zone names, instead of the full 417 item array.

let IANAtimeZones = ["Europe/Andorra","Asia/Dubai","Asia/Kabul",...]

* You can see the full array here

Next I found that the NSTimeZone method knownTimeZoneNames provides a list of all time zones iOS supports out of the box.  This gives me an easy way for me to later compare the IANA time zone database with those supported by iOS.  I use the below to get a list of those time zones known by iOS.

let iOStimeZones = NSTimeZone.knownTimeZoneNames()

To make writing any comparison testing alittle easier we create a method called tzExists.  This will allow us to compare a provided time zone name against all of those known by iOS.  In your production code you will want to cache the knownTimeZoneNames array for performance reasons.

func tzExists(name : String) -> Bool {
    return NSTimeZone.knownTimeZoneNames().contains(name)
}

Now that we have everything setup a simple loop can be used to compare our IANA time zones with those known to iOS.

for tz in IANAtimeZones {
    if(!tzExists(tz)) {
        print(tz + " not available")
    }
}

Oddly enough there are differences.  It could be I have an incomplete version of the IANA time zone database as iOS has 10 more known time zones then my IANA test harness.  Even with these differences the straight forward usage of NSTimeZone solves my problem perfectly.

Additional Resources:

  • Playground : A playground which shows the above objects, tests, and methods is available here.
  • Gist : The playground is available as a gist here.
Titanium

Settings Dialogs with Titanium

The handling for prompts for network, location, and other settings is fairly common when building apps.  I think we have all written code that verifies a network connection and then prompts the user to check their network settings.

For a recent project I encapsulated a network and location prompt into a CommonJS model called SettingsDialog.  This is just a light wrapper around Ti.UI.AlertDialog.  You can find the module here along with a sample app.js.

Network Example

Many of my apps deal with travel, so I end up dealing with network issues more than I would like.  Once a network issue has been identified you might have to alert the user that a feature is unavailable to them do to their network conditions.  The following example shows how to use the module to create a cross platform dialog with a network settings button.  When clicked on Android this will take the user directly to their Network Settings and on iOS will take them directly to their Application Settings.

The below is an example how to create a cross platform network dialog as shown below. When the user taps on the Settings button, they are taken to the device’s settings screen.  On iOS you are taken to the App’s Settings and on Android you are taken directly to the Network Settings screen.

Code:


//Before we get started, require the module into our project
var alertSettings = require("settings-dialogs");
	alertSettings.prompt({
		title:"Information",
		message:"We can't find a network connection. Please check your settings.",
		buttonNames:["Settings", "Continue"],
		settingsType : alertSettings.SETTINGS_TYPE.NETWORK, //The type of prompt
		settingsIndex : 0 //What button index should launch the settings
	}, function(d){
		console.log("prompt results = " + JSON.stringify(d));
	});

UI Example:

ios_network android_network

Location Example

With greater permissions being applied to both iOS and Android likely will need to handle the scenario where the user might change location permissions needed for a feature.  You can use the SettingsDialog module to create a prompt asking them to enable Location Services and providing a Settings button to jump them to the Settings screen to make the change.  The below example illustrates how to use the module to create cross platform location dialogs.

Code:


//Before we get started, require the module into our project
var alertSettings = require("settings-dialogs");
	alertSettings.prompt({
		title:"Information",
		message:"Please enable location services to use this feature.",
		buttonNames:["Settings", "Continue"],
		settingsType : alertSettings.SETTINGS_TYPE.LOCATION_SERVICES, //The type of prompt
		settingsIndex : 0 //What button index should launch the settings
	}, function(d){
		console.log("prompt results = " + JSON.stringify(d));
	});

UI Example:

ios_locationandroid_network

A full gist is available here.

iOS, Objective-C

CaptiveNetwork Depreciated in iOS9

If you use Network SSIDs in your application you most likely have a breaking change or two in store for you in upgrading to iOS9.  Most likely you are using a block of code that looks something like the below to get the current SSID of the device.

#import <SystemConfiguration/CaptiveNetwork.h>

-(NSString*) findSSID {
#if TARGET_IPHONE_SIMULATOR
 	return @"simulator";        
#else
	NSString * informationUnknown = @"unknown";        
	CFArrayRef interfaces = CNCopySupportedInterfaces();
	if(interfaces == nil){
		return informationUnknown;
	}
	CFIndex count = CFArrayGetCount(interfaces);
	if(count == 0){
		return informationUnknown;
	}
	CFDictionaryRef captiveNtwrkDict =
	CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(interfaces, 0));
	NSDictionary *dict = ( __bridge NSDictionary*) captiveNtwrkDict;
	CFRelease(interfaces);

	return (([dict objectForKey:@"SSID"]==nil)? informationUnknown :[dict objectForKey:@"SSID"]);
#endif
}

A quick look at the prerelease API documentation shows almost all of CaptiveNetwork’s properties and methods are depreciated.  This Apple developer forum post, highlights how you will now need to use the NetworkExtension framework’s new NEHotspotHelper class to access this information.

The below is how in iOS9 you can get an NSArray of all of the supported network interfaces.

#import <NetworkExtension/NetworkExtension.h>  

NSArray * networkInterfaces = [NEHotspotHelper supportedNetworkInterfaces];  
NSLog(@"Networks %@",networkInterfaces);  

The NEHotspotHelper class is part of the Hotspot improvements introduced in the NetworkExtension  framework.  There is a great WWDC video, “What’s New in Network Extension and VPN” which goes into detail on these new features and the associated breaking changes.

Unfortunately implementing NEHotspotHelper isn’t as easy as adding a conditional, you will need to add the new “com.apple.developer.networking.HotspotHelper”  entitlement in your project.  You will need to contact Apple to request this entitlement and complete a short questionnaire before this option will appear

Unlike most entitlements, you will need to contact Apple as outlined here to request access to the Network Extension entitlements for your team.  I’m still in the approval process so will see how easy it will be to switch to NEHotspotHelper.

Helpful Links:

iOS, Swift

How to tint an UIImageView Image

I continue to explore a few of the basics around using UIImageView and Swift to create interface elements.  In this post I will discuss how you can apply a tint to an UIImageView image.  The example I will use to show the image tint is to toggle on and off the selected state of a favorite “star” icon as shown below.

demo

There are many techniques for applying a tint to an image.  In this example, I’ll discuss the two approaches I like to use.

Just add two lines of code…

The easiest way to apply a tint to an UIImageView image is to set the imageWithRenderingMode of the image to AlwayTemplate and then apply a tint.  This only takes two lines of code to get the desired results.  The below snippet shows how to turn the UIImageView image blue.

myImageView.image = myImageView.image!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
myImageView.tintColor = UIColor.blueColor()

Or use an extension

Coding your image tint operations inline as discussed above works for most cases.  If you have a large number of image elements or different use cases you might want to explore using an extension method.  An extension method allows you to encapsulate the tint operating and make your code more readable.  For example, the snippet below turns the UIImageView image blue in just one line.

myImageView.tintImageColor(UIColor.blueColor())

To accomplish this we added the UIImageView+ImageTint extension to our project.  This extension simply encapsulates our inline code from above in a convenience method named tintImageColor.  The full extension method is shown below.

extension UIImageView {
    func tintImageColor(color : UIColor) {
        self.image = self.image!.imageWithRenderingMode(UIImageRenderingMode.AlwaysTemplate)
        self.tintColor = color
    } 
}

You can also find the extension method on Github here.

An example of both…

I’ve put together an example project ImageViewTintImage-Example here that walks you through how to apply both of these techniques to star and unstar UI elements.

iOS, Swift

Spinning UIImageView using Swift

Recently I’ve been exploring Swift and really been enjoying both the syntax and extension points.  Although still getting my legs under me with Swift and interface builder it has been a great deal of fun. For a recent project, I had to provide a subtle progress cue when the application was synchronizing information.  This cue was accomplished by simply replacing the UIImage of our existing UIImageView, then rotating the UIImageView using CABasicAnimation.  The below shows the end result in action.

ImageViewSpin

Rotation Extension – UIView+Rotation.swift

A majority of the logic behind this rotation effect is encapsulated in the UIView+Rotation extension class shown below.  The following shows the full extension method.  You can also view the extension method on github here.

extension UIView {
    func startRotating(duration: Double = 1) {
        let kAnimationKey = "rotation"
        
        if self.layer.animationForKey(kAnimationKey) == nil {
            let animate = CABasicAnimation(keyPath: "transform.rotation")
            animate.duration = duration
            animate.repeatCount = Float.infinity            
            animate.fromValue = 0.0
            animate.toValue = Float(M_PI * 2.0)
            self.layer.addAnimation(animate, forKey: kAnimationKey)
        }
    }
    func stopRotating() {
        let kAnimationKey = "rotation"
        
        if self.layer.animationForKey(kAnimationKey) != nil {
            self.layer.removeAnimationForKey(kAnimationKey)
        }
    }
}

Start Rotating

The first step in starting the rotation animation is to replace the existing UIImage with the one we will be animating.  In our case we do this by loading an image from our Asset Catalog, as shown below.

syncImage.image = UIImage(named:”sync-spinning”)

Next, since we have the UIView+Rotation extension method added to your project, you simply have to call the startRotating method on your UIImageView to start animating the UIImageView.  The following shows how to call the startRotating extension method.

syncImage.startRotating();

In our example project, we have encapsulated this into the startSpinning shown below.

func startSpinning() {
    syncImage.image = UIImage(named:"sync-spinning")
    syncImage.startRotating()
}

Stop Rotating

The process of stopping the animation is similar to that of starting the rotation process.  First we stop the rotation animation of the UIImageView by calling the stopRotating extension method as shown below.

syncImage.stopRotating()

Next, we replace the UIImage with our initial non rotating image.  As shown below we do this by loading an image from the Asset Catalog.

syncImage.image = UIImage(named:”sync-not-spinning”)

In our example project, we have encapsulated this into the stopSpinning shown below.

func stopSpinning() {
    syncImage.stopRotating()
    syncImage.image = UIImage(named:"sync-not-spinning")
}

Ok, so show me the code

The following shows the code used to call both the start and stop rotating animations. For demonstration purposes, a delay is added to trigger the stop rotating event to illustrate the full animation life cycle.

func startSpinning() {
    syncImage.image = UIImage(named:"sync-spinning")
    syncImage.startRotating()
}

func stopSpinning() {
    syncImage.stopRotating()
    syncImage.image = UIImage(named:"sync-not-spinning")
}

func handleSyncTap(sender: UITapGestureRecognizer? = nil) {
    startSpinning()

    let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * Double(NSEC_PER_SEC)))
    
    dispatch_after(dispatchTime, dispatch_get_main_queue(), {
        self.stopSpinning()
    })
}

A full working example of how to create a rotating UIImageView is available on github at https://github.com/benbahrenburg/SpinningImageView-Example

Walk Through

The following video tutorial walks through the UIImageView rotation example in great detail outlining everything from how the Storyboard UI elements are created through the UIGestureRecognizers added to trigger the rotation animations.