CA Tech Dojo -Swift編-

株式会社CyberAgentが主催する、CA Tech Dojo -Swift 編-に参加しました。

CA Tech Dojo -Swift編-
左上から、ハリー、やんやん、めい、かみじょー、まっつー、KURO、Abemaくん、人事のもりまゆさん、ぼく、りょうま、ふじけん

株式会社CyberAgentが主催する、2weeks iOS向け育成型インターンシップ、CA Tech Dojo -Swift 編-に参加しました。

2/16-3/1に開催されたので、記事を書き終わるまで、1ヶ月ほどかかりましたが...

CA Tech Dojo とは

以下、募集ページの抜粋です。

CA Tech Dojo -Swift 編- は、SwiftまたはDartを使用したアプリ開発経験のある学生を対象に約2週間でSwiftでのiOSアプリ開発のスキルを身に着けていただくインターンシップです。

Android編やGo編は数回開催されたことがあるが、Swift編は今回が初とのこと。

きっかけ

2022年12月に参加した学内ハッカソン、P2HACKSが終了した後、審査員としていらっしゃったCyberAgentエンジニアの方とお話しさせていただきました。その際に、このインターンについて教えていただき、応募させていただきました。

P2HACKS 2022 で最優秀賞と2つの企業賞を獲得
12/10-12/18に公立はこだて未来大学で行われた学内ハッカソン、“P2HACKS2022”に参加しました。

Day 1 (2023/02/16)

朝ごはんは渋谷パルコ5Fにあるスターバックスで。朝11時までだとワンサイズアップできるのが嬉しいですね笑

今回のDojoには僕を含めて9人の大学1・2年生が参加しました。サムネイル画像の場所に集合しました。到着するとすでに何人かいて、さっそく北海道から持ち込んだ白い恋人をプレゼント。

白い恋人

北海道に住んでいると自分でわざわざ買うことがないので、久しぶりに食べてみましたが、本当においしかった...

喜んでいただけたので、持って行って良かったです。

事前に作成したスライドを使った自己紹介、Dojo 1期生だったメンターさんのLT、今回開発するアプリのハンズオンがありました。

今回開発するアプリは、一言で言うと、 QRコードでプロフィールを交換するアプリ です。

イメージが湧きやすいように、完成したアプリのスクリーンショットをお見せします。

マイプロフィール表示・編集画面

ここに表示されるQRコードには、DeepLinkが埋め込まれています。

DeepLinkは、あらかじめ指定されたルール (形式) に則ったURLにアクセスすると、そのアプリを開いて、情報を受け取ることができる、ある種特殊なURLです。

今回のDeepLinkには、設定した自分の名前、Twitter、GitHubのユーザー名を含んでいます。どんなURLなのか気になる方は実際にスクリーンショットのQRコードを読み取ってみてください。

同じルールを設定したアプリをインストールした端末でQRコードを読み取ると、そのアプリを開くことができます。これを用いてプロフィールを交換するという構造です。

QRコードを読み取ると、交換済みプロフィール一覧画面に登録されます。

交換済みプロフィール一覧画面

タップするとそのプロフィールの詳細が表示されます。

交換済みプロフィール詳細画面

ユーザー名をタップするとそのサービスのプロフィール画面がWebで表示されます。

主な要件としては次の通り

  • サービスアカウント一覧
  • サービスアカウントQRコード閲覧
  • サービスアカウントID編集・モーダル・バリデーション
  • プロフィール一覧
  • データの永続化

今回のインターンシップでの目標は、このアプリを開発しながら、Swift Concurrency を理解して実際にアプリに取り入れて最大限に活用することです。

というのも、先のハッカソンで制作したアプリで、Swift Concurrency の存在を知り、調べはしたものの、いまいち自分のものに出来ていない感があったからです。

非同期処理の概念を一から理解し直し、iOSアプリで非同期処理を扱うためにどのような方法があるのか、またその中の1つである Swift Concurrency を使うメリットはどのようなところにあり、どのように使うのかを理解して、実際に使うことが目的です。

このアプリを開発すべく、僕を含めた9人の参加者が3つのグループに分かれて、それぞれのグループにメンターさんが2名ついてくださいました。

さあ、いよいよ開発スタート!!...と思いきや、まずは腹ごしらえということで、グループメンバーとメンターさんとランチへ。

渋谷焼肉 KINTAN

昼からこんなおいしい焼肉を食べられるとは思っていませんでした。

さて、腹ごしらえも済んだところでついに開発スタート。

まずはViewから作っていきます。

機能大きく2つのViewに分けます。自分のプロフィール閲覧・編集用MyProfileViewと、交換済みプロフィール一覧のFriendListViewの2つです。これらのViewをTabViewを使って切り替えられるようにします。

TabViewで切り替える

