Flutter開発を始めるときに最初にやっておくこと

Flutterのプロジェクトを立ち上げる機会があり、プロジェクト開始時にやっておいた方が良いと感じたものを列挙しました

目次

  • Widgetの学習
  • 静的解析
  • CI/CDの自動化
  • 状態管理方法の選定

Widgetの学習

Flutter開発をするにあたって、画面は提供されているWidgetを使って作っていきます。
iOS開発におけるUIKit(UILabelやTableView)のようなものです。
ある程度知らないと画面を組み立てられないですし、公式ドキュメントやYouTubeにまとまっているので一度目を通しておいた方が良いと思います。

flutter.dev

www.youtube.com

YouTubeは英語ですが、字幕をつけれますし動画があってわかりやすいのでオススメです。

静的解析

改行されていないことや、無駄なスペースがあるなどの本質的でないコードレビューをしなくて済むように最初に静的解析を入れておくと良いです。

Flutterではプロジェクト直下に analysis_options.yml を作成し、 flutter analyze コマンドを実行すると静的解析できま、細かい指摘だけでなくdart Flutter のより良い書き方も指摘してくれます。

また自動フォーマットもやりたい場合は flutter format -n ./lib を実行すると自動フォーマットできます。

dart.dev

開発途中から静的解析を入れて直して行くのは、いろんな箇所に影響が出てしまい導入するのが大変なので、経験上プロジェクトを始めるタイミングで入れておいた方のが良いと思います。

CI/CDの自動化

静的解析で触れましたが、途中で入れづらいのがCI/CDの自動化です。
プロジェクト開始時にワークフローを定義しておいた方がいいです。

Flutter 開発では以下のようなワークフローがあると便利です。

  • 静的解析の実行 (flutter analyze, flutter format -n ./lib --set-exit-if-changed)
  • 単体テストWidgetテスト実行 (flutter test)
  • デプロイ

iOS開発の場合は、dSYMのアップロードも自動化しておきましょう。

状態管理方法の選定

まだ新しい手法が出てきているタイミングで、完全にこのパターンが良いというのは現状ありません。そのため、アプリの特性に合わせて選定する必要があります。 管理方法のパターンが公式ドキュメントにまとまっているのでこちらを参考に考えると良いと思います。

flutter.dev

Web support for Flutter のプロジェクトを GitHub Action で Firestore Hosting にビルド&デプロイする

年末休みの間に、Flutter Webを使ったサービスを作りFirebase Hosting で公開したので公開手順をまとめました。

Flutter Webでアプリ開発する

2020年12月時点だと、Flutter でWeb開発をするには beta 版を使う必要があります。

セットアップはFlutterの環境構築後に以下コマンドを実行することでが完了します。

flutter channel beta
flutter upgrade
flutter config --enable-web

flutter.dev

セットアップ後に flutter create myapp を実行すると以下のようなディレクトリ構造でアプリが作成されます。

> myapp tree -L 1
.
├── README.md
├── android
├── build
├── integration_test
├── ios
├── lib
├── myapp.iml
├── pubspec.lock
├── pubspec.yaml
├── test
└── web // これが作成されていること

Firebase Hostingの設定を行う

Firebase Hosting にデプロイを行うのでその設定を行います。

GitHub Actionsの設定もfirebase cliがやってくれるので先にリポジトリを作り以下コマンドを実行します。
※ あとで細かい設定は手動で直せるので一旦デフォルトの設定で作ってしまっても大丈夫です。

firebase init

生成された firebase.json を以下のように修正します。

{
  "hosting": {
    "public": "build/web", // Futterが書き出すディレクトリに変更
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

ここまでできれば、以下コマンドを実行することでデプロイできます。

flutter build web
firebase deploy

GitHub Actions でデプロイを自動化する

先程のコマンドを実行時にデプロイの設定をしておけばある程度自動生成してくれていると思います。 自動生成された yml ファイルは nodejs 用になっていると思うので以下のように修正をします。

name: Deploy to Firebase Hosting

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-java@v1
        with:
          java-version: '12.x'
      - uses: subosito/flutter-action@v1
        with:
          flutter-version: '1.25.0-8.1.pre'
          channel: beta
      - run: flutter config --enable-web
      - run: flutter pub get
      - run: flutter build web
      # 以下は自動生成されたものをそのまま使う
      - uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: '${{ secrets.GITHUB_TOKEN }}'
          firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_XXXX }}' // Firebase init で生成された環境変数
          channelId: live
          projectId: xxxx // FirebaseのプロジェクトID
        env:
          FIREBASE_CLI_PREVIEWS: hostingchannels

