h5y1m141 / connextion

Other
0 stars 0 forks source link

1月第2週の課題 #10

Open h5y1m141 opened 9 years ago

h5y1m141 commented 9 years ago

@kozasaryosuke さん

1月第2週からTitaniumで最近主流のAlloyという仕組みを使った開発に着手してきます。

Webの世界のHTML/CSSのような感じで画面要素を分割する手法がAlloyの設計思想に組み込まれてるので、慣れてくると、どこにどんなファイルがあって、どこを修正、作成すればいいのかがわかるようになるので、かなり開発が楽に進められるようになってます。

手軽に使えて開発効率があがるJavaScriptライブラリを使う

Alloyでの開発に入る前に、Alloyで標準的に利用されてるJavaScriptのライブラリがあるのでそれの扱いを最低限おさえておきたいので以下をまずおねがいします

適当なサンプルアプリ&情報がみつからないので、過去書いた記事をまとめてるので少しお待ちください

kozasaryosuke commented 9 years ago

QiitaのWebAPIと連携させるアプリでMoment.jsを使う ですが、サンプル通りにやるとエラーが出てしまいます。momentja.jsにエラーが出ているような…なんかそんなメッセージが出ます。

momentjaを読み込まず、momentのみで実装すれば表示できました。

取り急ぎご報告です。

kozasaryosuke commented 9 years ago

2015-01-10 9 50 54

2015-01-10 9 50 18

h5y1m141 commented 9 years ago

@kozasaryosuke さん

momentja.jsにエラーが出ているような…なんかそんなメッセージが出ます。 momentjaを読み込まず、momentのみで実装すれば表示できました。

たしかにエラーになってますね・・Moment.jsのバージョンがあがって日本語化対応のほうが追いついてないのかなぁ・・とりあえずはMoment.js使うと便利なことが出来るっていうのを体感してもらいたかったので、momentのみで実装すれば表示できたのことで次に進むことにしましょう

Alloyでの開発について追記しましたのでそちらをチェックして先に進んでください

kozasaryosuke commented 9 years ago

2015-01-13 11 25 21

kozasaryosuke commented 9 years ago

Titanium + Alloy + napp.alloy.adapter.restapiで作る簡単Qiitaビューワーアプリですが、alloy.jsのせいなのか(???)エラーが出てしまいます。 alloy.jsはデフォルトの状態のままで特に何もしていないのですが、何かコードを書く必要があるのでしょうか。もしかしたら私が何かしらの手順を間違えているのかもしれません。コード貼りますね。

kozasaryosuke commented 9 years ago

restapi.js

/**
 * Rest API Adapter for Titanium Alloy
 * @author Mads Møller
 * @version 1.1.6
 * Copyright Napp ApS
 * www.napp.dk
 */

function S4() {
    return ((1 + Math.random()) * 65536 | 0).toString(16).substring(1);
}

function guid() {
    return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
}

function InitAdapter(config) {
    return {};
}

