Androidアプリエンジニアから見たiOSアプリ開発

N予備校iOSアプリ開発チームのyoppieです。
筆者のN予備校での経歴は

  • Androidアプリ開発チーム 2021年4月 - 2022年3月
  • iOSアプリ開発チーム 2022年4月 - 現在

です。iOSアプリ開発にチャレンジしたくなり、Androidアプリ開発からiOSアプリ開発に移りましたが、iOSアプリ開発とAndroidアプリ開発(以下I/A)でのさまざまな違いがあり、苦労しました。(iOSアプリ開発チームに移り10ヶ月経ちますが、今も苦労しています...)

I/Aでのさまざまな違いがありますが、この記事では筆者が特に違いを感じた点をまとめています。 I/Aどちらかのエンジニアの方がもう一方に移りたて、もしくは移ろうか迷っている方に向けた記事となっています。

IDEに関連した各種バージョン

一般的にXcodeでiOSアプリ開発を行い、Android StudioでAndroidアプリ開発を行います。
開発に必要となるツールのバージョンがそれぞれのIDEで異なるため、その違いについてまとめました。

Xcode

以下のツールのバージョンはXcodeのバージョンに紐づいています。

  • macOS
  • SDK
  • Deployment Target
  • Swift

これらの紐付きが存在することにより、例えば「最新バージョンのXcodeを利用するために、macOSを上げる」「最新バージョンのSwiftを利用するために、Xcodeのバージョンを上げる」といった作業が発生します。

参考

Android Studio

Android StudioはGradleというビルドシステムを用いてアプリをビルドします。ビルドに必要となるAndroid Gradle PluginのバージョンはAndroid Studioのバージョンに紐づいています。 また、GradleのバージョンはAndroid Gradle Pluginに紐づいています。
よって、Android Studioのバージョンを上げる場合、紐づいているAndroid Gradle PluginとGradleのバージョンに上げないとビルドが通らない事が多いです。
Android Gradle Pluginのバージョンを個別に上げる事ができますが、紐づいているAndroid Studioのバージョンを確認してから上げることをおすすめします。

それぞれのIDEのアップデート時の違い

XcodeのバージョンはSDKとSwiftのバージョンに紐づいているため、Android StudioよりXcodeのアップデートのほうが重いタスクになります。
ローカルではビルドできるが、CIでビルドできなくなる事象もちらほら耳にします。
Android StudioのノリでXcodeのバージョンを上げると詰むなーと感じました。

OSSライブラリの導入と管理

I/AそれぞれでOSSライブラリの導入手順と管理方法は異なります。

iOS

CocoaPods、Carthage、Swift Package Managerいずれかのツールを用います。
N予備校iOSアプリでは将来的にSwift Package Managerに移行予定で、現在はCocoaPodsを用いています。
それぞれの主な特徴は以下です。

Swift Package Manager

  • Xcodeの1つの機能として利用できる
  • 依存関係の管理が楽
  • 対応しているOSSライブラリが少ない

CocoaPods

  • CocoaPods自体の導入が必要
  • Podfileの作成など導入までに手間が発生する
  • 対応しているOSSライブラリが多い

Android

Gradleというビルドシステムを用いています。プロジェクト作成時にGradleは導入されています。プロジェクト内のGradleファイルに依存関係を記載し、GradleをsyncするとOSSライブラリを導入できます。

I/A間の違い

Androidアプリ開発を経験してきた筆者から見ると、iOSアプリ開発でのOSSライブラリ導入方法が3つ存在することが不思議です。
Swift Package ManagerはGradleによる導入と比べとても楽ですが、対応しているOSSライブラリが少ないのが難点だと思いました。
また、CocoaPodsはCocoaPods自体の導入手順とOSSライブラリの導入方法が一般的なパッケージ管理システムと似ていると感じたので、親しみやすかったです。

レイアウト作成