以上でデプロイのワークフローを設定することができました。
この設定だと main ブランチにプッシュするたびに自動デプロイされるのでお好みのワークフローに改善して使ってください。

終わりに

多くの記事では firebase ci:login で生成した token を使ってデプロイする方法が出回っています。
これだと生成したユーザーとしてFirebaseのコマンドが実行されるため、生成した人がやめたときのことや、トークンの管理を考えるとプロジェクトに紐づく Service Account で行ったほうが良いので firebase init 時に生成された Service Accountを使うのが良いと思います。

2020年 買って良かったもの

ディスプレイとゲーミングチェア

decoch.hatenablog.com

decoch.hatenablog.com

リモートワークが増えたのですぐに購入しました。最初は知らないメーカーで不安もありましたが非常に費用対効果が高かったです。
ノートパソコンで作業をすると姿勢が悪くなりすぐに肩が痛くなるので助かっています。

Mac mini

www.apple.com

もともとMacBook Proを使っていたのですが、Xcodeでビルドすると時間が非常にかかり、発熱もすごかったのでMac miniを購入しました。 フルカスタマイズしたおかけでビルド時間が短縮でき、ビルド中に他の作業もできるようになり満足しています。
ただ購入直後にm1のMacが発売されたのでもう少し待ってもよかったのかなと少しだけ後悔しています。

USBスイッチ

https://www.amazon.co.jp/gp/product/B072R16Z6S/

一社はPCを支給していただく形で働いており、Mac miniと支給されたPCを切り替えることが多かったので、 USBスイッチを購入しました。 購入前はコードの抜き差しが必要だったのですが、ボタン一つでキーボードとマウスを切り替えられるようになりました。

ディスプレイの切り替えだけまだコードの抜き差しが必要なのでHDMIのスイッチも購入しようかと思っています。

Amazon Alexa

https://www.amazon.co.jp/gp/product/B07PHPYPYK/

Amazonのセールで購入したので¥3,500ぐらいで購入できました。
手が離せない時にスマホを出さずに料理のタイマーや買い物リストへの追加をすることができ便利です。 年末には Nature Remo を購入したのでエアコンやテレビの操作もAlexaでできるようになりました。

2020年の振り返り

仕事納めしたので1年を振り返ってみたいと思います。(今年から毎年振り返りをちゃんとしていきたい)

フリーランス

今年の2月末に体調を崩してしまい会社を退職後したのですが、すぐに新型コロナウイルスの影響がありました。
この影響で面接予定の企業との連絡が取りづらくなったり、案件がなくなったりとタイミングがよくなかったので、知り合いに相談したところ2件ほどお仕事を紹介していただきフリーランスとして働くことになりました。

今は iOS アプリの開発と Flutter でのアプリ開発をメインでしています。

この大変な時期に参画させてくださりありがとうございました。

仕事関係

上述の退職前は、バックエンド開発とiOSの開発を半々ぐらいで行ってきたキャリアだったのですが、 今年は、バックエンドはFirebaseメイン、アプリはiOS, Flutter をやっておりアプリ開発に特化した1年でした。

CI/CD構築や、技術選定など責任ある仕事も任せていただきました。

今後の主軸をアプリにしていこうと思っていたので、良い案件に参加させていただけたと思っています。

リモートワーク

5月以降はコロナウイルスの影響で完全リモートになりました。一人で集中できる環境が手に入ったのはよかったのですが、外に出れずストレスがたまることは多かったり時間を同期して働きづらいので仕事の効率はあまり変わらなかったと思います。

自宅で働く時間が増えたので、ゲーミングチェアやディスプレイを購入して働く環境の改善もすることができました。

HKCの34インチのウルトラワイドディスプレイを買った - decochのブログ

作業用にゲーミングチェアを買った話 - decochのブログ

また、出社しないので郊外に引っ越したのですが、部屋が広くなったり、大きなスーパーが近くにあったりと非常にQOLが上がりました。

来年の抱負

今年は前述のように体調を崩したので、のんびりと過ごす時間を多く作ることができたと思います。

来年は引き続きモバイルアプリ開発を中心に働いて行こうと考えていますが 今年も外出はあまりできそうにないので、体調を崩さないよう気をつけていきたいと考えています。