function apiCall(_options, _callback) {
    if (Ti.Network.online) {
        var xhr = Ti.Network.createHTTPClient({
            timeout : _options.timeout || 7000
        });

        //Prepare the request
        xhr.open(_options.type, _options.url);

        xhr.onload = function() {
            var responseJSON, success = (this.status <= 304) ? "ok" : "error", status = true, error;

            // save the eTag for future reference
            if (_options.eTagEnabled && success) {
                setETag(_options.url, xhr.getResponseHeader('ETag'));
            }

            // we dont want to parse the JSON on a empty response
            if (this.status != 304 && this.status != 204) {
                // parse JSON
                try {
                    responseJSON = JSON.parse(this.responseText);
                } catch (e) {
                    Ti.API.error('[REST API] apiCall PARSE ERROR: ' + e.message);
                    Ti.API.error('[REST API] apiCall PARSE ERROR: ' + this.responseText);
                    status = false;
                    error = e.message;
                }
            }

            _callback({
                success : status,
                status : success,
                code : this.status,
                data : error,
                responseText : this.responseText || null,
                responseJSON : responseJSON || null
            });

            cleanup();
        };

        //Handle error
        xhr.onerror = function(e) {
            var responseJSON, error;
            try {
                responseJSON = JSON.parse(this.responseText);
            } catch (e) {
                error = e.message;
            }

            _callback({
                success : false,
                status : "error",
                code : this.status,
                error : e.error,
                data : error,
                responseText : this.responseText,
                responseJSON : responseJSON || null
            });

            Ti.API.error('[REST API] apiCall ERROR: ' + this.responseText);
            Ti.API.error('[REST API] apiCall ERROR CODE: ' + this.status);
            Ti.API.error('[REST API] apiCall ERROR MSG: ' + e.error);
            Ti.API.error('[REST API] apiCall ERROR URL: ' + _options.url);

            cleanup();
        };

        // headers
        for (var header in _options.headers) {
            // use value or function to return value
            xhr.setRequestHeader(header, _.isFunction(_options.headers[header]) ? _options.headers[header]() : _options.headers[header]);
        }

        if (_options.beforeSend) {
            _options.beforeSend(xhr);
        }

        if (_options.eTagEnabled) {
            var etag = getETag(_options.url);
            etag && xhr.setRequestHeader('IF-NONE-MATCH', etag);
        }

        if (_options.type != 'GET' && !_.isEmpty(_options.data)) {
            xhr.send(_options.data);
        } else {
            xhr.send();
        }
    } else {
        // we are offline
        _callback({
            success : false,
            status : "offline",
            offline : true,
            responseText : null
        });
    }

    /**
     * Clean up the request
     */
    function cleanup() {
        xhr = null;
        _options = null;
        _callback = null;
        error = null;
        responseJSON = null;
    }

}

