Tutorial: Criando um app HTML5 de um projeto iOS* nativo usando a Intel® HTML5 App Porter Tool – BETA

Catégories:

Download Sample App

Introdução

A principal meta deste tutorial é te ajudar a utilizar a Intel® HTML5 App Porter Tool – BETA para portar um app nativo do iOS* para HTML5. A Intel® HTML5 App Porter Tool – BETA irá gerar um código limpo e fácil de ser lido, mantendo o código gerado automaticamente o mais próximo possível do código original.

Objetivos

Com este documento você irá:

  • Aprender a configurar a ferramenta para obter os melhores resultados possíveis
  • Entender o feedback fornecido pela ferramenta
  • Aprender como finalizar o processo de tradução completando as partes do código que não puderam ser traduziadas pela ferramenta

Tutorial de Exemplo - “Balloon Ninja”

Balloon Ninja é um mini game onde você ganha pontos estourando balões. Quanto mais balões você estoura em um minuto, mais pontos você faz. Ele usa o JSONKit como API de terceiros para persistência. Ele também usa a NSFoundation, UIKit e a Audio Toolbox como API nativa. Para este tutorial, a Intel® HTML5 App Porter Tool – BETA terá uma taxa de tradução de API de aproximadamente 80%, tendo em vista que as funcionalidades mais comuns da NSFoundation e do UIKit são suportadas.

Veja abaixo uma screenshot do app original sendo executada no simulador do iOS*.

Do iOS* para o HTML5 em apenas 6 passos

Esta seção mostra os diferentes passos que você deve seguir com sucesso para executar a Intel® HTML5 App Porter Tool – BETA, finalizando o processo de port e obtendo uma versão traduzida em HTML5 da app de exemplo.

Passo 1 - Configurando o path do projeto e da saída

Selecione o projeto a ser traduzido, neste caso o Balloon Ninja, e o path de destino. Para o path de origem, selecione o diretório .xcodeproj, em caso contrário você terá uma mensagem de erro dizendo “This is not a valid Xcode* Project”Por favor, se certifique de ter direito de escrita em ambos os diretorios, de origem e destino.

Passo 2 - Selecionando os módulos que serão processados

Neste passo, desmarque o arquivo JSONKit.m para exclui-lo do processo de tradução. Como o JavaScript possui suporte nativo ao JSON, a melhor abordagem aqui é não traduzir esta biblioteca junto com o código, mas reescrever para fazer o gerenciamento JSON usando o suporte do JavaScript.

Nota: Selecionar com cuidado as classes para a tradução é um passo fundamental. Como critério geral, você deve evitar traduzir qualquer módulo do aplicativo original que implemente uma funcionalidade que já é suportada pelo JavaScript. Adicionalmente, você também pode evitar traduzir módulos que são implementados usando funcionalidades de baixo nível do C ou do Objective-C* que podem ser traduzidas para o JavaScript de forma não natural.

O checkbox na parte de baixo da tela acima te permite adicionar diretórios de include ou diretivas de compilação que estejam faltando, se necessário.

Neste caso, nenhuma configuração adicional é necessária tendo em vista que os fontes do JSONKit já estão incluidos como parte do projeto. Entretanto, se o processo de parsing falhar, é aqui onde você poderia adicionar qualquer cabeçalho de API ou framework que não foram incluidos no projeto. Estes erros podem ser causados por uma biblioteca de terceitos de fora do projeto ou por uma diretriz de pré-processamento que esteja faltando, como definições de pré-processamento para DEBUG ou RELEASE, ou qualquer #define que deve ser ajustado manualmente.

Passo 3 - Parsing dos módulos

Durante este passo, a Intel® HTML5 App Porter Tool – BETA tenta fazer o parse do projeto e determinar a parte do código que pode ser traduzida. Se a ferramenta não for capaz de fazer o parse de um arquivo, ela te permite corrigir os problemas editando os arquivos que não puderam fazer o parse corretamente, como mostrado nas imagens abaixo. Você também pode optar para ignorar (ex. pular) os arquivos que não puderam ter o parsing realizado. Depois de editar ou ignorar estes arquivos, você pode continuar para o Passo 4. Caso você não encontre nenhum problema no parsing, você verá a sequencia de telas abaixo.

 

 

 