SwiftUIのプレビューがうまくいかない時はカラー定義を見直してみよう

これはなに?

SwiftUIで開発をしていると開発初期はプレビューを使い効率的に開発ができていたのですがコード量が増えたとたんプレビューが表示されなくなりました。
どういったコードを書くとプレビューが表示されなくなるのか気になったので調べてたものです。

開発環境

  • Xcode12.3
  • MacOS BigSur 11.0.1

どういったコードを書くとダメなのか

デフォルトで定義されているカラーを extension で上書き使用するとプレビューが動かなくなります。

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .foregroundColor(.primary)
            .padding()
            
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

extension Color {
    static let primary = Color("Primary")
}

プレビュー画面

f:id:decoch:20201215120529p:plain
preview error

他にも .white , .black などがあります。今回サンプルに使った .primary というプロジェクト内のカラー定義で使いたい名前もデフォルトで定義されています。
独自カラーは割り切ってプロジェクト名をPrefixにつけて色を管理すると被りづらくプレビューを使ったまま開発ができて良いと思います。

iOSのプロジェクトに入ったらやること

SwiftでのiOSアプリ開発の仕事を請けることが多いのですが、 プロジェクトの初期や、途中から入った時に最初にやっておいた方がいいことを自分の備忘録としてまとめてみた。

  • Bitriseへの移行
  • dSYMのアップロード自動化
  • テスト自動化
  • TestFlight配布の自動化
  • デプロイ自動化
  • swiftlintの導入
  • swiftlintのルール管理
  • fastlane match での証明書管理
  • 開発版と本番を端末上に共存できるようにする
  • 開発版と本場のFIrebaseを分ける
  • Firebase Crashlytics でのクラッシュレポート
  • Firebase App Distribution での 開発版/本番アプリ配布
  • Firebase Performance の導入
  • Dynamic Links での起動
  • APNsをFirebase経由にする
  • storyboard と ViewController を1対1にする
  • Swift バージョンアップ
  • 強制アップデート
  • Associated Domainsの設定

KVS(Key Value Store)における履歴データのキー設計

目次

  • これは何?
  • 履歴データのキー設計
  • この設計をしない方が良いケース
  • まとめ

これは何?

大量のデータを扱うようになったためDynamoDBやFirestoreなどのKVSを扱う機会が増えています。KVSであるDynamoDB、Cloud Firestore、Cloud Bigtableで大量データになりがちな履歴データの設計をする機会があったのでその時に考えた設計をまとめたもの。

f:id:decoch:20200918144123p:plain

f:id:decoch:20200918144126j:plain

履歴データのデータ設計

この記事で扱う履歴データとは、会員のログイン履歴や、行動履歴のような「時間+何らかのデータ」を新しい順 or 古い順に保持する時系列データのことを想定しています。

KVSにおいてまず最初に考えることは、「Keyを何にするか」だと思います。

KVSのKeyの特徴として大抵の場合、データはKeyの辞書順に並びます。(例えば、keyが a, 1, 2 という3つのデータがあった場合、ソートを指定せずに取得を行うと、1, 2, a という順番でデータが返ってくるということ)

この特性を使って履歴データを扱う際は、

  • 新しい順で取得したい場合: タイムスタンプの反転(java.lang.Long.MAX_VALUE など)からタイムスタンプの値を引いた値
  • 古い順で取得したい場合: タイムスタンプ

のようにするとソートされた状態でデータを格納することができます。

これによりソートの指定をする必要がなくなり、複合Indexの設定を減らすことができる。

また、タイムスタンプだけでデータの登録をすると、データベースによっては読み込み&書き込みが一部のデータに偏りホットスポットができてしまうという問題があります。 親となるデータのID+タイムスタンプのようにすると処理するデータを分散し管理することができます。

データを絞り込んで表示したい場合は、DBによりクエリの指定方法は変わりますが文字列比較を行うKeyだけで絞り込むことができます。(Candidate_20200701 ≦ id < Candidate_20200801だと2020年の7月のデータを取得できる)

この設計はしない方が良いケース

時系列を使った絞り込みやソートを使わないケースでは、キーを有効活用できなくなるためこの設計はせずにデータの特性に合わせてキー設計をした方が良いです。

まとめ

  • 履歴データを管理する際はTimestampを利用したキー設計にすると絞り込み、ソートが簡単に行える
  • Prefixつけることでホットスポットになりづらいキー設計をすることができる
  • 時系列で扱わない場合は別のキー設計にした方が良い