Load image from URL into UIImageView

May 7th, 2008
NSString* mapURL = [theURLString stringByReplacingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
NSData* imageData = [[NSData alloc]initWithContentsOfURL:[NSURL URLWithString:mapURL]];
 
UIImage* image = [[UIImage alloc] initWithData:imageData];
[imageView setImage:image];
[imageData release];
[image release];

Asynchronous callbacks for copying large files

May 3rd, 2008

Not really Cocoa APIs but useful none the less.
http://www.cimgf.com/2008/05/03/cocoa-tutorial-file-copy-with-progress-indicator/

April 28th, 2008

Uli Kusterer writes:

“Sometimes, you need to draw text with more control than an NSTextField or NSTextView will let you do, and sometimes you need better performance than the NSStringDrawing category will provide. And maybe you need to draw text into a CGContext or even inside a Carbon application.

You may be thinking about CoreText right now, and how unfortunate it is that you still have to maintain compatibility with Mac OS X 10.4 “Tiger”, but there’s an easier way:”

http://www.zathras.de/angelweb/blog-cocoa-text-system-everywhere.htm

Plugin API in your Cocoa Application

April 21st, 2008

In this episode of Late Night Cocoa Scotty interviews Jesse Grosjean of Hog Bay Software about Plug-In API architectures and look at Jesse’s Blocks Framework.

Thanks Scotty and Jesse for this interview. Pretty much confirmed my ideas about how to create a plugin architecture but also gave me some new ideas like allowing plugins to be extended by other plugins as well.
Late Night Cocoa
Plugin API article
Apple Docs for loading code

Link to libxml in Cocoa for iPhone development

April 20th, 2008

libxml on the iPhone

SQLite3 in your Cocoa application

April 16th, 2008

I haven’t found a whole lot about integrating SQLite3 databases in a Cocoa application.  There’s no native Objective-C API for SQLite except for Core Data.  Core Data may be good but if you want to access the database in your own way you will want to program directly to the SQLite APIs.

 

You will be programming in C (unfortunately?) but the API is very easy and integrating it into your Cocoa application can be as easy as writing your own helper wrappers around the C API.

 The examples I’ll be talking about in this post assume the following database schema:

CREATE TABLE volumes('volumeID' INTEGER PRIMARY KEY AUTOINCREMENT, 'volumeName' TEXT, 'indexed' INTEGER(1));
CREATE TABLE files('volumeID' INTEGER, 'fileName' TEXT, 'fileType' TEXT, 'fileExtension' TEXT, 'fileSize' INTEGER, 'creatorCode' TEXT, 'modDate' TEXT, 'fullPath' TEXT);
CREATE INDEX fileNameIndex ON files(fileName);
CREATE INDEX fullPathIndex ON files(fullPath);

 

If you are on Leopard you are in luck because SQLite is built in.  You have command line access to the sqlite3 binary in your $PATH.  Check man sqlite3 for detailed instructions.  Using the above schema, you can create your database in the command line by using the command:

cd your_destination_dir
sqlite3 yourdatabase.extension

Change the above name and extension you want to use for the database. The extension doesn’t matter. Copy and paste in your schema and hit enter. Now you have your database created. Type “.quit” to quit the program when you’re done.

 

In the Finder, you will have to type Command+G and then type in /usr/lib/

Alternatively, if you are using XCode 3.x and using the default install location for the tools, you can go to /Developer/SDKs/MacOSX10.5.sdk/usr/lib/
or
/Developer/SDKs/MacOSX10.5.sdk/usr/lib/
Drag the file libsqlite3.0.dylib into your “Other Frameworks” group in XCode and answer yes to copy to destination folder if needed.  This will copy the libsqlite3.0.dylib file to your project directory.  Probably not needed but it doesn’t hurt anything.

An example function you can write to open a database is this:

 

-(BOOL)openDatabaseAtPath:(NSString*)path
{
 NSFileManager *fileManager = [NSFileManager defaultManager];
 //check if file is already there
 if([fileManager fileExistsAtPath:path])
 {
 	if(sqlite3_open([path UTF8String], &db) == SQLITE_OK)
 	{
 		NSLog(@"Database was opened at %@", path);
 		dbOpened = YES;
 		return YES;
 	}
 }
 
NSLog(@"Database not found at location %@", path);
 return NO;
}

 

This code will take an NSString* which is the full path to your database file.  The reason you want to check if the file exists is because the sqlite3_open function will create the file if it doesn’t exist.  This is probably not what you want, so first you may want to check if the file is there before opening it.

 

Example code to do a SELECT on a table:

 

-(int)volumeIsIndexed:(NSString*)volumnName
{
 if(!dbOpened)
 	return NO;
 
const char *sql = [[NSString stringWithFormat:@"SELECT volumeID FROM volumes WHERE volumeName ='%@'", volumnName] cStringUsingEncoding:NSUTF8StringEncoding];
 sqlite3_stmt *statement;
 BOOL ret;
 
if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK)
 {
 	int result = sqlite3_step(statement);
 	if(result == SQLITE_ROW)
 		ret = sqlite3_column_int(statement, 0);
 	else
 		ret = NO;
 
}
 sqlite3_finalize(statement);
 return ret;
}

