Help us understand the problem. What is going on with this article?

Titanium +Alloy+ACSの構成でユーザーログインでFacebookアカウントを利用する

More than 5 years have passed since last update.

はじめに

Titanium SDK 3.1以降、Titianium.Facebookモジュールではなく、Modules.Facebookに変更になってます。(詳しくは公式サイトを)

モジュールの導入時のセットアップで多少異なる点があるけど基本的な使い方としては大きな変更は無くその点での戸惑いはなかったのですが、Modules.Facebookを利用してACSへのユーザーログインでFacebookアカウントを利用する処理方法について情報がまとまっておらず、仕事で必要だったので調べたものをまとめることにしました。

想定する方

一応以下のような方を想定してまとめてみました。

  • Titanium Mobileでサンプルコードを書いたことはある
  • ACSという言葉は聞いたことがある
  • OAuthの概念はなんとなく知ってる

やりたいことはシンプルなのですがそれに関連するベースの知識が色々問われてくるため関連情報を以下簡単にまとめておきます

OAuthがわかりづらい人向けに

昔まとめたスライドがあってそのキャプチャー貼っておきます

shot-2015-01-04-8.13.09.png

shot-2015-01-04-8.11.10.png

ACSについて補足情報

  • ACSというのはTitanium Mobileの開発元であるAppceleratorが提供してるMBaaS(Mobile Backend as a Service)です。
  • AppceleratorCloudServicesの頭文字を取ってACSという言い方がよくされてます。ただ大分まえに名称がTitanium Cloud Servicesになってるので、TCSという言い方のほうが正しいのかな??
  • ACSの詳細は、英語ですが公式サイトをご覧ください

開発環境

  • Mac OS X 10.9.5
  • Titanium SDK 3.3.0.GA
  • XCode 5.1.1
  • alloy 1.4.1

開発前に必要な準備

プロジェクト設定

今回、ACSを利用するのでそれに合わせてプロジェクト設定を行います。

shot-2015-01-04-8.33.02.png

shot-2015-01-04-8.33.46.png

TitaniumのFacebookモジュールの読み込み設定

プロジェクト設定完了後にtiapp.xmlを以下のように編集します

編集前

    <modules>
        <module platform="commonjs">ti.cloud</module>
    </modules>

編集後

    <modules>
        <module platform="commonjs">ti.cloud</module>
        <module platform="iphone">facebook</module>
    </modules>

Alloy用の ACS Sync Adapter の配置

  1. PROJECT_FOLDER/app/assets配下に、alloyフォルダを作成して、その中にsyncフォルダを作成します
  2. 上記フォルダ配下にacs.jsというファイルを作成します
  3. aaronksaunders さんが作ったacs.jsのソースコードを上記2.で作成したacs.jsにコピペします

Facebook アプリID

これはTitaniumでの開発固有の話ではないのですが、Facebookと連携するアプリ開発をする場合にこの作業が必須になります。

開発者ページに行きます

shot-2015-01-04-8.38.32.png

今回はiOS向けアプリなのでこちらを選択

shot-2015-01-04-8.38.43.png

右上のリンクをクリックします

shot-2015-01-04-8.39.12.png

AppID登録に必要な内容を入力します

shot-2015-01-04-8.39.39.png

登録時に必要なセキュリティチェックの文字を入力します

shot-2015-01-04-8.39.47.png

登録完了

画面キャプチャーでは消去してますがここにAppIDが数字で記載されてるのでそれを控えておきます

shot-2015-01-04-8.40.28.png

ソースコードの概要

このページの最後にソースコード全体を記載しておきましたがAlloyのModelの処理とControllerの処理でちょっとハマり所があったのでその辺りちょっとだけ書いておきます

Modelの処理

Cloud.SocialIntegrations.externalAccountLoginを利用できるアダプターを準備します。

ソースコード全体は後述しますがポイントになる所の処理を抜粋します

  // 省略
  extendModel: function(Model) {
    _.extend(Model.prototype, {
      fbLogin: function(token, email, birthday){
        this.config.Cloud.SocialIntegrations.externalAccountLogin({
          type: 'facebook',
          token: token,
          email: email,         // (1)
          custom_fields: {
            birthday: birthday  // (2)
          }
        }, function (e) {
          // 省略
        });                
      }
    });
    return Model;
  }
  1. Web上で見かけるサンプルコードでは見かけなかったのですが、こういう感じでemailプロパティに、設定したいメールアドレスの情報を指定してあげることで、SocialIntegrations.externalAccountLoginの情報に対してメールアドレスの設定を行うことが出来ます。
  2. ACSのUserオブジェクトにはメールアドレスの設定項目が元々有るのですが、例えば誕生日のような項目は存在していません。その場合にはカスタムフィールドという仕組みを通じ独自の項目を設定することが出来ます。このような記述をしてあげることでカスタムフィールドに、birthdayという項目を設定して、任意の値を設定できます

Controllerの処理