function Sync(method, model, opts) {
    model.idAttribute = model.config.adapter.idAttribute || "id";

    // Debug mode
    var DEBUG = model.config.debug;

    // eTag enabled
    var eTagEnabled = model.config.eTagEnabled;

    // Used for custom parsing of the response data
    var parentNode = model.config.parentNode;

    // REST - CRUD
    var methodMap = {
        'create' : 'POST',
        'read' : 'GET',
        'update' : 'PUT',
        'delete' : 'DELETE'
    };

    var type = methodMap[method];
    var params = _.extend({}, opts);
    params.type = type;

    //set default headers
    params.headers = params.headers || {};

    // Send our own custom headers
    if (model.config.hasOwnProperty("headers")) {
        for (var header in model.config.headers) {
            params.headers[header] = model.config.headers[header];
        }
    }

    // We need to ensure that we have a base url.
    if (!params.url) {
        params.url = (model.config.URL || model.url());
        if (!params.url) {
            Ti.API.error("[REST API] ERROR: NO BASE URL");
            return;
        }
    }

    // Extend the provided url params with those from the model config
    if (_.isObject(params.urlparams) || model.config.URLPARAMS) {
        _.extend(params.urlparams, _.isFunction(model.config.URLPARAMS) ? model.config.URLPARAMS() : model.config.URLPARAMS);
    }

    // For older servers, emulate JSON by encoding the request into an HTML-form.
    if (Alloy.Backbone.emulateJSON) {
        params.contentType = 'application/x-www-form-urlencoded';
        params.processData = true;
        params.data = params.data ? {
            model : params.data
        } : {};
    }

    // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
    // And an `X-HTTP-Method-Override` header.
    if (Alloy.Backbone.emulateHTTP) {
        if (type === 'PUT' || type === 'DELETE') {
            if (Alloy.Backbone.emulateJSON)
                params.data._method = type;
            params.type = 'POST';
            params.beforeSend = function(xhr) {
                params.headers['X-HTTP-Method-Override'] = type;
            };
        }
    }

    //json data transfers
    params.headers['Content-Type'] = 'application/json';

    logger(DEBUG, "REST METHOD", method);

    switch(method) {
        case 'create' :
            // convert to string for API call
            params.data = JSON.stringify(model.toJSON());
            logger(DEBUG, "create options", params);

            apiCall(params, function(_response) {
                if (_response.success) {
                    var data = parseJSON(DEBUG, _response, parentNode);

                    //Rest API should return a new model id.
                    if (data[model.idAttribute] === undefined) {
                        //if not - create one
                        data[model.idAttribute] = guid();
                    }
                    params.success(data, JSON.stringify(data));
                    model.trigger("fetch");
                    // fire event
                } else {
                    params.error(_response.responseJSON, _response.responseText);
                    Ti.API.error('[REST API] CREATE ERROR: ');
                    Ti.API.error(_response);
                }
            });
            break;

        case 'read':
            if (model[model.idAttribute]) {
                params.url = params.url + '/' + model[model.idAttribute];
            }

            if (params.search) {
                // search mode
                params.url = params.url + "/search/" + Ti.Network.encodeURIComponent(params.search);
            }

            if (params.urlparams) {
                // build url with parameters
                params.url = encodeData(params.urlparams, params.url);
            }

            if ( ! params.urlparams && params.data) {
                // If we have set optional parameters on the request we should use it
                // when params.urlparams fails/is empty.
                params.url = encodeData(params.data, params.url);
            }

            if (eTagEnabled) {
                params.eTagEnabled = true;
            }

            logger(DEBUG, "read options", params);

            apiCall(params, function(_response) {
                if (_response.success) {
                    var data = parseJSON(DEBUG, _response, parentNode);
                    var values = [];

                    if (!_.isArray(data)) {
                        data = [data];
                    }

                    var length = 0;
                    for (var i in data) {
                        var item = {};
                        item = data[i];
                        if (item[model.idAttribute] === undefined) {
                            item[model.idAttribute] = guid();
                        }
                        values.push(item);
                        length++;
                    }

                    params.success((length === 1) ? values[0] : values, _response.responseText);
                    model.trigger("fetch");
                } else {
                    params.error(model, _response.responseText);
                    Ti.API.error('[REST API] READ ERROR: ');
                    Ti.API.error(_response);
                }
            });
            break;

        case 'update' :
            if (!model[model.idAttribute]) {
                params.error(null, "MISSING MODEL ID");
                Ti.API.error("[REST API] ERROR: MISSING MODEL ID");
                return;
            }

            // setup the url & data
            if (_.indexOf(params.url, "?") == -1) {
                params.url = params.url + '/' + model[model.idAttribute];
            } else {
                var str = params.url.split("?");
                params.url = str[0] + '/' + model[model.idAttribute] + "?" + str[1];
            }

            if (params.urlparams) {
                params.url = encodeData(params.urlparams, params.url);
            }

            params.data = JSON.stringify(model.toJSON());

            logger(DEBUG, "update options", params);

            apiCall(params, function(_response) {
                if (_response.success) {
                    var data = parseJSON(DEBUG, _response, parentNode);
                    params.success(data, JSON.stringify(data));
                    model.trigger("fetch");
                } else {
                    params.error(model, _response.responseText);
                    Ti.API.error('[REST API] UPDATE ERROR: ');
                    Ti.API.error(_response);
                }
            });
            break;

        case 'delete' :
            if (!model[model.idAttribute]) {
                params.error(null, "MISSING MODEL ID");
                Ti.API.error("[REST API] ERROR: MISSING MODEL ID");
                return;
            }
            //params.url = params.url + '/' + model[model.idAttribute];
            if (_.indexOf(params.url, "?") == -1) {
                            params.url = params.url + '/' + model.id;
                        } else {
                            var str = params.url.split("?");
                            params.url = str[0] + '/' + model.id + "?" + str[1];
                        }

            logger(DEBUG, "delete options", params);

            apiCall(params, function(_response) {
                if (_response.success) {
                    var data = parseJSON(DEBUG, _response, parentNode);
                    params.success(null, _response.responseText);
                    model.trigger("fetch");
                } else {
                    params.error(model, _response.responseText);
                    Ti.API.error('[REST API] DELETE ERROR: ');
                    Ti.API.error(_response);
                }
            });
            break;
    }

}