Example code to do an INSERT on your table:

 

-(int)addVolumeToDatabaseWithVolumeName:(NSString*)name
{
 if(!dbOpened)
 	return -1;
 int volumeID = [self volumeIsIndexed:name];
 
//volume is already in the database so just return the primary key for it
 if(volumeID > 0)
 	return volumeID;
 
const char* sql = [[NSString stringWithFormat:@"INSERT INTO volumes(volumeName, indexed) VALUES('%@', 1);", name] cStringUsingEncoding:NSUTF8StringEncoding];
 sqlite3_stmt *statement;
 if (sqlite3_prepare_v2(db, sql, -1, &statement, NULL) == SQLITE_OK)
 {
 	int result = sqlite3_step(statement);
 	sqlite3_finalize(statement);
 	if(result != SQLITE_ERROR)
 		return sqlite3_last_insert_rowid(db);
 }
 return -1;
 
}

 

As you can see, integrating SQLite3 in your Cocoa application is not that hard.  Unfortunately at the time of this writing there’s not a lot that I could find of integrating it into your Cocoa code.  Really, because Cocoa is a super set of C, you are not doing anything special with this.  But this will give you an example of how easily you can integrate your own embedded database with your application without having to go through Core Data.

Test from iPhone

April 12th, 2008

testing with iPhone

How to call a simple SOAP web service in Cocoa

April 10th, 2008

Note: This example web services uses the nusoap helper classes to do all the back end soap generation for me. The included php file requires this to work. Download here: nusoap

I’m now hosting the whole web service minus the sending of the SMS message since it costs money.  Point your SOAP client to Web service

Updated WSDL at WSDL

1. First you need to have access to your WSDL file if available. This will make your job much much easier later on. Presumably you have the developer tools installed. Go into Terminal.app and change directory(cd) to your project directory.  If you’re using my above code and you want to use your own web server, you MUST download nusoap and put the whole folder in the same directory as the php file that implements the web service.  Also, if you’re going to generate your own stub files from my WSDL file you can change the URL to your own server by changing the line <soap:address location=”http://localhost/webservice/se411webservice.php”/> to <soap:address location=”http://www.yourhost.com/yourdirectory/se411webservice.php”/>

2. Execute /usr/bin/WSMakeStubs -x Obj-C -name “WebService” -url http://www.yourhost.com/webservice.wsdl
This assumes that you have installed XCode 3.x and installed the command line tools. If not then WSMakeStubs will be in DEVELOPER_DIR/usr/bin where DEVELOPER_DIR is where you installed XCode and the related IDE applications to.

In the above command, change your URL to that of your WSDL file which can also be(I think) just the path on disk to the file as well.  Also, change your -name argument to be whatever you want your Objective-C files to be called.  Using “Webservice” will output “Webservice.h” and “Webservice.m”.

3. Now you have two Objective-C files that you can add to your XCode project.  I assume you know how to do this.

4.  Check the attached WSDL file to check the functions that it implements.  Also check the example php file that implements the web service.  Example SOAP web service WSDL file     Example PHP SOAP implementation

