Easy Xcode Template Customization

Sun. November 20, 2011
Categories: iOS, Python, Xcode
Tags: , , ,

I got tired of changing the comments, curly braces, etc every time I had to create a new class or protocol in Xcode, so I whipped up this little python script. What it does is remove most of the comments, move the { off of its own line and up to the end of the method definition line, and a few other nifty things. It’s not clean or pretty, but it does the job. Simply pass in a single parameter indicating the location of your base template file directory and any optional flags (-r, -v, -q, -i), and this will automatically create better templates.

Before:


//
//  ___FILENAME___
//  ___PROJECTNAME___
//
//  Created by ___FULLUSERNAME___ on ___DATE___.
//  Copyright (c) ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved.
//

#import "___FILEBASENAME___.h"


@implementation ___FILEBASENAMEASIDENTIFIER___

- (id)initWithStyle:(UITableViewStyle)style
{
    self = [super initWithStyle:style];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)didReceiveMemoryWarning
{
    // Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
    
    // Release any cached data, images, etc that aren't in use.
}

#pragma mark - View lifecycle

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Uncomment the following line to preserve selection between presentations.
    // self.clearsSelectionOnViewWillAppear = NO;
 
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [super viewDidDisappear:animated];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    // Return YES for supported orientations
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
#warning Potentially incomplete method implementation.
    // Return the number of sections.
    return 0;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
#warning Incomplete method implementation.
    // Return the number of rows in the section.
    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    // Configure the cell...
    
    return cell;
}

/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the specified item to be editable.
    return YES;
}
*/

/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }   
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }   
}
*/

/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
}
*/

/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Return NO if you do not want the item to be re-orderable.
    return YES;
}
*/

#pragma mark - Table view delegate

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Navigation logic may go here. Create and push another view controller.
    /*
     <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
     // ...
     // Pass the selected object to the new view controller.
     [self.navigationController pushViewController:detailViewController animated:YES];
     */
}

@end

After:


/*
 *  ___FILENAME___
 *  ___PROJECTNAME___
 *
 *  Author: ___FULLUSERNAME___
 *  Date: ___DATE___
 *
 *  Copyright ___YEAR___ ___FULLUSERNAME___
 *  http://hozbox.com
 */
    
#import "___FILEBASENAME___.h"
    
    
@implementation ___FILEBASENAMEASIDENTIFIER___
    
- (id)initWithStyle:(UITableViewStyle)style {
    if ((self = [super initWithStyle:style])) {
    }
    return self;
}
    
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
    
#pragma mark - View lifecycle
    
- (void)viewDidLoad {
    [super viewDidLoad];
    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.
    // self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
    
- (void)viewDidUnload {
    [super viewDidUnload];
}
    
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}
    
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
}
    
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}
    
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
}
    
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
    return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
    
#pragma mark - Table view data source
    
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Potentially incomplete method implementation.
    return 0;
}
    
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete method implementation.
    return 0;
}
    
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
        
        
    return cell;
}
    
/*
// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}
*/
    
/*
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
    if (editingStyle == UITableViewCellEditingStyleDelete) {
        // Delete the row from the data source
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
    }
    else if (editingStyle == UITableViewCellEditingStyleInsert) {
        // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}
*/
    
/*
// Override to support rearranging the table view.
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath {
}
*/
    
/*
// Override to support conditional rearranging of the table view.
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
    return YES;
}
*/
    
#pragma mark - Table view delegate
    
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    /*
    <#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:@"<#Nib name#>" bundle:nil];
    [self.navigationController pushViewController:detailViewController animated:YES];
    */
}
    
@end

Script:


#!/usr/bin/python

import fnmatch
import shutil
import glob
import time
import sys
import os
import re

RECURSIVE = False
VERBOSE = False
QUIET = False
INTERACTIVE = False


TEMPLATE_DEFAULT_DIR = os.path.join(r"/Developer", "Platforms", "iPhoneOS.platform", "Developer", "Library", "Xcode", "Templates", "File Templates")

IGNORED_LINES = [
                    "#import <UIKit/UIKit.h>",
                    "// Custom initialization",
                    "// Release any retained subviews of the main view.",
                    "// e.g. self.myOutlet = nil;",
                    "// Uncomment the following line to preserve selection between presentations.",
                    "// self.clearsSelectionOnViewWillAppear = NO;",
                    "// Releases the view if it doesn't have a superview.",
                    "// Release any cached data, images, etc that aren't in use.",
                    "// Release any retained subviews of the main view.",
                    "// e.g. self.myOutlet = nil;",
                    "// Return YES for supported orientations",
                    "// Return the number of sections.",
                    "// Return the number of rows in the section.",
                    "// Configure the cell...",
                    "// Return NO if you do not want the specified item to be editable.",
                    "// Return NO if you do not want the item to be re-orderable.",
                    "// Navigation logic may go here. Create and push another view controller.",
                    "// ...",
                    "// Pass the selected object to the new view controller.",
                    "// Implement loadView to create a view hierarchy programmatically, without using a nib.",
                    "// Implement viewDidLoad to do additional setup after loading the view, typically from a nib."
                ]

