古いXcodeに、新しいiOSがインストールされた実機を接続してインストール

依存しているライブラリの関係など、古いバージョンのXcodeでiOSアプリを開発しています。
ただ、iOSがアップデートされると実機へのインストール時に対応していないとエラーになります。
“iOS 12 not supported by Xcode 9.2 : Could not locate device support files”
のようなエラーです。

そんな時の対応方法です。

古いXcodeと最新のXcodeが共存している前提です。
Finderから最新のXcodeを右クリックして”パッケージの内容を表示”から下記ディレクトリに移動します。
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

同様に古いバージョンのXcodeから同じディレクトリを表示して、必要なiOSバージョンのディレクトリをコピーすれば、
新しいiOSの実機にもXcodeからインストールできます。

Monaca + Vue.js + Firebase で匿名認証

スマホアプリ開発でiOS、Androidの両方を行ってみて思うのが、やっぱり2つ開発はシンドイと思います。
で、考えるのはハイブリッド開発でしょうか?
Web、Xamarin、React nativeなどいくつか選択肢がありますが、まずはMonacaというHTML5ベースのプラットフォームを試します。

今回は試しにFirebaseと連携し匿名認証を行います。
ユーザーをこちらから特定する必要はないですが、ユーザーの属性情報だけ欲しいというような使い方です。
試して思ったのですが、FirebaseではSNSアカウント、メールアドレスなどで認証するのも簡単そうです。

Monacaの開発環境はクラウド上ではなくローカルで行いました。
MonacaでVue.jsのテンプレートから作成しています。
また、Firebaseで匿名認証を許可する設定まで完了してる前提でプログラムのみ紹介。

src/public/index.html.ejs

まずはFirebaseの設定です。最初 www/index.html に書いたのですが、何かのタイミングでファイルが戻ってしまう?現象があったので、index.html.ejs に書きました。

<body>
  <div id="app"></div>
  <script src="https://www.gstatic.com/firebasejs/5.5.5/firebase.js"></script>
  <script>
    // Initialize Firebase
    var config = {
      apiKey: "{Firebaseから}",
      authDomain: "{Firebaseから}",
      databaseURL: "{Firebaseから}",
      projectId: "{Firebaseから}",
      storageBucket: "{Firebaseから}",
      messagingSenderId: "{Firebaseから}"
    };
    firebase.initializeApp(config);
  </script>
</body>

ここの設定は Firebase console の “ウェブアプリに Firebase を追加” から取得できます。

src/Home.vue

Monacaのテンプレートに入っているHome.vueを変更しています。
イメージは起動時に匿名認証を行っているか判定し、行っていなければダイアログでユーザー属性を入力させ、Firebaseで匿名認証させます。
認証時の属性情報は Firebase の Cloud Firestore に保存します。
あとで気づいたのですが Cloud Firestore は現在ベータ版で、Webで”Firebase database”で検索すると Realtime Database の情報も出てくるので混乱しました。

<template id="main">
  <v-ons-page>
    <div style="display: table; height: 100%; width: 100%;">
      <div style="display: table-cell;text-align: center;vertical-align: middle;">
        <p>
          Hi!
        </p>
        <p>
          <v-ons-button modifier="quiet" style="background-color: transparent;" @click="signOut">
            登録解除
          </v-ons-button>
        </p>
      </div>
    </div>

    <v-ons-modal style="background-color: #FFFFFF;" :visible="showSignInModal">
      <div style="text-align: center; color: #333333;">
        <p>
          あなたのことを教えてください。
        </p>
        <p>
          <v-ons-select class="signInSelect" v-model="gender">
            <option disabled="disabled" value="">性別</option>
            <option v-for="(item, index) in genders" :key="index" :value="item.value">
              {{ item.text }}
            </option>
          </v-ons-select>
        </p>
        <p>
          <v-ons-select class="signInSelect" v-model="generation">
            <option disabled="disabled" value="">年齢</option>
            <option v-for="(item, index) in generations" :key="index" :value="item.value">
              {{ item.text }}
            </option>
          </v-ons-select>
        </p>
        <v-ons-button modifier="outline" class="signInBtn" @click="signIn">次へ</v-ons-button>
      </div>
    </v-ons-modal>

    <v-ons-modal :visible="showLoading">
      <p style="text-align: center">
        Loading <v-ons-icon icon="fa-spinner" spin></v-ons-icon>
      </p>
    </v-ons-modal>
  </v-ons-page>
</template>

<script>
export default {
  data() {
    return {
      showLoading: false,
      showSignInModal: false,
      gender: "",
      genders: [
        { text: "男性", value: "MALE" },
        { text: "女性", value: "FEMALE" },
        { text: "その他", value: "OTHER" }
      ],
      generation: "",
      generations: [
        { text: "10歳未満", value: "_10" },
        { text: "10-19歳", value: "10_19" },
        { text: "20-29歳", value: "20_29" },
        { text: "30-39歳", value: "30_39" },
        { text: "40-49歳", value: "40_49" },
        { text: "50-59歳", value: "50_59" },
        { text: "60-69歳", value: "60_69" },
        { text: "70-79歳", value: "70_79" },
        { text: "80歳以上", value: "80_" }
      ]
    };
  },
  methods: {
    signIn(event) {
      this.showLoading = true;
      // 匿名ユーザサインイン
      firebase
        .auth()
        .signInAnonymously()
        .catch(function(error) {
          alert(error.message);
        });
    },
    signOut(event) {
      firebase.auth().signOut();
    }
  created() {
    var self = this;
    firebase.auth().onAuthStateChanged(function(user) {
      if (user) {
        const firestore = firebase.firestore();
        firestore.settings({ timestampsInSnapshots: true });
        firestore
          .collection("users")
          .doc(user.uid)
          .set({
            gender: self.gender,
            generation: self.generation
          })
          .then(function() {
            self.showLoading = false;
            self.showSignInModal = false;
          })
          .catch(function(error) {
            console.error("Error writing document: ", error);
            alert("登録に失敗しました");
          });
      } else {
        self.showSignInModal = true;
      }
    });
  }
};
</script>

こんな感じでユーザー情報がFirebase の Authentication と Database(Cloud Firestore) に登録されます。

user.uid をキーにアプリデータをFirebaseのDatabaseに保存すれば、アンケートみたいなものは簡単に作れそうです。
もちろん要件によってはiOS、Androidを別々に開発すべきですが、要件を満たせるならハイブリッドで開発するのは十分にアリだと思います。
ただ、バックグラウンド処理やBLE周りとかどうなんでしょう?