20200404のiOSに関する記事は5件です。

iOS開発でDEV環境を構築する

iOSアプリを運用していくにあたって、アプリの配布やサーバーサイドとの連携は必須となります。
その際にクライアント側でDevやStaging、Releaseなど環境を使い分けたいケースは必ず出てきます。
今回はXcode上でDev環境を作る一連の流れをまとめてみました。
複数の環境を追加したい場合もこの流れで同じことを繰り返していけば良いので、簡単です。

この記事ではConfigrationを変更することで、DEV環境を作ります。
他にもTargetsを増やす方法やxcconfigファイルを使用する方法もあるので、チェックしてみてください。

ProjectのConfigurationのNameをDebug -> Developに変更

単純にリネームです。
3つ以上環境使い分けたいケースでは、ここで追加をしてください。

スクリーンショット 2020-03-30 22.19.53.png

Preprocessor Marcos(Project -> Build Setting)で、DEBUG=1 -> Develop=1に変更

スクリーンショット 2020-03-30 22.23.40.png

Other Swift flagの設定

#if DEVELOP とかできるようにするために行う。
-D <flag名>の形式で設定する。
複数やる場合は、-D <flag名> -D <flag名>

スクリーンショット 2020-03-30 22.30.10.png

DEV sheme作成

sheme選択画面でDEV版が選べるようになります。
↓こんな感じ。

スクリーンショット 2020-03-30 22.43.06.png

Manage schemeを選択。

該当プロジェクトを選んで、Editボタンをクリック。
スクリーンショット 2020-03-30 22.35.24.png

Duplicate schemeをクリックして、既存のshemeを複製。
スクリーンショット 2020-03-30 22.35.40.png

名前がcopy of <プロジェクト名>となっていると思うので、本番と見分けがつくように名前を設定。

スクリーンショット 2020-03-30 22.38.32.png

Run, Test, Profile, ArchiveのBuild configurationをチェックする。
今回はRun, ArchiveのときにDevelopを向いてるようにしました。
スクリーンショット 2020-03-30 22.39.43.png

Product Bundle Identifierを変更
スクリーンショット 2020-03-30 22.50.54.png

以上の作業で基本的なDEV環境作成はできていて、別々のアプリとして認識することが可能となりました。
次からは見た目もわかりやすくしていく作業です。

Product nameを変更

ここでアプリのアイコンの名前を変更することができます。
スクリーンショット 2020-03-31 4.11.03.png

Targets -> Build Setting -> Product Nameから$(TARGET_NAME)DEVに変更。

スクリーンショット 2020-03-30 23.20.33.png

アプリのアイコンを変更

まずはDev用のAppIconを作成し、AppIcon_devと命名します。
スクリーンショット 2020-04-04 22.26.39.png

Targets -> Build Settings -> Asset Catalog App Icon Set Nameを開き、Developのところを、AppIcon_devとします。
スクリーンショット 2020-04-04 22.28.55.png

アイコンが切り替わりました!
スクリーンショット 2020-04-04 22.31.34.png

FirebaseのDev版作成

iOS開発において、分析や配布のためにFirebaseを使用しているケースは多々あると思います。
DEV環境を作った際にはFirebaseでもその対応をする必要があります。

まずはFirebase上で、DevとReleaseでそれぞれプロジェクトを作成します。
その際にどちらもGoogleService-info.plistファイルを保存しておきます。
スクリーンショット_2020-04-04_22_09_01.png

こんな感じで名前を変えて、配置します。
スクリーンショット 2020-04-04 22.12.59.png

Targets -> Build Phase -> 左上のプラスボタンから新規でRun Scriptを作成します。
CONFIGURATIONに応じて、参照するGoogleService-Inforファイルを切り替えて、BUILT_PRODUCTS_DIRディレクトリにコピペしています。

