Dec 19 2011

Fundamental iOS design patterns: SharedInstance (Singleton in Objective C)

Category: Objective-c and CocoaDavide Zanotti @ 5:11 am

Brief introduction

Singleton is one of the (if not THE) most commonly used/abused design pattern adopted in software development.
Basically it’s used to restrict the instantiation of a class to one object only that can be used safely across application architecture. Its implementation is pretty simple compared to other patterns and it’s very similar for the most of programming languages, but in Objective C it has a slightly different approach due to the lack of “normal constructor” that are instead replaced by alloc/init paradigm.

How it works in Objective C

In Objective C, we can speak more appropriately about the SharedInstance pattern rather than traditional Singleton.
This one is often used in Apple frameworks and differently from its twin brother, it does not explicitly forbid the instantiation of multiple class instances, but it simply provide a class method that will return a shared instance. During the first call to that method the instance will be allocated and the next calls will return the previously object created. So this is a sort of hybrid approach.
A common class that use this pattern is UIApplication which implements a sharedApplication method (which returns the current running application). Differently from Singleton which use getInstance(), in Objective C the corresponding method has not a fixed name but it should start with “shared” followed by the “type” of class (so for UIApplication is “sharedApplication” for MYAmazingManager will be “sharedManager“). Anyway there are several exceptions, for example NSFileManager has a “defaultManager” acting as a sharedInstance accessor that could be renamed “sharedManager“.

Pattern implementation

In my singleton implementations I simply call the accessor “sharedInstance“, in this way I can use a simple pre-compiler macro that automatically synthesize this methods for me.
Even implementation of this pattern may vary and personally I adopted one which is thread-safe and rely on CGD (Grand Central Dispatch), which is an affective C level API provided by Apple in order to handle concurrency.

The implementation of my sharedInstance method is the following (comments explain how it works):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+ (id)sharedInstance
{
    // structure used to test whether the block has completed or not
    static dispatch_once_t p = 0;
   
    // initialize sharedObject as nil (first call only)
    __strong static id _sharedObject = nil;
   
    // executes a block object once and only once for the lifetime of an application
    dispatch_once(&p, ^{
        _sharedObject = [[self alloc] init];
    });
   
    // returns the same object each time
    return _sharedObject;
}

Pattern in action!

SharedInstance pattern is IMO the best way to share data across multiple viewControllers in iOS applications.
One practical example is a data manager to share Core Data stuff such managed object context. I realized my own manager so that I can access this objects anywhere with ease like:

1
[[MYDataManager sharedInstance] managedObjectContext];

Tags: , ,


Dec 12 2011

Quick iOS tip: where is the right place to add and remove observers in UIViewControllers?

Category: Objective-c and CocoaDavide Zanotti @ 12:30 pm

While I was debugging my iPhone app I noticed (thanks to my verbose logs) that certain methods were called multiple times… apparently like if they were in a loop. I lost several time trying to figure out why and then I realized that the problem was related to defining them in the wrong place! (technically not wrong at all, but definitely wrong in my app context).
I was adding listeners in viewDidLoad and removing them in viewDidUnload.
This is acceptable only if you have simple UIViewControllers that don’t work in composite controllers hierarchy (they are not contained in UINavigationController or UITabBarController).
viewDidLoad is called after the view controller’s view has been released, but in a controllers hierarchy this does not happen since controllers are retained by the container! This leads to multiple messages dispatched to registered observers, so if you have a UINavigationController containing a sequence of 5 viewController, handlers will be triggered 5 times (one for each controller).
The safely approach to add and remove listeners is instead in viewWillAppear: and viewWillDisappear:
Example:

1
2
3
4
5
6
7
8
9
10
11
- (void)viewWillAppear:(BOOL)animated
{
   [super viewWillAppear:animated];
   [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(foo:) name:UIKeyboardDidShowNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
   [super viewWillDisappear:animated];
   [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardDidShowNotification object:nil];
}

Tags: ,


Dec 08 2011

Generate beautiful code documentation with a simple shortcut using AppleDoc, Xcode custom behaviors and AppleScript!

Category: xcodeDavide Zanotti @ 3:48 am

This time I realized something really great and I’m truly satisfied about it!
Few days ago I discovered a fantastic open source project from GentleBytes called AppleDoc by reading a post on Cocoanetics.
This is a tool that generate an Apple-like HTML documentation from source files (by using the proper comment style) and it also generates installable docsets and Atom feeds for the download from a remote host.
After a couple of tests and document generations my big question was: “how can I automatize the process without launching a script every time and for each project I want to document?”.
The first obvious answer was “add a Run Script in the build phases!”… but it’s not a smart idea to launch a similar long script for every build, because it takes several seconds and potentially minutes depending on the amount of classes to analyze, and it’s often unnecessary to update a documentation if method signatures remain the same. So I searched for an alternative way to execute scripts from Xcode only when desired, by focusing my efforts on its “Behaviors” (Xcode > Behaviors > Edit Behaviors > +). Behaviors can be triggered by events (only default behaviors), using menu or with a custom shortcut and a behavior can perform several actions like running a script…
and here I started to waste a lot of time in order to realize what I had in mind (create a single auto-runnable script to generate code documentation for each project without hard-coding paths and names), because the environment variables I was using in Xcode build phases as a test can’t be used in external scripts (since they are not attached to the Xcode process as far I understood).
So the problem was: “how can I retrieve this variables dynamically from our beloved ide? is it possible?”… it’s been an hard google search plus a couple of questions asked on StackOverflow (Did I tell you how much I love that site? I really love it!) in order to clear my dubs… and yes, it’s possible thanks to AppleScript! Xcode exposes several objects that can be queried by this funny script language, so in this way I first realized an AppleScript to retrieve the variables I need (project name, project path and company) and then I invoked my old and refactored bash script passing them.
In conclusion I’m now able to launch document generation for each project I’m working on, by simply press ALT+CMD+D (a custom shortcut of my choice), I also configured a submarine sound effect and the display of a bezel alert… really wonderful!

This is the AppleScript used for the behavior that invoke the bash script by sending it the necessary arguments:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/osascript

tell application "Xcode"
    tell first project
       
        -- variables to export
        set projectName to (get name)
        set projectDir to (get project directory)
        set company to (get organization name)

        -- invoke script passing extracted variables
        do shell script ("sh /PATH_TO_SCRIPT/appledoc.generate.sh " & projectName & " " & projectDir & " " & company)
       
    end tell
end tell

Of course you have to replace “/PATH_TO_SCRIPT/appledoc.generate.sh” according to your path.
If you decide to create your own AppleScript script using the default editor (/Applications/Utilities/Apple Script Editor), remember to save it as a plain text and to include the proper header declaration (#!/usr/bin/osascript), otherwise Xcode will throw an exception trying to run it!

…and this is Sparta the bash (I added a couple of say commands in order to debug the process… remove comments to hear your mac speak!):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#! /bin/sh

# Input arguments:
# $1 -> project name
# $2 -> project path
# $3 -> company name

# dynamic variables
docsURL="http://www.$3.com/docs";
projectsPath="$2/../";
docsPath="${projectsPath}/AppleDocOutput";

#say "project is: $1";
#say "path is: $2";
#say "company is: $3";
#say "project path is: ${projectsPath}";

# create AppleDocOutput folder if not exists
if [ ! -d $docsPath ];
then
    #say "create output folder";
    mkdir "${docsPath}";
fi

#say "run appledoc";

#invoke appledoc passing computed arguments
/usr/bin/appledoc \
--project-name "$1" \
--output "${docsPath}/$1/" \
--docset-feed-url "${docsURL}/%DOCSETATOMFILENAME" \
--docset-package-url "${docsURL}/%DOCSETPACKAGEFILENAME" \
--docset-fallback-url "${docsURL}/$1Doc/" \
--ignore "$1Tests" \
"$2" > "${docsPath}/AppleDoc.log"

the bash invokes AppleDoc by assuming its location on /usr/bin/appledoc, the arguments passed are few since I use the GlobalSettings.plist for the main setting and I override only certain options (read GentleBytes reference for more info).
I also redirect the output of the command to a log file for an easy debug.
Remember to make both scripts executable (chmod +x) in order to use them!

…that’s all! If you want more info leave a comment (it’s free)

Tags: , , , ,


Nov 28 2011

Quick iOS tip: invoke UITableView dataSource and delegate methods from a UITableViewCell

Category: Objective-c and CocoaDavide Zanotti @ 6:03 am

I faced a situation in my app where I wanted to access to UITableView datasource from a UITableViewCell. Specifically I implemented the same behavior of left green (+) button in the label contained in the cell, so if you click on it the editing action “UITableViewCellEditingStyleInsert” will be committed.
Since a cell is contained in a table we can easily access to it using self.superview (cast is required because superview returns a basic UIView *), then once we are sure that the table has a valid dataSource, we can manually invoke the selector tableView:commitEditingStyle:forRowAtIndexPath:. In order to pass the proper indexPath dynamically we rely on table method indexPathForCell: (we don’t know in which cell we are but table does!).
The complete code snippet is the following:

1
2
3
4
5
6
7
8
UITableView *table = (UITableView *)self.superview;
SEL sel = @selector(tableView:commitEditingStyle:forRowAtIndexPath:);
   
if ([table isKindOfClass:[UITableView class]] && [table.dataSource respondsToSelector:sel]) {
    [table.dataSource tableView:table
             commitEditingStyle:UITableViewCellEditingStyleInsert
              forRowAtIndexPath:[table indexPathForCell:self]];
}

Tags: , , ,


Nov 04 2011

Xcode key bindings: how to create a custom shortcut to convert upper case text to lower case and viceversa

Category: xcodeDavide Zanotti @ 7:41 am

Since one of the most visited post on my blog is that one about case conversion in Eclipse, I decided to share how to implement 2 custom shortcuts to do the same in Xcode.
In “Preferences” panel, there is a tab called “Key Bindings“, here we can configure keys used for shortcuts. By default the most are already configured, but that’s not the case for text case conversion. By typing “case” to filter the long list, you can see 2 commands: “Lowercase word” and “Uppercase word“, all you have to do is to click on the “key” column and register your shortcut.
To avoid conflicts and to make them simple to remember I did choose “SHIFT + CTRL + L” for the first and “SHIFT + CTRL + U” for the second (but you can register your own one).

Tags: , ,


Next Page »