未熟学生エンジニアのブログ

未熟エンジニアのブログ

FlutterやWeb周り全般についてのブログ

Flutterを一年触って、エンジニアとして良かったこと

はじめに

2020年5月現在、Flutterを2019年の3月から初めてから1年と少し経ったので、Flutterを触ってきてエンジニアとしてよかったことについてまとめたいと思います。

Flutterが持つ特徴として、以下があると思っています。

  • 比較的新しいフレームワークである(React Nativeなどと比較)
  • 日本語の情報が少ない

これらは、ちょうど良い制約、またはチャンスとして捉えることができる と思っています。どういった良い制約、チャンスがあったのか、説明していきたいと思います。

GitHubで他の人が書いたコードを読む習慣がついた

特に mono0926 さんntaoo さんkaboc さんrrousselGit さん のコードを読みました。

正直理解が及ばない部分も多かったのですが、 そもそもあまり今までGitHubでコードを参考にすることがなかったので、良い書き方について多くの点を学べました。サンプルコード全体を読むことで、ディレクトリ構成やクラス、Widgetの切り分け方も学ぶことができました。

Flutterの場合、アーキテクチャ関連のトレンドの変化が速い(ScopedModel -> Provider、BLoC -> ChangeNotifierなど)印象なのですが、 解説記事だけでは(英語の場合が多いということもあり)理解できない部分も多かったため、サンプルコードを読む機会が増えました。これらの努力は時間がかかるという意味で制約ともとれますが、 必要に駆られて今まであまりやれなかったことがやれるようになったという意味では成長につながるよい機会となりました。

また、Flutterは時々バグがあることもあるので、Flutter公式のIssueをみることも多く、英語で書かれたIssueを読む機会も大幅に増えました。

APIリファレンスを読むのに抵抗がなくなった

Flutterを触り始めてから、Dart Docs形式のAPIリファレンスを読むことが増えました。例えば、以下のようなものです。

provider - Dart API docs

この手のリファレンスはわかりにくい印象があったので、代わりにわかりやすい日本語の解説があればそちらを読んで、とりあえず使えればOK、というように今までは考えていたのですが、最新のライブラリ(最近ではfreezed, state_notifier)では日本語の解説が少なく、リファレンスを読むことも多々ありました。

しかし慣れてくるとDart Docs形式のAPIリファレンスは割と読みやすく、こちらを読んだ方がわかりやすい場合もあり、かつ一時情報なので安心感もあるという風に思える用になりました。 苦手意識を克服できました。

言語・フレームワーク(Flutter/Dart)の最新の動向について調べる習慣がついた

Flutterは半年に一回ほどのペースでstable版のアップデートがあります。その際にmediumで公式からアナウンスがされるのですが、昨年末に記事が出た時からはこのアナウンス記事をそのまま読むようになりました。

Flutterのアップデートの際には対応するDartのバージョンも上がるため、Dartについても同様のアナウンス記事が出されるのでそれも読んでいます。

今まではこういった言語やフレームワークについての記事は日本語の記事が出てくるまで読んでいなかったのですが、最近では公式の記事をそのまま読むようにしています。

これも今までは抵抗感があり読んでいなかったのですが、 実際に読んでみるとすぐ使える便利な機能も多かった ので、読むのが楽しみになりました。

これは FlutterがUI系のフレームワークでありすぐに試せるものが多い から、ということもあると思います。例えばiOSのアップデートの場合、自分が使わない機能も多く含まれていることが多い印象です(機械学習関連やSiriなど)。

英語のドキュメントを読む抵抗がなくなった

ここまでで同じようなことを書いていますが、調べ物をする際に 英語を読むことを強制される場面も多いので(英語のリソースは良質で読みやすいものが多い のですが)、次第に英語を読むことにも抵抗がなくなりました。

ただし、抵抗がなくなったというだけで別に英語力が上がるわけではないため、読むスピードはそんなに変わっていない気がします(苦笑

それでも読むまでの抵抗がなくなったのは、特に個人開発の際には大きいと感じています。

iOS/Android両対応のアプリをリリースできた

チーム開発ですが、「スタマチ」というアプリをリリースすることができました。詳細は以下の記事にあります。iOS/Android両方のアプリをリリースすることで、今まで使ったことのなかったGoogle Playへのリリースフローも経験することができましたし、より多くのユーザが利用できるアプリとしてリリースできたのが良かったです。

qiita.com

技術記事をそこそこ見ていただけた

2019年は、Qiitaやこのブログにそこそこまとまった分量の技術記事を複数投稿し、40-300いいね(LGTM)ほどもらえています。

qiita.com

qiita.com

記事自体も頑張って書いたのですが、Flutterが比較的新しいフレームワークであり日本語の情報が少ないため、多くの人にみていただけた のではないかと思います。

今後は?

今後は、Flutter関連の有名な方の真似をしていきたいと思っています。

今後もFlutterを通してエンジニアとして成長していきたいです。

twitter.com

ターミナルのプロンプトにflutterのchannelを表示する

注意: 実用性は低いです

サンプル

f:id:swiftfe:20200503201948p:plain f:id:swiftfe:20200503202305p:plain

モチベーション

  • flutterのweb supportを使うにはbeta channelを使う必要があるが、他のweb supportの必要のないプロジェクトではmasterを使いたい
  • masterとbetaをすぐ判別がつくようにしたい

Building a web application with Flutter - Flutter より

As of 1.12, Flutter has early support for running web applications, but you need to be running the beta channel of Flutter at present. If you experience a problem that hasn’t yet been reported, please file an issue and make sure that “web” appears in the title.

zshrcのカスタマイズ

環境

git-promptを使っている場合

~/.zshrc

# flutterのパスを追加
export PATH=/Users/xxx/development/flutter/bin:$PATH

# flutter channelの結果から、現在のchannel名を取得 (* stable なら、stableを取得)
flutter_channel=$(flutter channel | tr '\n' ':' | sed -E "s/.+\* ([^:]+):.*/\1/")

# gitのプロンプト設定
export GIT_PS1_SHOWCOLORHINTS=1
export GIT_PS1_SHOWDIRTYSTATE=1
git_prompt_sh=/usr/local/etc/bash_completion.d/git-prompt.sh
if [ -e $git_prompt_sh ]; then
 source $git_prompt_sh
 
 # $flutter_channelをプロンプトに表示
 precmd () { __git_ps1 "%F{cyan}(flutter: $flutter_channel)%f " "%F{yellow}%c%f $ " " (%s) " }
fi

git-promptを使わない場合

git-promptを使っているのでわかりにくいですが、内部ではzshのPROMPT変数?を使うことで実現しているため、git-promptを使っていない人はPROMPT(bashの場合はPS1だったはず)を使うことで同様に使うことができます。

~/.zshrc

# flutterのパスを追加
export PATH=/Users/xxx/development/flutter/bin:$PATH

# flutter channelの結果から、現在のchannel名を取得 (* stable なら、stableを取得)
flutter_channel=$(flutter channel | tr '\n' ':' | sed -E "s/.+\* ([^:]+):.*/\1/")

# プロンプト設定
PROMPT="%F{cyan}(flutter: $flutter_channel) $ "

簡単な解説

$ flutter channelの出力は以下のようになります。

Flutter channels:
  beta
  dev
  master
* stable

この * stable のように * がついているのが現在のchannelになるので、sedにより正規表現で「stable」だけを抜き出します。それだけです。

この方法の問題点

  • シェルの起動が重くなる
  • $ flutter channel が重いため、コマンド実行ごとに channel 表示を切り替える用途には待ち時間の問題上、使えない
  • $ flutter channel stable などを使った際、表示は切り替わらず、シェルを再起動する必要あり
    • しかも切り替えたあとすぐは余計な出力が出るためにsedの処理に1分ほど時間がかかる
    • しかし、Android Studioで他のプロジェクトを開いた際にはシェルが再起動されるので実用上あまり問題はない

参考

Google Domainsでドメイン購入しました

Google Domainsでドメイン購入をしてみたのでメモ。

今回買ったのはtetsufe.dev。1540円/年(税込) で、1年+継続契約。54歳になるまでの30年間使うとしても、46200円。

購入理由

何かしら個人でページを作る際に便利かなと思って登録。アプリの登録時にCompany Domainを指定するなどの用途にも使うので、念のためあると安心

Google Domainを使ったのは、今まで使っていたお名前.comはメールが大量に届いて面倒(結局ほとんど迷惑メールに入れている)なのと、別のドメインレジストラを使ってみたいという理由から。

調べたこと

f:id:swiftfe:20200503013320p:plain

f:id:swiftfe:20200503013326p:plain

iPhoneがプッシュ通知を受け取る仕組み

IP通信の基本

Webサーバをインターネット上に公開する際、サーバに固定IPアドレスを割り当てる必要があります。

クライアントはそのIPアドレスを頼りにインターネット上につながったルータをたどり、そのWebサーバにいき着くことになるからです。

参考: https://www.vwnet.jp/Windows/Network/WhatIsNetwork.htm

疑問

しかし、プッシュ通知の場合はどうか?

こちらは逆に サーバからモバイル端末(iPhoneなど)へ通知(=情報)を送っているように見えますが、モバイル端末側には固定IPなど割り振られていないことが一般的なはず。ではどうやってモバイル端末に情報を送ることができるのだろうか?

そんなふうにふと疑問に思ったので調べてみました。

調査

iPhoneなどのiOS端末の場合は、APNs(Apple Push Notificaction Service)というAppleが提供するサービスを介して、アプリ開発者が端末にpush通知を送ることができます。

AppleのAPNs解説記事に、以下のような記述がありました。

On initial launch of your app on a user’s device, the system automatically establishes an accredited, encrypted, and persistent IP connection between your app and APNs. This connection allows your app to perform setup to enable it to receive notifications, as explained in Configuring Remote Notification Support.

意訳:ユーザのデバイス上の(あなたが開発した)アプリの最初の起動時、永続的なIPコネクションがアプリとAPNsの間で確立されます。このコネクションがpush通知をアプリが受け取るためのセットアップを(可能に)します。

ということだそう。

おそらく、persistent(永続的な)ということなので、 アプリを一度起動してからは基本的にずっと端末上のアプリとAPNsがコネクションを貼り続けている のでしょう(端末がインターネットにつながる状態になるたびに繋ぎ直すなどして)。

この時のコネクションは端末側からリクエストを送り、固定IPに紐づいたAPNsサーバにつないでいるのでしょう。そして、コネクションが一度張れる状態になってしまえばサーバからも通知を送ることができる。 そういうことだと思います。間違っていたら教えてください。

補足

ちなみにApple公式ではありませんが、日本語の記事もありました。

https://www.bizmobile.co.jp/tech_mdm_02.php?id=3564

こちらではよりAPNsだけでなくGCM(Google Cloud Messaging)の説明もされていました。

active recordパターンについて調べてみた

active recordパターンとは

  1. データ、2. ドメインロジック、3. データベースアクセスをカプセル化したメソッドの3つを一つのクラスとして作成するパターン。

RailsではActiveRecordという名前のクラスがありますが、これはActiveRecordパターンという一般的なパターンに基づいたもので、DjangoなどでもActiveRecordパターンは用いられています。

https://docs.djangoproject.com/en/3.0/misc/design-philosophies/

オブジェクトがそのままデータベースと一体化しているような形で操作できるため、非常に便利です。

ただし、オブジェクトのフィールドがデータベースのカラムに一致していないような場合にはかえって一部のフィールドだけ別で管理する必要が生じて複雑になってしまうため、万能とも言い切れません。

原典?

https://www.martinfowler.com/eaaCatalog/activeRecord.html

また、PoEAA(エンタープライズアプリケーションアーキテクチャパターン)で詳しく解説があります。

もう少し詳しい記事

https://www.techscore.com/tech/Ruby/Rails/other/designpattern/2/

複数人開発に必要な最小限のDjango自動テスト

テストは不安を退屈に変える賢者の石だ。 ストレスを感じれば感じるほど、頻繁にテストを走らせるようになる。テストをすぐに走らせれば、ミスをしでかす確率が減っている実感が得られ、結果的にストレスが減っていく。


私たちは、完璧を求めているのではない。すべてのものをコードとテスト両方の視点から捉えることによって欠陥を減らし、 自信を持って前に進めるようになろうと考えているのだ。

ーーー「テスト駆動開発」より引用ーーー

複数人開発でテストコードがなくて困った

最近「スタマチ」の開発中、Aさんが書いたサーバーサイド(Django)のコードをBさんやCさんがフロントエンド(Flutter)から使用しようとしたとき、サーバーサイド側のコードにミスがありデバッグに非常に手間取ったということがありました。

もし、サーバーサイドでテストが書かれていたら、どうだったでしょうか?以下のことができたと思います。

  • Aさんはテストでミスを事前に発見することができた
  • BさんとCさんはテストを読むことでAPIの使い方を正しく知る事ができた

あるエンドポイントに対してのテストコードを書くことは、

  • 品質の担保
  • 手動デバッグの手間の削減
  • フロントエンドエンジニアのためのわかりやすいドキュメント

の3つの意味で重要だと思っています。

特にフロントエンドエンジニアは、

  1. エンドポイントにどのようなHTTPリクエストを送ればどのようなレスポンスが返ってくるかだけが分かればよい
  2. その内部が正しく動いているかどうかまでは気にしたくない

と考えると思います。

テストコードを書くことで、1を明示し、2を担保することができます。

DjangoDRF)で最小限のテストを書いてみる

今回はDjango REST Framework で作った APIサーバーに対するテストを書いてみます。

最小限性

  • ある一つのエンドポイントに対して
  • リクエストを送り、そのレスポンスを検証

具体的には、reportという名前のユーザ通報機能用のアプリケーションのcreateメソッド(REST FrameworkのModelViewSetを継承)に対してのテストを書きます。

views.py

from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated

from .models import Report
from .serializers import ReportSerializer


class ReportViewSet(viewsets.ModelViewSet):
    queryset = Report.objects.all()
    serializer_class = ReportSerializer
    permission_classes = [IsAuthenticated]

    def create(self, request, *args, **kwargs):
      # 省略
      return Response({'message': '通報が完了しました。'}, status=201)

tests.py

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from .models import Report
from account.tests import *


class ReportTests(APITestCase):
    def test_create_report(self):
        reporting_user = create_user('test1', 'xxx@xxx.com', 'xxx')
        reported_user = create_user('test2', 'yyy@yyy.com', 'yyy')

        url = reverse('report-list')
        data = {'reporting_user_id': reporting_user.id,
                'reported_user_id': reported_user.id,
                'content': 'test'
                }

        self.client.login(username='test1', password='xxx')
        response = self.client.post(url, data, format='json')
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data, {'message': '通報が完了しました。'})

このようにテストを書くことで、フロントエンドエンジニア側から見て、先ほど挙げた二つの要素を満たすことができます。

  1. エンドポイントにどのようなHTTPリクエストを送ればどのようなレスポンスが返ってくるかだけが分かればよい(テストを読めば分かる。)
  2. その内部が正しく動いているかどうかまでは気にしたくない(テストが通っていれば、正しく動いている。)

また、 テストが通っていることを確認しておけば、サーバーサイドエンジニアは「自信を持って」コードをフロントエンドエンジニアに渡すことができます。

テストの実行

$ python manage.py test

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.410s

OK
Destroying test database for alias 'default'...

これでテストの追加は完了です。

テストが失敗する例

これで、例えば views.py で201ではなく200を返すように書き換えた場合、以下のようにテストが失敗します。

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_create_report (report.tests.ReportTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/xxx/report/tests.py", line 40, in test_create_report
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 200 != 201

----------------------------------------------------------------------
Ran 1 test in 0.411s

FAILED (failures=1)
Destroying test database for alias 'default'...

参考

DRFのテストについてのドキュメント https://www.django-rest-framework.org/api-guide/testing/

reverseに対応するurl名がわからなかったので、こちらを調べました https://www.django-rest-framework.org/api-guide/routers/#simplerouter

例えば、accounts-list で GET(list) と POST(create) の両方にあたります。これにハマりました

2019年のオススメ漫画・ラノベ・ゲームを3つ

2019年の個人的学びとオススメ漫画・ラノベ・ゲーム - 未熟学生エンジニアのブログ に入れようかと思ってやめた部分です。

1. しゅごキャラ!

小学生時代見ていたアニメ。少女漫画「なかよし」で連載している少女漫画ですが、「なりたい自分」を探す主人公たちを見ているとこの歳になってからの方が心に刺さるシーンが結構あります。全12巻揃えたので借りたい人いたら貸します。ファンでなければ正直最初の3巻だけ読めば十分です。

就活と関連づけて考えてる記事もあるようです。実際就活生にはオススメの作品です。

http://amsm10lc.hatenablog.com/entry/20160720/1469019588

気になった方はまずこちらからあらすじを読んでみてください。

https://honcierge.jp/articles/shelf_story/5098

普通にオススメできる作品なので、以下に当てはまる人は一度しゅごキャラ!を読んでみましょう。なりたい自分になるための勇気をもらえるかもしれません。

  • 周りの目を気にして「外キャラ」を作っている
  • なりたい自分がいくつもある
  • いい歳だし今更「キャラチェンジ」なんてできない…

何歳でも遅くはありません。30くらい?の先生がなりたい自分を見つけるエピソードもあります。(3巻)

2. やはり俺の青春ラブコメはまちがっている。

www.amazon.co.jp

この節で紹介する3作品の中では一番薦めやすい作品になります。ひねくれた主人公とクールな女の子と明るい女の子が織りなす 三角関係部活もの です。 シナリオが敢えてぼかして書かれている(語弊あるかも)ために考察が捗る作品 なのが他と違うポイントですね。後は主人公がすごいリアルなひねくれオタクって感じで共感できる部分も多いところが見どころですね。普段ラノベは読まないので、久しぶりの作品ですがかなり良かったですね。ちなみに14巻だけ読みました。他はアニメと考察ブログで補完しました

読んだきっかけ

ゆらりー。という昨年まで大学生YoutuberをしていたYoutuberがいて、その配信で絶賛されていたのがこの作品でした。そして考察ブログを見てみると、考察でいろんな解釈がされていて面白い作品だと思ったのでした。あと、昔アニメ研究会に2ヶ月ほど在籍していたときに上映されていたのがこれでした。

hyohyolibrary.com

感想

自分の予想をいい意味で裏切った切ない余韻を残す作品でした。三角関係という性質上、どうやっても切ない部分はあるに違いないということはわかっていたのですが、可哀想過ぎる。作者は切ない物語を書きたくて、そういう役割をもったキャラクターだったのだなと思うと・・っと書いていて思ったのですが、結構この作品は(最後まで)不遇なキャラが何人かいるので、その兆候はすでに見えていたのかもしれませんね。

春からアニメ化もされるようです。このタイミングで一気見してもいいかもですね。見たら泣きそう・・

www.tbs.co.jp

3. マブラヴマブラヴ オルタネイティブ

ギャルゲー・18禁ゲーをプレイしたことがない方はここは伝わらないので無視してください。また、これを最後に本記事は終わりなのでブラウザバックしてください。

store.steampowered.com

プレイ(視聴)したきっかけ

Youtubeの垢バレが怖いのでぼかしますが、とあるエンジニア系配信者さんが「シュタゲよりマブラヴの方が面白い」と言っていたのがきっかけでした。そして某サイトの評価をみてみると上位10件くらいに入っていて、こんなに評価高かったんだと思ってやることにしました。

マブラヴ」多分ネタバレなし感想

マブラヴ」はYoutubePSP版のプレイ動画が上がっていたのでそこで見ました(本当はよくないのですが)。

 泣ける ゲームです。個人的には過去最高シナリオのゲームでした。個人的には一番の見どころは「幼馴染キャラの一途な恋心と理不尽な運命」 なのですが(過ごした時間の長さを感じる演出がヤバイ)、 それ以外の要素も一級品です。ちなみにかわいいキャラを愛でるタイプのゲームではないので注意です。絵も古いですしね

  • パラレルワールド要素
    • 量子力学理論(もはやセット?)が出てきます
  • 地球外生命体とのロボットバトル
    • バトルは熱くなれる。ご都合主義ではなく、しっかりキャラが死にます。
    • ただし機体を操作するタイプのゲームが遊べるわけではありません
    • 無敵のラザフォード場、厨二心をくすぐられます。アクションが苦手なノベルゲーと相性いいなと思いながら見てました(まあこのゲームではアクションのアニメーションもあるんですが)
  • 先生が最高に悪カッコいい

ちなみに2020年にアニメ化もするようなので、そちらを見るのもアリかと思います。

animebu.com

話し始めると長くなるのでここら辺で終わりにします。