- 投稿日:2020-03-18T23:56:30+09:00
「ViewControllerをたくさん重ねると、いかにもメモリに悪そう」ということをXcodeを使って視覚的に表現してみた
1 はじめに
この記事は技術的な内容というよりは、実験系の内容になります。
ViewControllerを重ね合わせると、メモリに悪いというのは周知の事実ですが、視覚的に「いかにも悪そうだ〜」という記事は、私が探した限りでは見つけられなかったので掲載します。
2 試したこと
まず、最初に3つのViewControllerを用意します。
次にUIBUttonを用意して、下記のようなコードを書きます。
(UIButtonは省略します)ViewCotrollerを一つ用意して、画面遷移する(firstVC→secondVC)@objc func goSecondVC() { //secondVCを実体化させる let secondVC = SecondVC() //用意したVCへ画面遷移 self.present(secondVC, animated: true, completion: nil) }ViewControllerを一つ用意して、画面遷移する(secondVC→thirdVC)@objc func goThirdVC() { //thirdVCを実体化させる let thirdVC = ThridVC() //用意したVCへ画面遷移 self.present(thirdVC, animated: true, completion: nil) }ViewControllerを1つ用意して画面遷移する(thirdVC→firstVC)@objc func goFirstVC() { //firstVCを実体化させる let firstVC = FirstVC() //用意したVCへ画面遷移 self.present(firstVC, animated: true, completion: nil) }このコードでXcodeのシミュレターを立ち上げて、次々と画面遷移していきます。
これだけでも、
メモリをたくさん使っているように見えますね。ここまでくると、明らかにメモリを使ってそうだというのがわかります。
ちなみに下記サイトがメモリに関してわかりやすく説明されていました。
https://wa3.i-3-i.info/word16066.html3. メモリを圧迫しないためには
ViewControllerを消す必要があります。
例えば、thirdVCからfirstVCへ画面遷移するときに、このようなコードを書くことでViewControllerが破棄出来ます。
Swift 二つ前の画面に戻る方法を参考にさせていただきました。2つ前の画面に戻って、ViewControllerを破棄する(thirdVC→firstVC)@objc func dismissTwoVC() { //2つ前のVCへ戻り、子VC(secondVC,thirdVCを削除) self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil) }イメージはこのようになります。
(間違っていた場合は、ご教授いただけますと幸いです)
こうすることで視覚的にも「メモリに良さそう」な実装になります。
- 投稿日:2020-03-18T22:11:44+09:00
Azure PipelinesのYAMLでiOSアプリのApp Center配布パイプラインを構築する方法
「Azure PipelinesのYAMLでiOSアプリのCI/CD環境を構築する」は3部構成です。
記事を順番に読み進めると、Azure PipelinesでiOSアプリのCI/CD環境が構築できるようになります。
- 第一部:CI環境の構築
- 第二部:App Center配布パイプラインの構築 ←イマココ
- 第三部:TestFlight配布パイプラインの構築
はじめに
Azure Pipelinesを使い、iOSアプリをApp Centerへ配布するCDを構築します。
本記事で書かないこと
- Azure Pipelinesの概要や基本的な操作方法
私が以前書いた記事が参考になると思います- Visual Studio App Centerの概要や基本的な操作方法
- CI環境の構築で説明した内容
以前説明した内容を説明するのは冗長なのでしませんApp Centerのコネクションを追加
Azure DevOpsのプロジェクトに、App Centerを接続します。
Azure DevOps > Project Settings > Pipelines - Service connections > 「New service connection」をクリック
以下のURLからでもアクセスできる
https://dev.azure.com/{Organization}/{Project}/_settings/adminservices「Visual Studio App Center」を選択し、「Next」をクリック
API TokenにApp CenterのAPIトークン、Service connection nameに「App Center」など適当な名前を入力し、「save」をクリック
これでApp Centerの接続は完了です。
証明書とProvisioning Profileのアップロード
証明書とProvisioning ProfileをAzure Pipelines内の安全な場所へアップロードします。
Azure DevOps > Pipelines > Library > [Secure files]タブ > 「+ Secure file」をクリック
これをファイル数分繰り返せば、証明書とProvisioning Profileのアップロードは完了です。
P12ファイルのパスワードの追加
P12ファイルのパスワードをAzure Pipelinesの安全な場所で保持します。
まず変数のグループを作成し、その中に変数を追加します。
Azure DevOps > Pipelines > Library > [Variable group]タブ > 「+ Variable group」をクリック
Variable group nameに適当な名前を入力し、VariablesにP12ファイルのパスワードを追加し、「Save」をクリック
パスワードの変数名は適当な名前でOKです。
Standardのみ契約している場合、単純にP12Password
でいいと思います。これでP12ファイルのパスワードの追加は完了です。
Makefileの作成
アーカイブやIPAファイルの作成などの実行コマンドをまとめたMakefileを作成します。
CI環境の構築で紹介したコマンドは省略します。
Makefile
例はAd Hocなので、In-Houseの場合は変数名などを適当に修正してください。
DEVELOPMENT_TEAM
、PRODUCT_BUNDLE_IDENTIFIER
、ADHOC_PROVISIONING_PROFILE_SPECIFIER
は自分のプロジェクトの値に置き換えてください。
ADHOC_EXPORT_OPTIONS_PATH := ./ExportOptions/ExportOptionsAdHoc.plist
としているので、このパスに.plistファイルを配置してください。
Xcode上からアーカイブ→エクスポートすると.plistが生成されるので、それをそのまま使うのがオススメです。MakefileSDK := iphoneos CONFIGURATION := Release ARCHIVE_PATH := ./build/${PRODUCT_NAME}.xcarchive EXPORT_PATH := ./output/${SDK}/${CONFIGURATION} DEVELOPMENT_TEAM := XXXXXXXXXX PRODUCT_BUNDLE_IDENTIFIER := com.example.Foo ADHOC_PROVISIONING_PROFILE_SPECIFIER := Foo_AdHoc ADHOC_EXPORT_OPTIONS_PATH := ./ExportOptions/ExportOptionsAdHoc.plist .PHONY: generate-ipa-adhoc generate-ipa-adhoc: # Generate IPA file for Ad Hoc $(MAKE) archive-adhoc $(MAKE) export-archive-adhoc .PHONY: archive-adhoc archive-adhoc: $(MAKE) archive PROVISIONING_PROFILE_SPECIFIER=${ADHOC_PROVISIONING_PROFILE_SPECIFIER} .PHONY: export-archive-adhoc export-archive-adhoc: $(MAKE) export-archive EXPORT_OPTIONS_PATH=${ADHOC_EXPORT_OPTIONS_PATH} .PHONY: archive archive: set -o pipefail && \ xcodebuild \ -sdk ${SDK} \ -configuration ${CONFIGURATION} \ -workspace ${WORKSPACE_NAME} \ -scheme ${SCHEME_NAME} \ -archivePath ${ARCHIVE_PATH} \ CODE_SIGN_STYLE=Manual \ CODE_SIGN_IDENTITY="iPhone Distribution" \ PROVISIONING_PROFILE_SPECIFIER=${PROVISIONING_PROFILE_SPECIFIER} \ DEVELOPMENT_TEAM=${DEVELOPMENT_TEAM} \ PRODUCT_BUNDLE_IDENTIFIER=${PRODUCT_BUNDLE_IDENTIFIER} \ clean archive \ | bundle exec xcpretty .PHONY: export-archive export-archive: set -o pipefail && \ xcodebuild \ -exportArchive \ -archivePath ${ARCHIVE_PATH} \ -exportPath ${EXPORT_PATH} \ -exportOptionsPlist ${EXPORT_OPTIONS_PATH} \ | bundle exec xcprettyアーカイブ時に
CODE_SIGN_STYLE=Manual
を指定し、手動で署名しているのがポイントです。
これにより、開発時に自動署名していても確実に署名できます。今回のMakefileは、本記事で使うコマンドのみを抜粋および編集して紹介しています。
私が普段使っているMakefileの全容はGitHub Gistにあるので、よかったら参考にしてください。設定ファイルの構成
CI環境の構築で紹介したので省略します。
各項目の紹介
各項目を上から順に紹介します。
trigger
私は手動でApp Centerへ配布したいため、トリガーをOFFにしています。
trigger: nonepool
CI環境の構築と同様なので省略します。
variables
先ほど追加した変数を使うには、変数グループを明記する必要があります。
variables: - group: Foo-iOSsteps
パイプライン内のステップを1つずつ紹介します。
ライブラリのインストールなど、CI環境の構築と同様のステップは省略します。証明書のインストール
証明書をインストールします。
標準で用意されているタスクを使います。
certSecureFile
にP12ファイルの名前、certPwd
に先ほど追加したP12ファイルのパスワードの変数を指定します。- task: InstallAppleCertificate@2 inputs: certSecureFile: 'Certificate.p12' certPwd: $(P12Password) keychain: 'temp'Provisioning Profileのインストール
Provisioning Profileをインストールします。
証明書のインストールと同様、標準で用意されているタスクを使います。
provProfileSecureFile
にProvisioning Profileの名前を指定します。- task: InstallAppleProvisioningProfile@1 inputs: provisioningProfileLocation: 'secureFiles' provProfileSecureFile: 'Foo_AdHoc.mobileprovision'IPAファイルの作成
IPAファイルを作成します。
- script: make generate-ipa-adhoc displayName: Generate IPA file for Ad HocアーカイブとIPAファイルをアーティファクトのステージングへコピー
配布に問題があった場合に調査するため、アーカイブとIPAファイルをダウンロードできるようにします。
そのためには「ステージングへコピー→アップロード」と2つのタスクを実行します。
- task: CopyFiles@2 inputs: Contents: | **/*.xcarchive/**/* **/output/iphoneos/Release/* TargetFolder: '$(Build.ArtifactStagingDirectory)'
Contents
では、Makefileで定義したARCHIVE_PATH
とEXPORT_PATH
をワイルドカードで指定しています。
.xcarchiveファイルはフォルダ扱いのため、末尾を**/*
としています。アーティファクトへアップロード
直前のタスクでステージングへコピーしたので、本タスクでアップロードします。
- task: PublishBuildArtifacts@1 inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)' ArtifactName: 'drop' publishLocation: 'Container'これにより、パイプラインの実行結果からアーカイブとIPAファイルがダウンロードできるようになります。
App Centerへ配布
作成したIPAファイルをApp Centerへ配布します。
serverEndpoint
は先ほど追加したコネクションの名前を指定します。
appSlug
は自分のApp Centerの値に置き換えてください。
releaseNotesInput
はテストアプリの配布なのでいろいろな情報を表示しています。必要に応じて変更してください。
最後の$(ReleaseNote)
はパイプラインの手動実行時に値を入れることで、リリースノートを書けるようにしています。- task: AppCenterDistribute@3 inputs: serverEndpoint: 'App Center' appSlug: '{Organization}/{App}' appFile: '**/*.ipa' releaseNotesOption: 'input' releaseNotesInput: | - Environment:'Ad Hoc' - Configuration:Release - Certificate:$(APPLE_CERTIFICATE_SIGNING_IDENTITY) - Provisioning Profile:$(APPLE_PROV_PROFILE_UUID) - Branch:$(Build.SourceBranchName) - Last Commit ID:$(Build.SourceVersion) - Last Commit Comment:$(Build.SourceVersionMessage) - Release Note:$(ReleaseNote) destinationType: 'groups'設定ファイルの全体図
最後に設定ファイルの全体図を載せます。
trigger: none pool: vmImage: 'macos-latest' variables: - group: Foo-iOS steps: # 環境変数のエクスポート - script: | export DEVELOPER_DIR=/Applications/Xcode_11.3.1.app/Contents/Developer export MINT_PATH=mint/lib export MINT_LINK_PATH=mint/bin displayName: Export environment variables # Xcodeの一覧出力 - script: ls /Applications | grep 'Xcode' displayName: Show Xcode list # Xcodeのバージョン出力 - script: xcodebuild -version displayName: Show Xcode version # Bundlerで管理しているライブラリのインストール - script: make install-bundler displayName: Bundle install # Mintのインストール - script: brew install mint displayName: Install Mint # Carthageで管理しているライブラリのインストール - script: make install-carthage displayName: Install Carthage frameworks # ライセンス情報の生成、プロジェクトファイルの生成、CocoaPodsで管理しているライブラリのインストール - script: make generate-licenses displayName: Generate licenses, Xcode project, And Pod install # 証明書のインストール - task: InstallAppleCertificate@2 inputs: certSecureFile: 'Certificate.p12' certPwd: $(P12Password) keychain: 'temp' # Provisioning Profileのインストール - task: InstallAppleProvisioningProfile@1 inputs: provisioningProfileLocation: 'secureFiles' provProfileSecureFile: 'Foo_AdHoc.mobileprovision' # IPAファイルの生成 - script: make generate-ipa-adhoc displayName: Generate IPA file for Ad Hoc # アーカイブとIPAファイルをアーティファクトのステージングへコピー - task: CopyFiles@2 inputs: Contents: | **/*.xcarchive/**/* **/output/iphoneos/Release/* TargetFolder: '$(Build.ArtifactStagingDirectory)' # アーティファクトへアップロード - task: PublishBuildArtifacts@1 inputs: PathtoPublish: '$(Build.ArtifactStagingDirectory)' ArtifactName: 'drop' publishLocation: 'Container' # App Centerへ配布 - task: AppCenterDistribute@3 inputs: serverEndpoint: 'App Center' appSlug: '{Organization}/{App}' appFile: '**/*.ipa' releaseNotesOption: 'input' releaseNotesInput: | - Environment:'Ad Hoc' - Configuration:Release - Certificate:$(APPLE_CERTIFICATE_SIGNING_IDENTITY) - Provisioning Profile:$(APPLE_PROV_PROFILE_UUID) - Branch:$(Build.SourceBranchName) - Last Commit ID:$(Build.SourceVersion) - Last Commit Comment:$(Build.SourceVersionMessage) - Release Note:$(ReleaseNote) destinationType: 'groups'おわりに
Azure PipelinesのYAMLでiOSアプリをApp Centerへ配布することができました!
Fastlaneを使えばもう少しかんたんに実現できそうですが、知識不足なのでできませんでした…。
参考リンク
- 投稿日:2020-03-18T16:14:14+09:00
Azure PipelinesのYAMLでiOSアプリのCI環境を構築する方法
「Azure PipelinesのYAMLでiOSアプリのCI/CD環境を構築する」は3部構成です。
記事を順番に読み進めると、Azure PipelinesでiOSアプリのCI/CD環境が構築できるようになります。
- 第一部:CI環境の構築 ←イマココ
- 第二部:App Center配布パイプラインの構築
- 第三部:TestFlight配布パイプラインの構築
はじめに
Azure Pipelinesを使い、iOSアプリのビルドと単体テストを行うCIを構築します。
本記事で書かないこと
- Azure Pipelinesの概要や基本的な操作方法
私が以前書いた記事が参考になると思いますMakefileの作成
まず、ビルドや単体テストなどの実行コマンドをまとめたMakefileを作成します。
スクリプトを直接CIの定義ファイルに記述してもいいのですが、以下の理由から避けています。
- ローカルでもかんたんに各コマンドを実行したい
- 他のCIサービスに移行しやすくしたい
Makefileは長いので、折りたたみます。
Makefile
PRODUCT_NAME
は自分のプロジェクトの製品名に置き換えてください。MakefilePRODUCT_NAME := Foo SCHEME_NAME := ${PRODUCT_NAME} WORKSPACE_NAME := ${PRODUCT_NAME}.xcworkspace UI_TESTS_TARGET_NAME := ${PRODUCT_NAME}UITests TEST_SDK := iphonesimulator TEST_CONFIGURATION := Debug TEST_PLATFORM := iOS Simulator TEST_DEVICE ?= iPhone 11 Pro Max TEST_OS ?= 13.3 TEST_DESTINATION := 'platform=${TEST_PLATFORM},name=${TEST_DEVICE},OS=${TEST_OS}' .PHONY: install-bundler install-bundler: # Install Bundler dependencies bundle config path vendor/bundle bundle install --jobs 4 --retry 3 .PHONY: install-cocoapods install-cocoapods: # Install CocoaPods dependencies and generate workspace bundle exec pod install .PHONY: install-carthage install-carthage: # Install Carthage dependencies mint run carthage carthage bootstrap --platform iOS --cache-builds .PHONY: generate-licenses generate-licenses: # Generate licenses with LicensePlist and regenerate project mint run LicensePlist license-plist --output-path ${PRODUCT_NAME}/Settings.bundle $(MAKE) generate-xcodeproj .PHONY: generate-xcodeproj generate-xcodeproj: # Generate project with XcodeGen mint run xcodegen xcodegen generate $(MAKE) install-cocoapods .PHONY: build-debug build-debug: # Xcode build for debug set -o pipefail && \ xcodebuild \ -sdk ${TEST_SDK} \ -configuration ${TEST_CONFIGURATION} \ -workspace ${WORKSPACE_NAME} \ -scheme ${SCHEME_NAME} \ build \ | bundle exec xcpretty .PHONY: test test: # Xcode test # TEST_DEVICE=[device] TEST_OS=[OS] set -o pipefail && \ xcodebuild \ -sdk ${TEST_SDK} \ -configuration ${TEST_CONFIGURATION} \ -workspace ${WORKSPACE_NAME} \ -scheme ${SCHEME_NAME} \ -destination ${TEST_DESTINATION} \ -skip-testing:${UI_TESTS_TARGET_NAME} \ clean test \ | bundle exec xcpretty .PHONY: show-devices show-devices: # Show devices instruments -s devices今回のメインはAzure Pipelinesなので、Makefileの内容は解説しません。
私が以前書いたGitHub Actionsの記事が参考になると思うので、よかったらご参照ください。今回のMakefileは、本記事で使うコマンドのみを抜粋および編集して紹介しています。
私が普段使っているMakefileの全容はGitHub Gistにあるので、よかったら参考にしてください。設定ファイルの構成
今回はYAMLファイルを使ってCI環境を構築します。
Classic Editorを使ってGUIで構築する場合、私が以前書いた記事が参考になると思います。最初に全体の構成を説明します。
trigger: … schedules: … pool: vmImage: 'macos-latest' steps: …
名前 説明 trigger パイプライン実行のトリガー schedules パイプライン定期実行のスケジュール pool - vmImage 実行するマシン
iOSアプリはmacOSでしかビルドできないためmacos-latest
を指定しているsteps パイプラインのステップ 各項目の紹介
各項目を上から順に紹介します。
trigger
パイプラインを実行するトリガーを設定します。
私は以下のブランチのプッシュをトリガーにしています。
master
release/*
develop
feature/*
ブランチまで実行すると、料金が高くつくので避けています。trigger: batch: true branches: include: - master - release/* - develop # - feature/* paths: exclude: - README.md - LICENSE - Rambafile
README.md
やRambafile
など、iOSアプリに直接関係ないファイルは、プッシュしてもCIが動かないようにしています。schedules
パイプラインを定期実行するスケジュールを設定します。
私は毎日0時に以下のブランチをCIするようにしています。
develop
∵念のため
プッシュ時にCIを実行していれば、定期実行はしなくてもいいと思いますfeature/*
∵プッシュ時にCIしていないため
develop
ブランチへマージせずに帰宅しても、次の日の朝にCIの結果がわかるので便利ですschedules: - cron: "0 15 * * *" displayName: Daily midnight build branches: include: # - master # - release/* - develop - feature/* always: falsecron式で指定する時間はUTCであり、日本時間にするには9時間足す必要があります。
15:00 UTC
=00:00 JST
alwaysで
false
を指定すると、前回の実行から変更があったブランチのみを対象にします。pool
実行するマシンを設定します。
pool: vmImage: 'macos-latest'「設定ファイルの構成」で紹介した内容がすべてです。
steps
パイプライン内のステップを1つずつ紹介します。
ステップは
steps
に記述します。trigger: … schedules: … pool: vmImage: 'macos-latest' steps: # ここにステップを記述する …チェックアウト
特別なことをしない限り、チェックアウトするリポジトリやブランチは指定する必要がありません。
環境変数のエクスポート
環境変数をエクスポートします。
- script: | export DEVELOPER_DIR=/Applications/Xcode_11.3.1.app/Contents/Developer export MINT_PATH=mint/lib export MINT_LINK_PATH=mint/bin displayName: Export environment variables環境変数はYAMLで直接指定できそうですが、やり方がわからなかったので手動で行っています。
Xcodeの一覧出力(任意)
CIサーバー上にインストールされているXcodeの一覧を出力します。
- script: ls /Applications | grep 'Xcode' displayName: Show Xcode listXcodeのバージョン出力(任意)
指定したXcodeのバージョンを出力します。
- script: xcodebuild -version displayName: Show Xcode versionBundlerで管理しているライブラリのインストール
Bundlerで管理しているライブラリをインストールします。
- script: make install-bundler displayName: Bundle installMintのインストール
Mintをインストールします。
- script: brew install mint displayName: Install MintCarthageで管理しているライブラリのインストール
Carthageで管理しているライブラリをインストールします。
- script: make install-carthage displayName: Install Carthage frameworksライセンス情報の生成、プロジェクトファイルの生成、CocoaPodsで管理しているライブラリのインストール
ライセンス情報の生成、プロジェクトファイルの生成、CocoaPodsで管理しているライブラリのインストールを行います。
- script: make generate-licenses displayName: Generate licenses, Xcode project, And Pod installビルド
ビルドします。
- script: make build-debug displayName: Xcode build for debug単体テストの実行
単体テストを実行します。
- script: make test displayName: Xcode test設定ファイルの全体図
最後に設定ファイルの全体図を載せます。
trigger: batch: true branches: include: - master - release/* - develop # - feature/* paths: exclude: - README.md - LICENSE - Rambafile schedules: - cron: "0 15 * * *" displayName: Daily midnight build branches: include: # - master # - release/* - develop - feature/* always: false pool: vmImage: 'macos-latest' steps: # 環境変数のエクスポート - script: | export DEVELOPER_DIR=/Applications/Xcode_11.3.1.app/Contents/Developer export MINT_PATH=mint/lib export MINT_LINK_PATH=mint/bin displayName: Export environment variables # Xcodeの一覧出力 - script: ls /Applications | grep 'Xcode' displayName: Show Xcode list # Xcodeのバージョン出力 - script: xcodebuild -version displayName: Show Xcode version # Bundlerで管理しているライブラリのキャッシュ復元 # - task: Cache@2 # inputs: # key: 'gems | "$(Agent.OS)" | Gemfile.lock' # restoreKeys: | # gems | "$(Agent.OS)" # gems # path: $(Pipeline.Workspace)/vendor/bundle # displayName: Cache Gems # Bundlerで管理しているライブラリのインストール - script: make install-bundler displayName: Bundle install # Mintのインストール - script: brew install mint displayName: Install Mint # Mintで管理しているライブラリのキャッシュ復元 # - task: Cache@2 # inputs: # key: 'mint | "$(Agent.OS)" | Mintfile' # restoreKeys: | # mint | "$(Agent.OS)" # mint # path: $(Pipeline.Workspace)/mint # displayName: Cache Mint packages # Carthageで管理しているライブラリのキャッシュ復元 # - task: Cache@2 # inputs: # key: 'carthage | "$(Agent.OS)" | Cartfile.resolved' # restoreKeys: | # carthage | "$(Agent.OS)" # carthage # path: $(Pipeline.Workspace)/Carthage # displayName: Cache Carthage packages # Carthageで管理しているライブラリのインストール - script: make install-carthage displayName: Install Carthage frameworks # CocoaPodsで管理しているライブラリのキャッシュ復元 # - task: Cache@2 # inputs: # key: 'pods | "$(Agent.OS)" | Podfile.lock' # restoreKeys: | # pods | "$(Agent.OS)" # pods # path: $(Pipeline.Workspace)/Pods # displayName: Cache Pods # ライセンス情報の生成、プロジェクトファイルの生成、CocoaPodsで管理しているライブラリのインストール - script: make generate-licenses displayName: Generate licenses, Xcode project, And Pod install # ビルド - script: make build-debug displayName: Xcode build for debug # 単体テストの実行 - script: make test displayName: Xcode testシンプルなYAMLファイルなので、慣れれば読みやすいと思います。
キャッシュ
cache
タスクを使うと、ライブラリなどのキャッシュを取得できるのですが、エラーとなりうまく使えませんでした。
そのため、今回はキャッシュを使っていません。
コメントアウトしていますが、そもそも記述が間違っている可能性があります。おわりに
Azure PipelinesのYAMLで基本的なiOSアプリのCIを回すことができました!
キャッシュが取れていなかったり、静的解析していなかったりと改善点もあるので、知っている方がいたら教えていただけると嬉しいです。
参考リンク
- 投稿日:2020-03-18T16:00:32+09:00
【Sign in with Apple】「xxxxx@privaterelay.appleid.com」へメールを送った際バウンスする
はじめに
Sign In with Apple(Appleログインと後述)が2019年に発表され、2020年4月には必須化になるなか導入の際にハマったインフラ系のことについてメモ書きしておきます。
私はiOSエンジニアではないですがPMやっていたときに遭遇しました。
ユーザ登録時にAppleログインで会員登録する際にメールアドレスをサービス提供側に公開するか非公開にするか選択があります。
非公開を選択した場合、サービス側が取得できるメールアドレスは「xxxx@privaterelay.appleid.com」になります。
本家サイトには「xxxx@privaterelay.appleid.com」に返信すれば登録ユーザに届くというようなことを書いているけどもGmailで送信した場合に下記の様にバウンスしてきました。これの解決方法です。本記事で力になれないこと
これ以上読んで頂いても目的のものが含まれていない可能性を提示します。
- Gmailから発信されたメールの転送
- Gsuiteからの発信されたメールの転送
本記事はDNS周りの設定がメインとなります。
Gsuiteからの送信についてはもしかしたらやりようがあるかもしれませんが、Apple側の設定ができず。。。Gmailがフリーメールであるため作成可能なため受信は難しいかもしれません。解決策
SPF設定をしましょう(ドン!)
ここに書いてありあました。
https://developer.apple.com/documentation/signinwithapplejs/communicating_using_the_private_email_relay_serviceメールドメインを登録する
プライベートメールアドレスを持つユーザーにメールを送信するには、アウトバウンドメールまたはメールドメインを登録し、Sender Policy Framework(SPF)を使用してアウトバウンドメールを認証する必要があります。これらのメカニズムにより、このチャネルを介してユーザーとのみ通信できるようになり、スパムの通過を防ぎます。by Google翻訳
ネットワークで、認証系、、、ちょっとだけアレルギーがでました
SPFとは
細かい説明は下記サイトを見てください。
https://salt.iajapan.org/wpmu/anti_spam/admin/tech/explanation/spf/おさえておきたい事柄として、
受信したメールのドメインが信頼されているサーバから発信されたものなのかを確認する機構です。
メールの送信者ってプログラムで指定(もとい偽装)できますよね。下のスクショでいう送信元の話です。
SPFはそれがあっても安全さを担保する仕組みと思ってください。
送信者側設定
DNSの設定になります。
やることは対象ドメインに下記のTXTレコードを追加します。これで終了。
「このドメインはここから送信されたメールに対しては保証します!」って言うことなんですね
※自分自身は設定できません
"v=spf1 include:[許可するドメイン名] ~all"
設定の方法にはいろいろあるみたいなので必要に応じてアレンジできますね。
紹介されているのはIPアドレス、ドメイン名、メールサーバ、あわせ技ななどなど。
https://www.naritai.jp/guidance_spf_example.htmlチェックツールもいくつかありました。
下記サイトで紹介されているみたいなのでどうしてもうまく行かないときとかは使ってみてもいいかもしれません。
自ドメインの設定が正しいかや、メールの一斉配信ツールなどでメールサーバが外部にあるときとかはspfの設定があるかを確認することもできます。
https://asumeru.net/spf-record-check余談ではありますが今回nslookupのオプションでDNSのレコードを漁るということを覚えました。
これで、対象のドメインのDNS設定を観ることができます
nslookup -type=[レコード名] [ドメイン名]
例えば、
nslookup -type=mx [ドメイン名]
で、対象ドメインのメールサーバがわかります。Apple Developer側設定
下記の記事を参考にしてみてください。
やることとしては、受信可能にするドメインかメールアドレスを登録することだけです。
送信元とFromが一緒であればここの設定だけで大丈夫だと思います(多分)
https://notes.tret.jp/sign-in-with-apple-register/終わりに
Appleログインを実装する上では一定数ハマるひとがいなくなればといいなとおもいます。
セキュリティ周りをしっかりするのはいいけどもなかなか初見殺しで面倒に感じました。あと、黒い画面でネットワークのコマンド打って、ちょっとかっこいいと感じてしまったのは秘密です。
- 投稿日:2020-03-18T08:23:23+09:00
WKWebViewへの移行が完了してもアプリにはUIWebViewが潜んでいるかもしれない
はじめ
先日、アプリで使用しているWebViewを非推奨APIとなっているUIWebViewからWKWebViewへ移行しました。
これでアプリ内からUIWebViewはいなくなりました。
と思っていたのですが、それでもAppleから「UIWebViewを使わないでね」という連絡がやってきてしまいました。Dear Developer,
We identified one or more issues with a recent delivery for your app, <アプリ名>. Your delivery was successful, but you may wish to correct the following issues in your next delivery:
ITMS-90809: Deprecated API Usage - Apple will stop accepting submissions of apps that use UIWebView APIs starting from December 2020 . See https://developer.apple.com/documentation/uikit/uiwebview for more information.
After you've corrected the issues, you can upload a new binary to App Store Connect.
Best regards,
The App Store Teamプロジェクト内を「UIWebView」で検索したところPods内のAFNetworking/UIKitがヒットしました。
原因はAFNetworking/UIKitに含まれているUIWebViewの拡張機能でした。
これからプロジェクトを修正してUIWebViewを根絶やしにしていきたいと思います。
修正方針
AFNetworkingのissueを参考に修正します。
issueでは以下の修正方法などが紹介されています。
- AFNetworking/UIKit内のUIWebViewに関するファイルを削除する方法
- 不要な依存関係(AFNetworking/UIKit)を削除する方法
前者はチーム開発の場合に各々がファイルを削除することになり、ミスに繋がると思いました。
また、Pods内を編集すること自体にも抵抗があります。
今回は後者の方法で修正することにしました。修正
まずは、Podfileの該当行を修正します。
'UIKit'
以外の必要なsubspecsを指定します。Podfile# pod 'AFNetworking' # ↓ pod 'AFNetworking', :subspecs => ['Reachability', 'Security', 'Serialization', 'NSURLSession']そして、インポートするファイルが変わるためアプリ側のソースも合わせて修正します。
// #import "AFNetworking.h" // ↓ #import <AFNetworking/AFNetworking-umbrella.h>最後に、
pod install
すれば問題なく動くはずです。まとめ
アプリでUIWebViewを使用していないのにもかかわらず、Appleから連絡が来てしまう場合はライブラリが原因かもしれません。
ライブラリのissueを確認するなどして対応するといいと思います。
今後、UIWebViewが含まれているアプリはリジェクトされるようになります(新規アプリは2020年4月、アップデートは2020年12月から)。
本記事がアプリ開発の助けになれば幸いです。参考
- 投稿日:2020-03-18T07:41:51+09:00
【Swift】Sing in with AppleのボタンをStoryboardで設定する
Sing in with Appleでは
ASAuthorizationAppleIDButton
を使用する必要がありますが
このクラスはStoryboardでサポートされておらず
コード上で追加する必要があります。そこで
今回はCustom Classを作成することで
StoryboardでもSign in with Appleのボタンを設定する方法について
整理してみました。コードでの追加方法は以前↓に書かせていただきました。
https://qiita.com/shiz/items/5e094910f742c2ad72a4実装方法
UIButton
クラスのサブクラスを定義する
UIButton
クラスのサブクラスを定義します
(名前は任意です)class SignInWithAppleIDButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { super.init(coder: coder) } }
ASAuthorizationAppleIDButton
をプロパティに保持する実際にSign in with Appleを実行するための
ASAuthorizationAppleIDButton
をプロパティに保持します。class SignInWithAppleIDButton: UIButton { private var appleIDButton: ASAuthorizationAppleIDButton! ... }暗黙的にOptionalをUnwrapしていますが
次に定義するdraw(_:)
メソッドで必ず初期化をするため
問題はありません。
draw(_:)
メソッドをoverrideする
draw(_:)
メソッドの中で
ボタンを描画します。class SignInWithAppleIDButton: UIButton { super.draw(rect) appleIDButton = ASAuthorizationAppleIDButton(authorizationButtonType: .default, authorizationButtonStyle: .black) addSubview(appleIDButton) appleIDButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ appleIDButton.topAnchor.constraint(equalTo: self.topAnchor), appleIDButton.leadingAnchor.constraint(equalTo: self.leadingAnchor), appleIDButton.bottomAnchor.constraint(equalTo: self.bottomAnchor), appleIDButton.trailingAnchor.constraint(equalTo: self.trailingAnchor), ]) }ここでは
authorizationButtonTypeと
authorizationButtonStyleに
固定値を設定していますが
後ほどカスタム可能にします。Storyboardで設定可能にする
次にStoryboardで表示できるように
@IBDesignable
をクラスに設定します。@IBDesignable class SignInWithAppleIDButton: UIButton { super.draw(rect) appleIDButton = ASAuthorizationAppleIDButton(authorizationButtonType: .default, authorizationButtonStyle: .black) addSubview(appleIDButton) appleIDButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ appleIDButton.topAnchor.constraint(equalTo: self.topAnchor), appleIDButton.leadingAnchor.constraint(equalTo: self.leadingAnchor), appleIDButton.bottomAnchor.constraint(equalTo: self.bottomAnchor), appleIDButton.trailingAnchor.constraint(equalTo: self.trailingAnchor), ]) }ここまででStoryboardに
UIButton
を設置してCustom Classに
SignInWithAppleIDButton
を指定すると
下記のようにStoryboard上で表示させることができます。※ 制約などについてはAppleのHuman Interface Guidelinesに沿っています。
こちらについては後ほど記載します。ボタンをカスタマイズする
ここからはさらに
@IBInspectable
を使って
Storyboard上でボタンをカスタマイズ可能にしたいと思います。
ASAuthorizationAppleIDButton
の定義を見てみると
ASAuthorizationAppleIDButton.ButtonType
ASAuthorizationAppleIDButton.Style
cornerRadius
の設定が可能です。
extension ASAuthorizationAppleIDButton { @available(iOS 13.0, *) public enum ButtonType : Int { case signIn = 0 case `continue` = 1 @available(iOS 13.2, *) case signUp = 2 public static var `default`: ASAuthorizationAppleIDButton.ButtonType { get } } @available(iOS 13.0, *) public enum Style : Int { case white = 0 case whiteOutline = 1 case black = 2 } } @available(iOS 13.0, *) open class ASAuthorizationAppleIDButton : UIControl { public convenience init(type: ASAuthorizationAppleIDButton.ButtonType, style: ASAuthorizationAppleIDButton.Style) public init(authorizationButtonType type: ASAuthorizationAppleIDButton.ButtonType, authorizationButtonStyle style: ASAuthorizationAppleIDButton.Style) /** @abstract Set a custom corner radius to be used by this button. */ open var cornerRadius: CGFloat }そこでこの3つのプロパティをカスタマイズできるようにします。
@IBDesignable class SignInWithAppleIDButton: UIButton { ... @IBInspectable var cornerRadius: CGFloat = 6.0 @IBInspectable var type: Int = ASAuthorizationAppleIDButton.ButtonType.default.rawValue @IBInspectable var style: Int = ASAuthorizationAppleIDButton.Style.black.rawValue ... override func draw(_ rect: CGRect) { super.draw(rect) let type = ASAuthorizationAppleIDButton.ButtonType.init(rawValue: self.type) ?? .default let style = ASAuthorizationAppleIDButton.Style.init(rawValue: self.style) ?? .black appleIDButton = ASAuthorizationAppleIDButton(authorizationButtonType: type, authorizationButtonStyle: style) appleIDButton.cornerRadius = cornerRadius addSubview(appleIDButton) appleIDButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ appleIDButton.topAnchor.constraint(equalTo: self.topAnchor), appleIDButton.leadingAnchor.constraint(equalTo: self.leadingAnchor), appleIDButton.bottomAnchor.constraint(equalTo: self.bottomAnchor), appleIDButton.trailingAnchor.constraint(equalTo: self.trailingAnchor), ]) } }こうするとStoryboard上で設定が変更可能になります。
IBInspectable
ではenum
が指定できないため
RawValue
のInt
を設定し
enum
のcaseに存在しない値に関してはデフォルトの値を設定しています。
cornerRadius
の値はAppleがデフォルトで提供している値のようです。ボタンをタップ可能にする
ここまででデザインはできましたが
ボタンをタップしても動作しません。そこでボタンがタップされたことを
プロパティで保持しているASAuthorizationAppleIDButton
へ
伝えるようにします。@IBDesignable class SignInWithAppleIDButton: UIButton { private var appleIDButton: ASAuthorizationAppleIDButton! ... override func draw(_ rect: CGRect) { super.draw(rect) ... appleIDButton.addTarget(self, action: #selector(appleIDButtonTapped), for: .touchUpInside) ... } @objc func appleIDButtonTapped(_ sender: Any) { sendActions(for: .touchUpInside) } }
sendActions(for:)
はUIControlのメソッドで
関連しているUIControl(今回はUIButton
のsubViewのASAuthorizationAppleIDButton
)へ
イベントを伝播させることができます。https://developer.apple.com/documentation/uikit/uicontrol/1618211-sendactions
Human Interface Guidelines
上記のようにStoryboardで設定はできるようになるものの
ASAuthorizationAppleIDButton
はデザインに関して
多くのガイドラインが存在します。そこでそれらが記載されている
Human Interface Guidelines
を見ていきたいと思います。
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/全てに共通のルール
- 目立つように表示すること
- 他のサインインボタン(Facebook, Googleなど)よりも小さくしないこと
- スクロールしなくてもボタンは見えるようにすること
スクロールしなくてもボタンは見えるようにすることに関しては
iPhoneSEなどの小さい端末での表示に注意する必要がありそうですね。システムが提供するボタンを使用する場合
3つのスタイルの注意点
White
全プラットフォーム※で利用可能
※iOS, macOS, iPadOS, watchOS
- 暗い背景や十分にコントラストが与えられる色のついた背景で使用すること
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/White with Outline
iOS, macOS, webで利用可能
- 白い背景やWhiteで使うと十分にコントラストが与えられない背景色で使用すること
- 黒のアウトラインが見辛いので暗かったり飽和した背景とは一緒に使わないこと
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
Black
全プラットフォームで利用可能
- 白や明るい背景と一緒に使用すること
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/※ WatchOSの場合、純粋な黒の背景に対応するために
グレーのボタンが用意されている
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/Button Size and Corner Radius
アプリの他のボタンに合わせて調整が可能ですが
その中でも下記のようなガイドラインがあります。
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
- iOS, macOS, webではボタンの最小サイズと最小マージンを維持すること
ロケールでボタンタイトルの長さが変わるので注意が必要
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/カスタムボタンを作成する場合
iOS, macOS, Webで利用可能
Apple Design Resourcesからロゴのダウンロードが可能
https://developer.apple.com/design/resources/ロゴを使用する場合のガイドライン
- ロゴ自体をボタンとして使用しないこと
- ロゴファイルの高さをボタンの高さに合わせること
- 切り取らないこと
- 垂直方向(vertical)のpaddingを追加しないこと
- 独自のカラーを使用しないこと
左寄せロゴボタンを作成する場合
ボタンの高さを基にロゴファイルのフォーマットを選択する
SVGとPDFはベクター形式のなのでどの高さでも利用できるが、PNGは44ptの高さのみで使用すること
44ptはiOSでのデフォルトの高さで推奨される高さタイトル※にはシステムのフォントを利用すること
※ Sign in with Apple, Sign up with Apple, or Continue with Apple
タイトルのフォントサイズはボタンの高さの43%
逆を言えばボタンの高さはフォントサイズの233%
サイズの例
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/タイトルの大文字、小文字を変えない
- 最初の一文字を大文字にすること
具体的にはSign, Continue, Appleは最初が大文字
それ以外は全て小文字にするタイトルとボタンの右端の間のマージンをボタンの幅を最低8%以上にする
ボタンのサイズと周りのマージンを最小以上にする
システムで提供されているものと同様のガイドラインです。

参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/ロゴのみのボタンを作成する場合
ボタンのサイズを基にロゴファイルのフォーマットを選択する
- ボタンの高さを基にロゴファイルのフォーマットを選択する
- SVGとPDFはベクター形式のなのでどの高さでも利用できるが、PNGは44ptx44ptでのサイズでのみ使用すること
右側に平行方向のpaddingを入れない
- ロゴのみの画像はすでに適切なpaddingが全てのサイドに入っているので変更はしないこと
- アスペクト比は1:1を維持すること
デフォルトの四角い形を変更したい場合はMaskを利用すること
- 元の画像を切り取ったりPaddingを追加してはいけない
ボタンの周りのマージンは最低ボタンの高さの1/10以上にする
まとめ
Sign in with Appleで使用するボタンについて
Storyboardでの設定方法について見てみました。画面に関してStoryboardを使用している場合には
他のパーツと一緒にデザインできるので
役に立つのかと思っています。一方でしっかりと使用する際のガイドラインも存在し
そこを守らないとアプリの審査でリジェクトされる可能性もあるため
しっかりとチェックする必要がありますね。もし何か間違いなどございましたらご指摘ください??♂️
参照記事
https://developer.apple.com/documentation/authenticationservices/implementing_user_authentication_with_sign_in_with_apple
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ImplementingACustomControl.html
https://medium.com/swlh/how-to-use-asauthorizationappleidbutton-in-storyboard-653f9cd94817
- 投稿日:2020-03-18T07:41:51+09:00
【Swift】Sign in with AppleのボタンをStoryboardで設定する
Sign in with Appleでは
ASAuthorizationAppleIDButton
を使用する必要がありますが
このクラスはStoryboardでサポートされておらず
コード上で追加する必要があります。そこで
今回はCustom Classを作成することで
StoryboardでもSign in with Appleのボタンを設定する方法について
整理してみました。コードでの追加方法は以前↓に書かせていただきました。
https://qiita.com/shiz/items/5e094910f742c2ad72a4実装方法
UIButton
クラスのサブクラスを定義する
UIButton
クラスのサブクラスを定義します
(名前は任意です)class SignInWithAppleIDButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) } required init?(coder: NSCoder) { super.init(coder: coder) } }
ASAuthorizationAppleIDButton
をプロパティに保持する実際にSign in with Appleを実行するための
ASAuthorizationAppleIDButton
をプロパティに保持します。class SignInWithAppleIDButton: UIButton { private var appleIDButton: ASAuthorizationAppleIDButton! ... }暗黙的にOptionalをUnwrapしていますが
次に定義するdraw(_:)
メソッドで必ず初期化をするため
問題はありません。
draw(_:)
メソッドをoverrideする
draw(_:)
メソッドの中で
ボタンを描画します。class SignInWithAppleIDButton: UIButton { super.draw(rect) appleIDButton = ASAuthorizationAppleIDButton(authorizationButtonType: .default, authorizationButtonStyle: .black) addSubview(appleIDButton) appleIDButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ appleIDButton.topAnchor.constraint(equalTo: self.topAnchor), appleIDButton.leadingAnchor.constraint(equalTo: self.leadingAnchor), appleIDButton.bottomAnchor.constraint(equalTo: self.bottomAnchor), appleIDButton.trailingAnchor.constraint(equalTo: self.trailingAnchor), ]) }ここでは
authorizationButtonTypeと
authorizationButtonStyleに
固定値を設定していますが
後ほどカスタム可能にします。Storyboardで設定可能にする
次にStoryboardで表示できるように
@IBDesignable
をクラスに設定します。@IBDesignable class SignInWithAppleIDButton: UIButton { super.draw(rect) appleIDButton = ASAuthorizationAppleIDButton(authorizationButtonType: .default, authorizationButtonStyle: .black) addSubview(appleIDButton) appleIDButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ appleIDButton.topAnchor.constraint(equalTo: self.topAnchor), appleIDButton.leadingAnchor.constraint(equalTo: self.leadingAnchor), appleIDButton.bottomAnchor.constraint(equalTo: self.bottomAnchor), appleIDButton.trailingAnchor.constraint(equalTo: self.trailingAnchor), ]) }ここまででStoryboardに
UIButton
を設置してCustom Classに
SignInWithAppleIDButton
を指定すると
下記のようにStoryboard上で表示させることができます。※ 制約などについてはAppleのHuman Interface Guidelinesに沿っています。
こちらについては後ほど記載します。ボタンをカスタマイズする
ここからはさらに
@IBInspectable
を使って
Storyboard上でボタンをカスタマイズ可能にしたいと思います。
ASAuthorizationAppleIDButton
の定義を見てみると
ASAuthorizationAppleIDButton.ButtonType
ASAuthorizationAppleIDButton.Style
cornerRadius
の設定が可能です。
extension ASAuthorizationAppleIDButton { @available(iOS 13.0, *) public enum ButtonType : Int { case signIn = 0 case `continue` = 1 @available(iOS 13.2, *) case signUp = 2 public static var `default`: ASAuthorizationAppleIDButton.ButtonType { get } } @available(iOS 13.0, *) public enum Style : Int { case white = 0 case whiteOutline = 1 case black = 2 } } @available(iOS 13.0, *) open class ASAuthorizationAppleIDButton : UIControl { public convenience init(type: ASAuthorizationAppleIDButton.ButtonType, style: ASAuthorizationAppleIDButton.Style) public init(authorizationButtonType type: ASAuthorizationAppleIDButton.ButtonType, authorizationButtonStyle style: ASAuthorizationAppleIDButton.Style) /** @abstract Set a custom corner radius to be used by this button. */ open var cornerRadius: CGFloat }そこでこの3つのプロパティをカスタマイズできるようにします。
@IBDesignable class SignInWithAppleIDButton: UIButton { ... @IBInspectable var cornerRadius: CGFloat = 6.0 @IBInspectable var type: Int = ASAuthorizationAppleIDButton.ButtonType.default.rawValue @IBInspectable var style: Int = ASAuthorizationAppleIDButton.Style.black.rawValue ... override func draw(_ rect: CGRect) { super.draw(rect) let type = ASAuthorizationAppleIDButton.ButtonType.init(rawValue: self.type) ?? .default let style = ASAuthorizationAppleIDButton.Style.init(rawValue: self.style) ?? .black appleIDButton = ASAuthorizationAppleIDButton(authorizationButtonType: type, authorizationButtonStyle: style) appleIDButton.cornerRadius = cornerRadius addSubview(appleIDButton) appleIDButton.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ appleIDButton.topAnchor.constraint(equalTo: self.topAnchor), appleIDButton.leadingAnchor.constraint(equalTo: self.leadingAnchor), appleIDButton.bottomAnchor.constraint(equalTo: self.bottomAnchor), appleIDButton.trailingAnchor.constraint(equalTo: self.trailingAnchor), ]) } }こうするとStoryboard上で設定が変更可能になります。
IBInspectable
ではenum
が指定できないため
RawValue
のInt
を設定し
enum
のcaseに存在しない値に関してはデフォルトの値を設定しています。
cornerRadius
の値はAppleがデフォルトで提供している値のようです。ボタンをタップ可能にする
ここまででデザインはできましたが
ボタンをタップしても動作しません。そこでボタンがタップされたことを
プロパティで保持しているASAuthorizationAppleIDButton
へ
伝えるようにします。@IBDesignable class SignInWithAppleIDButton: UIButton { private var appleIDButton: ASAuthorizationAppleIDButton! ... override func draw(_ rect: CGRect) { super.draw(rect) ... appleIDButton.addTarget(self, action: #selector(appleIDButtonTapped), for: .touchUpInside) ... } @objc func appleIDButtonTapped(_ sender: Any) { sendActions(for: .touchUpInside) } }
sendActions(for:)
はUIControlのメソッドで
関連しているUIControl(今回はUIButton
のsubViewのASAuthorizationAppleIDButton
)へ
イベントを伝播させることができます。https://developer.apple.com/documentation/uikit/uicontrol/1618211-sendactions
Human Interface Guidelines
上記のようにStoryboardで設定はできるようになるものの
ASAuthorizationAppleIDButton
はデザインに関して
多くのガイドラインが存在します。そこでそれらが記載されている
Human Interface Guidelines
を見ていきたいと思います。
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/全てに共通のルール
- 目立つように表示すること
- 他のサインインボタン(Facebook, Googleなど)よりも小さくしないこと
- スクロールしなくてもボタンは見えるようにすること
スクロールしなくてもボタンは見えるようにすることに関しては
iPhoneSEなどの小さい端末での表示に注意する必要がありそうですね。システムが提供するボタンを使用する場合
3つのスタイルの注意点
White
全プラットフォーム※で利用可能
※iOS, macOS, iPadOS, watchOS
- 暗い背景や十分にコントラストが与えられる色のついた背景で使用すること
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/White with Outline
iOS, macOS, webで利用可能
- 白い背景やWhiteで使うと十分にコントラストが与えられない背景色で使用すること
- 黒のアウトラインが見辛いので暗かったり飽和した背景とは一緒に使わないこと
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
Black
全プラットフォームで利用可能
- 白や明るい背景と一緒に使用すること
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/※ WatchOSの場合、純粋な黒の背景に対応するために
グレーのボタンが用意されている
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/Button Size and Corner Radius
アプリの他のボタンに合わせて調整が可能ですが
その中でも下記のようなガイドラインがあります。
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
- iOS, macOS, webではボタンの最小サイズと最小マージンを維持すること
ロケールでボタンタイトルの長さが変わるので注意が必要
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/カスタムボタンを作成する場合
iOS, macOS, Webで利用可能
Apple Design Resourcesからロゴのダウンロードが可能
https://developer.apple.com/design/resources/ロゴを使用する場合のガイドライン
- ロゴ自体をボタンとして使用しないこと
- ロゴファイルの高さをボタンの高さに合わせること
- 切り取らないこと
- 垂直方向(vertical)のpaddingを追加しないこと
- 独自のカラーを使用しないこと
左寄せロゴボタンを作成する場合
ボタンの高さを基にロゴファイルのフォーマットを選択する
SVGとPDFはベクター形式のなのでどの高さでも利用できるが、PNGは44ptの高さのみで使用すること
44ptはiOSでのデフォルトの高さで推奨される高さタイトル※にはシステムのフォントを利用すること
※ Sign in with Apple, Sign up with Apple, or Continue with Apple
タイトルのフォントサイズはボタンの高さの43%
逆を言えばボタンの高さはフォントサイズの233%
サイズの例
参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/タイトルの大文字、小文字を変えない
- 最初の一文字を大文字にすること
具体的にはSign, Continue, Appleは最初が大文字
それ以外は全て小文字にするタイトルとボタンの右端の間のマージンをボタンの幅を最低8%以上にする
ボタンのサイズと周りのマージンを最小以上にする
システムで提供されているものと同様のガイドラインです。

参照元:https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/ロゴのみのボタンを作成する場合
ボタンのサイズを基にロゴファイルのフォーマットを選択する
- ボタンの高さを基にロゴファイルのフォーマットを選択する
- SVGとPDFはベクター形式のなのでどの高さでも利用できるが、PNGは44ptx44ptでのサイズでのみ使用すること
右側に平行方向のpaddingを入れない
- ロゴのみの画像はすでに適切なpaddingが全てのサイドに入っているので変更はしないこと
- アスペクト比は1:1を維持すること
デフォルトの四角い形を変更したい場合はMaskを利用すること
- 元の画像を切り取ったりPaddingを追加してはいけない
ボタンの周りのマージンは最低ボタンの高さの1/10以上にする
まとめ
Sign in with Appleで使用するボタンについて
Storyboardでの設定方法について見てみました。画面に関してStoryboardを使用している場合には
他のパーツと一緒にデザインできるので
役に立つのかと思っています。一方でしっかりと使用する際のガイドラインも存在し
そこを守らないとアプリの審査でリジェクトされる可能性もあるため
しっかりとチェックする必要がありますね。もし何か間違いなどございましたらご指摘ください??♂️
参照記事
https://developer.apple.com/documentation/authenticationservices/implementing_user_authentication_with_sign_in_with_apple
https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/
https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/ImplementingACustomControl.html
https://medium.com/swlh/how-to-use-asauthorizationappleidbutton-in-storyboard-653f9cd94817
- 投稿日:2020-03-18T00:41:00+09:00
Swift で map (closure) のこんな書き方
Swift の map などで使用する closure には書き方が色々ありますね。
下記のような書き方は知っていました。let numbers = [0, 1, 2, 3] // 下記すべて[String]型 ["0", "1", "2", "3"] numbers.map({ (number: Int) -> String in return String(number) }) numbers.map({ (number: Int) -> String in String(number) }) // 暗黙的な戻り値 numbers.map({ number -> String in String(number) }) // 引数の型省略 numbers.map({ number in String(number) }) // 戻り値の型推論 numbers.map({ String($0) }) // 引数名の省略 numbers.map { String($0) } // 括弧の省略こんな書き方を発見しました。勉強不足でした。
numbers.map(String.init)結果は上記の例と同じですが、
単純にnumbers.map{ String($0) }
と全く一緒というわけではなさそうです。
http://swiftlife.hatenablog.jp/entry/2015/12/27/213058 こちらで解説されていましたのでご参照ください。現に、Playgrounds で試すと結果の表示のされ方が異なります。右半分の表示です。
実験
イニシャライザ以外にも使えるのか
いけた。
// いけた func f(_ number: Int) -> String { return String(number) } numbers.map(f) // 引数ラベルをアンダースコアで省略できるようにしとかなくてもいい func ff(number: Int) -> String { return String(number) } numbers.map(ff) // クラス関数とかでも問題なく使用可 class C { class func f(_ number: Int) -> String { return String(number) } } numbers.map(C.f)関数の引数の数
closure の引数の数と、関数の引数の数があってないと エラー です。
引数2つ目以降無視されるとかはないです。// エラー!! func f(a: Int, b: Int) -> String { return String(a) } numbers.map(f) // これは OK (sorted のクロージャの引数は2つのため) func ff(a: Int, b: Int) -> Bool { return true } numbers.sorted(by: ff)参考