Archive

Archive for October, 2013

Text to Speech in Javascript

Just a short post about an undocumented Google API, Text-To-Speech.

function sayWord(word,lang)
{
var url = “http://translate.google.com/translate_tts?ie=UTF-8&q=” + word.toLowerCase() + “&tl=” + lang;
console.log(url);
var snd = new Audio(url);
snd.play();
}

Where “Word” is the phrase to say, and lang is the accent of the speaker. For example “Chat” in french is pronounced “Shaa’.

An important “Gotya” is that in iOS, this function must only be called in response to a click event. You cannot autoplay audio.

Categories: Uncategorized

Copy Prepopulated SQLite db with Phonegap

If you have an app that needs to access a large amount of offline data, say >2Mb, then you pass what’s sensible to include a json file included in the www folder of a phonegap project, and you need to use a heftier tool, such as SQLite.

One of the first problems you hit, is the fact that Phonegap SQLite plugins such as PGSqlite or lite4cordova require you to have the database file in the /Documents folder of your app, but you can only put the file in the /www folder using xcode.

Therefore, you have to write some ios native code to copy the file from the /www folder to the /Documents folder.

So, in AppDelegate.m, scroll to the function didFinishLaunchingWithOptions

And add the following code  :

    NSLog(@”I’ve started”);
NSString *src = [NSBundle.mainBundle.bundlePath stringByAppendingPathComponent:@”www/dict3.db”];
NSLog(@”%@”,src);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dest = [documentsDirectory stringByAppendingPathComponent:@”dict3.db”];
NSLog(@”%@”,dest);
NSFileManager *manager = [NSFileManager defaultManager];
NSError *error;
[manager copyItemAtPath:src toPath:dest error:&error];
if(error)
{
NSLog(@”%@”,[error localizedDescription]);
}

What this does, is that it gets the path to the source file, in my case /www/dict3.db, and a reference to the destination location, which is /Documents/dict3.db then uses NSFileManager to copy the file from one location to another.

Now, that would be all fine, apart from a nasty, that comes in the form of iCloud. It breaks apple’s data storage terms if you put large amounts of data in the Documents folder, that isn’t user-generated. Since apple will try to back this up on the iCloud, and it shouldn’t need to.

So, with a function that I unashamedly took from Stack Overflow (Sorry stack overflow, I still appreciate the trip to San Francisco that you sent me on!)…

