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.

Titanium AppStore Tools

Looking for away to make sure your uses have upgraded to the latest version of your app?  Using the AppStoreHelpers object within the benCoding.iOS.Tools module you can easily query the Apple AppStore for the latest version.  You are then a simple version compare away from having the information needed to prompt your user to upgrade or version specific service calls.

Show me an example

var tools = require('bencoding.ios.tools');
var appStoreHelpers = tools.createAppStoreHelpers();
function queryResults(e){
    
    Ti.API.info(JSON.stringify(e));
    
    Ti.API.info("success = " + e.success);
    Ti.API.info("appStoreVersion = " + e.appStoreVersion);
    Ti.API.info("appID = " + e.appID);
    Ti.API.info("code = " + e.code);
    Ti.API.info("installedVersion = " + e.installedVersion);
    var hasUpdate = (parseFloat(e.appStoreVersion) > parseFloat(e.installedVersion));
            
    if(hasUpdate){
        var alert = Ti.UI.createAlertDialog({
            title:'Update now', 
            message:'There is a new version available. Do you want to download now?',
            buttonNames : ['OK','Cancel']
        });
        
        alert.addEventListener('click', function(y){
            if(y.index == 0){
                appStoreHelpers.launch(queryITunesID);
            }
        });
        alert.show();
    }
};

//Let's pretend we are angry birds
var queryITunesID = '409807569';
//Call the check version method
appStoreHelpers.versionCheck(queryITunesID,queryResults);

How does it work?

You can look-up the version information for an iTunes Id by making a service call to http://itunes.apple.com/lookup. For example, you can get the latest information about Angry Birds by using http://itunes.apple.com/lookup?id=409807569

How do I get my iTunes Id?

To find your iTunes Id open iTunes and go to your app. Right click on the App’s iTunes coverart and select the Copy Link option. For example, if you do this on Angry Birds you will receive the following url https://itunes.apple.com/us/app/angry-birds-free/id409807569?mt=8.

The section after the id and before the question mark is your iTunes Id. Using our Angry Bird’s example their iTunes Id would be 409807569. This is the value you will want to use when launching or performing a version check.

Where can I get it?

The benCoding.iOS.Tools module is available on Github at https://github.com/benbahrenburg/benCoding.iOS.Tools

iOS Simulator Switching Devices

With Apple’s latest products, including the iPhone 5 we now have to worry about more form factors then ever. As the method for switching the device type in Titanium Studio is still pretty clumsy I went in search of an earlier way.

The Appcelerator forums had a great post by Rob Gabbard with an AppleScript helper to do just what I was looking for. With a small update to add support for the Retina iPad and iPhone 5 I can now switch devices must easier.

I wanted to share a link to this script for anyone that might have missed the QA post. Unfortunately this doesn’t address the need for the app to be launched again when the device is changed.

set selectedDevices to choose from list {"iPhone", "iPhone (Retina 3.5-inch)", "iPhone (Retina 4-inch)", "iPad", "iPad (Retina)"} with prompt "Choose device type:" default items {"iPhone"} without multiple selections allowed
if selectedDevices is not false then
    set selectedDevice to item 1 of selectedDevices as string
    set thePListFolderPath to path to preferences folder from user domain as string
    set thePListPath to thePListFolderPath & "com.apple.iphonesimulator.plist"
    
    tell application "System Events"
        tell property list file thePListPath
            tell contents
                set value of property list item "SimulateDevice" to selectedDevice
            end tell
        end tell
    end tell
end if

View the the gist of the script.

Titanium iOS adding isFile and isDirectory to the SDK

As I was building Dossier.js I ran into a small parity issue between iOS and Android.  On Android Titanium provides you two helper functions.  On a Ti.File object you can call isFile() to get a boolean indicator if your currently working with a file.  Likewise you can call isDirectory() to determine if you are working with a directory.  These are extremely helpful utilities when performing any kind of file system acrobatics.

How do we get this to work on iOS?  Easy, update the SDK and send a pull request.  Just shows how powerful open source can be.

Want to add this to your SDK now?  It couldn’t be easier, just follow the below three simple steps.

Updating your SDK

Find your SDK Directory

The first step is to find your Titanium SDK directory.  This is usually installed under a directory path similar to the one shown below.

path

Adding your methods

After locating your SDK directory, you will need to go to the Classes folder as shown below.

sdkdirectory

Look for the TiFilesystemFileProxy.m file and open this in your favorite text editor.