/////////////////////////////////////////////
// HELPERS
/////////////////////////////////////////////

function logger(DEBUG, message, data) {
    if (DEBUG) {
        Ti.API.debug("[REST API] " + message);
        if (data) {
            Ti.API.debug( typeof data === 'object' ? JSON.stringify(data, null, '\t') : data);
        }
    }
}

function parseJSON(DEBUG, _response, parentNode) {
    var data = _response.responseJSON;
    if (!_.isUndefined(parentNode)) {
        data = _.isFunction(parentNode) ? parentNode(data) : traverseProperties(data, parentNode);
    }
    logger(DEBUG, "server response", _response);
    return data;
}

function traverseProperties(object, string) {
    var explodedString = string.split('.');
    for ( i = 0, l = explodedString.length; i < l; i++) {
        object = object[explodedString[i]];
    }
    return object;
}

function encodeData(obj, url) {
    var str = [];
    for (var p in obj) {
        str.push(Ti.Network.encodeURIComponent(p) + "=" + Ti.Network.encodeURIComponent(obj[p]));
    }

    if (_.indexOf(url, "?") == -1) {
        return url + "?" + str.join("&");
    } else {
        return url + "&" + str.join("&");
    }
}

/**
 * Get the ETag for the given url
 * @param {Object} url
 */
function getETag(url) {
    var obj = Ti.App.Properties.getObject("NAPP_REST_ADAPTER", {});
    var data = obj[url];
    return data || null;
}

/**
 * Set the ETag for the given url
 * @param {Object} url
 * @param {Object} eTag
 */
function setETag(url, eTag) {
    if (eTag && url) {
        var obj = Ti.App.Properties.getObject("NAPP_REST_ADAPTER", {});
        obj[url] = eTag;
        Ti.App.Properties.setObject("NAPP_REST_ADAPTER", obj);
    }
}

//we need underscore
var _ = require("alloy/underscore")._;

//until this issue is fixed: https://jira.appcelerator.org/browse/TIMOB-11752
var Alloy = require("alloy"), Backbone = Alloy.Backbone;

module.exports.sync = Sync;

module.exports.beforeModelCreate = function(config, name) {
    config = config || {};
    InitAdapter(config);
    return config;
};

module.exports.afterModelCreate = function(Model, name) {
    Model = Model || {};
    Model.prototype.config.Model = Model;
    Model.prototype.idAttribute = Model.prototype.config.adapter.idAttribute;
    return Model;
};

controllersのindex.js

var qiitaItems, refreshMainMenu;

$.index.open();

$.activityIndicator.show();

qiitaItems = Alloy.createCollection('qiita');

qiitaItems.fetch({
  success: function() {
    var items, _stringify;
    $.activityIndicator.hide();
    _stringify = JSON.stringify(qiitaItems);
    items = JSON.parse(_stringify);
    return refreshMainMenu(items);
  },
  error: function() {
    $.activityIndicator.hide();
    return Ti.API.info("error");
  }
});

refreshMainMenu = function(items) {
  var bodyLabel, item, row, rows, titleLabel, _i, _len;
  rows = [];
  for (_i = 0, _len = items.length; _i < _len; _i++) {
    item = items[_i];
    Ti.API.info(item.title);
    row = $.UI.create("TableViewRow", {
      classes: "itemRow",
      data: item
    });
    titleLabel = $.UI.create("Label", {
      text: item.title,
      classes: "titleLabel"
    });
    bodyLabel = $.UI.create("Label", {
      text: item.raw_body,
      classes: "body"
    });
    row.add(titleLabel);
    row.add(bodyLabel);
    rows.push(row);
  }
  return $.mainMenu.setData(rows);
};

qiita.js