paths = []
args = sys.argv[1:]

if "-r" in args:
    args.remove("-r")
    RECURSIVE = True
if "-v" in args:
    args.remove("-v")
    VERBOSE = True
if "-q" in args:
    args.remove("-q")
    QUIET = True
if "-i" in args:
    args.remove("-i")
    INTERACTIVE = True

if not len(args):
    while not (os.path.exists(TEMPLATE_DEFAULT_DIR) and os.path.isdir(TEMPLATE_DEFAULT_DIR) and os.access(TEMPLATE_DEFAULT_DIR, os.F_OK | os.R_OK | os.X_OK)):
        try:
            TEMPLATE_DEFAULT_DIR = raw_input("Please provide the path of the Xcode templates directory (Usually %s): " % TEMPLATE_DEFAULT_DIR).strip()
        except KeyboardInterrupt:
            print
            sys.exit(1)
    args = [os.path.join(TEMPLATE_DEFAULT_DIR, "*.[hm]")]

for arg in args:
    if os.path.isfile(arg):
        paths.append(os.path.abspath(arg))
    else:
        if RECURSIVE:
            dirname = os.path.dirname(arg)
            basename = os.path.basename(arg)
            for root, dirs, files in os.walk(dirname):
                paths.extend([os.path.join(root, fname) for fname in fnmatch.filter(files, basename)])
        else:
            print "Error: Path given is directory but no recursive (-r) flag given!"
            sys.exit()

for f in paths:
    if INTERACTIVE:
        try:
            if not raw_input("Fix %s? [y/n]: " % f).strip().lower().startswith("y"):
                continue
        except KeyboardInterrupt:
            print "\n\n", sys.exit()

    if not (QUIET or INTERACTIVE):
        print "Starting on: %s" % f

    source = open(f, "r").read()

    if VERBOSE:
        print "\tFixing comments..."
    source = source.replace("""\
//
//  ___FILENAME___
//  ___PROJECTNAME___
//
//  Created by ___FULLUSERNAME___ on ___DATE___.
//  Copyright (c) ___YEAR___ ___ORGANIZATIONNAME___. All rights reserved.
//
""", """\
/*
 *  ___FILENAME___
 *  ___PROJECTNAME___
 *
 *  Author: ___FULLUSERNAME___
 *  Date: ___DATE___
 *
 *  Copyright ___YEAR___ ___FULLUSERNAME___
 *  http://hozbox.com
 */
""")
    new_source = "%s\n" % "\n".join(source.splitlines()[:10])
    source = "\n".join(source.splitlines()[10:])

    remove_next_brace = False
    in_interface = False
    self_init = None
    in_brace = False

    for line in [l.expandtabs(4) for l in source.splitlines()]:
        indent_count = len([ind for ind in line.split("    ") if not ind]) * 4
        line = line.strip()
        if line in IGNORED_LINES or (not line and in_brace):
            print "\tRemoving line:".ljust(25), "%s%s" % ((" " * indent_count), line)
            continue
        elif line.startswith("- (") and not line.endswith("{"):
            in_brace = True
            remove_next_brace = True
            line = line + " {"
            if VERBOSE:
                print "\tFixed line:".ljust(25),
        elif line.startswith("{"):
            in_brace = True
            if remove_next_brace:
                if VERBOSE:
                    print "\tRemoving line:".ljust(25), "%s%s" % ((" " * indent_count), line)
                continue
            else:
                if VERBOSE:
                    print "\tNot Modified:".ljust(25),
        elif line.startswith("}"):
            if VERBOSE:
                print "\tNot Modified:".ljust(25),
            in_brace = False
        elif line.startswith("@interface"):
            in_interface = True
            line = line + " {"
            if VERBOSE:
                print "\tFixed line:".ljust(25),
        elif line.startswith("@end"):
            if in_interface:
                line = "}\n\n" + line
                in_interface = False
            line += "\n"
            if VERBOSE:
                print "\tFixed line:".ljust(25),
        elif line.startswith("self = [super init"):
            self_init = line.strip(";")
            if VERBOSE:
                print "\tRemoving line:".ljust(25), "%s%s" % ((" " * indent_count), line)
            continue
        elif line.startswith("if (self)") and self_init is not None:
            line = line.replace("(self)", "((%s))" % self_init)
            self_init = None
            if VERBOSE:
                print "\tFixed line:".ljust(25),
        else:
            if VERBOSE:
                print "\tNot Modified:".ljust(25),

        if VERBOSE:
            print "%s%s" % ((" " * indent_count), line)
        new_source += "%s%s\n" % ((" " * indent_count), line)

    open(f, "w").write(new_source)
    if not QUIET:
        print "Done Fixing Template File: %s" % os.path.basename(f)

Comments

Leave a Reply