– (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = “com.apple.MobileBackup”;
if (&NSURLIsExcludedFromBackupKey == nil) {
// iOS 5.0.1 and lower
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
} else {
// First try and remove the extended attribute if it is present
int result = getxattr(filePath, attrName, NULL, sizeof(u_int8_t), 0, 0);
if (result != -1) {
// The attribute exists, we need to remove it
int removeResult = removexattr(filePath, attrName, 0);
if (removeResult == 0) {
NSLog(@”Removed extended attribute on file %@”, URL);
}
}

// Set the new key
return [URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}

then add the call it like so:

 // Anti-iCloud.
NSURL *destUrl = [NSURL fileURLWithPath:dest];
NSLog(@”%@”,destUrl);
[self addSkipBackupAttributeToItemAtURL:destUrl];

NSLog(@”Good to go!”);

You can use terminal to navigate to the paths shown in the console output, to see that the file, has in fact been copied.

Then, we can open up index.html, and write some JavaScript to see that the database is working.

  document.addEventListener(“deviceready”, init, false);

function init()
{
console.log(“Device Ready.”);
var db = window.sqlitePlugin.openDatabase({name: “dict3.db”, bgType:1});
console.log(“Database open”);
db.transaction(function(tx){
console.log(“Transaction started”);
tx.executeSql(“select ru from dictionary where word=’HELLO’;”,[], function(tx,res){
console.log(res.rows.item(0).ru);
});
});
}

This opens the database using the sqlLitePlugin, creates a transaction, then runs a simple select against the database. Obviously, your SQL statement will be different from mine.

This took me hours to figure out, so I hope it comes in useful to someone!

Categories: Uncategorized

Error Domain=NSCocoaErrorDomain Code=256

Image

I was working on a proof of concept app for Apple APS notifications, and happy to have the basics working.  The next step I had was to send the device token to my server, so that it could be stored in a database, and matched up with a user account.

So, I wrote a simple ASPX page that took in the querystring value “token” and stored it in the database (10 minutes work). then I wanted to write some Objective C code, to add to the didRegisterForRemoteNotificationsWithDeviceToken event so that it could call my URL, with the assigned device token.

A simple way of making a Syncronous HTTP request in Objective C is

NSError* error; // this is not required, but good to have

NSString* apiUrlStr = [NSString stringWithFormat:@”http://dev.testsite.com/test.json”%5D; // First create an NSString with the link that you need to query:

NSURL* apiUrl = [NSURL URLWithString:apiUrlStr]; // Convert the String to an NSURL.

NSString *apiResponse = [NSString stringWithContentsOfURL:apiUrl encoding:NSASCIIStringEncoding error:&error];

But, as soon as I tried this:

NSString* apiUrlStr = [NSString stringWithFormat:@”http://www.website.com/aps/notify.aspx?token=%@”,deviceToken];

I got this error in the console window

013-10-17 18:39:27.975 PushChat[136:307] My token is: <5214df32 2acfa9eb 57aafaa1 29417e30 f76a2c3d e3591538 975e5623 e091af36>
2013-10-17 18:39:28.048 PushChat[136:307] Url called is: http://www.website.com/aps/notify.aspx?token=<5214df32 2acfa9eb 57aafaa1 29417e30 f76a2c3d e3591538 975e5623 e091af36>
2013-10-17 18:39:28.191 PushChat[136:307] Api response: (null)
2013-10-17 18:39:28.255 PushChat[136:307] Any Error: Error Domain=NSCocoaErrorDomain Code=256 “The operation couldn’t be completed. (Cocoa error 256.)” UserInfo=0x144df0 {}

The simple solutions offered online were

A. Your phone has no Wifi connection. (DUH)

B. If connecting to HTTPS:// the certificate may be bad.

Neither were the case, so, applying some logic, I thought that perhaps it didn’t like the non-url-encoded querystring, with spaces and angle brackets. Which, ironically, needed to be stripped off at the server anyway, so I added the code:

NSString* strToken = [NSString stringWithFormat:@”%@”, deviceToken];

strToken = [strToken stringByReplacingOccurrencesOfString:@” ” withString:@””];

strToken = [strToken stringByReplacingOccurrencesOfString:@”<” withString:@””];

strToken = [strToken stringByReplacingOccurrencesOfString:@”>” withString:@””];

And, it worked perfectly!

My first bit of real problem solving in Objective C. Very happy 🙂

Categories: Uncategorized

Installing previous iOS SDKs for XCode

Everybody wants to program with the latest and greatest iOS SDK don’t they?, or, perhaps you want to support the millions of people still using old iOS devices? Dust off that old iPhone 3G, and here is how to manually install a specific older version of an iOS SDK without re-installing XCode.

1. Download XCode and iOS SDK 4.2 from https://developer.apple.com/downloads/index.action

2. Mount the DMG by double clicking on it

3. Open a terminal window and navigate to /Volumes/XCode and iOS SDK/Packages/

4. Copy the *.pkg files to a folder on your desktop (cp *.pkg /users/you/Desktop/sdk)

5. Navigate to your new folder /users/you/Desktop/sdk

6. Unarchive the pkg file for the required iOS version, using xar -xf iPhoneOSxxx.pkg

7. Double click on the Payload file to unzip it.

8. Navigate to the iPhoneOS folder, and copy this to /Developer/Platforms/iPhoneOS.platform/Developer/SDKS/

9. Restart XCode

10. Select your new iOS version under Base SDK in project settings.

Categories: Uncategorized

Open Map from BlackBerry Webworks

I do find that the BlackBerry invocation framework poorly documented, and confusing, since the format changed completely between the Playbook/Smartphone version and BB10 version, however, after a bit of searching and trial and error, I discovered how to open the native maps app using BlackBerry Webworks Invocation framework

 

blackberry.invoke.invoke({
action :”bb.action.MAP”,
type: “application/vnd.blackberry.string.address”,
data: “Paris, France” }
, onInvkeSuccess, onInvokeError);

 

Where onInvkeSuccess and onInvokeError are javascript functions that get called if things work, or don’t work.

This code applies to BB10 only.

 

 

Categories: Uncategorized

Microsoft Kills Nokia’s Symbian

Dear Nokia Developer,

 

With the growing business opportunities available on the Asha and Windows Phone platforms, we have been reviewing our developer content programs to see how we can maximize our support to you, our developers. As a result of this review, we have decided to focus our support and investment in new content toward Asha and Windows Phone. Over the next few months we will be transitioning our active developer support away from Symbian and MeeGo.

 

If you have Symbian and MeeGo content in the Nokia Store, it will continue to be available for download to customers, and you will continue to receive download and revenue reports as well as payouts for downloaded content. However, starting January 1, 2014, you will no longer be able to publish any new content or update existing content for Symbian and MeeGo.

 

We are very excited about the opportunities available with Asha and Windows Phone, and hope that you will bring your talents to these platforms. We believe that these changes will help improve our ability to support you as you develop fantastic apps for your customers.

 

Best regards,

The Nokia Developer Team

Categories: Uncategorized