Cordova 2.2.0 on iOS - RequireJS won't load Cordova correctly

Ganesh picture Ganesh · Nov 5, 2012 · Viewed 10.6k times · Source

I'm currently developing a webapp using Cordova (Phonegap), Backbone and JQtouch. Among other things, I need to add events in the user calendar.

Everything works fine on Android. I'm still using Cordova 2.0.0. (I didn't upgrade to the latest version). The scrolling works, navigation is OK, and I can add events in my calendar!

On iPhone, it is different. As I want my app to work on iOS 6, I got Cordova 2.2.0 on my Mac. Since then, I can't add events in the calendar anymore. It worked (on iphone) with cordova 2.0.0, but not now.

After investigation, I found out that cordova.exec() is undefined.

I searched a lot about this issue but it seems nobody, except me, met that problem for now.

Here are samples of code that are working with Cordova 2.0.0 but not with Cordova 2.2.0:

calendar.js, a calendar plug-in for Cordova. I did not write it. On Android I get the message "cordova.exec is defined" while on iOS I get the other one.

// Cordova Calendar Plugin
// Author: Felix Montanez 
// Created: 01-17-2012
// Contributors:
// Michael Brooks    

function calendarPlugin()
{
}

calendarPlugin.prototype.createEvent = function(title,location,notes,startDate,endDate)
{
    if('function' == typeof(cordova.exec)) {
        alert("cordova.exec is defined");
    } else {
        alert("cordova.exec is not defined");
    }
    cordova.exec(null,null,"calendarPlugin","createEvent", [title,location,notes,startDate,endDate]);
};

calendarPlugin.install = function()
{
    if(!window.plugins)
    {
        window.plugins = {};
    }    
    window.plugins.calendarPlugin = new calendarPlugin();
    return window.plugins.calendarPlugin;
};

cordova.addConstructor(calendarPlugin.install);

The code which call the function createEvent (it's working, as I got the previous alert)

if (confirm('Do you want to add this event in your calendar?')) 
{ 
    calendarPlugin.prototype.createEvent('<%= paramEvent_map %>', 'Geneva', 
        'Convocation', '<%= paramEvent_startDate %>', '<%= paramEvent_endDate %>'); 
}

A possible source of this problem may be the way I upgraded from Cordova 2.0.0 to Cordova 2.2.0: I just followed the tutorial "Upgrading Cordova 2.1.0 projects to 2.2.0". Should I have done "From 2.0.0 to 2.1.0" then "Form 2.1.0 to 2.2.0"?

I'd appreciate any suggestion about this, as I really don't want to restart my installation of phonegap.

On Mac, I'm working with Mountain Lion 10.8 and xCode 4.5, and I'm testing my app on iOS 4 and 6. On PC, I'm working with Aptana studio 3 and Eclipse 3.7.1, and I'm testing on Android 2.3.

--- EDIT: Reinstall Cordova 2.1.0 then upgrade to 2.2.0 ---

I just erased my project, uninstall Cordova, and redo everything "from scratch":

  • I installed Cordova 2.1.0 from their website.
  • I upgraded xcode to 4.5.2 (as recommended)
  • I created a xcode project in which I copied parts of my code.
  • I followed their tutorial to upgrade from Cordova 2.1.0 to Cordova 2.2.0
  • I set "Architectures" to "armv7, armv7s" and "Build Active Architecture Only" to "Yes"
  • I added the frameworks I needed for the calendar plug-in: EventKit and EventKitUI

Then I compiled and launch my project on my iPhone 3 (with iOS 6), and cordova.exec is still not defined!

--- EDIT: Declare my calendar plugin as a module ---

I added some code for the calendar plug-in directly in cordova-2.2.0.js (I'm desperate).

Now in cordova-2.2.0.js, I have the following lines: (I got them from here)

define("cordova/plugin/calendarplugin", function(require, exports, module) {
    var exec = require('cordova/exec');

    var calendarPlugin = function() {};

    calendarPlugin.prototype.createEvent = function(title,location,notes,startDate,endDate) {
         exec(null, null, 'calendarPlugin', 'createEvent', [title,location,notes,startDate,endDate]);
    }

    var myCalendarPlugin = new calendarPlugin();
    module.exports = myCalendarPlugin;
});

So I don't use cordova.exec() anymore, but this instead:

var exec = require('cordova/exec');

My calendar "plug-in" file now just contains the following line:

var mycalendarplugin = cordova.require("cordova/plugin/calendarplugin");

And this is how I'm using my new "module":

window.mycalendarplugin.createEvent('<%= paramEvent_map %>', 'Geneva', 
'Convocation', '<%= paramEvent_startDate %>', '<%= paramEvent_endDate %>');

And, surprisingly, the function exec() is called!

However, I got the following message: "ERROR: Attempting to call cordova.exec() before 'deviceready'. Ignoring." which should appears when "deviceReady" has not fired.

Sadly, this event NEVER fires. So my problem is slightly different now, but still remains.

--- EDIT: Comparison with Android ---

I added a few lines to listen to events, both in Android and iOS:

window.addEventListener('load', function () {
     alert("load triggered");
     document.addEventListener('deviceready', function () {
            alert("PhoneGap is now loaded!");
     }, false);
}, false);

On iOS, I get the message "load triggered" but not "PhoneGap is now loaded". After that, I still can't use exec().

On Android, I get no message at all. But I can use cordova.exec() with no problem.

--- EDIT: Redo the project from scratch ---

Instead of creating my project with cordova 2.1.0 then upgrading to cordova 2.2.0, I tried to create a sample project directly with cordova 2.2.0 then include the calendar plug-in (original version) in it.

And it works perfectly fine! With a little more code for iOS 6 (which needs an explicit autorisation from the user), I can add events in my calendar.

However, as soon as I add the rest of my project (html, css, js files), I get the same error: cordova.exec is undefined.

The responsible could be RequireJS, which may load cordova-2.2.0.js in a different way. It worked well with cordova 2.0.0 but seems not to with 2.2.0.

I'll try to see if I can load cordova-2.2.0.js before RequireJS, and still use it once cordova is loaded.

I'll keep you up to date :)

Answer

Ganesh picture Ganesh · Nov 7, 2012

Sorry to answer my own question.

It was what I thought in my last edit: RequireJS is messing with Cordova 2.2.0!

Before, I used this code to load cordova:

require.config({
  paths: {
    cordova: 'libs/cordova/cordova-2.2.0', ...

Before any of my script which use cordova, I was writing:

define([
  'jquery',
  'cordova',
  ...
], function($) { ... }

In my index.html, I had:

<script data-main="js/main" src="js/libs/require/require-jquery.js"></script>

And it worked well with cordova 2.0.0! But with cordova 2.2.0, this is just WRONG.

To resolve my problem:

I got rid of everything about cordova in the previous lines.

  • No more cordova in require.config.
  • No more cordova in the define part of my js functions.

Instead, I added just one line in my index.html:

<script type="text/javascript" src="libs/cordova/cordova-2.2.0.js"></script>
<script data-main="js/main" src="js/libs/require/require-jquery.js"></script>

And everything works fine! I can call cordova.exec() again! (Tested on iOS 4, iOS 6, and on iPhone 5).

To be honest, I don't understand very well how all of this work. I just suppose that cordova needs to be loaded before everything else (like jquery), and RequireJS is not good at doing this (or I don't know how to use it).

It was awful going through this. I'm glad it's over :)

Anyway, I hope this will be useful for someone.