NIKKEI TECHNOLOGY AND CAREER

iOS開発人材を育成、原点回帰のすすめ

Start Developing iOS Apps 活用法

はじめに

弊社の iOS 開発チームでは Storyboard を使わずに View をコードで実装している。アプリ開発初心者にとってコードと Storyboard の情報がインターネット上に混在するため、実装方法を調べることは困難を極めた。また、初心者用アプリ開発入門本をみてみても、全て Storyboard でチュートリアルが組まれているため、全てコードで書かれたようなまとまった情報が探しにくくストレスフルだった。クラスの概念をわかっていないノンエンジニアの新卒にはコードで全てを書くということは非常に厳しかったのである。わからない感覚がわからなくなる前にアプリ初心者がどこに躓き、何がわからないのかを紹介したい。

公式チュートリアルの概要

Apple 公式が出しているアプリ開発のドキュメント Start Developing iOS Apps をご存知だろうか。https://developer.apple.com/library/content/referencelibrary/GettingStarted/DevelopiOSAppsSwift/index.html#//apple_ref/doc/uid/TP40015214-CH2-SW1 FoodTracker というアプリ開発を通して Xcode やクラス、ライフサイクル、 MVC モデルの基本を学べる。このチュートリアルの流れはストーリーボードを用いて UI 設計を行い、UI とコードを繋いでいく。続いて、Custom View やモデルを作ってデータをいれていく。次の画面設計では iOS アプリ開発に欠かせない TableView をつくりセルの設計をする。Navigation の設定を行い画面遷移を学んだ後、ユニットテスト、データの削除、追加、永続化を行う。完成図は下記のようになる(Apple 公式より引用)

初心者がわかりにくいところ

クラスとインスタンスと型

つまるところ何故ドットでつながっていくのかわからなかった。Xcode の補完がどうしてできるのかもわからなかった。初心者視点のコードの見え方は次のコードを参照してほしい。詳解 Swift のクラス部分を読んでも写経してもクラスを使うというのがわからなかった。UIViewController を継承した ViewController クラスのプロパティである view は UIView クラスのインスタンスであり、UIView クラスはプロパティ backgroundColor を持っていて UIColor 型が入るという構造が分かるようになるまで時間がかかった。別の ViewController に遷移した時など ViewController 間でどのようにデータの受け渡しをすればいいのか分からなかった。

class ViewController: UIViewController{
      override func viewDidLoad() {
          self.view.backgroundColor = UIColor.white
          //↑selfって何? 次につつながるviewはどこからきたの・・??
          //self.backgroundColorだとなんでエラーになるの・・?
          let label = UILabel()
          //↑これなに?? 関数?
          label.text = "aaaa" //これはまだラベルのテキストに値が入るのはわかる
          label.textColor = UIColor.blue
          //↑ラベルのテキストカラーをブルーに変更できるがUIColorが何かわからない

          //↓この{}でかこまれてるの何?
          let myStackView: MyStackView = {
          //↓なんで上で使われてるMyStackViewとmyStackViewおなじ名前が使われててエラーにならないの?
              let myStackView = MyStackView()
              //↓この「.vertical」の「.」は何?どうしてこんな書き方出来るの?
              myStackView.axis = .vertical
              //↓この長い文字(translatesAutoresizingMaskIntoConstraints)は何?
              myStackView.translatesAutoresizingMaskIntoConstraints = false
              myStackView.title = "てすとてすと"
              return myStackView//←なんでreturnなんや・・・・
          }()//←この()が何故必要なのかわからない
      }
}

コマンドを押してクラスの詳細を見れると言われて見たものの・・で・・?どうするんだ・・としか思えなかった
// The completion handler, if provided, will be invoked after the dismissed controller's viewDidDisappear: callback is invoked.
    @available(iOS 5.0, *)
    open func dismiss(animated flag: Bool, completion: (() -> Swift.Void)? = nil)
    //この関数引数が2つのはずなのにanimated とかflagとかこれ何?
    //(() -> Swift.Void)? = nil これ何?何をいれたらいいの・・・