if [ "${CONFIGURATION}" == "Release" ]; then
cp "${PROJECT_DIR}/${PROJECT_NAME}/GoogleService/GoogleService-Info.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
else
cp "${PROJECT_DIR}/${PROJECT_NAME}/GoogleService/GoogleService-Info-Develop.plist" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app/GoogleService-Info.plist"
fi

スクリーンショット 2020-04-04 22.32.38.png

ハマったところ

アプリ起動後、画面を読み込む部分でクラッシュしました。
原因はDEV環境内で、Storyboard内のUIViewControllerのModuleが本番環境を向いていることでした。
Inherit Module From Targetにチェックをつけると、Moduleを指定しなくても、よしなにやってくれるので、クラッシュする場合は該当のVCのこの箇所をチェックすると良いです。
参考
スクリーンショット 2020-03-31 4.15.44.png

error: Could not get GOOGLE_APP_ID in Google Services file from build environment

これはFabric初期化のタイミングでGoogleService-Info.plistが生成されていないのが原因です。
Fabric導入した際に、"${PODS_ROOT}/Fabric/run"をRun Scriptとして追加したと思いますが、Firebase ConfigurationのScriptはそれより前に実行されるようにしてください。
スクリーンショット_2020-04-05_1_13_06.png

参考リンク

https://qiita.com/Todate/items/a2e6a26731c79bd23e02#%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%82%A2%E3%82%A4%E3%82%B3%E3%83%B3
https://qiita.com/KazaKago/items/aacf6eaec65b6d4244d0

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iosアプリ開発 初心者

はじめに

これからiosアプリを開発していくにあたって日々学んだことをアウトプットしていきます。
少しでも参考になれたらと思います。

Mac

AndroidアプリはMacとWindowsどちらでも開発可能ですが、iPhone用のアプリを開発するにはMacが必要です。

これからアプリを開発していこうと考えている方はMacのPCを
買うことをおすすめします。

Xode

XcodeはAppleが無料で提供しているiosアプリ開発では王道のツールです。
iPhone、iPad、Mac、Apple Watchのアプリを開発できます。

※XcodeはMacでのみ動作しますが、最新バージョンのXcodeをインストールするためには、MacのOSも最新バージョンである必要があります。

Swift

SwiftはAppleが開発した新しいプログラミング言語で2014年から使わています。

対象としているOSは、MacOSとiOSで、PhoneやiPadなどの端末で使えるアプリ、パソコン向けのアプリを開発することができます。

これまではObjective-C使われていましたが、スピード・安定性・コスト・メンテナンス面から考えるとSwiftが優れています。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Flutter] Dribbbleデザイン、そのまま作ろう その15

こんにちは 皆さん!Dreamwalkerです。
皆さんはFlutterを使って楽しんで開発進んでいますか?

今回は15回目の私がDribbbleのサイトの中で、
気に入れたデザインをFlutterで作ってみた。。ということになります。

Dribbbleというサイトは色んなデザイナーさん達が色んなデザインを載せていて
デザインをの知識というか、デザイナーじゃない人でも、開発に参考になったり、役に立ったりするサイトです。まあ、デザインに対して、色んなサイトありますけれど私はDribbbleを愛用してます。

:fire:今回のデザイン:fire:

今回は15番目の「DribbbleのデザインをFlutterでやってみた」になります。

Flight Ticket

右側の席を選ぶ画面を作ってみたいと思います。

image.png
https://dribbble.com/shots/10862680-Flight-Ticket/attachments/2517650?mode=media

:clap:結果:clap:

Screenshot_1586002832.png

:fire:必要なライブラリー:fire:

1.google_fonts: ^0.3.10

データモデル

seat.dart
class Seat {
  final bool isAvailable;
  Seat({this.isAvailable});
}

2次元配列

main.dart
_tmp = List.generate(_column, (index) => 
List<Seat>.generate(_row, (index) => 
Seat(isAvailable: random.nextBool())));
    _tmp.forEach((element) {
      element.forEach((e) {
        print(e.isAvailable);
   });
});