Facebookアカウントを利用した認可処理のサンプルコードはWeb上にいくつもあったのですが、

  • Facebookアカウントでのユーザー認証処理
  • ユーザー認証が終わったあとに、このアプリがあなたのxxにアクセスしますがよろしいでしょうか?というよくみる認可のポップアップ処理

という処理の区分けをどこでどのようにしてるのかがわからずに大分苦労しました。

しかも、TitaniumのModules.Facebookの仕組みを通じて、login/logoutのイベントがあるようだったので、ログインという言葉の印象から、その段階でFacebookのアカウントの属性情報をすべて取得してるのかとおもったらそれはまた別処理になるようでこの辺りがかなりハマり所かなと個人的には思いました。

Facebookの情報にアクセスするために必要な各種設定を行う

var fb = require('facebook');
fb.appid = Ti.App.Properties.getString('ti.facebook.appid') // (1);
fb.permissions = ['publish_stream','email','user_birthday']// (2);
fb.forceDialogAuth = false;
  1. 今回のサンプルでは取得したFacebookのappidをtiapp.xml内にti.facebook.appidというプロパティ名で記述してます。
  2. 今回作成するアプリから、Facebookアカウントの情報へのアクセスする時に、publish_streamだけだと、氏名程度しか取得できないため、permissionsの項目にemailuser_birthdayという項目を追加します

ログインイベント発火した時の処理を書く

fb.addEventListener('login', function(e) {
  if (e.success) {
    fb.requestWithGraphPath('me',{},"GET",function(e) {
      // 省略
    );

  } else if (e.error) {
    Ti.API.info(e.error);
  } else if (e.cancelled) {
    Ti.API.info("Canceled");
  }
});

ログインボタン的なものを準備

iconというID属性を付与したButton要素を配置しておき、クリックイベントで、Facebookの認可処理を開始する処理を呼び出します

$.icon.addEventListener('click', function(e) {
  fb.authorize();
});

ソースコード全体

controllers/index.js

var fb = require('facebook');
fb.appid = Ti.App.Properties.getString('ti.facebook.appid');
fb.permissions = ['publish_stream','email','user_birthday'];
fb.forceDialogAuth = false;
fb.addEventListener('login', function(e) {
  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);
        }
      }
    );

  } else if (e.error) {
    Ti.API.info(e.error);
  } else if (e.cancelled) {
    Ti.API.info("Canceled");
  }
});

$.fbloginLabel.addEventListener('click', function(e) {
  fb.authorize();
});

$.index.open();


models/social.js

exports.definition = {
  config: {
    columns: {
      active: "boolean"
    },
    adapter: {
      type: "acs",
      collection_name: "socialIntegrations"
    },
    settings: {
      object_name: "socialIntegrations",
      object_method: "socialIntegrations"
    }
  },
  extendModel: function(Model) {
    _.extend(Model.prototype, {
      fbLogin: function(token, email, birthday){
        this.config.Cloud.SocialIntegrations.externalAccountLogin({
          type: 'facebook',
          token: token,
          email: email,
          custom_fields: {
            birthday: birthday
          }
        }, function (e) {
          if (e.success) {
            var user = e.users[0];
            Ti.API.info('Success:\n' +
                  'id: ' + user.id + '\n' +
                  'first name: ' + user.first_name + '\n' +
                  'last name: ' + user.last_name);
          } else {
            Ti.API.info('Error:\n' +
                  ((e.error && e.message) || JSON.stringify(e)));
          }
        });        
      }
    });
    return Model;
  },
  extendCollection: function(Collection) {
    _.extend(Collection.prototype, {});
    return Collection;
  }
};

index.xml

<Alloy>
    <Window class="container">
    <Label id="fbloginLabel">Facebookでログイン</Label>
    </Window>
</Alloy>

index.tss

".container": {
      backgroundColor:"white"
},

"#fbloginLabel": {
    top:"40%",
    left:"30%",
    width:"60%",
    height:"10%"
}
"#icon" :{
    top:"40%",
    left:"5%",
    image: "facebook-icon.png"
}

tiapp.xml

tiapp.xmlの中にFacebookのappidを記載しておきます

<property name="ti.ui.defaultunit" type="string">dp</property>
<!-- ここから追加箇所 -->
<property name="ti.facebook.appid">xxxxxxxxxxx</property>
<!-- ここまで追加箇所 -->

終わりに

OAuthについての理解&それをTitaniumのモジュールを使った場合にどのように実装するのかという所について何かの参考になればと思ってまとめてみました。

自分自身でOAuthの概念は理解してるつもりなのですが、文章にまとめて気づいたこととして、Module.Facebookの機能が自分の想像してるものと一部異なる印象があるためどうもこの辺りが要因になってなかなか理解が進まないのかなとふと思いました

h5y1m141@github
クラフトビールが好きすぎて、iPhone&Android&Webアプリの開発をTitanium+CoffeeScript+Node.js+αな感じでやってます http://craftbeer-fan.info/ あとは、非公式のQiita Viewerアプリ作ってます https://github.com/h5y1m141/TiQiita 仕事では気づいたらRails4中心です
http://h5y1m141.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away