Passo 4 - Selecionar os métodos que serão traduzidos

Finalmente, depois que o projeto todo tiver passado pelo parsing e pela análise, o resultado é apresentado em um relatório simples. Você poderá decidir quais métodos serão traduzidos baseado na cobertura da API apresentada nas colunas da direita.

Desmarque a interface LeaderboardManager pois a serialização JSON será refeita diretamente em JavaScript.

 

Passo 5 - Lendo os resultados

Depois que a tradução estiver completa, é altamente recomendado que você veja o "Translation Report" e o "ToDo Report" disponíveis na tela final e também localizados no diretório TranslationReports da aplicação traduzida. O primeiro mostra detalhes da tradução propriamente dita, como o mapeamento entre os arquivos .m e .js. O segundo fornece uma lista de arquivos que representam os templates de declaração das APIs que não foram mapeadas para código HTML5 equivalente. Este relatório deve te orientar sobre o que você precisa completar para obter uma versão funcional do seu app em HTML5.

 

 

 

Passo 6 - Contectando os pontos que faltam

A fase de tradução automática executada pela ferramenta foi finalizada. Agora, você precisa completar o port das APIs e funcionalidades que não são suportadas pela ferramenta. Selecione Open Application para abrir o Microsoft* Visual Studio* (se disponível, senão selecione Open Project Folder), para que você possa editar os arquivos JavaScript. Se você olhar dentro de cada arquivo JavaScript ToDo, você verá métodos lançando excessões e comentários sobre onde aquela função está sendo chamada no projeto. Você precisa completar estes métodos que a ferramenta não pode traduzir.

6.1. Desabilitando excessões lançadas pelos placeholders de API

Remova todas as excessões lançadas pelos placeholders de API adicionando a seguinte linha no  main.js.

APT.Global.THROW_IF_NOT_IMPLEMENTED = false;

6.2. Implementando as APIs de áudio

Abra o arquivo todo_api_js_c_global.js. Você verá 3 constantes e 2 métodos para implementar, mas você não precisa implementar o código para as constantes. Copie o código abaixo para completar a implementação das funções AudioServicesCreateSystemSoundID e AudioServicesPlaySystemSound.

c_global.AudioServicesCreateSystemSoundID = function(audioFileName, audioObjRef) {
    if(c_global.sounds === undefined) c_global.sounds = {};
    audioFileName = audioFileName.toString();

    if(c_global.sounds[audioFileName] === undefined)
    {
            c_global.sounds[audioFileName] = new Audio(audioFileName);
    }

    audioObjRef.value = c_global.sounds    [audioFileName];
};

c_global.AudioServicesPlaySystemSound = function(soundObj) {
    soundObj.play();
};

6.3. Emule a API de animação que está faltando na UIView

A versão atual da ferramenta suporta apenas métodos de animcação que não recebem blocos como argumentos.

Abrindo o arquivo todo_api_js_apt_view.js, você encontrará os métodos de animação da UIView que não são suportados pela ferramenta. Como a UIView está mapeada para a  APT.View no JavaScript, todos os métodos da UIView que não são suportados serão mapeados para placeholders na APT.View. O código abaixo é uma tradução simples que não executa as animações, mas aplica as alterações depois que o timeout for completado e chama o callback de complementação. Substitua o código na função _animateWithDuration_animations_completion com o código fornecido abaixo.

APT.View._animateWithDuration_animations_completion = function (duration, animationsBlock, completionBlock) {
    // setup a timer to call animation and completionBlock,  convert duration from seconds to milliseconds
    window.setTimeout(function () {
        // call the function with changes in views
        // changes will not be animated but just applied inmediatelly
        animationsBlock();
 
        // call completion callback
        if (completionBlock instanceof Function) completionBlock();
    }, duration * 1000, true);
};