:clap:全てのコード

main。

main.dart
import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutternotebook5th/generated/i18n.dart';
import 'package:google_fonts/google_fonts.dart';

class Seat {
  final bool isAvailable;
  Seat({this.isAvailable});
}

class FlightTicketApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: TicketPage(),
    );
  }
}

class TicketPage extends StatefulWidget {
  @override
  _TicketPageState createState() => _TicketPageState();
}

class _TicketPageState extends State<TicketPage> {
  Color appColor = Color(0xff3959b4); // 57 89 180 dec to hex 39 59 b4
  List<Seat> _item;
  int _row = 10;
  int _column = 14;
  List<List<Seat>> _tmp;

  @override
  void initState() {
    // TODO: implement initState
    Random random = Random();
    //TODO: One Dimension Array
    _item = List<Seat>.generate(10, (index) => Seat(isAvailable: random.nextBool()));
    _item.forEach((element) {
      print(element.isAvailable);
    });

    //TODO : 2 Dimension Array
    _tmp =
        List.generate(_column, (index) => List<Seat>.generate(_row, (index) => Seat(isAvailable: random.nextBool())));
    _tmp.forEach((element) {
      element.forEach((e) {
        print(e.isAvailable);
      });
    });

    super.initState();
  }

  List<Widget> _buildSeatTitle() {
    return <Widget>[
      Expanded(
        child: Container(),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "A",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "B",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "C",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "D",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "E",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "F",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "G",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "H",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "I",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(
          child: Center(
            child: Text(
              "J",
              style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
            ),
          ),
        ),
      ),
      Expanded(
        child: Container(),
      ),
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: <Widget>[
            Positioned(
              left: 8,
              right: 24,
              top: 0,
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: <Widget>[
                  Icon(Icons.keyboard_arrow_left),
                  Spacer(),
                  Text(
                    "Your Ticket",
                    style: GoogleFonts.montserrat(fontSize: 20),
                  ),
                  Spacer(),
                ],
              ),
            ),
            Positioned(
              left: 0,
              right: 0,
              top: 48,
              bottom: 0,
              child: Container(
                decoration: BoxDecoration(
                  color: appColor,
                  borderRadius: BorderRadius.circular(24),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    Expanded(
                      flex: 2,
                      child: Padding(
                        padding: const EdgeInsets.only(left: 24, top: 16),
                        child: Text(
                          "Select Seats",
                          style: GoogleFonts.montserrat(color: Colors.white, fontWeight: FontWeight.w500, fontSize: 16),
                        ),
                      ),
                    ),
                    Expanded(
                      flex: 28,
                      child: Column(
                        children: <Widget>[
                          Expanded(
                            flex: 1,
                            child: Row(
                              children: _buildSeatTitle(),
                            ),
                          ),
                          Expanded(
                            flex: 15,
                            child: ListView.builder(
                                itemCount: _tmp.length,
                                itemBuilder: (context, index) {
                                  List<Seat> _seatItems = _tmp[index];
                                  int idx = 30 + index;
                                  return Padding(
                                    padding: const EdgeInsets.all(6.0),
                                    child: Row(
                                      children: <Widget>[
                                        Expanded(
                                            child: Text(
                                          idx.toString(),
                                          style: GoogleFonts.montserrat(fontSize: 12, color: Colors.white),
                                        )),
                                        Expanded(
                                          child: _seatItems[0].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: _seatItems[1].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: _seatItems[2].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: Container(),
                                        ),
                                        Expanded(
                                          child: _seatItems[3].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: _seatItems[4].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: _seatItems[5].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: _seatItems[6].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(child: Container()),
                                        Expanded(
                                          child: _seatItems[7].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: _seatItems[8].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: _seatItems[9].isAvailable
                                              ? Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                    color: Colors.white,
                                                    borderRadius: BorderRadius.circular(6),
                                                  ),
                                                )
                                              : Container(
                                                  margin: EdgeInsets.symmetric(horizontal: 3),
                                                  height: 20,
                                                  width: 24,
                                                  decoration: BoxDecoration(
                                                      border: Border.all(color: Colors.white),
                                                      borderRadius: BorderRadius.circular(6)),
                                                ),
                                        ),
                                        Expanded(
                                          child: Container(),
                                        ),
                                      ],
                                    ),
                                  );
                                }),
                          )
                        ],
                      ),
                    ),
                    Expanded(
                      flex: 4,
                      child: Column(
                        children: <Widget>[
                          Expanded(
                            child: Padding(
                              padding: const EdgeInsets.symmetric(horizontal: 16),
                              child: Row(
                                children: <Widget>[
                                  Container(
                                    height: 24,
                                    width: 24,
                                    decoration: BoxDecoration(
                                        color: Colors.indigoAccent, borderRadius: BorderRadius.circular(6)),
                                  ),
                                  Padding(
                                    padding: const EdgeInsets.all(8.0),
                                    child: Text(
                                      "Choosen",
                                      style: GoogleFonts.montserrat(color: Colors.white, fontSize: 11),
                                    ),
                                  ),
                                  Spacer(),
                                  Container(
                                    height: 24,
                                    width: 24,
                                    decoration: BoxDecoration(
                                        border: Border.all(color: Colors.white),
                                        borderRadius: BorderRadius.circular(6)),
                                  ),
                                  Padding(
                                    padding: const EdgeInsets.all(8.0),
                                    child: Text(
                                      "Available",
                                      style: GoogleFonts.montserrat(color: Colors.white, fontSize: 11),
                                    ),
                                  ),
                                  Spacer(),
                                  Container(
                                    height: 24,
                                    width: 24,
                                    decoration:
                                        BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(6)),
                                  ),
                                  Padding(
                                    padding: const EdgeInsets.all(8.0),
                                    child: Text(
                                      "Blocked",
                                      style: GoogleFonts.montserrat(color: Colors.white, fontSize: 11),
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          ),
                          Expanded(
                            child: Padding(
                              padding: const EdgeInsets.only(right: 24, top: 8),
                              child: Align(
                                alignment: Alignment.centerRight,
                                child: MaterialButton(
                                  elevation: 8,
                                  color: appColor,
                                  onPressed: () {},
                                  child: Text(
                                    "Confirm",
                                    style: GoogleFonts.montserrat(
                                      fontSize: 12,
                                      color: Colors.white,
                                    ),
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                    Expanded(flex: 4, child: Container()),
                  ],
                ),
              ),
            ),
            Positioned(
              bottom: 0,
              right: 0,
              left: 0,
              child: Container(
                height: 64,
                padding: EdgeInsets.symmetric(horizontal: 24),
                decoration: BoxDecoration(
                    color: Colors.grey[50],
                    borderRadius: BorderRadius.only(
                      topLeft: Radius.circular(24),
                      topRight: Radius.circular(24),
                    )),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: <Widget>[
                    IconButton(
                      icon: Icon(Icons.home),
                      onPressed: () {},
                    ),
                    IconButton(
                      icon: Icon(Icons.card_giftcard),
                      onPressed: () {},
                    ),
                    IconButton(
                      icon: Icon(Icons.style),
                      color: Colors.indigoAccent,
                      onPressed: () {},
                    ),
                    IconButton(
                      icon: Icon(Icons.person_outline),
                      onPressed: () {},
                    )
                  ],
                ),
              ),
            )
          ],
        ),
      ),
    );
  }
}

:clap:終わりに

今回も読んでくださってありがとうございます。:bow_tone2:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift UITableViewの編集モードでのセル並び替え機能

canMoveRowAtを呼び、trueにする。

sample.swift
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
        return true
    }

moveRowAtクラスに並び替えの細かい操作を記入する。

sample.swift
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
     //並び替え中に他に行いたい操作を書く(データの保存など)
        }

上の2つのクラスどちらも呼び出さないと並び替えをすることは可能にならない。
canEditRowクラスを呼び出さなくても編集モード中に並び替えだけは可能になる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】Firebase Cloud Messagingで利用するAPNs認証キー・証明書の作り方

以前開発したときは、APNs証明書だけだったのに知らぬうちに2つの方法ができていたので備忘録です。

そもそもAPNsとは?

Apple Push Notification Service(以下、APNs)は、iOSやtvOS、MacOS上で実行されているアプリケーションに対して開発者側から通知を送るためのAppleによるサービスのことを指します。
誤解されがちなのは、Firebase Cloud Messaging(以下、FCM)などの通知を行えるサービスと各アプリケーションが1対1で繋がっているようにとらわれがちですが、Appleデバイスを対象に通知を送付する際は必ずAPNsを介して送付されています。

当たり前ですが、APNsに対してどんなリクエストでも送れてしまったら意図しない通知がユーザーの手元に届いてしまいますので、サーバー側とAPNs間のセキュアな通信を担保するために証明書または、認証キーが必要です。

認証キーと証明書

現在は、Appleデバイスに対してPush通知を送るために用いる認証方法が2つ存在しています。
記事によってはどちらかのみ記載している例があるため、それぞれ記載します。

Apple Push Notification Service SSL

今まで、Push通知を行う際に用いられてた証明書タイプの認証方法です。
アプリごとに証明書を発行する必要があり、発行から1年間の有効期限付きです。
また、SandboxとProductionそれぞれの証明書を発行する必要があります。
Sandboxでは上手く行っていたのに、AdhocやReleaseのアプリに通知が飛ばないとかは、証明書の登録漏れとかが原因だったりすることが多いです。

Apple Push Notification Authentication Key

こちらが新しい方法。(新しいといってもだいぶ前からなんですが、Push関連やることが少なかったので浦島太郎でした。)
アプリごとに証明書を用意する必要がなくベンダー共有の認証キーです。
有効期限がないため、更新する手間がないのがメリットです。

基本的には、こちらの認証方法が現在では推奨されています。

発行方法

Apple Push Notification Authentication Key

スクリーンショット 2020-04-04 19.18.39.png
Apple Developer Programのページにアクセスし、左側の「Program Resources」以下に表示されている「Certtificates, Identifiers & Profiles」をクリックします。
スクリーンショット 2020-04-04 19.25.01.png
次に「Keys」にアクセスし、Keys横のプラスボタンをクリックします。
スクリーンショット 2020-04-04 19.26.54.png
Keyの登録画面になるので、Keyの名称とAPNsのチェックボックスにチェックを入れてContinueをクリックします。
再度確認画面が表示されるのでRegisterをクリックするとKeyを生成することができます。
スクリーンショット 2020-04-04 19.29.30.png
生成後、p8形式のキーのダウンロードが可能になります。生成されたキーは1度だけダウンロードできます。
1度ダウンロードした後、再ダウンロードすることはできませんので注意が必要です。

Apple Push Notification Service SSL

こちらが古い方法。
Authentication Keyと異なりいくつかのステップがあります。(Developer Programのページで完結しないのが面倒)スクリーンショット 2020-04-04 20.10.40.png

1.Certificate Signing Requestの発行

Certificate Signing Reques(以下、CSR)とは、証明書を発行するときに認証局に対してメッセージのことです。
CSRの発行は、手持ちのMacのキーチェーンアクセスアプリから行います。
スクリーンショット 2020-04-04 19.37.21.png
キーチェーンアクセスから証明書アシスタント認証局に証明書を要求を選択します。
スクリーンショット 2020-04-04 19.47.00.png
上記の画面が表示されるので、メールアドレスと通称部分に任意の内容を記入します。
要求の処理では、ディスクに保存鍵ペア情報を指定にチェックを入れます。
入力とチェックを入れたら続けるを選択します。
CertificateSigningRequest.certSigningRequestの保存先の選択が求められるので、任意のディレクトリに保存しましょう。
スクリーンショット 2020-04-04 19.50.16.png
鍵ペア情報の選択が求められますが、こちらは初期値のままで大丈夫です。
スクリーンショット 2020-04-04 19.51.09.png
完了すると上記画面が表示されます。
以上でCSRの発行は完了です。

2. 証明書の生成

スクリーンショット 2020-04-04 19.18.39.png
認証キー同様に、Apple Developer Programのページにアクセスし、左側の「Program Resources」以下に表示されている「Certtificates, Identifiers & Profiles」をクリックします。
スクリーンショット 2020-04-04 20.03.14.png
次に「Certificates」にアクセスし、Certificates横のプラスボタンをクリックします。
スクリーンショット 2020-04-04 20.04.10.png
Create a New Certificate画面が開きますので、Servicesの項目配下にあるApple Push Notification service SSLにチェックを入れます。
証明書にはSandbox環境だけへの接続を認可するものとSandboxとProduction環境どちらへの接続を認可するものがあります。
後者のキーだけでも開発用APNsキーと本番用APNsキーを兼用することはできますが、基本的にはSandbox用とProduction用は別々に発行したほうが良いと思います。
スクリーンショット 2020-04-04 20.09.06.png
Platformと通知を利用するアプリのAppIDの選択が求められるので、通知を利用するAppIDを探し選択し次に進みます。
スクリーンショット 2020-04-04 20.10.40.png
先程発行したCSRファイルの登録が求められるので、Choose Fileから発行したCSRファイルを選択します。
スクリーンショット 2020-04-04 20.12.20.png
次へ進むと証明書のダウンロードが可能になります。生成した証明書をダウンロードしましょう。
cer形式の証明書がダウンロードできます。

3. p12形式に変換する

Firebase Cloud Messagingでは、cer形式の証明書のままではアップロードできませんので手元でp12形式で書き出す必要があります。
スクリーンショット 2020-04-04 20.16.30.png
ダウンロードしたcer形式の証明書をダブルクリックし、キーチェーン.appに証明書を追加します。
スクリーンショット 2020-04-04 20.17.32.png
追加した証明書上で右クリック「キー名」を書き出すを選択します。
スクリーンショット 2020-04-04 20.22.24.png
証明書の名称、保存先の入力選択が求められるので任意の内容に設定します。フォーマットは「個人情報交換(.p12)」のままで大丈夫です。
スクリーンショット 2020-04-04 20.23.35.png
保存時にパスワード設定が求められます。任意のパスワードを設定して書き出しましょう。
OKを押すとまれにキーチェーンアクセスからのパスワード入力を求められることがありますが、Macのログインパスワードを入力すればOKです。
任意のパスにp12形式の証明書が書き出されていれば成功です。

Firebaseに認証キー・証明書をアップロードする

スクリーンショット 2020-04-04 20.26.06.png
Firebaseのコンソールを開き、Push配信を行うプロジェクトを選択します。
選択後、プロジェクトの概要横に表示されている歯車アイコンをクリックし、設定ページを表示します。
次にクラウドメッセージングの設定項目を選択し、iOSアプリの設定から証明書をアップロードすることができます。
Push通知を送るには、認証キー・証明書どちらかをアップロードすればFCMからAPNsを通して、アプリがインストールされている端末に対して通知を送ることが可能になります。

重ねてにはなりますが証明書を用いる場合、開発環境用と本番環境用それぞれの証明書をアップロードする必要があります。
片方だけアップロードしてもどちらかの環境では通知が届かないということが発生しますので注意が必要です。

おわりに

FCMでは、現在認証キーを用いることが推奨されていますが未だに証明書形式でのFCMの利用方法などが記載されている事例が見られたので、改めて2つの方法があるよーということを書かせていただきました。
駆け足にはなりましたが参考になりますと幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む