exports.definition = {
  config: {
    URL: "https://qiita.com/api/v1/items",
    adapter: {
      type: 'restapi',
      collection_name: 'qiita'
    },
    extendModel: function(Model) {
      return _.extend(Model.prototype, function() {
        return Model;
      });
    },
    extendCollection: function(Collection) {
      return _.extend(Collection.prototype, function() {
        return Collection;
      });
    }
  }
};

index.tss

"#mainWindow":{
    statusBarStyle:0,
    translucent:false,
    navTintColor:"#0066ff",
    backgroundColor:"#fcfcfc",
    tabBarHidden:true
}
"#mainMenu":{
    backgroundColor:"#fcfcfc",
    separatorColor: '#cccccc',
    width:Ti.UI.FULL,
    height:Ti.UI.FULL,
    left:0,
    top:0,
    zIndex:1

}

"#activityIndicator":{
    top:"50%",
    left:"20%",
    textAlign:'center',
    backgroundColor:"#222",
    font:{
        fontSize:18
    },
    color:'#fff',
    zIndex:10
}

".itemRow":{
    width:Ti.UI.FULL,
    height:"15%",
    hasChild:true,
    backgroundColor:"#fcfcfc"
}

".titleLabel":{
    width:"90%",
    height:"20%",
    top:"5%",
    left:"5%",
    textAlign:'left',
    color:'#59BB0C',
    font:{
        fontWeight:'bold',
        fontSize:16
    }

}
".body":{
    width:"90%",
    height:"70%",
    top:"25%",
    left:"5%",
    textAlign:'left',
    color:'#222',
    font:{
        fontSize:12
    }

}

viewsのindex.xml

<Alloy>
  <TabGroup>
    <Tab id="tabOne">
      <Window id="mainWindow" class="container" title="Qiita">
        <ActivityIndicator id="activityIndicator" message="Loading.." />
        <TableView id="mainMenu" />
      </Window>
    </Tab>
  </TabGroup>
</Alloy>
kozasaryosuke commented 9 years ago

ちょっと待ってください…自力で解決できるかもしれません。。

kozasaryosuke commented 9 years ago

すみません。やっぱりダメでした。

[ERROR] :  Script Error {
[ERROR] :      backtrace = "#0 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy.js:194\n#1 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy.js:170\n#2 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy/controllers/BaseController.js:173\n#3 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy/controllers/index.js:98\n#4 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy/controllers/index.js:83\n#5 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy/backbone.js:760\n#6 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy/sync/restapi.js:162\n#7 () at file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy/sync/restapi.js:43";
[ERROR] :      line = 48;
[ERROR] :      message = "'undefined' is not an object (evaluating 'copy.colors')";
[ERROR] :      name = TypeError;
[ERROR] :      sourceId = 302250656;
[ERROR] :      sourceURL = "file:///Users/kozasaryousuke/Library/Developer/CoreSimulator/Devices/CA4D0955-551F-4D4B-AB8A-B86A752310BE/data/Containers/Bundle/Application/57B0ED18-F112-43BB-B63B-6F4AA6B14410/restapi.app/alloy.js";
[ERROR] :  }

こんな感じのエラーコードが出ています。。

h5y1m141 commented 9 years ago

@kozasaryosuke さん、 うーん見る限りでは間違ってる感じしないですね。ひとまず気になったのはqiita.jsの配置場所がどのようになってますか?

Alloyは、決まった箇所に決まったネーミングルールでファイルを配置されてないと処理がうまく行われないようになってます。

なんとなく、qiita.jsは、以下のようにmodelというディレクトリの下に配置しておく必要あります

├── alloy.jmk
├── alloy.js
├── assets
│   ├── alloy
│   │   └── sync
│   │       ├── restapi.js
│   ├── iphone
│   └── mobileweb
├── config.json
├── controllers
│   └── index.js
├── models
│   └── qiita.js
├── styles
│   └── index.tss
└── views
    └── index.xml
kozasaryosuke commented 9 years ago