画面やViewを作成する際にレイアウトが必要となることが多いです。
I/Aそれぞれでレイアウト作成方法が異なるためまとめてみました。

iOS

StoryBoardかXibでレイアウトを作成します。
GUIで組み立てていくことになります。Xcodeにxmlで開くこともできますが、かなり複雑なのでxmlを書くことは不可能に近いです。

GUI
xml

アプリ開発に入門したての方はGUIで組み立てられるため、入りやすい印象です。

Android

xmlでレイアウトを作成します。 GUIで組み立てる方法とxmlを書く方法があります。

GUI
xml

xmlで書けることはメリットの1つですが、設定できる項目が多いです。作成したいレイアウトを実現するためにどの項目に何を設定すべきかを判断する作業に時間がかかる事があります。

I/A間の違い

I/A両方ともxmlを書く事ができますが、StoryBoardとXibはそれを前提としていません。
特にGUIでレイアウトを修正したPRへのコードレビュー時、複雑なxmlの確認作業は至難の業だと感じました。

例えばこちらは背景色の指定方法への指摘です。

PRへの指摘

かなり注意深く確認しないと見逃してしまう指摘だと思います。

画面作成

画面を作成する際に、iOSではViewControllerを作成し、AndroidではActivityやFragmentを作成します。
I/Aで画面作成時の考え方や、レイアウトとの紐付け方などが異なるためまとめてみました。

iOS

SwiftUIを用いている場合を除き、画面を作成するためにUIViewControllerを継承したViewControllerを作成します。

画面構成

基本的に1画面に対しViewControllerを1つ作成します。例えばこのような画面構成の場合はViewControllerが4つ必要になります。

画面構成

レイアウトとの紐付け

レイアウトに配置したViewをViewControllerで扱う場合、StoryBoardまたはXibとViewControllerのファイルを開き、レイアウトのViewをcontrolを押しながらViewControllerのファイルへドラッグ&ドロップします。
こうすることで、レイアウトのViewに紐づけられたプロパティをViewControllerに定義できます。

レイアウトとの紐付け

Android

iOSと違い、ActivityとFragmentの2種類を用いて画面を構成します。

画面構成

親にActivity、子にFragmentを用いる場合が多いです。例えばこのような画面構成の場合はActivityを1つ、Fragmentを3つとする構成が推奨されています。

画面構成

この画面構成をSingle Activityと呼び、実装内容がシンプルになり保守性が高くなるメリットがあります。AndroidアプリをSingle Activityに移行するためにやったことの記事で詳しく記載されているので、こちらも是非読んでみてください。

レイアウトとの紐付け

さまざまな方法がありますが、最近ではViewBindingを用いる方法が推奨されています。 例えばこのようなFragmentのレイアウトがあるとします。

<!-- sample_fragment.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/sample_text_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

このレイアウトをSampleFragmentでViewBindingを用いて実装した例です。

class SampleFragment : Fragment() {

    private var _binding: SampleFragmentBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = SampleFragmentBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view:View, savedInstanceState: Bundle?){
        super.onViewCreated(view, savedInstanceState)
        binding.sampleTextView.text = "サンプル"
    }
}

I/A間の違い

AndroidではActivityとFragmentで使用用途やライフサイクルなどが異なるため、相違点に注意が必要ですが、iOSでは全ての画面をViewControllerで作るため入門しやすかったです。
また、レイアウトとの紐付け方法ですがAndroidのBindする実装方法に慣れてしまっていたので、iOSのGUIによる視覚的な紐付け方法に驚きました。

最後に

I/Aでのさまざまな違いの中から、筆者が特に違いを感じた点をまとめてみました。
読者の何かの役に立てれば幸いです。
「AndroidでやってたアレはiOSではどうするんだったかな?」「画面作成の方法はI/Aでどういう違いがあったかな?」といったことをよく考えてしまうので、自分の備忘録としてもまた記事を書きたいです。