TiFileSystemProxy

Scroll down in the file until you see the method –(id)createFile:(id)args right above this method paste in the below.

-(id)isFile:(id)unused
{
	BOOL isDirectory;
	return ([fm fileExistsAtPath:path isDirectory:&isDirectory] && !isDirectory);
}

-(id)isDirectory:(id)unused
{
	BOOL isDirectory;
	return ([fm fileExistsAtPath:path isDirectory:&isDirectory] && isDirectory);
}

view on gist

Once completed, save the file (TiFilesystemFileProxy.m)

Clean your project & done

You will need to clean your Titanium Projects before the SDK modifications will be available.  To clean your projects using Titanium Studio go to the Project menu and select Clean… as shown below.

cleanmenu

In the Clean Dialog Window, select Clean All Projects.

cleandialog

You are now ready to use these new functions in your Titanium Projects.

How what? How does it work?

After adding these updates to your SDK, the Ti.File object will have the same isFile and isDirectory methods as they do on Android.  Below is a demonstration app.js you can use for testing.


//Test if the resources folder is a directory.
var resourceDir = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory);
Ti.API.info('resourceDir ' + resourceDir);
Ti.API.info('resourceDir nativePath ' + resourceDir.nativePath);
Ti.API.info('resourceDir isDirectory ' + resourceDir.isDirectory());
Ti.API.info('resourceDir isFile ' + resourceDir.isFile());

var newDir = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory,'mydir');
if(!newDir.exists()){
  Ti.API.info("Created mydir: " + newDir.createDirectory());
}

var newFile = Titanium.Filesystem.getFile(newDir.nativePath,'newfile.txt');
newFile.write("\nText appended via write()", true);

Ti.API.info('newdir ' + newDir);
Ti.API.info('newdir nativePath ' + newDir.nativePath);
Ti.API.info('newdir isDirectory ' + newDir.isDirectory());
Ti.API.info('newdir isFile ' + newDir.isFile());

Ti.API.info('newFile ' + newFile);
Ti.API.info('newFile nativePath ' + newFile.nativePath);
Ti.API.info('newFile isDirectory ' + newFile.isDirectory());
Ti.API.info('newFile isFile ' + newFile.isFile());

Ti.API.info('Now open a window so we can test on device easier');

// Create a simple window to show our results
(function(){

	var win = Ti.UI.createWindow({
		backgroundColor:'#fff',layout:'vertical'
	});

	win.add(Ti.UI.createLabel({
		top:10, text:'resourceDir isDirectory ' + resourceDir.isDirectory()
	}));

	win.add(Ti.UI.createLabel({
		top:10, text:'resourceDir isFile ' + resourceDir.isFile()
	}));

	win.add(Ti.UI.createLabel({
		top:10, text:'newdir isDirectory ' + newDir.isDirectory()
	}));

	win.add(Ti.UI.createLabel({
		top:10, text:'newdir isFile ' + newDir.isFile()
	}));

	win.add(Ti.UI.createLabel({
		top:10, text:'newFile isDirectory ' + newFile.isDirectory()
	}));

	win.add(Ti.UI.createLabel({
		top:10, text:'newFile isFile ' + newFile.isFile()
	}));

	win.open();

})();

Ti.API.info('End Test');

Titanium iOS KeyChain support with Securely

I’m happy to announce the the availability of the Securely Titanium module.  The goal of Securely is to provide a security toolkit for Titanium developers to write more secure apps.

KeyChain Properties API

The first release of Securely (iOS only) provides a Properties API which provides access to the iOS KeyChain.  Designed for ease of use the Properties module should be familiar to anyone using Titanium as it mimics the popular Ti.App.Properties API.

var securely = require('bencoding.securely');
Ti.API.info("module is => " + securely);

//You can provide optional identifier and accessGroup 
//If none are provided we will use your bundle id as the identifier
var properties = securely.createProperties({
     identifier:"Foo",
     accessGroup:"Bar"
});

Since the Securely Properties module mimics Ti.App.Properties you simply call setString when you would like to save a value.  Instead of being written to Properties it is instead persisted to the device’s KeyChain.

properties.setString('String','I am a String Value ');

Similarly you use the getString method just like you would for properties, but instead you are retrieving values from the KeyChain.  We even support default values for API compatibility.

Titanium.API.info('String: ' + properties.getString('whatever',"Fred"));

All of your other Ti.App.Properties you rely upon also have a compatible equivalent in Securely.  To see the full list please read our documentation here.

