Uma aplicação exemplo usando a API nativa do iPhone com HTML5 e um banco de dados SQL local

Esses dias eu tive a oportunidade de testar o PhoneGap, um framework open-source para criar aplicações móveis nativas com padrões web. Dessa forma, a primeira coisa que vem a mente quando se pensa sonre HTML5 é que mesmo que os navegadores móveis sejam rápidos para implementar novos recursos, você não pode contar totalmente em “padrões” mas sim em “rascunhos”. As consequencias disso são um documentação ruim e dificuldade de achar ajuda em tópicos e discussões pela web.

O objeto: Um leitor do dashboard do Tumblr para iOs

O PhoneGap anuncia o suporte a seis plataformas, o que representa uma vantagem a produtos usados em dispositivos diferentes como iOs, Android e Blackberry.

O protótipo do exemplo desse artigo precisa testar o ambiente iOs com PhoneGap, para compilar uma aplicação nativa com duas funcionalidades básicas da API do Tumblr:

  • Sistema de Login / Autenticação.
  • Acesso ao Dashboard.

Para cumprir essas tarefas, em uma aplicação iOs nativa, os programadores armazenam as credenciais de login em um banco de dados SQLite3. No PhoneGap não funciona dessa maneira, mas podemos usar o banco de dados do webkit junto com os recursos de armazenamento do HTYML5.

Xcode & configuiração

Instalar o PhoneGap no OSZ é bem fácil, em seguida vem o processo de carregar o Xcode e escolher “Create a New Xcode project”.

A partir desse ponto, o conteúdo para pasta WWW será nosso diretório de trabalho, onde iremos colocar nosso arquivos.

Se quiser emular a interface e comportamento do iOs, você deve mexer no código CSS e considerar implementar o iScroll4 by Cubiq. Esse plugin vem solucionar em um problema do mecanismo de renderização do webkit que não permite posicionar conteúdo fixo como cabeçalhos no topo e  menus no final da página, típicos de aplicações iOs.

Assim, inclua uma planilha personalizada e bibliotecas javascript como recursos no Xcode e coloque eles na pasta www. Se você estiver preocupado com a performance em navegadores móveis, não há dúvida de que essa forma é mais eficiente. Após teste de algumas aplicações jQtouch em um iPhone 3G, a experiência não foi satisfatória.

De qualquer forma, alguma ajuda com JavaScript pode permitir que você ganhe algum tempo, dessa forma dê uma olhada no arquivo micro.js para verificar qual biblioteca é mais adequada as suas necessidades. Para os propósitos desse protótipo, xui ou snackjs são boas soluções.

Interface e comportamento

Assim, você precisará de um cabeçalho, um rodapé e um empacotador (wrappler) onde o usuário irá rolar o conteúdo através do toque. Para obter detalhes de como configurar o iScroll veja página oficial do Cubiq.

Primeiro, incua sua biblioteca JavaScript na seção HEAD:

<script type="text/javascript" charset="utf-8" src="iscroll.js"></script>
<script type="text/javascript" charset="utf-8" src="xui.js"></script>

Modifique a planilha CSS para emular os elementos da interface do iOs (veja a planilha completa no demo do iScroll):

#header {
    position:absolute; z-index:2;
    top:0; left:0;
    width:100%;
    height:45px;
    line-height:45px;
    background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #96D9FF), color-stop(0.05, #189AD6), color-stop(1, #0A577A));
    padding:0;
    color:#eee;
    font-size:20px;
    text-align:center;
}
#footer {
    position:absolute; z-index:2;
    bottom:0; left:0;
    width:100%;
    height:48px;
    background-image:-webkit-gradient(linear, 0 0, 0 100%, color-stop(0, #999), color-stop(0.02, #666), color-stop(1, #222));;
    padding:0;
    border-top:1px solid #444;
}
#wrapper {
    position:absolute; z-index:1;
    top:45px; bottom:48px; left:0;
    width:100%;
    background:#aaa;
    overflow:auto;
}

Modifique o CSS até que sua aplicação se pareça com a figura abaixo:

Essa será a estrutura básica para exibir e inserir itens do dashboard (post do Tumblr) em nossa aplicação. Basicamente é uma estrutura LIST em HTML.

A primeira coisa a fazer agora é adicionar a obtenção de credenciais de usuário para usar a API do Tumblr.

Dissemos que iremos implementar isso com banco de dados HTML5, assim o algoritmos é bem simples: uma vez que a aplicação for iniciada

  • Abrimos o banco de dados local
  • Tentar pegar (select) o nome do usuário e senha da tabela “auth”
  • Se um erro for encontrado (table not found), significa que não temos nenhuma informação de login, assim temos que criar a tabela e carregar uma seção de “login”
  • Se obtermos os dados de login, vamos iniciar a API do Tumblr

 

Armazenar o email/senha no banco de dados SQL do webkit

Primeiro, abra (ou crie) o banco de dados local:

try
{
   var shortName = 'tapp';
   var version = '1.0';
   var displayName = 'Tumblr App Database';
   var maxSize = 65536;
   db = openDatabase(shortName, version, displayName,  maxSize);
}
catch(e)
{
   console.log(e);
}

Agora, tente obter os dados de autenticação do Tumblr do usuário a partir desse banco de dados:

try
{
    db.transaction(
    function(transaction)
    {
        transaction.executeSql('SELECT user, passwd FROM auth', [], authDataHandler, errorHandler);
    });
}
catch(e)
{
    alert(e.message);
    return;
}

Nesse ponto, fazemos um select SQL em nosso banco de dados previamente selecionado, delegando a função authDataHandler para responder ao sucesso da chamada, e errorHandler em caso de falha de acesso.

No primeiro caso: tabela não existe, a transação sql emite um código de erro 1; temos que criar a tabela de autenticação e levar o usuário para a tela de login.

errorHandler = function(transaction, error)
{
    if(error.code == 1) //table auth does not exists
    {
        try
        {
            db.transaction(
                function(transaction) //create auth table
                {
                    transaction.executeSql('CREATE TABLE IF NOT EXISTS auth(user text primary key, passwd text);', [], nullDataHandler, errorHandler);
                    //load login screen with xhr call (xui library)
                    x$('#scroller').xhr('inner', 'login.html');
                });
        }
        catch(e)
        {
        }
    }
    return true;
}

At this point we load a login page with a form and  (submit) event management:

<div id="login">
    <form id="inputForm">
        <ul>
            <li><input type="text" id = "username" placeholder = "username" /></li>
            <li><input type="password" id = "password" placeholder = "password" /></li>
            <li><input type="submit" id = "saveAuth" value = "Save" /> </li>
        </ul>
    </form>
</div>

E anexar o evento para a operação de submissão:

document.getElementById('inputForm').addEventListener("submit",function(e)
{
    e.preventDefault();
    var email = document.getElementById('username').value;
    var passwd = document.getElementById('password').value;
    var req = snack.request({
        method: 'post',
        url: 'http://www.tumblr.com/api/authenticate',
        data: {
            email: email,
            password: passwd
        }
        },function(err, res)//the callback
        {
            if(err)
            {
                err == '403' ? alert('wrong login/password.') : alert('Sorry unable to process request.');
                return;
            }
            else
            {
                that.saveAuthData(email, passwd);
                return;
            }
        });
        return false;
}, false);

Nesse pedaço de código estamos escutando o evento submit, após disparado nós pegamos o nome de usuário e senha do formulário e fazemos a requisição de autenticação junto a API do Tumblr. Nesse código usamos a função Snack para fazer a chamada.

Se ocorrer um erro, nós avisamos o usuário, caso contrário as credenciais de login são aceitas e procedemos o armazenamento delas no banco de dados local para uso posterior.

Na seção callback, depois de testar as credencias do usuário, passos po e-mail e senha para a função saveAuthData: agora temos que criar uma operação CREATE TABLE e INSERT INTO através de uma transação sql:

