はじめまして。タップル誕生のiOSエンジニアとして一ヶ月間インターンシップをいたしましたbobchanです。
現在、公立はこだて未来大学修士1年です。今回のインターンを通して感じた貴重な体験内容について紹介するために、記事を書きました。

インターンを申し込んだ理由

理由は2つあります。

  1. 今自分に必要な考え方が知りたい
  2. 現場の活きている知識・ノウハウを吸収したい

1つ目の理由について説明します。学生生活が終わりに近づいてきた今、エンジニアを職とするにあたって何を意識する必要があるのか、残り1年何に時間を費やすのかを考えるようになりました。そこで、この問いに対するヒントを得るためには、就業型のインターンシップが適していると感じたため、エントリーしました。
2つ目の理由について説明します。個人の意見ですが、インプットとしてネット記事や書籍では限界があると思っています。しかし、就業型のインターンシップでは、実際のプロダクト開発を通して、様々なtips、尊敬する社員からのコードレビューなど数多くのインプットを得ることができます。それと同時に、アウトプットもできます。多くのユーザーに使われているプロダクトに、自分のコードが流れていることは高揚感とともに責任感も出てきます。

マジマッチチャレンジ

maji match logo

マッチングエージェントでは、マジマッチチャレンジというチャレンジを4ヶ月に1回程度のペースで行っています。そのチャレンジ内容は、もっとユーザーの体験が向上させるために必要な施策を、2日間で取り組むという内容です。また、これらの施策は、チームメンバー全員から募ったものであり、その数は40以上にもなります。その数は40以上にもなります。詳しくはこちらの記事で紹介されています。

自分も社員の方々からサポートをいただきながら、1つの施策にチャレンジすることができました。正直、コードリーディングをしながら短期間で成果をあげるということは経験したことがなく、苦戦しました。しかし、最後に成果発表&社長からフィードバックをいただけたことで、達成感を感じることができました。

マジマッチチャレンジ_成果発表

成果発表会の様子

業務内容について

まずタップルのチーム体制についてご紹介します。タップルではチーム制がとられており、それぞれが異なる目標を掲げております。詳しくはこちらをご覧ください

私自身も1つのチームに所属して機能開発に携わりました。チーム単位で見ると10人前後ですが、1つの施策単位まで落とし込むと、エンジニアがiOS/Androidで1名ずつ、プランナー、デザイナー1名なんていうこともあります。少ないメンバーで構成することで、それぞれが責任を持って開発しています。開発した施策が、リリースされた時の感動は凄まじく、同時に責任感を感じることができました。

環境について

施策単位で言うとコードレベルで開発するのは、1人かもしれません。しかし、コードレビューはもちろんのこと雑談レベルも含めて、何でも聞くことができる環境がそこにはありました。
下記は実際のPRです。コードレビューだけではなく、チームで使われているextensionを紹介していただけました。

tapple_enginnerjob

コードレビューの様子

また、初日にPRを出し翌朝にマージされたときの様子は下記の通りです。

tapple_enginnerjob_firstPR

初PR

tapple_enginnerjob_salck

初日のセットアップと翌朝のslack

これは本当にありがたいことであり、楽しみながらのびのびと開発することができました。それ以外に、他のインターン生の卒業式など様々な要素から、暖かいチームであることを感じました。

集合写真送別時の集合写真

ちなみに、このブログ内容もGithub上でレビューしていただきました。

tapple_enginnerjob_blogPR

Swift I/O

タップル誕生のiOSチームでは、週に1度Swiftに関する情報を共有するSwiftIOがあります。
私はある回にて、Setについて調べました。
Setとは、Array、dictionaryと並ぶCollectionTypeの1つです。特徴は以下の通りです。

  1. ユニークなデータを保持する
  2. 単一なデータが望ましい
  3. 順序は保証しない

幸運なことに、Setについて調べて紹介した後に使用する場面に遭遇しました。
具体的には、以下のようなstructに対してSetを適用したいということでした。

struct Text {
       let id: Int
       let text: String
}

setを使用するためには、Hashableである必要があります。そのため、下記の様に変更しました。

struct Text: Hashable {
      let id: Int
      let text: String
}

これでHashableに準拠しました。しかし、私が遭遇した場面では、部分一致だとしても同一のものとして扱いたかったのです。
そのため、下記の様に変更しました。

struct Text {
     let id: Int
     let text: String
}

extension Text: Hashable {
      var hashValue: Int {
            return id.hashValue
      }

      static func == (lhs: Text, rhs: Text) -> Bool {
            return lhs.id == rhs.id
      }
}

これで、idが等しければ同一のものとして扱うことができます。
少し特殊な場面でしたが、調べた内容を業務に活かせたことは嬉しかったです。

現場の知識・ノウハウについて