まずはこんな感じでざっくりViewを構成していきます。基本的にNavigationViewで画面遷移をしやすくして、TitleやToolbarItemを簡単に使えるようにします。

ボタンなどのパーツはそれぞれViewに分割して、Listを使って表示していきます。

ざっくりとした構成を決めてから、必要なデータ構造を組んでいきます。

データ構造を組んで、1日目が終了。

1日目の進捗は以下の通りです。

  • MyProfileViewの一通りの機能
  • iOSのカメラアプリでQRコードを読み取ってFriendListに追加する機能

夜ご飯は、Cafe&Meal MUJI 渋谷西武で、健康的な夜ご飯でした。写真は撮り忘れました...

窓からはApple Store 渋谷が!!渋谷はなんと素晴らしい場所なのでしょうか...

Day 2 (2023/02/17)

2日目の朝ごはんはKrispy&Kremeドーナツ。例によって写真は撮り忘れました。

AbemaTowersに到着して、朝会が終わると、早速開発に着手。

今日は、昨日残した、データの永続化、ユーザーネームのバリデーション機能、に着手します。

データの永続化 (ローカル) の方法は主に3つあります。

  • データをエンコードして、JSONファイルとしてAppディレクトリに保存
  • UserDefaults を使う
  • CoreData を使う

構造化されたデータを保存するという目的では、CoreDataを使うのが最も効率的かと思うのですが、過去にCoreDataを使おうとして上手く使えた記憶がなかったので、一旦却下。

UserDefaultsは独自の構造体を扱うのに一手間必要なはず...

とりあえず永続化することが目的で、後々Firestore を使うつもりだったので、最もシンプルな、JSONファイルをAppディレクトリに保存する方法を採用しました。

今日のお昼ご飯は、お寿司!!

KINKA sushi bar izakaya 渋谷

次に、ユーザーネームのバリデーションです。

TwitterとGitHubのユーザーネームポリシーに合わせて正規表現を作り、Stringクラスを拡張する形で、バリデータを実装しました。

extension String {
    func isValidName(service: Service) -> Bool {
        var pattern: String = ""
        switch service {
        case .twitter:
            pattern = "^[a-zA-Z0-9_]{5,15}$"
        case .github:
            pattern = "^[a-zA-Z0-9][a-zA-Z0-9-]{0,37}[a-zA-Z0-9]$"
        }
        guard let regex = try? NSRegularExpression(pattern: pattern) else { return false }
        let matches = regex.matches(in: self, range: NSRange(location: 0, length: self.count))
        return matches.count > 0
    }
}

これをこんな感じで使いました。

username = name.isValidName(service: service) ? name : nil

ここまでで、一通りの要件は満たしたはずです。

たしか、2日目時点でバリデータまで実装できていたのは僕だけだったと思います。

一人でもくもくしすぎて、ずっとこんな感じでした。

仲良くなれなかったという訳ではなく、僕の話しかけないでオーラがすごかったらしいです。反省...

学内ハッカソンなどでのSwiftの経験がアドバンテージとなり、開発としてはここまで順調すぎるくらいに順調だったのです...

夜ごはんは、つるとんたん!!

つるとんたん

大きさがバグってました。

Day 3 (2023/02/20)

土・日・祝日は会社がお休みなので、2日間空けて開発再開。といいつつも、みんな土日も開発をしていたようです。 (僕もですが...)

日報によると、朝からアーキテクチャで迷走していたらしいです。

というのも、最初はアーキテクチャなんて考えてもいませんでしたが、先の学内ハッカソンあたりから、なんとなくMVVMっぽいアーキテクチャの組み方をするようになっていました。

今となっては信じられないのですが、MVVMを理解していないが故、結果、巨大なViewModelを最上位のViewで定義して、EnvironmentObjectでどこのViewからでも使えるようにしていたのです。

結果、コードがぐちゃぐちゃになり、アプリ全体の見通しが悪くなりました。

一旦、メンターの方にコードレビューをしていただき、大量のアドバイスをいただきました。本当にありがとうございました...

メンターさんとお話し中

今日のお昼ごはんは、用意していただいたお弁当!!

生姜焼き弁当

みんなで美味しくいただきました。

メンターの方のコードレビューを待っている間に、兼ねてからの目標であった非同期処理を実装するため、Swift Concurrencyについてお勉強をしました。

この動画を見まくりました。

目的のもの以外にも学ぶことが多いことから、こういうハンズオン系の動画や記事が大好きです。

実際に手を動かす題材があることも影響したのか、前よりも理解できた部分が格段に増えました。

あとは、アーキテクチャについてひたすら調べたり、考えたりしました。

調べている中で、TCAの存在を知りました。