function saveAuthData(email, passwd)
{
    try
    {
        db.transaction(
        function(transaction)
        {
            transaction.executeSql('CREATE TABLE IF NOT EXISTS auth(user text primary key, passwd text);', [], nullDataHandler, errorHandler);
            transaction.executeSql('INSERT INTO auth(user, passwd) VALUES("'+email+'", "'+passwd+'");', [], loginNullDataHandler, errorHandler);
        });
    }
    catch(e)
    {
        console.log(e.message);
    }
    function loginNullDataHandler()//data has been saved in db
    {
        location.href = "index.html";
    }
}

 

Requisitando o dashboard do Tumblr via JSONP

Agora que conseguimos autenticar o usuário no banco de dados local, devemos voltar para a página index.html. Na próxima vez que o usuário acessar nossa aplicação, o processo será iniciados a partir da autenticação do usuário através do banco de dados local, sem passar pela tela de login.

Nós declaramos anteriormente o caso onde não existe tabela de onde pegar os dados, agora precisamos especificar o caso de sucesso: authDataHandler().

authDataHandler = function(transaction, results)
{
    //just check, for security reasons if there are data in db
    if(results.rows.length == 0)
    {
        x$('#scroller').xhr('inner', 'login.html');
    }
    else if(results.rows.length > 0)
    {
        that.userEmail = results.rows.item(0).user;
        that.userPassword = results.rows.item(0).passwd;
        var params =
        {
            url: 'http://www.tumblr.com/api/dashboard/json',
            data:{
                email: that.userEmail,
                password: that.userPassword
            },
            key: 'callback'
        }
        var req = snack.JSONP(params,
            function(data)//the callback
            {
                that.getDashboard(data);
            });
    }
}

Uma vez obtido o e-mail e senha do usuário do banco de dados local, criamos uma requisição JSONP via SnackJS para fazer uma requisição de acesso ao dashboard. Na função de callback, passos os dados retornados para a função getDashboard que iremos definir nas próximas linhas de código; basicamente iremos navegar pela estrutura JSON e pegar post individuais do dashboard, sempre checando o tipo (exemplo: audio, ofot, normal):

function getDashboard(data)
{
    for(var i=0; i< data.posts.length; i++)
    {
        var post = data.posts[i];
        //check post type and inject content into [ul] element
        else if(post.type == "regular")
        {
            var body = post["regular-body"];
            var title = post["regular-title"];
            var author = post.tumblelog["name"];
            var el = document.createElement('li');
            el.innerHTML = "<h1>"+author+"</h1><h2>"+title+"</h2><p>"+body+"</p>";
            document.getElementById('thelist').appendChild(el);
        }
        else if(post.type == "photo")
        {
            console.log("photo");
            var photoLink = post["photo-url-250"];
            var el = document.createElement('li');
            el.innerHTML = "<img src="+photoLink+" />";
            document.getElementById('thelist').appendChild(el);
        }
[..]

Na próxima vez que o usuário carregar essa aplicação, a autenticação será pulada (até que o usuário apague o banco de dados do safari).

O exemplo termina aqui, mas deve estar claro como proceder no caso de operações futurar para adicionar novas funcionaldiades como escrever um post ou carregar uma foto.

Conclusões

Banco de dados local é um recurso incrível, com apliucações nativas empacotadas pelo PhoneGap seu potencial fica maior.

Apesar do uso de padrões web, desenvolver para dispositivos móveis não é como desenvolver para desktops: vocÊ precisa tomar muito cuidado com a performance. Poder escrever aplicações nativas para iPhone usando HTML5 não significa ter uma festa amadora usando jQuery. Desenvolver para dispositivos é uma prova real de suas capacidades. Abaixo seguem algumas sugestões:

  • Tente usar javascript nativo o máximo que puder.
  • Obvioamente evite animação baseada em Javascript (e conte com as propriedades de aceleração baseada em hardware do CSS).
  • Evite bibliotecas pesadas, dê uma olhada na microjs.com.
  • As funções básicas de jQuery podem ser facilmente substituidas por implementações puramente javascripts.

Traduzido de