Get the module

The module is available at benahrenburg/Securely on github.

Need an example?

The module’s example folder contains a sample app.js demonstrating all of the available methods.  You can view the sample in on github here.

Learn more

To learn more please visit the Securely github repo and read the documentation.

What’s next?

I am currently working on adding Encryption and File Handling features to Securely.  An Android version is also in the planning stages.

Changing a Titanium iOS Module’s GUID

Occasionally you will need to a module’s GUID.  This is commonly done to avoid licensing conflicts with open source modules.

Updating a module’s GUID is a straight forward and simple process.  The below steps demonstrate how to change the GUID in your Titanium iOS custom module.

Before getting started it is important to note that Appcelerator doesn’t support updating your module’s GUIDs so please make a back-up of your original files just in case.

Generate a new GUID

The first step is to generate a new GUID.  I like to use GuidGenerator.com. Using this site we generate a GUID of 2016a336-bc2-494a-b112-98d6edd999de that we will us to update our Xcode module project.

guidgenerator

Updating the Manifest File

When you create a Titanium Module project, a manifest file is automatically created for you.  This file contains your project name, version information, module id, and your module’s GUID.

The manifest file is located in the root of your Xcode project, as shown below.

manifest_file

Open this file in your favorite text editor and change the existing GUID to the new one generated by GuidGenerator.com.  In our case the manifest should now look like the below.

 

manifest_guid_update

Updating your Xcode Project

The next step is to update your module’s moduleId method.  This is located in the <Your Module Name>Module.m file of your project. In this example our module is called BencodingNetwork, so we will want to look in the BencodingNetworkModule.m project file.

finding_class

You will want to open this class file in Xcode and update the moduleGUID property.  You simply update the return value with the same GUID used in updating our manifest file.

xcode_update

Almost done, one last step

Now that you’ve updated your module’s GUID you will need to re-build your module.  To do this open terminal in the root of your module’s folder and run the ./build.py script as shown below.  This will generate a new compile module zip for you to use in your Titanium projects.

termina

Opening Custom Url Schemas with Titanium

Apple’s iOS implements a custom URL scheme is a mechanism through which third-party apps can communicate with each other.  This allows your app to launch other apps.

Using this powerful iOS feature is simple, just provide the app’s url to the Titanium.Platform.openURL method.

List of Custom Url Schemes Resources

The most challenging part of using Custom Url Schemes is finding available url’s to call.  Several new services have been released to meet this need.  Below is a few of the most comprehensive resources I have found.  Unfortunately many of the examples only show the top level Url without detailed functionality mapping.

Custom Url Examples:

Using the Custom Url Scheme resources discussed above you can quickly integrate with a wide variety of services using the same implementation pattern as shown below.  The below  are a few examples showing how you can use custom urls within your Titanium app.

FaceTime:

//Make a facetime call

var url = "facetime://1234567890";
if (Titanium.Platform.canOpenURL(url)) {
    Titanium.Platform.openURL(url);
}
else {
    alert('Cannot open app');
}

iBooks:

//Open an iBook from the App Store

var url = "itms-books://itunes.apple.com/us/book/the-zombie-survival-guide/id419952002";
if (Titanium.Platform.canOpenURL(url)) {
    Titanium.Platform.openURL(url);
}
else {
    alert('Cannot open app');
}

Facebook:

//Open the facebook app to the friends section

var url = "fb://friends";
if (Titanium.Platform.canOpenURL(url)) {
    Titanium.Platform.openURL(url);
}
else {
    alert('Cannot open app');
}

More Resources

Titanium MapView ImageOverlays, TileOverlays, GeoJSON and More

I’m happy to announce the latest release of the benCoding.MapView module.

This is a big release that provides support for the following:

In addition to the above features the module’s memory footprint has greatly been reduced.

The module is fully documented with samples demonstrating how to use each feature.  To learn more please visit our module documentation here.

What to see more of the module in action?  The module’s samples has been updated with videos highlighting many of the advanced features of the MapView module.  Please visit the our showcase  to view the demos in action.

A special thanks to Jeff Bonnes (@jeffbonnes) for contributing TileOverlay support.

New Features In Action

Here are a few of the new features in action.

 

Supporting Different orientationModes in your Universal iOS App

Recently I had a requirement to create a universal app that supported different orientation modes for iPhone and iPad. 

