Sunday, September 18, 2011

Xcode snippet #2: Archiving objects with NSKeyedArchiver

Archiving objects with NSKeyedArchiver is the process of saving your objects to a binary file. This process is called encoding. In this step-by-step tutorial we will persist a settings object to a file, and bring it back to life using NSKeyedUnArchiver. Archiving objects is very convenient way for saving small amounts of data like settings in your iPhone app.

Step 1: Comply with the NSCoding protocol
The first thing we have to do is to let our object comply with the NSCoding protocol. This protocol adds encoding and decoding methods to our object. Complying is easily done by adding to the object like this:
@interface Settings : NSObject<NSCoding> {
}
Step 2: Implement the encodeWithCode and initWithCoder methods
We have to tell the NSKeyedArchiver object how our object should be encoded. Simply said tell it what our properties are and what types they are etc.

This is done the encodeWithCoder method (as implemented by the NSCoding protocol), here under you see an example for an object, boolean and int property.
- (void)encodeWithCoder:(NSCoder *)coder {
        [coder encodeObject:self.name forKey:@"name"];
	[coder encodeBool:self.newsLetter forKey:@"newsLetter"];
	[coder encodeInt32:self.count forKey:@"count"];    
}

To unarchive our object we have to implement the initWithCoder method: (Make sure that the keys in both the encodeWithCoder and initWithCoder are the same for each property)

-(id)initWithCoder:(NSCoder *)coder {
	if ((self = [super init]))
	{
          self.name = [[coder decodeObjectForKey:@"name"] retain];  
          self.newsLetter = [coder decodeBoolForKey:@"newsLetter"]; 
          self.count = [coder decodeInt32ForKey:@"count"];    
 	}
	return self;
}

That is pretty much all we have to do before we can archive and unarchive.

Step 3: Implement the archive and unarchive methods
What is left is to make two methods for un- and archiving. The saveSetting methods has two parameters, namely settings, the object to archive and the name for the file we want to save it to. With help of the getFullFilePath method we get a full file path to the Document directory of our device. To bring our object back to live use the loadSettings method.
-(NSString*) getFullFilePath:(NSString*)name
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains
       (NSDocumentDirectory, NSUserDomainMask, YES);
	NSString *savePath = [paths objectAtIndex:0];
    NSString *theFileName = [NSString stringWithFormat:@"%@.setting", name];   
    return [savePath stringByAppendingPathComponent: theFileName];
}

-(void)saveSetting:(Settings *)settings name: (NSString *)name
{
    NSString *filePath = [self getFullFilePath:name];
    NSData *theData = [NSKeyedArchiver archivedDataWithRootObject:settings];
    [NSKeyedArchiver archiveRootObject:theData toFile:filePath]; 
}

-(Settings*)loadSetting:(NSString *)name 
{
    NSString *filePath = [self getFullFilePath:name];
    
    NSData *theData = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    Settings *settings = [NSKeyedUnarchiver unarchiveObjectWithData:theData];
    return settings; 
    
}
Saving arrays of objects
It is also very easy to save lists of objects to file. Suppose we have a list with Settings and want to store them in one file. You can achieve this by archiving a NSMutableArray. For example:
// Archiving
NSMutableArray *array = self.self.settingsList;
[NSKeyedArchiver archiveRootObject:array toFile:
                      [savePath stringByAppendingPathComponent: theFileName]];

//UnArchiving
NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithFile:
                      [savePath stringByAppendingPathComponent: theFileName]];
if ([array count] > 0) {
    self.settingsList = array;
    }

Conclusion, the NSKeyedArchiver and NSKeyedUnarchiver are pretty easy and straight forwarded to use for persisting objects. It is very handy for saving small objects likes settings etc.
More information:
NSKeyedArchiver Class Reference
NSCoding protocol reference

Monday, April 04, 2011

Xcode snippet #1: Reading the contents of a directory

Below you will find a code snippet which reads the contents of a specific directory (in this case the documents directory) from your device. It looks for file with has a suffix .list, stripes the filename from it with the substringToIndex method and adds that name to another array.

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0]; 
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsPath error:nil];
for (NSString *dir in dirContents) {
  if ([dir hasSuffix:@".list"]) {
      NSRange range = [dir rangeOfString:@"."];
      NSString *name = [dir substringToIndex:range.location];
      if (![name isEqualToString:@""]) {
          MyListItem *listItem = [[MyListItem alloc] init];
          listItem.name = name;
          [lists addObject:listItem];
          [listItem release];
      }
  }
} 

