W3C home > Mailing lists > Public > public-device-apis@w3.org > June 2010

Re: API patterns and requirements

From: Wojciech Masłowski <wmaslowski@opera.com>
Date: Fri, 18 Jun 2010 12:41:12 +0200
Message-ID: <4C1B4D48.7060109@opera.com>
To: Rich Tibbett <rich.tibbett@gmail.com>
CC: public-device-apis@w3.org
W dniu 2010-06-17 16:23, Rich Tibbett pisze:
> I've been thinking about the general interface patterns that we are 
> using in the some of the APIs, particularly with a focus on Contacts 
> and System-Info.
>
> On the one hand there is a requirement that API calls be asynchronous. 
> On the other hand, in order to ensure a reasonable user experience 
> (i.e. don't bombard the user with more than one permission request at 
> a time) I think we should include a requirement for serial access to 
> each API (or collectively, all APIs) - that no more than one API call 
> should be executable concurrently. I'd like to explore if this is a 
> reasonable assumption for e.g. Contacts and whether any changes to the 
> pattern used may be required for this.
>
> The following pattern, as used in the IndexedDB specification [1], 
> would be applied to System-Info and Contacts APIs:
>
> /_SystemInfo API example:_/
>
> navigator.system.request.onsuccess = function() {
>     var batteries = navigator.system.request.result;
>     for(i in batteries) alert(batteries[i].level);
> };
> navigator.system.get('Power');
>
>
> /_Contacts API example:_/
>
> navigator.contacts.request.onsuccess = function() {
>     var contacts = navigator.contacts.request.result;
>     // do something with contacts
>     for(i in contacts) alert(contacts[i].displayName);
> }
> navigator.contacts.request.onerror = function{
>     var error = navigator.contacts.request.error;
>     alert("[" + error.code + "]" + error.message);
> }
> navigator.contacts.find(['displayName', 'addresses'], {multiple: true, 
> limit: 3});
>
>
I don't really think that mandating that only one call is going to help 
with a problem of annoying user with security requests in any way. 
Consider the following use case: web app wants to get power and network 
info. With your suggested solution it would look like that.

navigator.system.request.onsuccess = function() {
     doSomethingWithBatteries(navigator.system.request.result);
     navigator.system.request.onsuccess = function() {
         doSomethingWithNetworks(navigator.system.request.result);
     };
     navigator.system.get('Network'); // triggers 2nd permission request
};
navigator.system.request.onerror = function() {
     // even if we don't get access to Power we want to try to get 
Network info
     navigator.system.request.onsuccess = function() {
         doSomethingWithNetworks(navigator.system.request.result);
     };
     navigator.system.get('Network'); // triggers 2nd permission request
};

navigator.system.get('Power'); // triggers 1st permission request

If we allow concurrent request the code looks like this

navigator.system.get('Power', doSomethingWithBatteries ); // triggers 
1st permission request
navigator.system.get('Network' doSomethingWithNetworks); // triggers 2nd 
permission request

In both examples the user will be asked for permission twice. The first 
one is much more complicated.

Additionally with your solution we have to specify what happens in this 
case :

navigator.system.request.onsuccess = fun1
navigator.system.get('Network');
navigator.system.request.onsuccess = fun2

will fun1 be called or fun2?

> This pattern provides two additional benefits over the current APIs:
>
> 1. only one async method (e.g. in the Contacts API this includes 
> find(), save() and remove()) can be requested at any one time (i.e. 
> serial access only).
I don't see how mandating only one async call gives advantage over 
possibility of many concurrent calls. In both cases developer will spam 
user with permission requests if he requests data too often.
> 2. the 'result' and 'error' objects become a permanent well-known 
> fixture on the DOM (@ navigator.contacts.request for Contacts and 
> navigator.system.request for SysInfo). A common DOM node is useful 
> if/when integrating such APIs with HTML <input...> elements (very much 
> in a similar vein to the File API [2]).
>
> In an ideal world a Contact input element would be implemented as 
> <input type="contact" ... /> which, on click, would bring up a 
> 'contacts picker' [2]. When contacts are selected by the user and this 
> element goes in to an equivalent of the 'File Upload' state, as 
> defined in [3], the *.onsuccess (or *.onerror) callbacks above would 
> be invoked according to whatever the current web app has specified. In 
> any current user agent this could be implemented as follows:
>
> <input type="button" id="getcontacts" />
> <script type="text/javascript">
>     function getContacts() {
>           navigator.contacts.find(['displayName', 'addresses'], 
> {multiple: true, limit: 3});
>     }
>
>     navigator.contacts.request.onsuccess = function() {
>         var contacts = navigator.contacts.request.result;
>         // do something with contacts
>         for(i in contacts) alert(contacts[i].displayName);
>     }
>     navigator.contacts.request.onerror = function{
>         var error = navigator.contacts.request.error;
>         alert("[" + error.code + "]" + error.message);
>     }
>
>     // Bind button
>     var el = document.getElementById("getcontacts");
>     el.addEventListener("click", getContacts, false);
> </script>
>
> Final thought: we may want to bind access to navigator.contacts.* and 
> navigator.system.* methods to click events (specifically, bind such 
> methods to HTML anchor and button element click events only).
>
The concept of having contact input field is interesting but I don't 
know if having them bound to global objects would be the cleanest 
solution. Lets say the web app wants to provide 2 buttons one to search 
for contacts and one to delete contact. In that case onsuccess callback 
would be called both for find and delete success which would probably 
lead to messy code? Personally I'd rather have something like this:
<input id="getcontacts" type="contact" properties="name,address" 
filter="work">
...
  var el = document.getElementById("getcontacts");
  el.addEventListener("contactSelected", doSthWithContacts, false);

> Just thought I would throw this out there as I thought it might be 
> worth exploring further particularly in the context of some of the 
> feedback from the WebApps WG. Any thoughts or developments on this 
> proposal would be appreciated.
>
> regards, Richard
>
>
> [1] http://www.w3.org/TR/IndexedDB/
> [2] http://dev.w3.org/html5/spec/Overview.html#file-upload-state
> [3] http://dev.w3.org/2009/dap/contacts/contacts_picker.png


-- 
Wojciech Masłowski
Engeneering CORE Wrocław
Opera Software ASA
http://www.opera.com
Received on Friday, 18 June 2010 10:42:00 GMT

This archive was generated by hypermail 2.2.0+W3C-0.50 : Wednesday, 9 May 2012 00:14:10 GMT