5.  The attached web service implements a function called getAirportInformationByAirportCode.  In your newly generated .h and .m files you will have a class for each function defined in the WSDL.  The getAirportInformationByAirportCode function takes a single parameter that is a 3 character airport code like “SFO”, “YXU”, etc..  

6.  All you have to do now in your Objective-C code is the following:

 

getAirportInformationByAirportCode* webservice = [[getAirportInformationByAirportCode alloc] init];
 [webservice setParameters:[NSDictionary dictionaryWithObject:[airportCode stringValue] forKey:@"airportCode"]];

This creates your web service and sets the parameters.  Your parameter can be from an NSTextField or whatever input you want.

7.  In your WSMakeStubs generated files, look for the functions -(id)resultValue and change it to this if you would like which will return the entire result to your code when you call it:

 - (id) resultValue
{
    return [super getResultDictionary];//return [[super getResultDictionary] objectForKey: @"parameters"];
}

The reason I do this is because then in your calling code you can just do this:

 NSDictionary* results = [[webservice resultValue] objectForKey:@"/Result"];

The commented out code would return nothing unless your result gets returned in a very specific format.  My implementation just returns a raw SOAP message and the generated code doesn’t seem to like that.  So commenting out the code in the resultValue function allows you to receive the entire result back and you can do what you please after wards.

How to set up a simple ripple transition filter in Cocoa

April 10th, 2008

This code will show you how to do a Core Animation ripple transition between two views.
This code does not take into account any memory management so make sure to do proper releases where appropriate or use garbage collection.

First, download these images that you will use in your project:
Shine image Transition Mask Image

You should add these two images into the Resources group in your XCode project.

You need to load these two images in your application. I suggest to do it in the awakeFromNib function.
You should define your images somewhere, preferably in your header file:

	CIImage         *inputShadingImage;         // an environment-map image that the transition filter may use in generating the transition effect
    CIImage         *inputMaskImage;            // a mask image that the transition filter may use in generating the transition effect

Load the images:

NSBundle *bundle = [NSBundle bundleForClass:[self class]];
	// Preload shading bitmap to use in transitions (borrowed from the "Fun House" Core Image example).
    NSData *shadingBitmapData = [NSData dataWithContentsOfFile:[bundle pathForResource:@"restrictedshine" ofType:@"tiff"]];
    NSBitmapImageRep *shadingBitmap = [[[NSBitmapImageRep alloc] initWithData:shadingBitmapData] autorelease];
    inputShadingImage = [[CIImage alloc] initWithBitmapImageRep:shadingBitmap];
 
    // Preload mask bitmap to use in transitions.
    NSData *maskBitmapData = [NSData dataWithContentsOfFile:[bundle pathForResource:@"transitionmask" ofType:@"jpg"]];
    NSBitmapImageRep *maskBitmap = [[[NSBitmapImageRep alloc] initWithData:maskBitmapData] autorelease];
    inputMaskImage = [[CIImage alloc] initWithBitmapImageRep:maskBitmap];

Set up a custom animation

CATransition* animation = [[CATransition alloc] init];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
[animation setDuration:1.0];



Second, define the Core Image filter to use for the transition

CIFilter *rippleFilter = [CIFilter filterWithName:@"CIRippleTransition"];
        [rippleFilter setDefaults];
		[rippleFilter setValue:[CIVector vectorWithX:(rect.size.width/2.0) Y:(rect.size.height/2.0)] forKey:@"inputCenter"];
		[rippleFilter setValue:[CIVector vectorWithX:rect.origin.x Y:rect.origin.y Z:rect.size.width W:rect.size.height] forKey:@"inputExtent"];
		[rippleFilter setValue:inputShadingImage forKey:@"inputShadingImage"];
		[animation setFilter:rippleFilter];




Lastly, you need to set the animation onto the view that you want to use it for. In this example I am using an NSBox that contains an IKImageView. When I swap the IKImageView with a new IKImageView that has a different image in it, the transition between it will be the above ripple animation/transition.

//imagewell1 is IKImageView
NSView *holder = [imagewell1 superview];
[holder setAnimations: [NSDictionary dictionaryWithObjectsAndKeys:animation, @"subviews", nil]];
[[holder animator] replaceSubview:imagewell1 with:imagewell2];
[animation release];