Wednesday, October 27, 2010

Developing for the iPad

The past months we have been busy with a new challenge: Developing for the iPad.
I can tell you that developing for the iPad is a total different experience compared to Delphi and Visual Studio. Never the less it is great fun! Besides the fact that it is a fun platform to work on it is even more fun to learn a new platform, language and IDE.

The tools: XCode and Interface builder
We decided to go the hard way and develop with Apples native IDE XCode. As .NET developer an easier approach would be to choose for a Mono solution like MonoTouch, however we felt that we had to know 'the metal' first.
The main tools you use developing for iPad are XCode and Interface builder. XCode is a good to work with IDE, however compared to Visual Studio it lacks some features. (Must say I still find new features, even today). One annoying point of XCode is that it opens every file in a new window (when double clicking on a file) so after a while it can be hard to seperate the trees from the forest with all that open files. However if you click once you can keep them in one window but for some reason that is hard to persist.
A big plus for XCode, in my opinion, is that it helps you to work in the MVC design pattern way which gives you clean code.
XCode does not offer an integrated form designer like VS does instead you make your views (forms) with Interface builder.

The language Objective-C
The language, Objective-C, looks at first very weird with all that brackets but there are great resources online that helped us to get started.

If you would like to start iPad development take a look at some resources we gathered along the way:
Apples developer website (Get yourself a free account)
Stanford University lectures on iTunes U (Highly recommended!!!!)
Cocoa Dev Central - Learn Objective-C

It is likely that I will write some more blogposts about this new adventure in the future.

Tuesday, August 17, 2010

Fooling around with TWebbrowser compilation post

In the statistics of my blog I find a lot of people searching for Delphi’s TWebbrowser solutions and examples. I wrote a few post about some TWebbrowser specific problems and possible solutions.
Because they are in seperated, non related, posts I thought it would be nice to compilate all the knowledge in one blogpost. Well here it is. :)

Fooling around with TWebbrowser #1
Describes how to use TWebbrowser for editing HTML

Fooling around with TWebbrowser #2
Describes how to retain the browser session within you application

Fooling around with TWebbrowser #3
Describes some TWebbrowser basics like how to get the right popwindowsize, how to fill up a combobox with visited websites, how to enable/disable navigate buttons etcetera.

Fooling around with TWebbrowser #4
Describes two solutions for the Enter key problem.

Posting data with TWebbrowser
Describes how to post data to websites using TWebbrowser

Saving TWebbrowser content with IPersistFile
Describes two ways to save the content of the current loaded document in the browser.

Two way interaction with Javascript in Winforms using Webbrowser
Although about Winforms C# this should be compatible with Delphi TWebbrowser.

Enjoy!

Wednesday, May 19, 2010

Using Extension methods on Lists to make it fluent

Since C# 3.0 you can write extension methods on any class that you want, even if you don’t have the source code of the class. In other words you can extend a class with your own methods.

Extension methods can only be declared in static classes as static methods.

Extensions on List<T> classes can be very handy and make your code more readable and fluent.
Suppose you have this mixed Animal list with Dogs and Cats from this blogpost.

List<Animal> animalList = new List<Animal>(); 
animalList.Add(new Dog("Dog1", ConsoleColor.Red));
animalList.Add(new Dog("Dog2", ConsoleColor.Green));
animalList.Add(new Dog("Dog3", ConsoleColor.Red));
animalList.Add(new Cat("Cat1", ConsoleColor.Black));
animalList.Add(new Cat("Cat2", ConsoleColor.Black));
animalList.Add(new Cat("Cat3", ConsoleColor.Black));

(Never mind the ConsoleColor for the animal ;-) )

Suppose we want all the red dogs from the animalList, we could write our code like:


//Get the Red docs

List<Dog> redDogList = animalList.OfType<Dog>().Where(n => n.Color == ConsoleColor.Red).ToList();

This works, however everytime we want to get some colored animal we would get the same type of code. We would probably write some static methods to avoid this :


 List<Dog> redDogList = ListUtils.GetTheDogs(animalList);


Even better is to make this static extension methods so that the functionality looks to be encapsulated by the list its self. We can then also make the method names more readable, for instant not “GetTheDog”, but “WhichAreDog”.