クロージャーと省略

また Swift では省略できる書き方がある。たとえばクロージャーを関数に渡す場合、そのクロージャー式が引数の最後であれば引数を記述する括弧の外に出せる。また引数がクロージャー式だけという場合は引数リスト自体を省略出来る。これを接尾クロージャーというのだが、まさか引数が外に書かれているなど想像すらしていなかったので、関数の後ろに繋がっているクロージャーや map や filter を何故このようにかけるのかがわからなかった。これは調べようと思ってもそのような記述ルールや名称があるということを知らないためずっと引っかかっていた。

引数がクロージャーで一つしかない場合の例
let array = [1,2,3,4,5]
let newArray = array.map({ $0 * 2 })
//↑[2, 4, 6, 8, 10]になる
後置記法で書くと
let newArray = array.map(){ $0 * 2 }
//↓もっと省略すると()を省略してもいい
//(ネットで調べても下記のような例で書かれていて省略されてるのに気がつかない)
let newArray = array.map{ $0 * 2 }

ViewController と View と ViewModel などのアプリの設計

はじめに躓くのが ViewController と View と ViewModel の違いがわからないことである。チームにジョインした時にはすでに完成しているアプリを修正する作業を行う必要があるが、名前が似ていて自分がどのファイルを見ているのかわからなくなりかなり混乱した。アプリ開発入門本には、すべてを ViewController に実装を書いていくので View と ViewModel の関係がいつまでたってもつかめなかった。確かにチュートリアルどおりやればそのアプリは作れるが、応用がきかなくて自分のアプリを作ろうと思ってもどうすればよいのかわからず停滞した。一番大事なのが ViewController であり、それぞれの画面に存在していて、管理を行っている。また、View と ViewModel にわかれているのも、設計をわかりやすくするためであり、それぞれは拡張できるパーツであるということが分かるまでに時間がかかってしまった。

delegate

あるクラスで行いたい処理を他のクラスに任せたり、任せた処理を指定したクラスに通知する仕組みのことである。たとえば UIkit の TextField にタップして文字を入力し終わった時(ユーザーがキーボードの return を押した時)に実行される関数が用意されている。アプリで操作された内容とタイミングを通知する仕組みのおかげで、文字が入力された後に処理を行いたいことを実装できる。何がわかりにくいかというと、Text Field の delegate 通知先を設定するときのこの一文である。

textField.delegate = self

textField と delegate が何故「.」でつながるのかわからないのと何故 self を入れるのかがわからないのである。当時は self が何を指しているのかがわからなくて、そのインスタンス自身といわれても納得ができなかったのである。 delegate メソッド一覧は関数の定義をみるとこのように書いてある。アプリ内で改行(Return)ボタンがタップされた時に呼び出される関数 textFieldShouldReturn を例にとる。

public protocol UITextFieldDelegate : NSObjectProtocol {
  ~省略~
 @available(iOS 2.0, *)
    optional public func textFieldShouldReturn(_ textField: UITextField) -> Bool
}

実装
extension ViewController: UITextFieldDelegate {
// MARK: リターンキーが押された時に実行される関数
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        //↑上の引数のtextFieldが何か、どこから来たのかわからない
        //何故この関数が実行されるのかもわからない
        //↓キーボードを隠す関数
        textField.resignFirstResponder()
        return true
    }

ご丁寧に引数、型、返り値まで説明しているのにこの関数の定義をみて関数の実装をすることすらできなかったのである。

iOS 開発入門者脱出のためのロードマップ

チームにジョインした時こうすればもっと早くクラスの概念やアプリ開発の概要を理解できたであろう反省を踏まえて考えてみた。私はよくわからないまま現場に入ってしまい、現場で巡り合ったものとちょっとずつちょっとずつ理解しながら今ようやく中級者への門を叩いている気がする。