There are two key points to implementing a universal app that support different orientations by device.

  1. Explicitly define your launch orientations in your tiapp.xml for both iPhone and iPad
  2. On Window create explicitly set the orientation mode property.

Setting iPhone and iPad Launch Orientations

By configuring your orientation modes in your tiapp.xml tells the app what orientations are supported when the app is loading and your splash screen is active.  Since Titanium 2.0 you have the ability to set the orientation modes using info.plist format as shown below.

  1. Add the UISupportedInterfaceOrientations node as shown below. This specifies the orientation mode used by the iPhone.
  2. Add the UISupportedInterfaceOrientations~ipad node as shown below. This specifies the orientation mode the iPad will use.

If you do not add the iPad orientation node(UISupportedInterfaceOrientations~ipad) your app will use the UISupportedInterfaceOrientations settings to determine default orientation behavior.

The example below shows setting the launch orientations for the iPhone to Portrait and the iPad to both Portrait and Landscape.

<?xml version="1.0" encoding="UTF-8"?>
<ti:app xmlns:ti="http://ti.appcelerator.org">
    <deployment-targets>
        <target device="mobileweb">false</target>
        <target device="iphone">true</target>
        <target device="ipad">true</target>
        <target device="android">false</target>
        <target device="blackberry">false</target>
    </deployment-targets>
    <ios>
        <plist>
            <dict>
                <key>UISupportedInterfaceOrientations</key>
                <array>
                    <string>UIInterfaceOrientationPortrait</string>
                    <string>UIInterfaceOrientationPortraitUpsideDown</string>
                </array>
                <key>UISupportedInterfaceOrientations~ipad</key>
                <array>
                    <string>UIInterfaceOrientationPortrait</string>
                    <string>UIInterfaceOrientationPortraitUpsideDown</string>
                    <string>UIInterfaceOrientationLandscapeLeft</string>
                    <string>UIInterfaceOrientationLandscapeRight</string>
                </array>
            </dict>
        </plist>
    </ios>
</ti:app>

For more information please reference the iOS Specific Section documentation here.

Creating Windows with Explicit Orientations

It is a best practice to always set the orientations you wish to support on Window creation.

Below is a helper function and sample I use when creating all of my windows.  This can also be helpful for setting a common backgroundColor, or other setting.

1. CommonJS Helper ( file called helper.js)

var _isPhone = (Ti.Platform.osname == "iphone");
exports.makeWindow=function(a){
    a = a || {};
    var win = Ti.UI.createWindow(a);
    if(_isPhone){
            win.orientationModes = [
                Ti.UI.PORTRAIT,
                Ti.UI.UPSIDE_PORTRAIT
            ];  
    }else{
            win.orientationModes = [
                Ti.UI.PORTRAIT,
                Ti.UI.UPSIDE_PORTRAIT,
                Ti.UI.LANDSCAPE_LEFT,
                Ti.UI.LANDSCAPE_RIGHT
            ];  
    }

    return win; 
};

2. Sample Window Creation (app.js)

var helpers = require('helpers');
var winConfig = {
        title:"Foo", backgroundColor:'#fff', tabBarHidden:true, barColor:'#000
};
var win = helpers.makeWindow(winConfig);
var testLabel = Ti.UI.createLabel({
        text:"this is a test, turn the device to see if it worked", 
        color:"#000", top:40, left:25, right:25, height:60,
        textAlign:"center", font:{fontSize:12}
});
win.add(testLabel);
win.open();

For more information about window orientation please see the documentation here.

Setting the Minimum iOS Version for your Titanium Project

With Appcelerator’s release of Titanium SDK 2.1.2 you can now specify the minimum iOS version in your tiapp.xml file.  This is a big step forward in managing all of your cross platform configurations in one file.  For me this has removed the latest need for a custom plist,

Just add the min-ios-ver node to the ios configuration section in your project’s tiapp.xml.  The below example sets a minimum iOS SDK level to 5.0.

<ios>
    <min-ios-ver>5.0</min-ios-ver>
    <plist>
        <dict>
            <key>UIPrerenderedIcon</key>
            <true/>
            <key>UIStatusBarStyle</key>
            <string>UIStatusBarStyleBlackTranslucent</string>
            <key>UISupportedInterfaceOrientations</key>
            <array>
                <string>UIInterfaceOrientationPortrait</string>
                <string>UIInterfaceOrientationPortraitUpsideDown</string>
            </array>
        </dict>
    </plist>
</ios>

This has streamlined the deployment process for me both via Titanium Studio and Transport.py.