For example:
public static List<Animal> WhichAreDog(this List<Animal> animalList)
{
  return animalList.OfType<Dog>().Cast<Animal>().ToList();
}



public static
List<Animal> HavingColor(this List<Animal> animalList, ConsoleColor color)
{
  return animalList.Where(n => n.Color == color).ToList();
}

The “this” before the first parameter of the method indicates that this is an extension method on this parameter, in this case the list with animals.


Now getting the red dogs looks like this:


List<Animal> allRedDogs = 
animalList.WhichAreDog().HavingColor(ConsoleColor.Red);

Much more readable and fluent!

Monday, May 03, 2010

Exploring LINQ #3

LINQ keeps amazing me in its power and beauty. So much that for each problem I have to solve (coding problem that is)  I find myself wondering can this be done with LINQ, and how.

I am working on a rewrite of an old application to C#. For some reason the original designers had put some ‘model’ data into a none relational database. Instead of making a good relational data model the data has been put as follows (simplified record from the database) :

Option1 = “1;2;3”
Option2 = “A;B”
Value = 1

In other words foreach possible combination of option1 and option2 there is a value. (1-A-1, 1-B-1, 2-A-1 etc.) I would prefer a relational set up, but to honor the past this will have to stay that way. For use in our program it  is required to have all possible combinations into a single flat list which we then can query with LINQ.
Of course it easy to construct some code that would knock this down into a flat list though I don’t think any will be as elegant and as fast to produce as this LINQified solution.

Suppose we read the raw data from the database into a list with raw objects looking like this:

List<Raw> rawList = new List<Raw>();
rawList.Add(new Raw() { Option1 = "1;2;3",
Option2 = "A", Value = 1 });
rawList.Add(new Raw() { Option1 = "4;5",
Option2 = "A;B", Value = 2 });
rawList.Add(new Raw() { Option1 = "1;2;3",
Option2 = "C", Value = 3 });

Using LINQ it is easy to combine the original raw list with the “;” splitted list (arrays in fact) from the fields Option1 and Option2. From that a flat list can be constructed which holds all the combinations:


List<Raw> flatList = (from n in rawList
from opt1 in n.Option1.Split(';')
from opt2 in n.Option2.Split(';')
select new Raw
{
Option1 = opt1,
Option2 = opt2,
Value = n.Value
}).ToList();
flatList.ForEach((n) => Console.WriteLine(n.Option1 +
"-" + n.Option2 +
"-" + n.Value.ToString()));

Resulting in a ‘flat’ list:


1-A-1
2-A-1
3-A-1
4-A-2
4-B-2
5-A-2
5-B-2
1-C-3
2-C-3
3-C-3


Querying this list is now child’s play:

int Result = (from n in flatList
              where (n.Option1 == "4") && (n.Option2 == "B")
              select n.Value).FirstOrDefault();

Tuesday, March 09, 2010

Two way interaction with JavaScript in Winforms using Webbrowser

In a recent project I had to integrate Google maps into a Winforms application. To use Google Maps from a desktop client you must be able to call the Javascript in webpage, which can be done with the Webbrowser component.

Calling JavaScript from C#
Calling JavaScript in a Webbrowser component is very easy. JavaScript is exposed through the IHTMLWindow2 interface which can be referenced from the IHTMLDocument2 interface through the parentWindow property.

doc = webBrowser1.Document.
DomDocument as IHTMLDocument2;
doc.parentWindow.execScript("createMapMarker
("52.3738007, 4.8909347", 1);", "JavaScript");

Calling C# from JavaScript
Calling C# code from JavaScript can be done using the ObjectForScripting property of the Webbrowser component. The object can be called in JavaScript using window.external. The communication is established through COM interop so the class should be visible for COM.
//Class example
[ComVisible(true)]
public class ExternalApplication
{
public string GetApplicationName()
{
return "The application";
}
}
//Connect it to the webbrowser
webbrowser1.ObjectForScripting
= new ExternalApplication();

From JavaScript call the external application, our C# app, like this:

(Google maps click event example)
function createMapMarker(lat, lng, html) {
var point = new GLatLng(parseFloat(lat),parseFloat(lng));
var marker = new GMarker(point);
GEvent.addListener(marker, "click", function() {
var appname = window.external.GetApplicationName();
alert(appname);
});
map.addOverlay(marker);
return marker;
}

For Delphi equivalent code take a look at Steve Trefethen blog serie about Google Maps / Delphi integration here.