- 投稿日:2020-07-14T22:13:52+09:00
[2020July] iPad の UDID を Linux で確認する
はじめに
iPhone や iPad のアプリをテスト配信する際、純正の TestFlight を利用しない場合は、AdHoc 配信の延長となり、Provisioning Profile に端末の UDID を登録してビルドする、という手順から逃れられないようです。
不幸にも端末に一意の UDID が資産管理台帳に掲載されていない場合でも、トランキーロ、慌てる必要は無さそうです。
UDID は Linux でも確認できます。
手順
簡単です。
lsusb -v
コマンドで表示されます。$ lsusb -v (省略) Bus 001 Device 010: ID 05ac:12ab Apple, Inc. iPad 4/Mini1 Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 2.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x05ac Apple, Inc. idProduct 0x12ab iPad 4/Mini1 bcdDevice 5.03 iManufacturer 1 Apple Inc. iProduct 2 iPad iSerial 3 0123456789012345678901234567890123456789 bNumConfigurations 4 (省略)iSerial の40桁の16進数が Xcode で表示される UDID と同じ値です。
おわりに
「Windows UDID」と検索すると出てくる記事でも、同じ情報を引用する方法が載っています。なので、たぶん、しばらくこの方法が通用するんじゃないでしょうか。
まぁでも、一度だけ正規の手順で調べて台帳に書いておく、それが正解だと思います。
- 投稿日:2020-07-14T19:01:23+09:00
SwiftUI でアプリ全体の TintColor を変更する
SceneDelegate で色を指定する
SwiftUI プロジェクトを新規作成すると、 SceneDelegate 内に UIWindow を生成するコードがデフォルトで用意されるはずです。
生成された window のtintColor
プロパティに任意の色を指定すると、アプリ全体の TintColor を変更することができます。SceneDelegate.swiftfunc scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let contentView = ContentView() if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) // この 1 行を追加する window.tintColor = .systemGreen self.window = window window.makeKeyAndVisible() } }今回の例では systemGreen を指定していますが、 型は UIColor なのでアプリのイメージカラーに応じて自由に設定できます。
例のように systemColor にしておくと、ダークモード時にシステム側が上手く色を調整してくれるので大変便利です。
カスタムカラーを用いるかどうかはデザイナーさんと相談して決めてください。window.tintColor = UIColor(displayP3Red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)ちなみに残念ながら
window.tintColor
で指定した値は Preview には反映されません。余談
SwiftUI で色を変更する手段として、
foregroundColor()
,accentColor()
,background()
などを設定する方法があります。
これらをContentView
のようなルートとなる View に設定して色を変えるという方法も考えられます。struct ContentView: View { var body: some View { TabView { ... }.accentColor(.green) } }この場合は Preview にも反映されるため、その恩恵を受けることができます。しかし、
色の指定が反映されない View もあるので注意が必要です。
例えば Button 内の Text がデフォルトカラーのまま表示されることを確認しています。
この挙動はおそらく不具合ではなく SwiftUI の仕様です。全体の色を一括で変更したい場合は
tintColor
を指定し、accentColor()
などは各 View に対して細かな指定をしたい場合のみ設定する方が良いと思います。
- 投稿日:2020-07-14T18:12:15+09:00
【Swift】NSLayoutAnchorを使用し、AutoLayoutをコードで実装してみる(画面の右下に固定したボタンを表示)
はじめに
いつもはStoryboardからAutoLayoutを使用し制約を付けていたのですが、
コードから制約をつける機会があり、NSLayoutAnchorを使用したので、その方法を記載したいと思います。
今回はTwitterのように、画面の右下に固定されたボタンを表示させる方法を書いていきます(UITableViewの上に設置しています)。
環境
- Swift:5.1.3
- Xcode:11.3.1
NSLayoutAnchorの使い方
こちらの記事が詳しいです。
どの位置を起点にして制約をつけるかは、先ほどの記事を引用させていただくと下記のようになります。
プロパティ名 位置 leadingAnchor 左端 trailingAnchor 右端 topAnchor 上端 bottomAnchor 下端 centerYAnchor オブジェクトのY座標軸の中心 centerXAnchor オブジェクトのX座標軸の中心 widthAnchor オブジェクトの横幅 heightAnchor オブジェクトの縦幅 実装してみる
// UIButtonを生成する let plusButton = UIButton() // 必ずfalseにする(理由は後述) plusButton.translatesAutoresizingMaskIntoConstraints = false // tintColorを黒にする plusButton.tintColor = .black // グレーっぽくする plusButton.backgroundColor = UIColor(red: 0.92, green: 0.92, blue: 0.92, alpha: 1) // 正円にする plusButton.layer.cornerRadius = 25 // plustButtonのImageをplus(+)に設定する plusButton.setImage(UIImage(systemName: "plus"), for: .normal) // ViewにplusButtonを設置する(必ず制約を設定する前に記述する) self.view.addSubview(plusButton) // 以下のコードから制約を設定している // plustButtonの下端をViewの下端から-50pt(=上に50pt) plusButton.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -50).isActive = true // plustButtonの右端をViewの右端から-30pt(=左に30pt) plusButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -30).isActive = true // plustButtonの幅を50にする plusButton.heightAnchor.constraint(equalToConstant: 50).isActive = true // plusButtonの高さを50にする plusButton.widthAnchor.constraint(equalToConstant: 50).isActive = true注意点
制約は必ず部品を配置してから
部品を配置していないのに制約をつけることはできません。
つまり、今回で言うと制約を設定する前にself.view.addSubview(plusButton)
を記述しておく必要があります。
そうしないとビルドは通りますが、下記実行時エラーでクラッシュします。Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors <NSLayoutYAxisAnchor:0x600003dec2c0 "UIButton:0x7fa4a7704d90.bottom"> and <NSLayoutYAxisAnchor:0x600003d6b140 "UIView:0x7fa4a77024c0.bottom"> because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'translatesAutoresizingMaskIntoConstraints は false に
translatesAutoresizingMaskIntoConstraintsはAutoLayout以前に使われていたAutosizingというレイアウトの仕組みを、AutoLayoutに変換するかどうかを設定するフラグらしいです。
デフォルトでは、trueになっていますが、Autosizingの制約とAutoLayoutの制約がコンフリクトを起こす可能性があり、意図した動作にならないことがあるのでfalseにしましょう。
今回の場合、falseに設定しないとplusButtonが表示されませんでした。
- 投稿日:2020-07-14T17:44:51+09:00
SwiftUI で @State の初期状態を外から受け取る
@State private var の場合
例えば、テキストを編集するための View について考えてみましょう。
- 前画面から既存の文字列を受け取り、 TextField の初期値として表示する
- 表示された文字列を修正することができる
このような仕様の場合、 1 は以下のようなコードで実現できます。
struct TextEditView: View { @State private var text: String init(text currentText: String) { _text = State(initialValue: currentText) } var body: some View { TextField("", text: $text) } ... } let currentText = "Today was a good day." TextEditView(text: currentText)あれ?こっちは??
struct TextEditView: View { @State private var text: String init(text currentText: String) { text = currentText // ❗️ Return from initializer without initializing all stored properties } ... }初期化が完了していないというエラーが出ます。text に代入してるのに。。
実は初期化する必要があるのは、
文字列
ではなく文字列の状態
String
ではなくState<String>
text
ではなく_text
1です。 text の 初期状態 をセットしてあげる必要があります。
(初期値ではなく、初期状態と表現する方が適切かと思います。)今回の例では
currentText: String
をState
でラップし 状態 として代入しています。_text = State(initialValue: currentText)@State var の場合
こちらは素直にコンパイルが通ります。
struct TextEditView: View { @State var text: String ... } let currentText = "Today was a good day." TextEditView(text: currentText)イニシャライザが内部でうまく処理してくれてるのかな。
_ (アンダースコア) を付けることで Property Wrapper 自身にアクセスできる。 ↩
- 投稿日:2020-07-14T13:19:08+09:00
GitHub Actions で Flutter プロジェクトを Firebase にデプロイしたい
はじめに
数ヶ月前に構築した Flutter 用の CI / CD 環境が使われることなく眠っているので供養のためにも記事にしておきます。
困っている方の参考になれば嬉しいです。GitHub Actions についてはこちら
https://github.co.jp/features/actionsワークフローの概説
前提条件
プロジェクトのシークレットに下記 8 項目をセットする必要があります。
- CERTIFICATES_P12 // p12 (Base64) - CERTIFICATES_P12_PASSWORD // p12 のパスワード - FIREBASE_APP_ID_ANDROID // Android の Firebase AppID - FIREBASE_APP_ID_IOS // iOS の Firebase AppID - FIREBASE_TOKEN // Firebase のトークン - PROVISIONING_PROFILE // mobileprovision (Base64) - SLACK_WEBHOOK // Slack のトークン - TEAM_ID // Apple Developer Program の TEAM IDhttps://github.com/muhiro12/flutter-deploy-workflow/blob/master/README.md
テスト & モジュール作成
build: # iOS の ipa を生成したいので macOS で実行する runs-on: macos-latest steps: - uses: actions/checkout@v2 # ProvisioningProfile をシークレットから取り出して、プロジェクトに無理やりセットする - name: Import Provisioning Profile run: | mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles echo '${{ secrets.PROVISIONING_PROFILE }}' | openssl base64 -d -out ~/Library/MobileDevice/Provisioning\ Profiles/decoded.mobileprovision # 証明書を設定 - name: Import Code-Signing Certificates uses: Apple-Actions/import-codesign-certs@v1 with: p12-file-base64: ${{ secrets.CERTIFICATES_P12 }} p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }} # Flutter コマンド! - name: Flutter action uses: subosito/flutter-action@v1.3.0 - run: flutter pub get - run: flutter test - run: flutter build ios - run: flutter build apk # この時点で Android モジュールは作成完了 # Xcode で Archive - name: iOS Build Action uses: yukiarrr/ios-build-action@v0.5.0 with: project-path: ios/Runner.xcodeproj p12-base64: ${{ secrets.CERTIFICATES_P12 }} mobileprovision-base64: ${{ secrets.PROVISIONING_PROFILE }} code-signing-identity: iOS Distribution team-id: ${{ secrets.TEAM_ID }} workspace-path: ios/Runner.xcworkspace export-method: development certificate-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }} output-path: app-release.ipa # デプロイは Ubuntu で行うため、一度モジュールをアップロードする - name: Upload artifact uses: actions/upload-artifact@v1.0.0 with: name: ios path: app-release.ipa - name: Upload artifact uses: actions/upload-artifact@v1.0.0 with: name: android path: build/app/outputs/apk/release/app-release.apkFirebase にデプロイする
deploy: needs: [build] # 2020/04 時点 # Firebase-Distribution-Github-Action が macOS 未対応のため Ubuntu で実行する runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 # モジュールをダウンロードする - name: Download artifact uses: actions/download-artifact@v1.0.0 with: name: ios - name: Download artifact uses: actions/download-artifact@v1.0.0 with: name: android # Firebase にデプロイ! - name: Firebase App Distribution uses: wzieba/Firebase-Distribution-Github-Action@v1.2.1 with: appId: ${{secrets.FIREBASE_APP_ID_IOS}} token: ${{secrets.FIREBASE_TOKEN}} groups: testers file: ios/app-release.ipa - name: Firebase App Distribution uses: wzieba/Firebase-Distribution-Github-Action@v1.2.1 with: appId: ${{secrets.FIREBASE_APP_ID_ANDROID}} token: ${{secrets.FIREBASE_TOKEN}} groups: testers file: android/app-release.apkSlack に通知する
notify: needs: [build, deploy] runs-on: ubuntu-latest steps: - name: Slack Notification uses: rtCamp/action-slack-notify@v2.0.0 env: SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} SLACK_COLOR: '#171515' SLACK_ICON: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png SLACK_USERNAME: ${{ github.repository }} SLACK_TITLE: Now Available SLACK_MESSAGE: https://appdistribution.firebase.dev/app_distro/projectsおわりに
ポイントは、 macOS で生成したモジュールを upload / download で Ubuntu に渡すところです。
全体的に結構苦労した覚えがありますが、自分のスキル的にも、環境的にも、今はもっと最適化できる気がしています。ちなみに今は Bitrise を利用していますが、圧倒的に簡単に環境構築できます。
無料期間、無料枠などあるのでそちらも是非お試しください。yaml ファイルの全容はこちら
https://github.com/muhiro12/flutter-deploy-workflow/blob/master/.github/workflows/main.yml