Thursday, December 23, 2010

Great Year 2010 and Embracing the New Year 2011

Year 2010 is coming to the end. I have a great year 2010. I have been working in one company as an IT consultant for the whole year. Even though the job requirement are not very challenge, I have been always set up new heights for me and continue to learn and explore new stuff in this year. This is very productive year for me. Not only I gained so much in a wide range of areas, but I also pick some of my old skills and knowledge back, such as OPC and COM in Windows. I feel very applaud of myself when I see my accomplishments.

In addition to my work, I have spent much my after-work time on personal persuasions. I think I have made great progress in my iOS development. My application is close to the finish stage. During the development, I have gained great skills and knowledge of Objective-C, Cocoa framework, and iOS. I enjoy my journey in the year of 2010. At the same time, as always, I have been keeping up with the evolution of iOS in past two years. I watched all the technical videos of WWDC 2010, and some wonderful podcasts such as CTN, and app review shows.

The most important thing I have to say about my year in 2010 is the web or internet. In a sense, it really extend my life. Without it I would spend more time and energy to struggle. I have taken so much from the web, the open community: learning, enjoying and sharing. It has enriched my life so much. I am so grateful to live in such a wonderful time and word.

I like two phrases. One is "stay hungry and stay foolish", from Steve Jobs' 2005 Stanford Commencement Address. Another one is "You Can't Take Money to Eternity", same applies to knowledge. I want to stay as I am and to share what I have. This is the way to extend human's life.

Now it is time to embrace the New Year 2011!

Read More...

Sunday, December 19, 2010

NULL Issues in ABContact Open Source Project

I found a nice open source project for Mac/iOS Address Book data source. The project contains several key wrapper classes: ABContact, ABGroup, and ABContactsHeler. Mac OS/iOS has extensive APIs for accessing and editing AB records, but they are all C libraries. The project provides nice Objective-C wrapper classes for those libraries.

Today, I found several bugs with multi value properties. Some NULL issues in the wrapper class ABContact.m methods have not been handled. As a result, I got EXC_BAD_ACCESS exception. Basically, if a record has no multi-value property defined, for example address property, the CFTypeRef value will be NULL. The fix is very easy: checking NULL before using CFTypeRef value. Here are my updated codes:

#pragma mark -
#pragma mark Getting MultiValue Elements
- (NSArray *) arrayForProperty: (ABPropertyID) anID
{
NSArray *items = [NSArray array];
CFTypeRef theProperty = ABRecordCopyValue(record, anID);
// the return value is NULL if no multi property is defined for the record.
// therefore, check its NULL first before getting values
// Updated by David Chu, same apply to the following methods
if (theProperty != NULL ) {
items = (NSArray *)ABMultiValueCopyArrayOfAllValues(theProperty);
CFRelease(theProperty);
[items autorelease];
}
return items;
}

- (NSArray *) labelsForProperty: (ABPropertyID) anID
{
NSMutableArray *labels = [NSMutableArray array];
CFTypeRef theProperty = ABRecordCopyValue(record, anID);
if ( theProperty != NULL ) {
for (int i = 0; i < ABMultiValueGetCount(theProperty); i++)
{
NSString *label = (NSString *)ABMultiValueCopyLabelAtIndex(theProperty, i);
[labels addObject:label];
[label release];
}
CFRelease(theProperty);
}
return labels;
}

+ (NSArray *) arrayForProperty: (ABPropertyID) anID inRecord: (ABRecordRef) record
{
NSArray *items = [NSArray array];
// Recover the property for a given record
CFTypeRef theProperty = ABRecordCopyValue(record, anID);
if (theProperty != NULL) {
items = (NSArray *)ABMultiValueCopyArrayOfAllValues(theProperty);
CFRelease(theProperty);
[items autorelease];
}
return items;
}

The original codes do not check NULL cases. With those updates, my codes resume normal. In addition to NULL checking, I also make sure there is no memory leak, as the same way as the original codes do. All the copied NSArray result are set with autorelease.

Read More...

Wednesday, December 15, 2010

XCode Splash Screen

Just finished watch a short podcast show by CTN (Cocoa Touch Netcast). It is about 2 minutes show on how to enable settings for XCode Splash screen. For example, this is my XCode splash screen:


The way offered by Robert is actually to edit the plist file in /Library.... I don't like this way to modify plist file, since it may mess up the file. Instead, I prefer to use console tool or command defaults to modify it.

First, check the default setting. Open Terminal and type the command:

defaults read com.apple.xcode XCShowSplashScreen 1

or use the pipe and grep command to search for "Splash"

defaults read com.apple.xcode |grep Splash

I did not find the setting for XCShowSplashScreen. I have never disabled my splash screen. It looks like that the default setting is to show the splash screen if the setting is not defined in plist. Then I disabled my splash screen and use the defaults read command to read it again. After that, I saw it was set to 0.

To enable it, use the defaults write command:

defaults write com.apple.xcode XCShowSplashScreen 1

You may check it again by defaults read command.

Read More...

Saturday, December 04, 2010

PowerShell Tip: Dynamic Data Type

