35
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SwiftWednesdayAdvent Calendar 2023

Day 16

SwiftUIを使って10日でSNSアプリをリリースしたので知見を公開してみる

Last updated at Posted at 2023-12-15

はじめに :beginner:

DeNAでiOSエンジニアをしている@tsuzuki817です!

本記事は SwiftWednesday Advent Calendar 2023 の16日目の記事です🎅🎄

昨日は @treastrain(Ryoga Tanaka)さんの【初心者向け】iPad だけで Google の AI モデル「Gemini」を使ったアプリを作ってみよう!でした :tada:

Geminiを使ってみようと思える良記事でしたね:pray:

さて、今回は SwiftUIを使って10日でSNSアプリをリリースしたので知見を公開してみるという記事です!

本当はもっと技術よりの記事を書こうと思っていたのですが、書こうとしていた内容が iOS 17 だと解消されている問題だったので急遽内容を変更して、最近リリースしたアプリについて書かせていただきますのでぜひ飛ばし飛ばしでも良いので見てってください🙏

本編 :santa:

アプリをリリース

個人開発したアプリ 「:igyo:」 つい先日リリースしました :tada:

ざっくり説明すると、スタンプコミュニケーションをベースとしたゆるいSNSアプリです。
(ぜひダウンロードしたり、友達・知人・家族に紹介してみてね)

igyo .001.png

このアプリはiOS 17以上をターゲットとしたアプリです。
UIはSwiftUIで作り、バックエンドはFirebaseを利用しています。

調べてみるとこのアプリを作り始めたののは12/2でした。
平日は本業+副業+趣味+家族の時間にリソース割いており毎日1~2時間、土日は金沢旅行で片道5時間を爆走するなどしていてガッツリ開発の時間を確保できていない中での10日間でアプリをリリースすることができました。

もっと暇な時だったら2日間でもいけた(盛)

スクリーンショット 2023-12-15 1.14.58.png

開発編

アイデアが浮かんだらとりあえず作り始めてしまいましょう!
手を動かしながら作った方が実際に動くものを触りながら開発できるのでより良い体験を作り出すことができると信じてます。

また、開発途中に今の実装とはあまり関係のない機能を思いつくことがあると思います。
その際にすぐにそちらの作業に移行してしまうのは自分は極力避けています!

理由は単純で行なっている作業がおざなりになってしまうからです。
アイデアが浮かんだらすぐにgithubのissueに書き込んで忘れないようにしたら一旦我慢しましょう🐵

次からは今回作ったアプリの各画面を紹介しながらどのように作っただとか、リジェクトの危険性がある箇所について話していきます!

初回起動

Firebase Analitics, Admobなどを使っている場合はトラッキングの許可のリクエストが必要なので、初回起動時に表示させています。

Info.plist に Privacy - Tracking Usage Descriptionを記入した文言がダイアログの中に表示されます。

スクリーンショット 2023-12-15 1.57.57.png

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を使って作っています!
自由に作れますし、画像への出力も早いので便利です🐣

スクリーンショット 2023-12-15 21.11.01.png

プライバシー

AdMob利用時の「Appのプライバシー」の入力はこちらを参考に入力してます🙇
https://zenn.dev/kazushige/articles/9afa10b36d6828

宣伝のための道具

App Store Marketing Toolsを使うと無料でアプリのショートリンクやアイコン、アイコンつきQRコードなど生成できるのでぜひ宣伝に利用しましょう!
https://tools.applemediaservices.com/app-store

最後に

ざっくりとですが、アプリをリリースするまでの知見をまとめてみました!
この記事を読んで少しでのアプリ開発のトラブル解消に役立てれば嬉しいです!

35
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?