この記事は、iOSアプリ開発から公開までの流れ の第8章です。
本稿では、アプリの利用規約の作成と同意取得の方法について記載します。
利用規約の内容について
Apple の審査では利用規約がないと申請が通らなくなっているようなので、英語/日本語の文章を作成しました(プライバシーポリシーの間違いでした...)。
私は利用規約というものを初めて作成したこともあり、無責任にここで掲載するの控えます。
GitHub にアップロードしたので、参照されたい方はそちらを確認してください。
ただし、内容は保証致しかねます。m(_ _)m
同意取得の方法について
標準的な API などは無いみたいなので、以下の仕様で作り込むことにします。
- 初回起動時(未同意の場合)に表示する
- 端末の設定言語が日本語の場合に日本語の規約、それ以外は英語の規約を表示する
- 規約ファイルは HTML 形式とする(Safari でも確認するため)
- 同意ボタンを用意してボタンをクリックした場合にのみアプリを利用可能とする
- 同意した日時を記録する
- 規約内容
および同意した日時をアプリ利用中に確認可能とする
(補足) アプリアップデート時の処理はその時に作り込む予定です。
1. 規約文章のファイルを追加します
HTML のテンプレートは無いため、Empty ファイルを追加します。
2. 任意のファイル名を入力して Create
3. HTML 形式で規約文章を作成します
日本語ファイルと英語ファイルの両方を作成します。(注:表示内容は暫定の文章です)
4. HTML ファイルを表示するビューを作成します
Gitリポジトリへのコミットとプッシュで作成したビューに WKWebView を使って表示します。
現時点の SwiftUI には WebView がなく、WebKit を使用するのが一般的みたいです。
Locale.preferredLanguages.first で現在の設定言語の優先順位の1番目を取得し、日本語と英語の HTML ファイルを切り替えるようにします。
import SwiftUI
import WebKit
struct SocPingTerms: View {
let isAgreed: Bool
var dateFormatter = DateFormatter()
var body: some View {
VStack(spacing: 0) {
if isAgreed {
ZStack {
Color.white
HStack() {
Spacer()
Text("同意日時: " + self.getAgreementDate())
.font(.system(size: 10, weight: .semibold))
.foregroundColor(Color.init(UIColor.systemGray))
Spacer()
}
}
.frame(height: 14)
}
WebView(url: self.getTermsURL())
}
}
private func getTermsURL() -> URL {
let fileName = Locale.preferredLanguages.first!.hasPrefix("ja") ? "TermsOfService_ja" : "TermsOfService"
let url = Bundle.main.url(forResource: fileName, withExtension: "html")
assert(url != nil, "Bundle.main.url(\(fileName)) failed")
return url!
}
func getAgreementDate() -> String {
let value = UserDefaults.standard.object(forKey: "agreementDate")
guard let date = value as? Date else {
return "N/A"
}
self.dateFormatter.calendar = Calendar(identifier: .gregorian)
self.dateFormatter.locale = Locale(identifier: "C")
self.dateFormatter.timeZone = .current
self.dateFormatter.dateFormat = "YYYY/MM/dd HH:mm:ss"
return self.dateFormatter.string(from: date)
}
}
fileprivate struct WebView: UIViewRepresentable {
var url: URL
func makeUIView(context: Context) -> WKWebView {
return WKWebView()
}
func updateUIView(_ uiView: WKWebView, context: Context) {
uiView.load(URLRequest(url: url))
}
}
5. 呼び出し元でビュー表示を制御します
画面全体を覆うモーダルビューを用いて、同意ボタンを押すとモーダルビューが閉じるようにします。
.sheet ではハーフモーダルビューになってしまいプルダウンでも閉じることができるため、.fullScreenCover を使用します。
アプリ起動時の初期化時に UserDefaults から同意有無を確認に、未同意の場合に表示するようにします。また、同意ボタンを押した際に「同意したこと」「同意した日時」を UserDefaults に記録します。
import SwiftUI
struct ContentView: View {
@State var isTermsPresented: Bool = true
@State var isTermsPresented2: Bool = false
init() {
if UserDefaults.standard.bool(forKey: "isAgreed") {
_isTermsPresented = State(initialValue: false)
}
}
var body: some View {
VStack {
Text("Hello, world!")
.fullScreenCover(isPresented: $isTermsPresented) {
VStack(spacing: 0) {
ZStack {
Color(red: 0.000, green: 0.478, blue: 1.000, opacity: 1.0)
.edgesIgnoringSafeArea(.all)
HStack(alignment: .center) {
Spacer()
Text("利用規約")
.foregroundColor(Color.white)
.font(.system(size: 20, weight: .semibold))
Spacer()
}
}
.frame(height: 50)
SocPingTerms(isAgreed: false)
ZStack {
Color(red: 0.918, green: 0.918, blue: 0.937, opacity: 1.0)
.edgesIgnoringSafeArea(.all)
Button(action: {
UserDefaults.standard.set(true, forKey: "isAgreed")
UserDefaults.standard.set(Date(), forKey: "agreementDate")
self.isTermsPresented = false
}) {
HStack {
Spacer()
Text("同意する")
.font(.system(size: 20))
Spacer()
}
}
}
.frame(height: 60)
}
}
Button(action: {
self.isTermsPresented2 = true
}) {
HStack {
Spacer()
Text("利用規約を開く")
Spacer()
}
}
.sheet(isPresented: $isTermsPresented2) {
SocPingTerms(isAgreed: true)
}
}
}
}
6. 表示結果はこんな感じ
タイトルとボタンが両方日本語のままになっています。
ここは、C言語でいうところのメッセージカタログを使うため、マルチ言語への対応で対応します。
7. シミュレータの言語設定を変更するには
Settings > General > Languages & Region > PREFERRED LANGUAGE ORDER で優先順位を変更できます。
終わり。