現場でわからないものに出会う → ぼんやりとそういう概念があることを知る(でもわからない・使いこなせない)→ よくわからないが調べながら写経する → 時間をおく → 本などをパラ読みした時に過去に調べた知識に出会う → 知識がつながる(これ進研ゼミでみた!現象である)これの継続なのではないかと思った。なんにしても手を動かして試せないと身につかないと実感している。やらないと出来るようにならないのである。下記の例は帰宅した後に自分が行っていたことである。

初心者入門書を一周

「詳細!Swift3 iPhone アプリ開発入門ノート」の前半部分で Swift の基本文法を写経して、分からなかった文法やクラスなどを復習した。「これからつくる iPhone アプリ開発入門」で6個のサンプルアプリを写経した。これで Xcode と Storyboard の基本的な使い方を身につけることができる。

公式チュートリアルを Storyboard を使って一周する

英語なので、基本となる用語がわからないと心が折られがちである。また、Storyboard でつなげるところで躓いて先に進めない事があった。N 予備校プログラミングコースの iOS 開発入門は公式チュートリアルを日本語化した上でわかりにくいところを追加で解説してあるので1000円課金してみるのもいい方法だと思う。

公式チュートリアルをすべてコードで作る

ある程度現場でクラスの概念を掴んだ後に腕だめしとして、Storyboard を使わずにすべてコードで実装した。できるだけ小さいパーツを作り組み立てることや身につけたクラスの概念を確認しながら小さく試せた。下記の項目は公式チュートリアルでは Storyboard で設定されていたものをコードで書く場合に特に違う箇所をあげたものである。

  • 詳細情報のページ

公式チュートリアルでは使われていなかったが UIStackView を用いて View を作った。

  • モーダル遷移かプッシュ遷移かで使う関数が違う

新規でデータを追加する場合も、既存のデータを使って詳細画面を表示する場合も同じクラスを使うため、それをどうやって出し分けるのかが苦戦した

//MARK: キャンセルボタンをタップしたときのアクション
    @objc func tappedLeftBarButton() {
        //現在モーダル遷移かプッシュ遷移どちらで表示されているのか判定する
        let isPresentingInAddMealMode = presentingViewController is UINavigationController
        //モーダルの時の遷移方法
        if isPresentingInAddMealMode {
            self.dismiss(animated: true, completion: nil)
        }
        //プッシュの時の遷移方法
        else if let owingNavigationController = navigationController {
            owingNavigationController.popViewController(animated: true)
        }
        else {
            fatalError("The MealViewController is not inside a navigation controller.")
        }

    }
  • 一番最初に起動する画面を設定する
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
  //アプリのインターフェースの背景
  //windowはイベントを制御し、アプリを制御する基本となるたくさんのタスクを実行する
  var window: UIWindow?

    // 起動した直後に走る関数
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    //ストーリーボードを使わない場合は、このwindowを自分で作る必要がある。
    self.window = UIWindow(frame: UIScreen.main.bounds)
    //アプリが起動した時に一番最初に表示されたいViewControllerのインスタンスをつくる
    let initialViewController = MealTableViewController()

    //UINavigationControllerにrootViewControllerにしたい、viewControllerを入れてインスタンスを作成
    let navigationController = UINavigationController(rootViewController: initialViewController)

//アプリが立ち上がって一番最初に表示される画面をrootViewControllerという
    //windowのプロパティであるrootViewControllerに、表示させたいUIViewControllerを設定する
    self.window?.rootViewController = navigationController

    //現在のウィンドウを表示し、それを同じレベルまたはそれ以下の他のすべてのウィンドウの前に置く便利な関数。
    self.window?.makeKeyAndVisible()

    return true
  }

Github 参照先

すべてコードで書いたものはこちらにある。 https://github.com/marina1017/FoodTracker 次は RxSwift を使って電子版アプリの設計に似せたものを意識して個人アプリを練習でつくりたい。

中川万莉奈
NEWSROOM ENGINEER中川万莉奈

Entry

各種エントリーはこちらから

キャリア採用
Entry
新卒採用
Entry
カジュアル面談
Entry