6.4. Implementar a serialização JSON

Como o JavaScript fornece suporte nativo para o tratamento do JSON, é melhor fornecer uma nova implementação para a serialização diretamente em JavaScript. Aqui nós usamos a API localStorage do HTML5 para implementar a persistência.

Abra o arquivo leaderboardmanager.js e substitua o código para as funções leaderboard, addScore_name, saveData e getData com o código fornecido abaixo.

application.LeaderboardManager.leaderboard = function() {
    return application.LeaderboardManager.getData("leaderboard");
};
 
application.LeaderboardManager.addScore_name = function(score, name) {
    var leaderboard = application.LeaderboardManager.leaderboard();
    
    var scoreDic = new APT.Dictionary();
    scoreDic[new String("name")] = name;
    scoreDic[new String("score")] = score;
    leaderboard.push(scoreDic);
    
    leaderboard = leaderboard.sort(function (a, b) {
        if (a.score > b.score) return -1;
        if (a.score < b.score) return 1;
        return 0;
    });
    
    var leaderboardDictionary = { "leaderboard": leaderboard };
    var leaderboardString = JSON.stringify(leaderboardDictionary);
    application.LeaderboardManager.saveData("leaderboard", leaderboardString);
};
 
application.LeaderboardManager.saveData = function (name, data) {
    localStorage[name] = data;
    return true;
};
 
application.LeaderboardManager.getData = function (name) {
    var data = localStorage[name];
 
    if (data == null) {
        return new Array();
    }
    else {
        var jsonVar = JSON.parse(data);
        for (var i = 0; i < jsonVar.leaderboard.length; i++)
        {
            jsonVar.leaderboard[i] = new APT.Dictionary().initWithDictionary(jsonVar.leaderboard[i]);
        }
        return jsonVar.leaderboard;
    }
};

6.5. Substituindo a funcionalidade do observer

Abra o arquivo todo_api_application_balloonninjaviewcontroller.js e substitua a implementação das funções addObserver_forKeyPath_options_context e removeObserver_forKeyPath pela implementação fornecida abaixo. Note que esta é uma implementação bem simples de um observer, que irá fazer o aplicativo funcionar. Você pode encontrar diversas implementações de observers em JavaScript na Internet.

var observers = {};

application.BalloonNinjaViewController.prototype.addObserver_forKeyPath_options_context = function (this1, arg2, arg3, arg4) {
    var propertyName = arg2.valueOf();
    var propertySetName = "set" + propertyName.substr(0, 1).toUpperCase() + propertyName.substr(1);
    if (this1[propertySetName] === undefined || !(this1[propertySetName] instanceof Function)) {
        console.log("Could not find property " + propertySetName);
        return;
     }

    var dict = new APT.Dictionary();
    var oldFunction = this1[propertySetName];

    this1[propertySetName] = function () {
        // call original function
        oldFunction.apply(this1, arguments);
       
        dict.setValueForKey(arguments[0], "new");
        // call observer function
        this1.observeValueForKeyPath_ofObject_change_context.call(this1,
            null,
            this1,
            dict,
            this1
            );
    };

    observers[propertyName] = { "observerFunctionName": propertySetName, "originalFunction": oldFunction };
};

application.BalloonNinjaViewController.prototype.removeObserver_forKeyPath = function (this1, arg2) {
    var observer = observers[arg2];
    var observerFunctionName = observer["observerFunctionName"];
    var originalFunction = observer["originalFunction"];
    this1[observerFunctionName] = originalFunction;
};

6.6. Implemente isUserInteractionEnabled

Abra o arquivo todo_api_application_ballonview.js e substitua a implementação da função isUserInteractionEnabled com o código abaixo.

application.BalloonView.prototype.isUserInteractionEnabled = function() {
    return this.jQElement().attr("disabled") !== "disabled";
};

6.7. Re-fatore as chamadas dinâmicas

Abra o arquivo balloonninjaviewcontroller.js olhe a função observeValueForKeyPath_ofObject_change_context e remova a chamada para integerValue() da primeira linha, como no código abaixo.