GitHub - pointfreeco/swift-composable-architecture: A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind.
A library for building applications in a consistent and understandable way, with composition, testing, and ergonomics in mind. - GitHub - pointfreeco/swift-composable-architecture: A library for bu…

これが悲劇の始まりだったのかもしれません...

今日の夜ご飯は、ホテル宿泊組で、担々麺を食べにいきました!

Day 4 (2023/02/21)

この日は、AirDropでDeepLinkのURLを共有する機能を新たに実装しました。

iOS16からの新機能である ShareLink を使用して簡単に実装できました。

今日のランチ!

一緒にランチに行った社員さんは、なんとはこだて未来大学出身の先輩でした!

東京で、同じ大学の先輩と食べることができてよかったです。

明日、Firestoreによるデータ永続化をするために、Firebase Authによるログイン機能もサクッと実装。

今日は割とスムーズに進みました。

Day 5 (2023/02/22)

今日は昨日の計画通り、Firestoreでデータ永続化をしました。

FirestoreにCustomDataTypeのデータを保存するために、DocumentIDを含んだ構造体を作成しました。

その型のデータをエンコードしてFirestoreに保存します。

Firebase Authに引き続き、Firestoreによる永続化もサクッとできたので、よかったです。

今日のランチはパスタ!!

また、Concurrency を使って、ログイン処理中やデータ保存中、呼出中にProcessingViewが表示されるようにしました。

思い通りに非同期処理が走っていて、非常に気持ちよかったです。(?)

Day 6 (2023/02/24)

昨日は祝日だったため、オフィスにはいきませんでした。

今日は、アプリ内カメラの実装、登録済みの友達リストにおける検索機能を実装しました。

今日のランチは、用意していただいたお弁当!

インターン期間中、Abema Towersに出入りするために、毎日違うQRコードをスキャンしなければなりませんでした。

毎回、出入り口の前であたふたしていました。

その日のQRコードが表示される機能を、アベマくんロゴの別のタブで実装して、アプリに組み込みました。

ちなみにこのアベマくんは、CustomSymbolとしてAssetsに配置したものです。

帰りには、ドヤ顔でスキャンしました。

Day 7 (2023/02/27)

この日、ついに、アーキテクチャ問題を解決しようと、TCAのお勉強を始める決意をしました。

Reduxと同じような概念と思想があるのだと理解しましたが、これを残りの期間で実用するのは不可能だと感じ、断念。

今日のお昼は、用意していただいたお弁当です!

みんなで美味しくいただきました。

ということで、メンターさんからのコードレビューにより、ソースコードのリファクタリングをしました。

Day 8 (2023/02/28)

明日はついに最終日。

そんな土壇場で、何を思ったか、TCAによる実装を開始しました。

おそらくあの場で、成功すると思っていたのは僕だけでしょう。

作業は夜遅くまで続きました...

Day 9  (2023/03/01)

最終日、発表会当日...

結局、コードフリーズまでに、満足いく状態にすることはできませんでした。

コア機能は実装できましたが、Concurrencyで記述した非同期処理が思った通りに動かず、完全に動くところまで持っていくことができませんでした。

冷静に考えれば分かりきったことでしたが、なぜか非常に悔しい思いでいっぱいでした。

この学びを還元しようと、最終発表には、TCAについての学びを盛り込みました。

最終発表のタイトルスライド
最終発表のスライド資料
発表の様子

帰ったら、さらに学びを深めて、できることを増やして、夏にCA Tech Jobで戻ってくるという決意をしました。

バイバイ、Abema Towers

2週間、お世話になりました。絶対に、また来ます!!

2週間を振り返る

インターンが終了後、早速、今回作ったアプリの機能をTCAで完璧に実装すべく、すぐに取り掛かりました。

今あるものを書き換えるのではなく、1から作り直し、使いまわせるViewやLogicは使いまわしました。

Firebaseに関わる機能は省略し、結果、TCAで実装することができました!

今回のインターンでメンターさんをはじめとする社員さんとの会話の中で得た知識が多くありました。

最終日の帰り道でパシャリ

また、一緒に参加したメンバーとも仲良くなれたと思います。僕は思ってます。

おそらく今年のWWDC23は一緒に見ることでしょう。

記事執筆時点でインターン終了後から2ヶ月ほど経過していますが、その間にSwiftをたくさん書きました。

例えば、SwiftLintやSwiftGenのようなプラグイン系は、インターン終了後の別アプリの開発で大いに役立ちました。

また、XcodeGenの存在も知りました。今は、SwiftPMを用いたプロジェクト管理をしているので、そのお話は、別記事にまとめようと思います。

楽しく、学びの多い2週間を過ごすことができ、素晴らしい経験をさせていただきました。

人事の方、メンターの方をはじめとするCAの社員さん、一緒に参加したメンバーには、非常に感謝しています。ありがとうございました!