qiita.jsの位置は間違ってないようです。 alloy.jmkというファイルは(任意)と書いてあったのでいまはない状態ですが、関係ないですよね? (ファイルを作成しても動きませんでした。)

h5y1m141 commented 9 years ago

alloy.jmkというファイルは(任意)と書いてあったのでいまはない状態ですが、関係ないですよね?

はい、関係無いですね。alloy.jmkは、設定を色々カスタマイズしたい場合に記述するファイルなので基本的にはこれが無くってもOKです。

あとは、Titaniumというかスマートフォン向けのアプリでよくあるのですが、実際に書いたソースコードは、コンパイルという作業を経て、iPhoneシュミレーター or 実機で利用できるような仕組みになってます。

そのコンパイル作業はとても時間がかかるため、毎回毎回ゼロからコンパイルされるのではなく、以前コンパイルされたもの(= キャッシュ という言い方がされます。おそらく言葉を聞いたことがあるかと思います)はそのまま利用されます。

そのキャッシュファイルが原因でたまに謎のエラーが生じることがあるので、念のためキャッシュクリアーを試してもらえませんか?

キャッシュクリアーは、Titanium Studioのこのメニュー↓のように進むと実行できます skitched-20120914-192527

上記と別に、私の手元の環境で先ほど貼っていただいた内容でどのような結果になるのか検証してみますね

h5y1m141 commented 9 years ago

@kozasaryosuke さんが貼ってくれたソースコードをそのままコピペしたらこのように意図通り表示されましたね

shot-2015-01-13-19 10 35

可能性としては、私のMacの環境はXCode、Titaniumの開発環境とも少しバージョンを古めにしてます。

TitaniumSDK3.3.0というバージョンで、Alloyもバージョンが少し古いのですが、おそらく@kozasaryosuke さんは、3.4.0というバージョンでXCodeも6という最新のものを利用されてるのでそのあたりが要因かなぁ・・

shot-2015-01-13-19 11 50

kozasaryosuke commented 9 years ago

キャッシュクリアーしたのですが、できません〜

http://developer.appcelerator.com/question/178170/error-in-alloy-after-upgrading-from-150-rc3-to-151ga

このリンク先の人も、最新バージョンにより謎のエラーが出る、みたいなこと言ってますね。もしかしたら関係あるのかも。。

おっしゃる通り、僕の環境は最新バージョンで3.4.1です。

kozasaryosuke commented 9 years ago

バージョン落としたものをインストールした方が良さそうですよね?

2015-01-13 19 16 51

kozasaryosuke commented 9 years ago

ちなみにAlloyのバージョンは1.5.1です。それが原因かもしれませんね。

h5y1m141 commented 9 years ago

@kozasaryosuke さんがリンク貼ってくれたError in Alloy after upgrading from 1.5.0-rc3 to 1.5.1.GAのページはさっき私も見てたのですが、なんかエラー内容とか使ってるSDKのバージョンとかからすると似た症状な気がしますね

バージョン落としたものをインストールした方が良さそうですよね?

たしかに、そうしたほうが良さそうなのですが、これが結構やっかいで

  1. iOSの開発にそもそも必要なXCodeが現在6という最新のものを使ってると思うので、まずは旧バージョンの5をダウンロードしつつ、念のため両方が使えるように設定する。こちらのページが参考になります。
  2. 上記完了したら、こちらのページを参考にして、TitaniumStudioからどっちのXCodeを利用するかコマンドラインで設定するようにする

上記補足事項

1.の手順でXCodeのバージョン5の名前は、XCode5としたほうが作業やりやすくなります 2.コマンドラインを実行するためのターミナルは以下の場所にあります

shot-2015-01-13-21 24 54

ターミナルをダブルクリックしたら、以下を入力します

sudo xcode-select -s /Applications/Xcode5.app/Contents/Developer/

shot-2015-01-13-21 26 00

