Developing Titanium module for iOS

  • Tutorial
In this article, I will describe the creation of a native iOS module for Titanium . The purpose of the article is to show the basic things when creating a Titanium module, so that if necessary you are not afraid to expand / accelerate the basic functionality of Titanium.SDK. The main task of the module will be to save a pdf file with overlaying pictures on top of the pages.

Who just needs such a module - the source code .

Titanium allows you to create mobile applications in JavaScript. But unlike phonegap, it does not just wrap the html5 application in WebView, but launches a nodeJS server, on which it all spins. All this is good, but JavaScript is not so friendly with pdf. For rendering, there is pdf.js in which just on a mobile safari, not everything is smooth, for generation there is jspdfwhere, in addition to poor documentation, memory problems. The fact is that a large file is generated in RAM and then only saved, but often the OS kills the application before the file can be saved.
So, under the cut, a relatively simple way to speed up the application.

It is supposed to use Titanium SDK not lower than 3rd version. After installing titanium on your MacOS, the “titanium” command will be available in the console, this is not what we need. And we need "titanium.py" from "~ / Library / Application \ Support / Titanium / mobilesdk / osx / [SDK Version] /". Add aliases if you have not done so previously.

$ cat ~/.bash_profile 
alias ios_builder="/Users/peinguin/Library/Application\ Support/Titanium/mobilesdk/osx/3.1.3.GA/iphone/builder.py"
alias titanium.py="/Users/peinguin/Library/Application\ Support/Titanium/mobilesdk/osx/3.1.3.GA/titanium.py"

After that, you can create a module skeleton.

$ titanium.py create --platform=iphone --type=module --dir=/Volumes/yanpix_projects --name=pdfsaver --id=ti.pdfsaver

You can assemble the module with the command:

$ ./build.py

But so far there is nothing to collect. For starters, it's best to outline how you will use the module. In my case, I had the original pdf document and canvas for each of its marked pages. In some other cases, I needed a thumbnail of the first page of a pdf document.

After you decide on the required functionality, describe it in “example / app.js”.

// TODO: write your module tests here
var pdfsaver = require('ti.pdfsaver');
Ti.API.info("module is => " + pdfsaver);
var old = Titanium.Filesystem.getFile(Titanium.Filesystem.getTempDirectory(),'test.pdf');
var newpdf = Titanium.Filesystem.getFile(Titanium.Filesystem.getTempDirectory(),'export.pdf');
pdfsaver.saveInExportFileWithDrawings(
	old.resolve(),
	newpdf.resolve(),
	{
		1: 'data:image/png;base64,[base64 image representation],
		4: 'data:image/png;base64,[base64 image representation]'
	},
	1
);
var jpeg = Titanium.Filesystem.getFile(Titanium.Filesystem.getTempDirectory(),'export.jpeg');
pdfsaver.saveThumbnail(
	newpdf.resolve(),
	jpeg.resolve()
);


If in the application you can get the “Resource directory”, then in this case it is better to simply drop the files into the “Temp directory” of the application (~ / Library / Application Support / iPhone Simulator / 5.1 / Applications / [app ID] / tmp).

If you believe the documentation, then "TiPdfsaverModuleAssets.h" and "TiPdfsaverModuleAssets.m" do not make sense to change - they are frayed all the same. Your code should be written in "TiPdfsaverModule.m" and, accordingly, "TiPdfsaverModule.h". Here is the code of my functions:

#pragma Public APIs
-(void)saveThumbnail:(id)args{
	NSString *pdf = [args objectAtIndex:0];
	NSString *jpeg = [args objectAtIndex:1];
	CFURLRef url = CFURLCreateWithFileSystemPath (NULL, (CFStringRef)pdf, kCFURLPOSIXPathStyle, 0);
	CGPDFDocumentRef templateDocument = CGPDFDocumentCreateWithURL(url);
	CFRelease(url);
	CGPDFPageRef templatePage = CGPDFDocumentGetPage(templateDocument, 1); // get the first page
	CGRect templatePageBounds = CGPDFPageGetBoxRect(templatePage, kCGPDFCropBox);
	UIGraphicsBeginImageContext(templatePageBounds.size);
	CGContextRef contextRef = UIGraphicsGetCurrentContext();
	CGContextTranslateCTM(contextRef, 0.0, templatePageBounds.size.height);
	CGContextScaleCTM(contextRef, 1.0, -1.0);
	CGContextDrawPDFPage(contextRef, templatePage);
	UIImage *imageToReturn = UIGraphicsGetImageFromCurrentImageContext();
	UIGraphicsEndImageContext();
	CGPDFDocumentRelease(templateDocument);
	[UIImageJPEGRepresentation(imageToReturn, 1.0) writeToFile:jpeg atomically:YES];
}
-(void)saveInExportFileWithDrawings:(id)args{
	NSString *fresh = [args objectAtIndex:0];
	NSString *exportpath = [args objectAtIndex:1];
	NSDictionary *drawings = [args objectAtIndex:2];
	NSNumber *all = [args objectAtIndex:3];
	CFURLRef url = CFURLCreateWithFileSystemPath (NULL, (CFStringRef)fresh, kCFURLPOSIXPathStyle, 0);
	CGPDFDocumentRef templateDocument = CGPDFDocumentCreateWithURL(url);
	CFRelease(url);
	size_t count = CGPDFDocumentGetNumberOfPages(templateDocument);
	UIGraphicsBeginPDFContextToFile(exportpath, CGRectMake(0, 0, 612, 792), nil);
	for (int pageNumber = 1; pageNumber <= count; pageNumber++) {
		id image = [drawings objectForKey:[NSString stringWithFormat:@"%d",pageNumber ]];
		if(image == nil && [all boolValue] == NO){
			continue;
		}
	    CGPDFPageRef templatePage = CGPDFDocumentGetPage(templateDocument, pageNumber);
	    CGRect templatePageBounds = CGPDFPageGetBoxRect(templatePage, kCGPDFCropBox);
	    UIGraphicsBeginPDFPageWithInfo(templatePageBounds, nil);
	    CGContextRef context = UIGraphicsGetCurrentContext();
	    CGContextTranslateCTM(context, 0.0, templatePageBounds.size.height);
	    CGContextScaleCTM(context, 1.0, -1.0);
	    CGContextDrawPDFPage(context, templatePage);
	    CGContextTranslateCTM(context, 0.0, templatePageBounds.size.height);
	    CGContextScaleCTM(context, 1.0, -1.0);
	    if(image != nil){
	    	NSURL *url = [NSURL URLWithString:image];    
			NSData *imageData = [NSData dataWithContentsOfURL:url];
			UIImage *ret = [UIImage imageWithData:imageData];
	    	[ret drawInRect:CGRectMake(0, 0, templatePageBounds.size.width, templatePageBounds.size.height)];
	    }
	}
	CGPDFDocumentRelease(templateDocument);
	UIGraphicsEndPDFContext();
}

You can pass as many arguments as you like into functions and get values ​​through "objectAtIndex". Or check for an index. All parameters are passed as objects by reference.

Now to check the correct operation of the module you need to do:

$ titanium.py run

When you make sure everything is working correctly. Build through “build.py” and unzip the zip file, which is located in the root of the module, to the root of your project. Also add the tag “module” to the tag “modules” in the “tiapp.xml” file of the project.

ti.pdfsaver

The article describes only the basic things. Not a word has been said about View and Proxy. But this is enough not to get depressed when the customer asks for something that will slow down significantly on JS, but rewriting the application from scratch is already cognitive. On JS, you can quickly develop the application itself, catch exceptions, but for the application to work quickly it is worth using the native code. I think over time all the code of my application will be rewritten to objective-c which will, in the end, give the opportunity to completely abandon Titanium.

Also popular now: