iOS App Dev Tutorials: UIKit essentials

https://developer.apple.com/tutorials/app-dev-training/creating-a-list-view

SwiftUIのチュートリアルは去年の終わりくらいにやって、Flutterに似てとっつきやすいと思っていましたが、 まだまだUIKitの出番が多いとのことで学習することにしました。

チュートリアルをこなす上でつまづいたところや気づきがあったところをメモしております。

Section 1

UIKitのチュートリアルなので、まずはinterfaceでstoryboardを選ばないといけません。

ですが、最初interfaceが選べずそのままnextしたら、SwiftUIになってしまいました。

interfaceが選べない

でも、後でやり直したら、storyboardが選べるようになってました。何が違うんだ?

storyboard見えた

Section 2

ここで若干つまづいたのは、collection view controllerを追加するところです。

そもそもLibrary iconはどこにあるんだ?から始まりましたが、ググるとShift + command + Lで開くことがわかりました。

でも検索結果に何も出ない。

検索結果に何も出ない

で、少ししてstoryboardファイルが選択している時じゃないと検索できないことに気づきました。

その他、このChapterで気づいたところ

DEBUG blockってのが便利そう

import Foundation

struct Reminder {
    var title: String
    var dueDate: Date
    var notes: String? = nil
    var isComplete: Bool = false
}

#if DEBUG
extension Reminder {
    static var sampleData = [
        Reminder(title: "Submit reimbursement report", dueDate: Date().addingTimeInterval(800.0), notes: "Don't forget about taxi receipts"),
        Reminder(title: "Code review", dueDate: Date().addingTimeInterval(14000.0), notes: "Check tech specs in shared folder", isComplete: true),
        Reminder(title: "Pick up new contacts", dueDate: Date().addingTimeInterval(24000.0), notes: "Optometrist closes at 6:00PM"),
        Reminder(title: "Add notes to retrospective", dueDate: Date().addingTimeInterval(3200.0), notes: "Collaborate with project manager", isComplete: true),
        Reminder(title: "Interview new project manager candidate", dueDate: Date().addingTimeInterval(60000.0), notes: "Review portfolio"),
        Reminder(title: "Mock up onboarding experience", dueDate: Date().addingTimeInterval(72000.0), notes: "Think different"),
        Reminder(title: "Review usage analytics", dueDate: Date().addingTimeInterval(83000.0), notes: "Discuss trends with management"),
        Reminder(title: "Confirm group reservation", dueDate: Date().addingTimeInterval(92500.0), notes: "Ask about space heaters"),
        Reminder(title: "Add beta testers to TestFlight", dueDate: Date().addingTimeInterval(101000.0),  notes: "v0.9 out on Friday")
    ]
}
#endif

こんなコードが出てくるんですが、構造体を宣言して、その下でテスト用のデータを設定しています。

やりすぎるとコードが見辛くなると思うんですが、自分は、テストデータを実際のコードの近くに置いておけるのは便利だと思いました。

離れた位置にあると探すのが面倒になるので。

久しぶりにFlutterFireを使ってみて、AnalyticsのDebugViewにデータが表示されるまで

半年ぶりにflutterfireでも使おうかと思ったらこんなメッセージが

firebase.google.comのドキュメントに統合されたみたいですね。

新しいプロジェクトを始めるときは、ドキュメントを見返すことから始めないといけませんね。

とりあえず、flutter upgrade

Flutter 3.0.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision f1875d570e (13 days ago) • 2022-07-13 11:24:16 -0700
Engine • revision e85ea0e79c
Tools • Dart 2.17.6 • DevTools 2.12.2

Running flutter doctor...
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.0.5, on macOS 12.4 21F79 darwin-arm, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] VS Code (version 1.69.2)
[✓] Connected device (2 available)
[✓] HTTP Host Availability

• No issues found!

新しいパソコンで Firebase CLI がインストールされてなかったので、自動インストール スクリプトでインストールしてみることに

https://firebase.google.com/docs/cli#install-cli-mac-linux

curl -sL https://firebase.tools | bash

$ curl -sL https://firebase.tools | bash
Password:
-- Checking for existing firebase-tools on PATH...
-- Checking your machine type...
-- Downloading binary from https://firebase.tools/bin/macos/latest
######################################################################## 100.0%#=#=#                                                                         
-- Setting permissions on binary...
-- Checking your PATH variable...
-- firebase-tools@11.3.0 is now installed
-- All Done!

さらにfirebase loginしてdart pub global activate flutterfire_cliを実行

そしてflutterfire configureするにはflutter projectが必要なので flutter create -t skeleton .を実行

firebaseのプロジェクトはflutterfire configureの過程でも作れるみたいですが、 一意のIDが必要なので、webのコンソールから作った方が手っ取り早い。

そしてやっとここでflutterfire configure実行。実行結果↓

この結果は、webのコンソールにも反映されている

そして嬉しいのが、androidiosの設定も追加してくれるところです。 https://github.com/na8esin/flutterfire_cli_sample/commit/c4d1e59fc4a09d3c83b73e78d0e6a0ce64cc78d4

firebase_coreを追加してみます。

flutterfire_cli_sample$ flutter pub add firebase_core
Resolving dependencies...
  async 2.8.2 (2.9.0 available)
  characters 1.2.0 (1.2.1 available)
  clock 1.1.0 (1.1.1 available)
  fake_async 1.3.0 (1.3.1 available)
+ firebase_core 1.20.0
+ firebase_core_platform_interface 4.5.0
+ firebase_core_web 1.7.1
+ flutter_web_plugins 0.0.0 from sdk flutter
+ js 0.6.4
  matcher 0.12.11 (0.12.12 available)
  material_color_utilities 0.1.4 (0.1.5 available)
  meta 1.7.0 (1.8.0 available)
  path 1.8.1 (1.8.2 available)
+ plugin_platform_interface 2.1.2
  source_span 1.8.2 (1.9.1 available)
  string_scanner 1.1.0 (1.1.1 available)
  term_glyph 1.2.0 (1.2.1 available)
  test_api 0.4.9 (0.4.12 available)
Changed 6 dependencies!

git checkoutしてみます。

flutterfire_cli_sample$ git checkout 
M   ios/Flutter/Debug.xcconfig
M   ios/Flutter/Release.xcconfig
M   macos/Flutter/Flutter-Debug.xcconfig
M   macos/Flutter/Flutter-Release.xcconfig
M   macos/Flutter/GeneratedPluginRegistrant.swift
M   pubspec.lock
M   pubspec.yaml

前から、ここまで自動で追加してくれましたっけ?
半年、離れただけで、かなり便利になっている気がする。

https://github.com/na8esin/flutterfire_cli_sample/commit/53b19113fd03b99da21b9c476ca0c4f92670e98e

https://firebase.google.com/docs/flutter/setup?platform=android#initialize-firebase では、flutterfire configureをやることになってますが、最初にやってるので特に変化はなし。

lib/main.dartに下記を追加していきます。

import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';

await Firebase.initializeApp(
  options: DefaultFirebaseOptions.currentPlatform,
);

ここでいつもならFirestoreを追加しますが、今までやったことがないanalyticsを追加していきます。
※サンプルに載ってるため

flutter pub add firebase_analytics

https://github.com/na8esin/flutterfire_cli_sample/commit/799afc6ba1ccbdcc5bfee79c374fdbadd954a69c

そして、skeletonで作ったのが意味無くなりますが、下記のコードを指示通りコピペ

https://github.com/firebase/flutterfire/tree/master/packages/firebase_analytics/firebase_analytics/example/lib

さらにこんなエラーが出ましたが、

Using `ARCHS` setting to build architectures of target `Pods-Runner`

Podfileの設定をとりあえず、こんな感じでplatform :ios, '10.0'

デバッグ有効化
Enabling Debug Mode for Firebase Analytics for Flutter - Stack Overflow

そして、Xcodeから起動すると下記のようにDebugViewに表示されます(vscodeからの起動だと何故かうまく行きませんでした)

簡単ですが、以上になります。

Session : Symbols hash keys getting converted to literals keys in next request

github.com

issue自体は古いですが最近調べても同じ挙動でした(rails6)

ただ、memory cacheもredis cacheもリクエストの前後でkeyが変わったりしないので、sessionに複雑な構造を持たせる場合はプロジェクトの最初の方から対応しておかないとはまる。

https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb#L90

やっぱりrails7でもまだご健在な仕様みたいです。

この現象に出会ったのは、ログイン前のショッピングカートをsessionに保存するという仕様のためでした。

ログイン後はもちろんDBなので、ログイン前後で実装が異なり2倍近いコストがかかります(いやそれ以上か?)。

mysqlにあるmemory engineみたいなのもAuroraには無い様子。

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Reference.html

InnoDB ストレージエンジンを使用します

railsのコードの一行の長さが気になったのでrubocopでなんとかできないか考えてみた

typescriptやdartのプロジェクトだと市民権を得ているformatterがあるので何も迷わないと思いますが、 railsだとなんかみんな長く書く。

そこで
https://techracho.bpsinc.jp/hachi8833/2019_05_24/74318
の記事から

https://github.com/rubocop/rubocop/pull/6824/files

にたどり着いたが、ActiveRecordのscopeの改行がなんか嫌な感じがしたのでやめました。

これが

scope :with_books, lambda {
  includes(:books)
    .order(:order, :updated_at)
}

こんな感じで、lambdaが次の行に行きます。

scope :with_books,
  lambda {
    includes(:books)
      .order(:order, :updated_at)
}

これも慣れれば、気にならなくなるのか。。。?

MX Master 3 for macのジェスチャーボタンの動きがいきなり変わった。それとssl証明書問題

けど、Logi Optionsの再インストールで治った。

ジェスチャーボタンをmission controlに割り当てていてかなり便利だったが、 何かのタイミングで(もう一台のmacに繋いだ時か?)動きが変わって不便だった。

動きが変わったというのは、ジェスチャーボタンを押すたびに 別のブラウザに切り替わるというものでした。

バイスの追加・削除でも変化がなかった(Optionsに表示されなくなった) ので、再インストールしました。

あと、余談ですが、最近のchromeではssl証明書の更新漏れででサイトが見れなくなった時、キャッシュをクリアしないと、回避できない場合があるので、注意。

AWS cliとSession Managerをインストール

sudo ./sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
Password:
env: python: No such file or directory

これならどうだ!

sudo python3 sessionmanager-bundle/install -i /usr/local/sessionmanagerplugin -b /usr/local/bin/session-manager-plugin
Password:
Creating install directories: /usr/local/sessionmanagerplugin/bin
Creating Symlink from /usr/local/sessionmanagerplugin/bin/session-manager-plugin to /usr/local/bin/session-manager-plugin
Installation successful!

OK!

ecrからイメージをpullしてみる

前述の手順からecs execで中には入れたがデプロイ方法がわからないのでとりあえず、先輩がpushしたイメージをpullしてみることに

https://github.com/awslabs/amazon-ecr-credential-helper
というものが必要らしい

~/.docker/config.json

{
  "credsStore" : "desktop"
}

は削除してから、helperの設定を追加するのか。。。?

{
  "credsStore" : "desktop"
  "credHelpers": {
    "public.ecr.aws": "ecr-login",
    "<aws_account_id>.dkr.ecr.<region>.amazonaws.com": "ecr-login"
  }     
}

よくわからないので、こんな感じで並べておいてみることに

accepts_nested_attributes_for, FormOptionsHelper.selectのauxiliary hidden field

dhhがaccepts_nested_attributes_forをkillしたがってる件

https://github.com/rails/rails/pull/26976#pullrequestreview-8449983

代替としてはActiveModel内に頑張って書いていく

form helperのselectのauxiliary hidden field

https://github.com/rails/rails/blob/984c3ef2775781d47efa9f541ce570daa2434a80/actionview/lib/action_view/helpers/form_options_helper.rb#L137-L143

multiple: trueにする場合は補助的なhiddenタグがデフォルトで追加されます。
この挙動が不要な場合はinclude_hidden: falseで無効にすることができます。

f:id:ta_watanabe:20220406225456p:plain

html.erb

<%= form_with do |form| %>
  <%= form.select("person_name", ["佐藤", "鈴木", "高橋"], {}, { multiple: true }) %>
<% end %>

htmlの出力結果

<form action="/home/form_options_helper_select" accept-charset="UTF-8" method="post"><input type="hidden" name="authenticity_token" value="xxxx" autocomplete="off" />
  <input name="person_name[]" type="hidden" value="" autocomplete="off" /><select multiple="multiple" name="person_name[]" id="person_name"><option value="佐藤">佐藤</option>
<option value="鈴木">鈴木</option>
<option value="高橋">高橋</option></select>
</form>

rails cでも確認できる

[35] pry(main)> helper.select("post", "person_name", ["佐藤", "鈴木", "高橋"], {}, { multiple: true })
=> "<input name=\"post[person_name][]\" type=\"hidden\" value=\"\" autocomplete=\"off\" /><select multiple=\"multiple\" name=\"post[person_name][]\" id=\"post_person_name\"><option value=\"佐藤\">佐藤</option>\n<option value=\"鈴木\">鈴木</option>\n<option value=\"高橋\">高橋</option></select>"