はじめに
DeNAでiOSエンジニアをしている@tsuzuki817です!
本記事は SwiftWednesday Advent Calendar 2023 の16日目の記事です🎅🎄
昨日は @treastrain(Ryoga Tanaka)さんの【初心者向け】iPad だけで Google の AI モデル「Gemini」を使ったアプリを作ってみよう!でした
Geminiを使ってみようと思える良記事でしたね
さて、今回は SwiftUIを使って10日でSNSアプリをリリースしたので知見を公開してみる
という記事です!
本当はもっと技術よりの記事を書こうと思っていたのですが、書こうとしていた内容が iOS 17
だと解消されている問題だったので急遽内容を変更して、最近リリースしたアプリについて書かせていただきますのでぜひ飛ばし飛ばしでも良いので見てってください🙏
本編
アプリをリリース
個人開発したアプリ 「:igyo:」
つい先日リリースしました
ざっくり説明すると、スタンプコミュニケーションをベースとしたゆるいSNSアプリです。
(ぜひダウンロードしたり、友達・知人・家族に紹介してみてね)
このアプリはiOS 17以上をターゲットとしたアプリです。
UIはSwiftUIで作り、バックエンドはFirebaseを利用しています。
調べてみるとこのアプリを作り始めたののは12/2でした。
平日は本業+副業+趣味+家族の時間にリソース割いており毎日1~2時間、土日は金沢旅行で片道5時間を爆走するなどしていてガッツリ開発の時間を確保できていない中での10日間でアプリをリリースすることができました。
もっと暇な時だったら2日間でもいけた(盛)
開発編
アイデアが浮かんだらとりあえず作り始めてしまいましょう!
手を動かしながら作った方が実際に動くものを触りながら開発できるのでより良い体験を作り出すことができると信じてます。
また、開発途中に今の実装とはあまり関係のない機能を思いつくことがあると思います。
その際にすぐにそちらの作業に移行してしまうのは自分は極力避けています!
理由は単純で行なっている作業がおざなりになってしまうからです。
アイデアが浮かんだらすぐにgithubのissueに書き込んで忘れないようにしたら一旦我慢しましょう🐵
次からは今回作ったアプリの各画面を紹介しながらどのように作っただとか、リジェクトの危険性がある箇所について話していきます!
初回起動
Firebase Analitics, Admobなどを使っている場合はトラッキングの許可のリクエストが必要なので、初回起動時に表示させています。
Info.plist
に Privacy - Tracking Usage Description
を記入した文言がダイアログの中に表示されます。
SwiftUIで表示させたい場合は以下のように didBecomeActive
をキャッチしてリクエストします。
import AppTrackingTransparency
...
ContentView()
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
ATTrackingManager.requestTrackingAuthorization(completionHandler: { _ in
})
}
チュートリアル
TabView
を使って実装、 TutorialStateを enum
で作ってTabViewにBindingさせてます。
(enum
大好き)
TutorialState.swift
enum enum TutorialState: CaseIterable {
case first
case second
case third
case force
var title: String {
switch self {
case .first:
"ようこそ"
case .second:
"褒めよう"
case .third:
"守ろう"
case .force:
"投稿しよう"
}
}
var description: String {
switch self {
case .first:
"このアプリにあなたを否定する人は誰もいません"
case .second:
"あなたも誰かを褒めるところから始めてみましょう"
case .third:
"ふさわしくない投稿からこの世界を守りましょう"
case .force:
"あなたの偉業を周知させよう"
}
}
var imageName: String {
switch self {
case .first:
"post001"
case .second:
"post002"
case .third:
"post003"
case .force:
"post004"
}
}
}
サインアップ画面
シンプルに今はiOSのみの提供しかしていないので Sign in with Apple
ボタンを置いてます。
(他のSNSの認証機構を実装している場合、Appleでのサインインの提供が必須)
また、このアプリはUGCを提供しているので利用規約の同意やプライバシーポリシーへの事前同意が必要になります。
(User Generated Contents: 一般のユーザーによって制作・生成されたコンテンツ)
投稿する前だったらどこでも良いのですが、一番らくなサインアップ画面の下においてます。
サインアップにはFirebaseAuthを利用しています。
https://firebase.google.com/docs/auth/ios/apple?hl=ja
認証に成功したらFirestoreにuserデータを作成します。
try await firestore.collection(collectionPath)
.document(id)
.setData(
[
"id": id,
"name": name,
"imageUrl": "",
"createdAt": FieldValue.serverTimestamp(),
"updatedAt": FieldValue.serverTimestamp(),
]
)
認証後
認証後、アイコンを設定させます。
LazyVGridを使ってグリッドレイアウトは表現しています。
タイムライン
ScrollView + LazyVStackで構成されています。
また、3点リーダーの箇所はMenuを使って実装しています。
UGCの場合コンテンツをユーザーが非表示にできるようにしなくてはならないです。
この値は単純にAppStrageで保持しています。
@AppStorage("hiddenPostIDs") var hiddenPostIDs: [String] = []
配列はそのまま AppStorageを適応できないので以下のExtensionを使って配列に対応させています。
extension Array: RawRepresentable where Element: Codable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode([Element].self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "[]"
}
return result
}
}
設定画面
アカウント削除機能も必須なので実装しておきましょう!
https://developer.apple.com/jp/news/?id=12m75xbj
以下のように認証情報を削除してあげたり、サインアウトさせてあげる必要があります。
try await Auth.auth().currentUser?.delete()
try Auth.auth().signOut()
ストア申請編
スクショの作り方
自分はKeynoteを使って作っています!
自由に作れますし、画像への出力も早いので便利です🐣
プライバシー
AdMob利用時の「Appのプライバシー」の入力はこちらを参考に入力してます🙇
https://zenn.dev/kazushige/articles/9afa10b36d6828
宣伝のための道具
App Store Marketing Toolsを使うと無料でアプリのショートリンクやアイコン、アイコンつきQRコードなど生成できるのでぜひ宣伝に利用しましょう!
https://tools.applemediaservices.com/app-store
最後に
ざっくりとですが、アプリをリリースするまでの知見をまとめてみました!
この記事を読んで少しでのアプリ開発のトラブル解消に役立てれば嬉しいです!