/* call to 'integerValue()' removed */
cell.detailTextLabel().setText(APT.Global.sprintf(new String("%i"), scoreDic.objectFor(new String("score"))));

Abra o arquivo observeValueForKeyPath_ofObject_change_context e remova a chamada para integerValue() da primeira linha, como no código abaixo.

/* call to 'integerValue()' removed */
APT.Global.log(new String("Value Changing %d"), change.objectFor(new String("new")));

A chamada para integerValue() não é traduzida pela ferramenta (não é necessário no JavaScript) pois ela é uma mensagem dinâmica para um objeto genérico Objective-C. A versão atual da ferramenta não suporta a tradução de mensagens dinâmicas. Por isso, é recomendado que sejam fornecidos valores temporais e informações de tipo no seu código fonte original antes de executar a tradução.

6.8. Ajuste os arquivos CSS

No próximo passo, você irá alterar as propriedades geradas nos arquivos CSS. A ferramenta gera um par de arquivos .html e .css para cada view encontrada no arquivo xib traduzido.

A versão atual da ferramenta não suporta todas as propriedades ou tipos de valores existentes em arquivos .xib. Para alguns labels, o exemplo utiliza outros espaços de cores que não o RGB. Estas propriedades de cores não são traduzidas e precisamos atualizar o código gerado.

Abra o arquivo WriteScoreViewController_View_XXXXXX.css (o número no final não é importante) e para cada div#Label com uma propriedade olor: faltando, adicione uma nova linha "color: white;". Veja o exemplo abaixo.

div#Label_760896869
{
    text-align: left;
    width: 105px;
    height: 23px;
    position: absolute;
    left: 168px;
    opacity: 1;
    top: 54px;
 
    /* new attribute color added */
    color: white;
}
div#Label_934540285
{
    text-align: right;
    width: 54px;
    height: 21px;
    position: absolute;
    left: 14px;
    opacity: 1;
    top: 116px;
 
    /* new attribute color added */
    color: white;
}

Para tornar o código mais legível, você pode alterar o atributo 'id' no arquivo .html, mas lembre-se que você também irá precisar atualizar o arquivo xibboilerplatecode.js. Os números no atributo id, são números utilizados internamente nos arquivos .xib da aplicação original. Nas próximas versões da ferramenta, você verá ids mais amigáveis.

Depois, abra o arquivo BalloonNinjaViewController_View_XXXXX.css e adicione propriedades aos seletores de labels CSS para aumentar a fonte e torna-la negrito.

div#Label_88872199
{
    text-align: center;
    width: 55px;
    height: 37px;
    position: absolute;
    left: 133px;
    opacity: 1;
    top: 7px;
    color: rgba(255,246,6,1);
    
    /* change the font to be bold and 16pt*/
    font-weight: bold;
    font-size: 16pt;    
}
div#Label_397160484
{
    text-align: center;
    width: 114px;
    height: 37px;
    position: absolute;
    left: 206px;
    opacity: 1;
    top: 7px;
    color: rgba(0,255,74,1);
    
    /* change the font to be bold and 16pt*/
    font-weight: bold;
    font-size: 16pt;
}

O último arquivo que você precisa atualizar é o arquivo LeaderBoardViewController_View_XXXXX.css. Abra este arquivo e ajuste a propriedade background-color da TableView para transparente como mostrado abaixo.

div#TableView_91620652
{
    /* change background color to transparent */
    background-color: transparent;
   
    /* and change width from fixed size to relative */
    width: 80%;
    height: 283px;
    position: absolute;
    left: 54px;
    opacity: 1;
    top: 80px;
}

Agora, você está pronto para testar sua nova aplicação HTML5 executando seu projeto no emulador do Windows 8*.

* Other names and brands may be claimed as the property of others.

Fichier attachéTaille
Téléchargement tutorialsampleapp-2812-52.zip214.5 Ko
Reportez-vous à notre Notice d'optimisation pour plus d'informations sur les choix et l'optimisation des performances dans les produits logiciels Intel.