インターンシップの参加理由の1つである”現場の知識・ノウハウ”について紹介します。

開発を効率よくするためのツールやライブラリはたくさんあります。実際、タップル誕生でも開発効率を向上させるためにいくつものライブラリを使用しています。

本記事では、特に感動したライブラリの一部をご紹介します。

Sourcery

Sourceryは、指定したテンプレートからSwiftを生成してくれるライブラリです。

みなさんはテストでモックを書くときに、どうされていますか?

全て手打ちした場合、typoの確率もあがると思います。そんな時に、活躍してくれるのがSourceryです。

下記にタップル誕生で使用されている一部を示します。

protocol CardRepository: AutoMockableRepository {
    func load() -> Observable<CardResponse>
}

// Mock
final class MockCardRepository: CardRepository {
    //MARK: - load
    var loadClosure: (() -> Observable<CardResponse>)?

    func load() -> Observable<CardResponse> {
        return loadCountClosure.map({ $0() })!
    }
}

AutoMockableRepositoryなRepositoryに対してMockRepositoryを生成する

public struct AnnouncementResponse: Codable {
    public let id: Int
    public let title: String
    public let url: String
}

// MARK: AnnouncementResponse Factory
extension AnnouncementResponse {
    static func mock(
        id: Int = Int.mock(),
        title: String = String.mock(),
        url: String = String.mock()
    ) -> AnnouncementResponse {
        return AnnouncementResponse(
            id: id,
            title: title,
            url: url
        )
    }
}

structに対してmock()メソッドを呼べるようにする

このようなテンプレートを用意しておくことによって、API周りのテスト用のコードを自動生成してくれます。

その結果、以下のようなメリットを享受できます。

  • コマンド1つでテスト用のコードが完成する
  • “テストを書く”という時間が減る

SwiftGen

SwiftGenは、文言やAssetなどのリソースに対して、コードから呼び出すためのコードを自動生成してくれます。

みなさんは、アプリ内で使用する文言やAssetをどうやって管理していますか?

  • 全て手打ちする
  • enumを使用する

ちなみに私は全て手打ちしてきました。その結果、表記揺れや同じAssetを追加するなどをやってきました。
これらを解決してくれるライブラリが、SwiftGenです。
導入手順はシンプルです。

  1. CocoaPodsやHomebrewからインストールする。
    target 'App名' do
      # Comment this line if you're not using Swift and don't want to use dynamic frameworks
      use_frameworks!
    
      # Pods for SampleLocation
      pod 'SwiftGen'
    
    end
    $ brew update
    $ brew install swiftgen
  2. 設定ファイルを書く
    設定ファイルとしてswiftgen.ymlをプロジェクトファイル直下に配置します。

    // 文言用
    strings:
     inputs: "$PROJECT_NAME"/Resources/Localizable.strings
     outputs:
     templateName: structured-swift4
     output: "$PROJECT_NAME"/Sources/StringsGen.swift
    
    // Asset用
    xcassets:
     inputs: "$PROJECT_NAME"/Images.xcassets
     outputs:
     templateName: swift4
     output: "$PROJECT_NAME"/Sources/AssetsGen.swift
  3. 自動生成したい対象を書く
    下記は文言の例です。

    "dialog.end_dating_fever.title" = "フィーバータイム終了!";
    "dialog.end_dating_fever.description" = "明日もお楽しみに!";

    “$PROJECT_NAME”/Resources/Localizable.strings

  4. swiftgenコマンドを実行する
    $ swiftgen

以上の手順によって、コードが自動生成されます。

internal enum EndDatingFever {
   /// 明日もお楽しみに!
   internal static let description = L10n.tr("Localizable", "dialog.end_dating_fever.description")
   /// フィーバータイム終了!
   internal static let title = L10n.tr("Localizable", "dialog.end_dating_fever.title")
}

最後に

今回1ヶ月という短い期間ではありますが、以下のことを学ぶことができました。

  • 効率よく開発を進めるノウハウ
  • 標準ライブラリを駆使したコードの書き方
  • コードレビューの粒度・指摘方法

コードレビューについてはもらうだけに留まってしまいましたが、大学に戻った後はレビューする側として学んだ内容を活かしていこうと思います。

学ぶことがたくさんあった一方で、不足している感じたことも多くあります。
1つ具体例をあげると、インプットを使える手札にする能力です。
例えばSetの話にしても、インターン前は全く知りませんでした。さらに、Setを学んだ後も、最初はif文を使って愚直に表現していました。
このことから、インプットしたことを使える手札として定着させることが苦手だということがわかりました。
したがって今後は、小さいアプトプットだけではなく、開発しているアプリに積極的に取り組むということを意識していこうと思います。

最後になりますが、学びや感じたことに加えて将来なりたいと思うロールモデルに出会えたことも大きな財産です。本当にお世話になりました。