I have used PowerShell(PS) for quite a while. I really enjoy its power and great features. I did not spent time to learn PS thoroughly or systematically(such as C/C++, .Net C# or VB, or Objective-C). I just learn it by examples and by demand. The main reason is that PS covers a wide range of areas, from DOS command to .Net and other scripts. I just don't have time to learn it in a long time span.

One of PS great features is its dynamic data type. All the variables are defined by $ prefix. You can define a variable with strong data type. However, sometimes you just need to take the advantage of its dynamic data type. For example, the following code is to get files. The result may be null, one file or a collection of files:

$fs = Get-Item -Path "*.txt"

In order to find out if the result contain any file, you have to check the cases of empty, one object or a collection of objects.

$fs = Get-Item -Path "*.txt"
if ($fs -eq $null) {
Echo "empty files"
}
if ($fs.Count -eq $null) {
Echo ("one file: {0}" -f $fs)
}
else {
Echo ("collection of files. Count: {0}" -f $fs.Count)
$fs
}

Read More...

XCode 3.2.5 and iOS SDK 4.2

Xcode 3.2.5 and iOS SDK 4.2 was available on November 22, 2010. I downloaded the whole package (52GB) couple weeks ago. It took me about 2hours to get the package(not sure why my Internet or browser was so slow).


I had a little trouble to compile my app after the installation, as same I did last update (3.2.5 on September 30, 2010). I had to refresh my base frameworks. This time it was much better than the previous time. I only spent about a few minutes to make my app codes working in XCode.

Read More...

Sunday, November 21, 2010

iPhone Dev: Build + Analyze

There are many ways to detect iPhone project's code potential issues such as memory leak and performance issues. One very simple way is to build with Analyze. There is shortcut key for it: Shift+Command+A.

My current project is targeted to Simulator - 4.1. When I first time tried to use this Analyze feature, I got several messages saying "Analyzer skipped this file due to parse errors" for a lots of files. There were no analyze result for those files. Soon I found a solution to resolve most skips. SO has several posts on this issue. One of solution is to add a line to project settings (from project Get Info):


However, I still got this skip warning for MyLogger class. Eventually, I found is a C code issue. Here is a definition of enum type:

typedef enum {
LogLevelDebug = 1,
LogLevelWarning = 2,
LogLevelError = 3,
LogLevelInfo = 4,
LogLevelNone = 100
} UIUInteger, MyLoggerLevel;

After I removed UIUInteger, I got no skip warnings.

Read More...

Saturday, November 20, 2010

Powershell Scripts and Batch File

Normally I create a PS script project and then run it as a job in a batch file. Occasionally, I need to execute a batch job from my PS script. Here are two tips.

First to execute a PS script in batch file, run it from PS with script name and additional arguments:

Powershell myscript.ps1 argument1, 'argument two'

Notice that if there is a space in an argument, use single quote instead of double quotes.

Second tip is about calling a batch file from PS script, use the cmd /c to execute a batch file. For example, the following case is to use PS script to format the current date time asa string and then run a bat with the string as its argument:

# get the current date time
$date = Get-Date
# format the date time as a string
$argDateTime = "{0:d4}{1:d2}{2:d2}_{3}{4}{5}" `
-f $date.Year, $date.Month, $date.Day, $date.Hour, $date.Minute, $date.Second
# build a command line: $args[0] is a bat file
$cmdApp = ("{0} {1}" -f $args[0], $argDateTime)
# run the bat with a formatted date time string as argument
cmd /c $cmdApp

Read More...

Saturday, November 13, 2010

NANT: Exec a Target with Arguments

Recently I encountered an minor issue when I tried to use NANT to build a Visual Studio solution. Finally I narrowed down to the problem. It was the Exec target failure when one of its parameter argument contains a space. I thought that I could use a function or tool to convert the argument (a path) from a long name to DOS short name. Unfortunately, there is not such function in NANT for Windows build.

I posted a question to SO. I soon got a answer. In addition to that, I also figured out a way to resolve the issue. See my answer for detail explanation with a simple test case.

By the way, I used NANT extensively long time ago when I was a system analyst in a company's RTD, about 10 years ago. I created a NANT project to build an open source project by using Microsoft .Net Framework SDK (1.0) without using Visual Studio. Later I also used NANT in an Agile environment as a way to test UNIT tests, and to deploy the project. I really enjoy this great tool.

Read More...

Saturday, November 06, 2010

Using Excel to Compare SQL Database Tables

It is a very common task to compare data content between two SQL database tables. If two tables are within a SQL database, I normally use TSQL EXCEPT and UNION to get difference, detail in my previous blog: Comparing Two Tables By SQL Stored Procedure.

I could add linked server in SQL database so that the same method could be used to compare two tables in two difference SQL databases. However, it would cause too much trouble, such as change SQL settings, or caching millions rows of data from linked server.

The alternative way is very simple. I just run a SQL query to get data in SQL server management studio. Then copy the result to excel, either copy or export. Do the same thing against another SQL server again. By using Excel, it is very easy to compare cell by cell to get any difference.

The following are steps I use in Excel.

Excel Worksheets

The excel file contains four worksheets. The first one is comparison, which will be discussed in detail latter. The second one an third one are data result from two SQL database tables, for example, source_table1 and test_table1. SQL worksheet contains SQL query used to get data.


In the worksheet of comparison, the comparison is done by the following formula. For example, for cell A1 column:

=IF(source_table1!A1=test_table1!A1, source_table1!A1, "Diff (" & source_table1!A1 & " : " & test_table1!A1 & ")")

For the data cell A2:

=IF(source_table1!A2=test_table1!A2, "YES", "Diff (" & source_table1!A2 & " : " & test_table1!A2 & ")")

The formula of A2 comparison applies to all other data cells. Just copy the formula and paste to all the cells you want to compare. Excel is smart enough to adjust cell coordinates to get the comparison results. The following is an example of comparison result:


If you have thousands of raws of data in Excel, you can use Excel's Filter feature to narrow down the differences easily. From menu Data|Filter|AutoFilter to turn on the filter:


On the header filters, you can configure filter to whatever levels you want. Here are two examples:




As you can see, by using Excel it makes much easy to compare two tables and find out the differences.

Import Data Issues

The above method works fine for most of cases. I had a problem to get data from SQL to Excel last week when I was working on one SQL database table. I had spent for 2 hours+ finally I found the problem. Some rows contain double quotes in their raw data. For example, there was " being used in a table as inch unit. This unbalanced quote caused the Excel to import rows from either clipboard or csv file.

Excel treats " as string data type mark. In order to make importing to work, it has to be escaped. I used VIM to resolve the issue. Just simply search for " by typing /" in VIM, it will mark and find those quotes. Then I used VIM's Replace feature to escape all the single quote by the command(replacing " with ""):

:%s/"/""/g

After the normalization of data, I got the expected data from SQL query result to my Excel.

Excel Being too Smart

This issue is beyond the main topic of comparing data between SQL database tables. However, it is worth to leave a note here.

Here is the story of what I do to import or copy data from SQL query result to Excel. For the first data source, I copy the result of SQL query into clipboard. I think that by default, the delimiter used to separate columns is a tab char. I use Paste Special feature from Excel to paste pure text. Then Excel will prompt me to select what delimiter is and preview data in a grid. I like it very much. However, when I repeat the same steps to copy the data result from the second data source, the Excel does not prompt me, nor provide preview. It just copy data to columns. It is too smart to remember the delimiter.

However, I would like to preview data before pasting. I could not find a way to disable this smart feature. Finally I found an alterative way. I pasted the data to VIM first. Then replace the tab to semi-command (make sure it is not used in the raw data):

:%s/\t/;/g

Then copy the whole text to Excel. This change will fool the Excel to prompt the preview as expected.

Read More...

Saturday, October 23, 2010

.Net Debug Class (3)

Implementation Class: DianosticsDurationHelper

To calculation duration between two debug calls, I created an implementation class: DianosticsDurationHelper. In addition to two public properties in the base class, the implementation class provides a few public methods.


As you can see, the duration helper class has very simple APIs. One method Indent() is used for indentation or un-indentation, and one method for debug a message. The duration calculation is done within the class.

As discussed above, the calculation is done through delegates. Two private methods are defined here. One is for obtaining the current date time value, and another for calculating the duration value. Those methods are used as delegates to the base class calls.

private DateTime GetDateTime()
{
return DateTime.Now;
}

private string CalculateDuration(DateTime dt, DateTime poppedValue)
{
float duration = (float)((new TimeSpan(dt.Ticks - poppedValue.Ticks)).Hours) * 3600.0f +
(float)((new TimeSpan(dt.Ticks - poppedValue.Ticks)).Minutes) * 60.0f +
(float)((new TimeSpan(dt.Ticks - poppedValue.Ticks)).Seconds) +
(float)((new TimeSpan(dt.Ticks - poppedValue.Ticks)).Milliseconds) / 1000.0f;
return string.Format(" duration: {0:N2}", duration);
}

The remaining part of the duration helper class is as followings:

internal class DianosticsDurationHelper : DianosticsHelperBase<DateTime>
{
public DianosticsDurationHelper(string className) : base(className)
{
}

public DianosticsDurationHelper DebugMessage(string message)
{
base.debugMessageBase(message);
return this;
}

public DianosticsDurationHelper Indent()
{
return DebugIndent(true);
}

public DianosticsDurationHelper UnIndent()
{
return DebugIndent(false);
}

private DianosticsDurationHelper DebugIndent(bool indent)
{
if (indent)
{
base.debugIndent(GetDateTime);
}
else
{
base.debugUnindent(GetDateTime, CalculateDuration);
}
return this;
}
...// private methods used as delegates, see above.
}

Usage Examples

In many of projects, I used this duration class to get duration time between the begging and end of each call. Here are some example codes.

[STAThread]
static void Main() {
// customize indent string to --
DiagnosticsDurationHelper.IndentString = "--";
MyForm mainForm = new MyForm();
Application.Run(mainForm);
// ...
}

// MyForm.cs
class MyForm : Form {
// some helper methods using duration helper class
private DianosticsDurationHelper _debugHelper =
new DianosticsDurationHelper("MyForm");
// other data members
// helper methods to use the debug instance
private void DebugMessage(string message) {
_debugHelper.DebugMessage(message);
}

private void DebugMessage(string message, bool indent) {
if (indent) {
_debugHelper.DebugMessage(message).Indent();
}
else {
_debugHelper.UnIndent().DebugMessage(message);
}
}
// use debug helper methods to log duration...
private void MyForm_Load(object sender, EventArgs e) {
DebugMessage("MyForm_Load", true);
// ...
DebugMessage("Some information...");
// ...
DebugMessage("MyForm_Load", false);
}
// ...
}


By the way, in my helper class and base class, I tried to use fluent interface pattern for APIs. As you can see, the usage of those APIs is much fluent and clean.

To get the debug result in my Visual Studio's output console, I run my application in debug mode. Alle debug strings will be in the output console. Here are some results:

--[ 10/18/2010 11:21:58 AM ] MyForm::MyForm_Load
----[ 10/18/2010 11:23:58 AM ] DBGateway::DBGateway
...
----[ 10/18/2010 11:23:58 AM duration: 0.02 ] DBGateway::DBGateway
--[ 10/18/2010 11:22:58 AM ] MyForm::Some information ...
...
--[ 10/18/2010 11:21:58 AM duration: 0.22 ] MyForm:: MyForm_Load

Here is the source codes with an example.

Read More...

Sunday, October 17, 2010

.Net Debug Class (2)

My debug class contains one base class. Let's see the structure of the base class.

DianosticsHelperBase Class

My debug class is a simple base class, DianosticsHelperBase:


The above class picture shows all the public properties and protected helper methods for derived classes. Let's discuss the design of the base class.

Layout Format of Debug Message

The main purpose of the debug class is to print or log messages. The message consists two key parts: timestamp and message, with additional optional parts: context, indent, and calculated value.

The context is used as a convenience to identify message's context, for example, a class or module name could be used to mark debugged messages within the context.

Indentation is a nice feature to layout debug messages. The indentation and un-indentation are done through two method calls. DebugIndent() and DebugUnindent(). The indentation string can be set by the static property InentString. Null or empty string can be set to IndentString to disable indentation, or other strings to customize an indentation string, such as "—". The default indent string is a string of two spaces:

private static string _indentStr = "  ";

The base class provides another nice feature to calculate a value between two points. This is done by providing delegates to method calls: DebugIndent() and DebugUnindent(). If null delegates are passed to the calls (parameters), no calculation will be donem, thus disable the calculated value feature.

The layout format of debug messages is set by the static property Format of the base class. The default layout is:

//indent, timestamp, calculatedValue, context, message
private static string _format = "{0}[ {1}{2} ] {3}::{4}";

Constructor

Typically, an instance of the debug class is a member of a class where debug messages will be logged by the instance within its internal codes. A context string for the instance is set through the base class CTOR. The context of the debug instance is set in its creation time.

private string _context;
private string _calculatedValue;

public DiagnosticsHelperBase(string context) {
if (context != null) {
_context = context;
}
else {
_context = "";
}
_calculatedValue = "";
}

Helper Methods

A message is logged or output by the protected method debugMessage. In this method, a message is formatted as a string and output to a media. In the following codes, the output is passed to Output console in the Microsoft Visual Studio.

private string _context;
private string _calculatedValue;
private static bool _logMessage = System.Diagnostics.Debugger.IsAttached;

protected DiagnosticsHelperBase<T> debugMessage(string message) {
if (_logMessage)
{
System.Diagnostics.Debug.Print(
string.Format(_format, getIndent(), DateTime.Now,
_calculatedValue, _context, message));
_calculatedValue = "";
}
return this;
}

private string getIndent() {
string sVal = "";
if (_indent > 0)
{
int i = 0;
do
{
sVal = string.Format("{0}{1}", sVal, _indentStr);
i++;
} while (i < _indent);
}
return sVal;
}

The method checks _logMessage before further logging messages. This flag is set by Syste.Diagnostics.Debugger.IsAttached. If the build is in release mode, this flag will be False, as a result, no calls to log messages.

The concept of a calculated value is based on the consideration of calculating a value between two debug points, for example, a duration time value between the beginning and the end of a method call. This is done within the base class by two method calls: debugIndent() and debugUnindent(). How a value is obtained and how the calculated value is done are provided by passing delegates as parameters to those method calls. This provides a flexible way to generate calculated values such as duration, memory usage, disk free spaces and so on.

protected DiagnosticsHelperBase<T> debugIndent(GetValue<T> pushedValueDelegate) {
if (_logMessage)
{
if (pushedValueDelegate != null)
{
T value = pushedValueDelegate();
_debugDts.Push(value);
}
_calculatedValue = "";
_indent++;
}
return this;
}

protected DiagnosticsHelperBase<T> debugUnindent(GetValue<T> fromValueDelegate, GetCalculatedValue<T> calculatedValueDelegate) {
if (_logMessage)
{
if (calculatedValueDelegate != null && fromValueDelegate != null)
{
T value2 = _debugDts.Pop();
T value1 = fromValueDelegate();
_calculatedValue = calculatedValueDelegate(value1, value2);
}
if (_indent > 0)
{
_indent--;
}
}
return this;
}

All the methods in the base class are protected. They are only available for a derived class to implement specific debug usages. For example, the following duration helper class is an example for logging messages with duration information.

Read More...

Monday, October 11, 2010

.Net Debug Class (1)

I wrote a blog on debug or logger class in Objective-C about couple months ago. Actually, that class was based on my prevous .net debug class. It has a feature to print debug information in a nice indention layout, which was extended to my Objective-C class. I think my .Net is very useful, at least for me, and it is more generic, with an additional feature to calculate a value between paired messages(indent and un-indent).

Background Story

I was assigned to a task to resolve a painful slow performance issue of a .Net Windows application project. I did not spend much time to understand the complete business logic of the application. I went to the issue directly. What I did was to create a debug class to print out time and duration of each method or event call. Based on that information, I then focused on the longest duration calls to find out ways to improve the performance issue. The task was finished in very short period of time and the performance was greatly improved. My debug class is a very generic and useful tool. I have used this in my other projects with great success. With this experience and design, I created a similar logger class in Objective-C when I started iPhone development.

The intension to create this debug class is mainly based on three considerations. First is to generate debug message with duration information for each method or event call. This is the main reason and it is very easy to do. This could be done without a class. However, I would like to have debug message in a consistent format or indention. A class can encapsulate the implementation and provide unified interface. This second consideration actually was trigged by System.Diagnostics.Debug class, which has a nice property IndentLevel and a method Indent.

The third consideration is the maintenance. Since the debug calls are spread out all over the places in a project, it will be really hard to clean up them when the project is finally released. I would like to keep the debug feature or codes in my project without removing them, but also have least impact on its performance. As you will see, I have introduced delegates and early checks to avoid unnecessary calls if debug is disabled.

Delegates Used in the class

I like to use delegates as a way to pass custom implementation to my class. The first advantage is that it follows Open/closed-principle. It opens for extension or plug-in, but close my class for any modifications. The second advantage is to improve performance. Delegates normally used as parameters. They are pointers to functions or methods. If a delegate is not called for execution, its internal codes will never be evaluated. The following delegates are defined:

public delegate T GetValue<T>();
public delegate string GetCalculatedValue<T>(T value1, T value2);

The first one is a delegate to get a value. The delegate provides a way to let client to define his/her implementation to get a value. For example, for duration case, a delegate can be defined just returning a value of DateTime.Now.

The second delegate is for calculate a value based two parameter values, from value1 to value2.

Read More...

Friday, October 01, 2010

XCode 3.2.4 and iOS 4.1

Today I checked the Apple Development web site to see if there is any new Xcode and iOS available. Then I found that Xcode 3.2.4 and iOS 4.1 are available for download. I remember that when I just finished watching WWDC10 videos, I could not get the new iOS 4.1. It was only available for registered developers. I am very glad that now it is open for all developers.

My current XCode is 3.2.1 and iPhone SDK is 3.1.3. The new package is in download progress. The size of the package is 2.94GB. Like I have recorded the space compassion my iMac updates, here is the difference of free space between before and after.

Command: df -lakUsed(Kilobytes in 1024-blocks)
Before ...1,609,459
After...2,428,476
Difference (A-B)819,017


Issues After the Update

After the update, I tried to open the project I am working on. The first issue is that I lost my Framework references. For example, my UIKit.framework item in Frameworks became red. iPhone 3.1.3 frameworks were all gone.

That issue actually was not hard to fix. I opened the project Get Info window, and changed the Base SDK for All the configurations to iOS 3.2. Then select a format for my project.


The second issue was not so straightforward. I saw an error when I tried to build the project. It is code signing issue with key chains.


After I googled the web for solutions, I found this answer from SO. Basically, default device for my build was set to iPhone, instead of Simulator. For device build, I do need to be a registered developer.

After I fixed those two issues, I am now back to my progress of development. XCode and iOS updates are very well back-compatible. The updates do not like Visual Studio update with a lots of changes or conversions. Maybe my project is just too simple?

Read More...

Friday, September 24, 2010

Use iOS Address Book

Recently I have encountered an issue about using iOS Contacts or Apple's Address Book as data source for my application. The AB provides nice detail data structure for contacts, as well as views and APIs for adding, editing and deleting contacts. The issue is how I build a bridge between the AB contacts and my application.

Initially, I thought that the contacts in my app are mainly company contacts. In the AB framework, a contact record can be a person with names such first and last name, or an organization with a name, or both. Actually, when I stand back, my app's contact can be a company or a person. There should be no clear line between them. Therefore, I should not limit my data source to organization or company only.

Then I was puzzled by the issue who to present contacts from AB to my app. I don't mind to present all the contacts, but I would like to provide a convenient way to present a sub-set of contacts. I don't feel that it is good way to divide contacts into persons and companies.

Further exploration on AB framework and studying some example codes, I realized that contacts can be categorized into groups. Groups are defined by user. A group in AB is also record which has only one property kABGroupNameProperty. That opened my eyes. Why not let users to define their contact collection in group? Then my app will use that group as data source. Now I think that it's the best solution.

With this discovery, I find out a very useful and simple application case. By searching for the case in iTunes, I found a couple of apps. The app is very simple. Its main feature is for a user to manage or organize contacts into groups. I could work out a different and simple app. It is not hard to do it. I may get it done in a very short period of time. Sit back and rethink about this struggle period, my past pause and hard time are not a waste. You never know that a bright, wide and smooth pathway will suddenly present to your front when you think you are driving into a no-exit road.

Read More...

Thursday, September 23, 2010

Wrapper Helper Classes for ABAddressBook

I have been looking for ways to integrate Address Book - Contracts to my application. Although Address Book Programming Guide for iOS provides information, APIs and example codes, all those resources are far away from what I need.

Then I found this ABContactHelper project on github. The project contains several wrapper ObjC classes to wrap CF classes: ABContact for contact, ABGroup for group and ABContractHelper for CF functions. I like those helper classes. The only thing I found so far I don't like is the contactName property. It formats a name in western way, not localized. Instead, the compositeName is an alternative choice, which just uses iPhone's AddressBook C function to return a composite name.

Those helper ObjC classes provide a good overview of ABAddress Book framework.

Read More...

Monday, September 13, 2010

Backup SQL Server by Using PowerShell Scripts

There are many ways to back up SQL server databases. Normally it is done through DBA to create a scheduled job on SQL Server. However, this requires a full version of SQL server. For Microsoft SQL Server 2005/2008 Express version, one of its limitation is that the free version does not provide job scheduling. During my past working experience, I fount several ways to the backup.

The basic requirements for the database backup job are:

  • The job can be scheduled as an automation job without user interaction
  • The job is preferred in script for each maintenance, for example, database, user/pwd, and backup location changes.
  • The backup job is centralized on one place so that several SQL databases are backed up on one central location.

SQL Server Utility

The first tool I found is to use SQL management tool SQLMaint.exe. It comes with SQL Server Management Studio 2005. Here is the technical information of this tool. For example, I use the following batch commands to do a database backup for MyDatabase on SQL server PC001\sqlexpress:

@echo off
REM
REM This path is SQL binary folder for sql maintanance app
REM
pushd "C:\MSSQL2005\MSSQL.2\MSSQL\Binn"
sqlmaint.exe -S PC001\sqlexpress -U dbbackup -P pwd
-D myDatabase -CkDB -BkUpOnlyIfClean
-Rpt C:\MSSQL_Backup\Log\PC001MyDatabase_backup_log.txt
-VrfyBackup -BkUpMedia DISK
-BkUpDB C:\MSSQL_Backup\DB
-DelBkUps 3days -DelTxtRpt 3weeks
-HtmlRpt C:\MSSQL_Backup\HTMLRpt\PC001MyDatabase_backup_report.html
-DelHtmlRpt -3weeks
popd
@echo on

The batch commands work fine with only SQL Server 2005 database; however, it does not work for SQL Server 2008 or Expression databases.

PowerShell Solution One

By googling web, quickly I found an alternative way to do the job. Those scripts are based on Microsoft.SqlServer.xxx classes. That's very cool! Based on those scripts and my requirement, I created a function. This function does database backup by either Windows log-on user credential, or SQL Server user credential. In either case, the credential user should be configured in the SQL server with db_backupoperator permission.

The function takes following parameters:

  • SQL Server Name, for example, PC001\SQLEXPRESS
  • database name, for example, myDatabase
  • folder: a path on SQL server where the backup file will be saved
  • SQL user name. This is optional. If it is not supplied, the current Windows log-on user's credential will be used
  • password for the above SQL server user. Optional


Here is the script:

function BackupSQLDb (
[string]$p_sqlServerName = ${throw "Missing sql server name "},
[string]$p_db = ${throw "Missing parameter database name"},
[string]$p_DestFolder = ${throw "Missing parameter destination folder"},
[string]$p_userName,
[string]$p_password
)
{
#load assemblies
#note need to load SqlServer.SmoExtended to use SMO backup in SQL Server 2008
#otherwise may get this error
#Cannot find type [Microsoft.SqlServer.Management.Smo.Backup]: make sure
#the assembly containing this type is loaded.

[System.Reflection.Assembly]::
LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
#Need SmoExtended for smo.backup
[System.Reflection.Assembly]::
LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null
[System.Reflection.Assembly]::
LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo") | Out-Null
[System.Reflection.Assembly]::
LoadWithPartialName("Microsoft.SqlServer.SmoEnum") | Out-Null

$sqlServername = $p_sqlServerName

$sqlUserName = $p_userName
$sqlPWD = $p_password
#create a new server object
$server = New-Object ("Microsoft.SqlServer.Management.Smo.Server")
-ArgumentList $sqlServername # "PC001\sqlexpress"
$backupDirectory = $p_DestFolder
#display default backup directory
Write-Debug ("Default Backup Directory: {0}" -f $backupDirectory)
if ( $sqlUserName -ne $null -and $sqlUserName.length -gt 0 ) {
$server.ConnectionContext.LoginSecure=$false;
$server.ConnectionContext.set_Login($sqlUserName)
$securePassword = ConvertTo-SecureString $sqlPWD -AsPlainText -Force
$server.ConnectionContext.set_SecurePassword($securePassword)
}

$db = $server.Databases[$p_db]
$dbName = $db.Name
if ( $dbName.length -gt 0 )
{
$timestamp = Get-Date -format yyyyMMdd_HHmmss
$backupFile = $backupDirectory + $dbName + "_" + $timestamp + ".bak"
Write-Output ("Start backup database ""{0}"" to ""{1}"""
-f $dbName, $backupFile)

$smoBackup = New-Object ("Microsoft.SqlServer.Management.Smo.Backup")
#BackupActionType specifies the type of backup.
#Options are Database, Files, Log
#This belongs in Microsoft.SqlServer.SmoExtended assembly
$smoBackup.Action = "Database"
$smoBackup.BackupSetDescription = "Full Backup of " + $dbName
$smoBackup.BackupSetName = $dbName + " Backup"
$smoBackup.Database = $dbName
$smoBackup.MediaDescription = "Disk"
$smoBackup.Devices.AddDevice($backupFile, "File")
$smoBackup.SqlBackup($server)
Write-Output ("Finished backup database ""{0}"" to ""{1}"""
-f $dbName, $backupFile)
}
else {
Write-Output ("ERROR: invalid database name or database does not exist: {0}"
-f $p_db)
}
}

I only tested this function on Windows XP and Windows 2008 Server with SQL Server 2008 Express installed.

PowerShell Solution Two

The above script function works fine with SQL Server 2005 and 2008 and Express versions. However, it does not work for SQL Server 2000! I realized one day that there is option to obtain the backup TSQL scripts from SQL Server Management studio. I verified that in the TSQL command stays same in all SQL Server versions: 2000, 2005 and 2008. How about to make a connection to SQL server and run the TSQL command? Quickly I come to the solution two.

function BackupSQLDb (
[string]$p_sqlServerName = ${throw "Missing sql server name "},
[string]$p_db = ${throw "Missing parameter database name"},
[string]$p_DestFolder = ${throw "Missing parameter destination folder"},
[string]$p_userName,
[string]$p_password
)
{
$timestamp = Get-Date -format yyyyMMdd_HHmmss
$backupFile = $p_DestFolder + $p_db + "_" + $timestamp + ".bak"
$backupDescription = "Full backup of {0}" -f $p_db
Write-Output ("Start backup database ""{0}"" on SQL Server({2}) to ""{1}"""
-f $p_db, $backupFile, $p_sqlServerName)
# TSQL command for backup
$tsqlCmd = "BACKUP DATABASE {2} TO DISK = N'{0}' WITH NOFORMAT, NOINIT, NAME = N'{1}', SKIP, NOREWIND, NOUNLOAD, STATS = 10"
-f $backupFile, $backupDescription, $p_db
$con = $null
if ( $p_userName -ne $null -and $p_userName.length -gt 0 ) {
# Use SQL user/password
$con = "Data Source={0};Initial Catalog={1};User ID={2};Password={3}"
-f $p_sqlServerName, $p_db, $p_userName, $p_password
}
else {
# Use Windows log on credential
$con = "Data Source={0};Integrated Security=SSPI;Persist Security Info=True;Initial Catalog={1}"
-f $p_sqlServerName, $p_db
}
Write-Output ("Connecting to {0} ..." -f $con)
$cn = new-object System.Data.SqlClient.SqlConnection ($con)
$cn.Open()
$cmd2 = new-object "System.Data.SqlClient.SqlCommand" ($tsqlCmd, $cn)
$result = $cmd2.ExecuteNonQuery()
$cn.Close()
Write-Output ("Backup database is done with result {0}" -f $result)
}


I did similar tests for this function. It seems that it works for all SQL Servers, 2000, 2005, and 2008.

References

SQL Server PowerShell : How to Backup SQL Server Databases Using SMO and PowerShell

SQL Server PowerShell : Basics – Connecting to SQL Server

JBs Powershell blog: SQL Queries.

Read More...

Saturday, September 11, 2010

Old Windows Tool: XCOPY

XCOPY is an old Windows Tool. I remember that I first used this tool to copy files with sub-directiories when my computer was DOS based one. It is a nice tool to copy files. At my current work, this tool is also used to copy only most recently changed files as a way to back files. The option is /D/Y.

XCOPY source [destination] /D /Y


In the past week, I was working on a project to migrate services and scheduled jobs to a new Windows Server 2008 box. One simple task was to move a XCOPY job, copying files from a mapped Windows box to an imported UNIX based network drive. To my surprise, the same command does not work any more. It always copy whole files again, about several GBs. Somehow, XCOPY does not work for CIFS type network drive.

It is possible that the file date time stamp retrieved from the network drive might be truncated after seconds, while the full date time stamp are available in Windows file system. As a result, this may lead to always copy new files to the destination. I got this explaining from my SF question.

I also tried to use ROBOCOPY. It did not work neither. Finally, one of my network team member suggested me back to the old tool XCOPY with different options. It works again! Here is the command:

XCOPY source [destination] /M /Y /C

/M for copying only archived files and clear archive flag after the copy. I added /C option to let copy process to continue regardless of any failure.

The reason I like to use XCOPY is that it is more tolerance or not so aggressive. Even a file is hold by another process, it still does the copy. I tried SyncToy tool and it does not work with files being accessed by another process. My backup job is doing realtime file backup and those files are constantly updated by realtime process. Therefore, /C option lets me to continue the backup process and try it again on the next day.

I am happy with the alternative solution.

Read More...

Friday, August 27, 2010

A Nice Video Podcast on Objective-C by Robert Van Slooten

Last night I found a nice podcast on Objective-C from my iTunes. The podcast is called as Cocoa Touch Netcast (CTN), hosted by Robert Van Slooten. The first show was published on February 2(Tuesday), 2010. Up till now, it is the 2nd episode of part 3.

What I Like

I really enjoyed the shows. First I like the structure and design he created those shows. He uses slides, screen captures(by using Quicktime recorder?), and pictures. All those are easily available or to do on iMac. In most cases, one or more slide shows outline clear and important points or a block of codes first. After that, a screen capture with an animation transition is smoothly connected to a live show of XCode and an example project with codes. He also uses related pictures during the show or at the end. This is nice change or tone on the screen. He must make some efforts to search for those pictures.

The second feature I like is that each show is very short, from 5 minutes to over 20 minutes(but less than 30 minutes). That's really cool to cover so much information or complicated concepts in such short period of time. His voice is clam, slow and clear, and his words or sentences are effectively terse. For some complicated concepts, he uses very brief key words to get the point related to the context. I always try to consider too much when I try to explain something. Cocoa and ObjC are very comprehensive and rich frameworks. In stead of throwing out extensive and unrelated information, he starts from a simple and clear path to the point. Sometimes, he jumps to thousand feed high to give an overall view, without any fear to confuse viewers with new and unknown terms or concepts. If there are some example codes, there is just one project per show. I admire his knowledge and teaching skills. As a viewer or learner, I had great time to enjoy his shows!

What I Hope

One thing I feel not comfortable sometimes when I hear music with pictures when I have my headset on. The sound are little bit too loud, comparing to the previous clam and slow speech, especially at the end of show. The volume can be adjusted to the same level of speech.

Another thing I think he could improve is to add English subtitles on his show and also make it as an option to show or hide, just like WWDC10 shows. Apple uses podcast producer to generate podcasts. Not sure if the application has any tool to generate scripts or not. If it is possible, that would be great.

References

By googling "cocoa touch netcast", I found Robert's podcast web page. I think that he uses MobileMe to host his shows. During the loading time in my browser I saw the loading status showing data from www.me.com. Robert also includes his show codes on the web.

I think Robert is a Canadian. The show of P2-E6 on Properties shows a picture of his boat(?) with a Canadian flag.

Read More...

Saturday, August 21, 2010

App Installation as Admin in Windows Server 2008

Today at work, I tried to get help from Capstone on their application work in Windows Server 2008. The application is a 32-based application as a VBS script host with ability to communicate with their ParcView suite. The application has a configuration setting for data connection, by using UDL connection string.

The issue I encountered was that the UDL is a connection to an Oracle database, and it seems that the UDL string cannot be accepted by the application. To cut the story short, Captone's tech support guru finally identified that the issue was caused by Oracle drive installed on this Windows box. The network team at work installed 64-bit oracle driver (Oracle g10), while the application requires the 32-bit driver. Unfortunately, the person who is responsible for the installation is on vacation this week. Therefore, Capstone person helped me to install the Oracle g11 client, 64-version first and then 32-version next.

The 64-version installation went OK. However, we had trouble to install the 32-version. There were several failures during the preparing stage. He finally figured out that he has to run the installation oui as administrator. I saw it so simple to run installer as administrator. Since my log-in name is a member of Administrators group, in Windows Server 2008 (VISA-like Windows), just right click on oui and run it as administrator:



to my surprise, no password was requested. I think it might be Windows Server 2008 and my log-in is in Administrators. Anyway, that reminded me a case I did before: to configure PowerShell Execution settings.

By default, the execution policy for PS is Restricted. I needed to change it to RemoteSigned. However, I could not do it even my log-in name is in Administrators. It seems Windows Server 2008 has strong security. What I did was to make a request to the network team to log in as administrator to run Set-ExecutionPolicy command with RemoteSigned.


When I realized that it was so simple to run installation as admin, I tried PS right afterward.


Now, after I run the PS as admin and I can set policy. That's pretty cool!

Read More...

Saturday, August 14, 2010

HTML5 and Web Browsers

I just finished watching several WWDC10 videos on HTML5. I am very impressed by the new features in HTML5, with CSS3 and JavaScript, to support interactions, animations, and offline data store. Apple claims that Safari is 100% compatible with HTML5, and provided some examples on the web. My understanding is that all those new features are just API gateways, and it will be up to the browser to provide support in its back-end engine.

Based on Wikipedia's List of Browsers, Safari and Chrome are based in Webkit, an open source project. Apple's Safari on Mac, Windows, iPhone, iTouch and iPad are based on Webkit. As a result, HTML5 is fully supported by Apple products. Here I say Apple products do not include Safari for Windows, see my comments later on.

It is very interesting to read the history section of W3C web document on HTML5. HTML was originally used as a standard web site design language in 1990. Then it was almost stopped its evolution to HTML4 in 1998, with the new direction towards an equivalent XHTML as replacement.

Mozila and Opera proposed form based features, later as HTML5, in 2004. Soon Apple joined the force to the effort to move to HTML5. Now it becomes the draft of W3C standard.

To verify if a browser supports HTML5, CSS and JavaScript, there are some web based test sites available. Acid3 and HTML5 tests are two well known ones. The following is the result I run on Aug 12, 2010 in my Windows XP box at work):

 Acid3Html5
IE612/10012 (scores)
IE820/10027
Firefox94/100139
Chrome100/100197
Safari (Windows)100/100207
Safari (Mac)100/100208


It seems that Safari has the best support. However, when I tried a well-known HTML video site (dart moor). Safari for Windows fails to play the video, while Both Chrome and FireFox for Windows play the video beautifully. I tried Safari for Mac at home, it plays the video without any issues.

Then, I created a simplified html file to test. The following is a simplified html file with the HTML5 video tag:

<!DOCTYPE html>
<html>
<head>
<title> Dartmoor - 1 </title>
<meta name="viewport" content="width-1400"/>
</head>
<body>
<video id="myVideo" width = "1280" height="544" controls
poster="http://assets1.jilion.com/08062010162347/images/embed/sublime/video/poster.jpg?1281111824">
<source type="video/mp4" src="http://medias.jilion.com/sublimevideo/dartmoor.mov"></source>
<source type="video/mp4" src="http://medias.jilion.com/sublimevideo/dartmoor.mp4"></source>
<source type="video/webm" src="http://medias.jilion.com/sublimevideo/dartmoor.webm"></source>
<source type="video/ogg" src="http://medias.jilion.com/sublimevideo/dartmoor.ogv"></source>
HTML5 videos support is required to play this video.
</video>
</body>
</html>


It is interesting to find out that the tests do not really guaranty browser's capability or supportability. The real site or html file says it all, at least for this test case. Not sure why Apple's Windows version of Safari is so behind to its Mac and other browsers. Does Apple intentionally do that or is there something else missing in my Windows XP?

I recall that one of Howcast podcasts (Tech & Gagdets) on iTunes: How to transfer contacts from your computer to your iPhone. It mentioned that PC users can only sync from one application at a time, while Mac users can sync from multiple applications. That may explain why many Apple applications for Windows, including Safari, are not as same as the Mac original ones.

Read More...

Sunday, August 08, 2010

Update on my iPhone Development

In my past month, the progress of my iPhone application development has been very slow, almost on hold. There have been too many things happening during the time. A family of my relatives came to my house for visit. That's the main reason I cannot concentrate on my project. My evenings and weekends have been taken for meals, talks, and sight seeing (we went to Banff for the last weekend, three days).

Another reason is that I encountered an issue that I don't have clear idea how to integrate Apple's contact information into my app. I need to provide an UI for user to add and edit contact information. Apple's AddressBook framework provides APIs to access to contact database. For adding a new contact, what I need is to either to select one from address book or add new contact. The UIs AddressBook provides do not have one with a list of contact and add button. I could not find examples for my case. Therefore, I have to figure out how to reuse Apple's framework UIs instead of creating my own one.

In the past months, I have been watching WWDC10's videos as well. Now I have about only 20 left. All those videos have the most recent changes and introductions to new features/framework for iOS4. I enjoy watch most of them very much. Some of them are too far away beyond my reach, but it is good to get some sense about them. I'll continue to finish all of them.

The family has left this morning. Now I am back to normal and have time to think about my app.

Read More...

Thursday, July 29, 2010

Mac OS, iOS, XCode4 and Foundations/Frameworks

WWDC10 videos provides a wide range of detail information about Mac OS and iOS, mainly for Mac and iPhone developers. Not only did I enjoy so much learning the new frameworks and technologies from those video sessions, but I also gained better understanding of the evolution of Mac OS and iOS, as well as the foundations or building bricks behind them.

Core Building Brick of Mac OS X and iOS

When Mac OS X Snow Leopard was released on August 8, 2009, I was amazed by its great offer: to give me back hard drive space, as I made a note in my mac blog. I got 11GB space back after I updated my OS X to Snow Leopard.

I was puzzled by this result. For sure, I guess that Apple must have done tremendous work to fine-tune its core OS engine in many ways such as rewriting its OS to optimize OS frameworks. WWDC10 reveals more information on how this was done. Apple has rewritten its compiler. The Snow Leopard was built on the new compiler LLVM, and iOS on its way from GCC to LLVM. With LLVM and Clang (compiler front end for C/C++ and Objective-C), Apple said that OS X was reduced in file size by 27%.

Compiler is the fundamental tool to convert source codes are converted into a binary language that a computer machine understands. Windows or Mac OS are built by compilers. No matter what languages we use, or whatever frameworks we have a in a language, eventually they will be compiled into machine language as packages. I think that Apple is right on the key point to restructure its compiler on assembly or machine codes. It is really hard work but all the effort has tremendous water ripple effect on Mac OS and Apple's future.

According to Apple's developer web site on What's New in XCode4, LLVM compiles codes twice quickly as GCC. The new compiler generates optimized machine codes tailored to today's modern chip architecture with a set of highly optimized libraries. As a result, the compiled applications run faster than GCC's as well.

That's why I call it as Mac OS' core building brick.

XCode4

Snow Leopard OS X has been in use for about one year. It has been approved as a solid and great OS. Now it is time for Apple to move on.

XCode 3 has been used as an IDE for developing Mac and iPhone applications. WWDC10 revealed a new beta XCode4 released for paid developers. Basically XCode4 is built based on LLVM and LLDB (as debuger). The compiler has a parser part to process source codes. XCode4 utilizes the parser to provide right and accurate information about source codes with the most advanced technology and architecture built in LLVM. As a result, XCode4 provides a much better development environment and tools for developers, than the current XCode3. For me, this is a great news. I just cannot wait for its release.

Other Foundations/Frameworks

WWDC10 reveals many other new foundations/frameworks for iOS. Most of those foundations/frameworks have been available in Mac OS since the Snow Leopard release. Those foundations/frameworks provide various ways to felicitate mobile application development, some of them with lower access to Apple's hardware, and some new ones are specially for iOS such as multitasking, iAd, GameKit, and more.

Many of new features in iOS are from Mac OS X or Snow Leopard. For example, blocks are very powerful feature in Mac OX and now available in iOS. It makes multitasking much easy to implement and many other foundations/frameworks with blocks are available as they are.

EventKit.framework provides interfaces to access calendar events on devices.

The Core Motion framework (CoreMotion.framework) provides a single set of interfaces for accessing all motion-based data available on a device. The framework supports accessing both raw and processed accelerometer data using a new set of block-based interfaces.

iAd. Framework provides simple interface APIs for developers to to deliver banner-based advertisements from their applications.

Graphics and Multimedia frameworks provides APIs in either Objective-C or C level APIs to access to graphics, video and audios devices.

GameKit provides multi-player capabilities to mobile game applications with peer to peer network connectivity, using a Bluetooth connection - no pairing required. In addition to that, it provides scores and achievement rewards to users.

iOS provides over 1500 new APIs and above are just a few of them. Refer to Apple web sites for more detail information such as What's New in iOS 4.0.

Read More...

Saturday, July 17, 2010

MyLogger Class in Objective-C (4)

In this final wrap of my MyLogger class, I'll show you my experience and usages. As most Objective-C developers know, NSLog is a C function to print out messages. It is very useful, however, as it is a function, where parameters limit its usage. It possible to pass complicated parameters, however, that' may be too difficult or just impossible. In case if you want to define different storage or format for logging message, class is the way to go. That's my initial intension to define a wrapper class for NSLog: MyLogger.

MyLogger's logging engine still uses NSLog. You can easily extend it to other storage. The API provides methods to set intention and logging message with various levels.

Usage

Normally, I initialize MyLogger settings initially in main(), where you rarely make changes. This will keep all the logging messages in the same format and consistency style. The settings are default logging level, and optional indention char and format. The great advantage of strategy is that you can easily set logging level to none if you want to disable the logging feature and you would not need to remove codes from your projects all over the places.

Here is an example of MyLogger settings:

#import <UIKit/UIKit.h>
#import "MyLogger.h"

int main(int argc, char *argv[]) {

  NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  // Setup the default settings for logger
  MyLogger.defaultLoggingLevel = LogLevelDebug;
  MyLogger.indentChar = '-';

  int retVal = UIApplicationMain(argc, argv, nil, nil);
  [pool release];
  return retVal;
}

Then you are ready to use MyLogger in other places in your project. For me as a newbie of Objective-C developer, Cocoa framework and Objective-C are overwhelming to digest. It was very easy to get lost and frustrated. The way I used MyLogger is to place it in each method in my classes in a pair: logging at the first line of the method and logging at the exist point of the method, and set indent at the entry and outdent at the exit. I do get a lots debug messages; however, since all the messages are in a nice indention structured layout, it makes much easy to read and understand the flow of my class. That has been great help for me.

Another practice I have is that for each class where I want to do logging, I define a private var MyLogger* member. In the class constructor, I create the var and initialize it with a context name of that class. In this way, all the logging messages will have a clear context string to identify the messages. To set context in one place makes my logging much easy to maintain in case I need to rename my class as an example.

Taking MyClass as example, here is the way I add logging feature to the class:

// .h file
#import <UIKit/UIKit.h>
@class MyLogger;
...

@interface MyViewController : UITableViewController
  <NSFetchedResultsControllerDelegate, UITableViewDelegate> {

  ...
  @private
    MyLogger* mLogger;
  ...
}
...
@end

// .m file
#import "MyLogger.h"
...
@implementation MyViewController
...
- (id)initWithStyle:(UITableViewStyle)style {
 if (self = [super initWithStyle:style]) {
   mLogger = [[MyLogger alloc] initWithContext:@"MyViewController"];
   [[mLogger debug:@"initWithStyle: %@", style == UITableViewStylePlain ? @"Plain" : @"Group"] indent:YES];
   ...
   [[mLogger indent:NO] debug:@"initWithStyle: Done"];
 }
 return self;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  [[mLogger debug:@"numberOfSectionsInTableView:"] indent:YES];
  NSInteger count = [[self.fetchedResultsController sections] count];
  [[mLogger indent:NO] debug:@"numberOfSectionsInTableView: DONE"];
  return count;
}

- (NSFetchedResultsController*) fetchedResultsController {
  [[mLogger debug:@"fetchedResultsController"] indent:YES];
  if (mFetchedResultsController == nil) {
    ...
      [mLogger debug:@"created new NSFetchResultsConroller obj: %@"
        mFetchedResultsController];
    ...
  }
  [[mLogger indent:NO] debug:@"fetchedResultsController DONE"];
  return mFetchedResultsController;
}
...
@end

Here is a list of my loggging messages:
...
2010-07-09 16:05:28.203 ExpenseLog[2648:207] [DEBUG] MyViewController - initWithStyle: Plain
2010-07-09 16:05:28.209 ExpenseLog[2648:207] [DEBUG] MyViewController - initWithStyle: Done
2010-07-09 16:05:28.210 ExpenseLog[2648:207] [DEBUG] MyViewController - viewDidLoad
2010-07-09 16:05:28.211 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController
2010-07-09 16:05:28.211 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext
2010-07-09 16:05:28.212 ExpenseLog[2648:207] ------[DEBUG] MyViewController - creating new managedObjectContext
2010-07-09 16:05:28.212 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext DONE
2010-07-09 16:05:28.212 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext
2010-07-09 16:05:28.213 ExpenseLog[2648:207] ----[DEBUG] MyViewController - managedObjectContext DONE
2010-07-09 16:05:28.213 ExpenseLog[2648:207] ----[DEBUG] MyViewController - created NSFetchedResultsController obj: <NSFetchedResultsController: 0x8611340>
2010-07-09 16:05:28.214 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE
2010-07-09 16:05:28.215 ExpenseLog[2648:207] [DEBUG] MyViewController - viewDidLoad DONE
2010-07-09 16:05:28.215 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView:
2010-07-09 16:05:28.216 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController
2010-07-09 16:05:28.216 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE
2010-07-09 16:05:28.216 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView: DONE
2010-07-09 16:05:28.218 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView:
2010-07-09 16:05:28.218 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController
2010-07-09 16:05:28.219 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE
2010-07-09 16:05:28.219 ExpenseLog[2648:207] [DEBUG] MyViewController - numberOfSectionsInTableView: DONE
2010-07-09 16:05:28.219 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:numberOfRowsInSection:
2010-07-09 16:05:28.220 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController
2010-07-09 16:05:28.220 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE
2010-07-09 16:05:28.220 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:numberOfRowsInSection: DONE
2010-07-09 16:05:28.221 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:cellForRowAtIndexPath:
2010-07-09 16:05:28.221 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController
2010-07-09 16:05:28.222 ExpenseLog[2648:207] --[DEBUG] MyViewController - fetchedResultsController DONE
2010-07-09 16:05:28.223 ExpenseLog[2648:207] [DEBUG] MyViewController - tableView:cellForRowAtIndexPath: DONE
...

I think it is really worthwhile to spend some time design this helper class. The benefits I have been received are tremendous. It greatly helps me to understand Cocoa framework, saves me enormous amount of time, and keeps my mind sharp on the business logic I want to implement.

With new iOS available, I think this class can be further enhanced with block feature to improve the performance. For example, I could put all the messages into a block so that when a logging level is disabled, the block would not be evaluated or executed at all.

Reference

Read More...