あとは、TitaniumのSDKを3.4.1から3.3.0とかの1つ前のものをダウンロードしてやるのですが、このあたりの作業手順が慣れないとちょっと難しいと感じる所があるので、途中不明な点があれば、都度コメントいただくか、明日の午後8時とかに、ここまでの作業振り返りを兼ねつつ、画面共有などしながら、トラブルシューティングも出来ますので、そのあたりは遠慮なくお知らせください!

kozasaryosuke commented 9 years ago

ここ二日、課題を進められていませんが、明日は時間が取れるので進められると思います! もう少し時間を割けると踏んでいたのですが。。思うように進まず…ですが、焦らず最速で進められればと思ってますので、宜しくお願いします!

h5y1m141 commented 9 years ago

ここ二日、課題を進められていませんが、明日は時間が取れるので進められると思います!

@kozasaryosuke さん、了解です。私が今日は日中、夜とも時間があまり取れ無さそうなのであまりフォロー出来ないかもしれませんが、何か詰まった所が出てきたら、コメントしておいてください。

今日の進み具合見て、来週の課題設定を考えてみますね

kozasaryosuke commented 9 years ago

Alloyのバージョンを1.5.1から1.5.0に変えたらとりあえず表示はできました!

kozasaryosuke commented 9 years ago

2015-01-16 12 24 17

kozasaryosuke commented 9 years ago

Titanium +Alloy+ACSの構成でユーザーログインでFacebookアカウントを利用するですが、最終的にどのようなものができるのかわからないため、合ってるのかよくわからないのですが、たぶんできてなさそうです。エラーが出てます。acs.jsにエラーが出てます。

kozasaryosuke commented 9 years ago

2015-01-16 12 54 03

kozasaryosuke commented 9 years ago

2015-01-16 12 56 47

kozasaryosuke commented 9 years ago

23行目の

    debugger;

が「Syntax Error: unexpected token "debugger"」と出てしまっています。

h5y1m141 commented 9 years ago

@kozasaryosuke さん

最終的にどのようなものができるのかわからないため、

失礼しました(^_^;)

画面キャプチャ

起動時にこのようにFacebookでログインというラベルが表示される

shot-2015-01-11-8 24 34

ラベルクリックすると、Facebookアカウントでログインする画面に切り替わる。

今回作ってるアプリから、Facebookの個人情報にアクセスしてもいいかどうか尋ねられるこのような表示がでます。(画面上部の赤い警告の部分が出ないかもしれませんが、似たような画面に切り替わればOKです)

shot-2015-01-11-8 30 04

Facebookアカウントでログイン&アプリから個人情報へのアクセスをOKあとの処理

controllers/index.jsの以下が実行されます。TitaniumのFacebook機能では、最低限の情報しか取得できないため、別途requestWithGraphPath()という機能を通じて、そのアカウントの個人情報(メールとか誕生日)を取得しにいきます。

  if (e.success) {
    fb.requestWithGraphPath('me',{},"GET",function(e) {
        if (e.success) {
          var obj = JSON.parse(e.result),
              moment = require('alloy/moment'),
              token, user, email, birthday;

          email = obj.email;
          birthday = moment(obj.birthday, "MM/DD/YYYY");
          token = fb.accessToken;
          Ti.API.info("Success: " +
                      "email" + email +
                      "birthday" + birthday +
                      "token" + token
                     );
          user = Alloy.createModel('social');
          user.fbLogin(token,email, birthday);
        }
      }
    );

その他、要修正箇所

記事を書いた時に、私の手元で作っていたサンプルアプリでは自作のFacebookアイコン画像を表示するようにしてしまっていてそのままのXMLファイルを記事に書いてしまってました。

そのため、以下のように変更してもらえればと思います

<Alloy>
    <Window class="container">
    <Label id="fbloginLabel">Facebookでログイン</Label>
    </Window>
</Alloy>
kozasaryosuke commented 9 years ago

2015-01-17 10 14 51

kozasaryosuke commented 9 years ago

できました〜

h5y1m141 commented 9 years ago

おー、出来ましたね!