iOS開発初心者に知ってもらいたい開発スキル

この記事は画像がたくさんだったりしてすごく長いです。


SwiftでiOS開発の勉強をしている会社の新人と話をしているときに実装中のコードを見せてもらうと気になる実装があった。

let action = UIAlertAction(title: "title", style: .default, handler: { (action: UIAlertAction!) in
    ...
})

UIAlertActionのイベントハンドラになるクロージャの引数の型が違う。
このイニシャライザのメソッド定義はこちら。

open class UIAlertAction : NSObject, NSCopying {
    public convenience init(title: String?, style: UIAlertActionStyle, handler: ((UIAlertAction) -> Swift.Void)? = nil)
}

handlerの引数クロージャの引数はUIAlertActionなのに、なぜかUIAlertAction!と書いていた。

handler:((UIAlertAction) -> Swift.Void)?

どうしてUIAlertAction!にしたのか確認すると

<有料のプログラミング学習支援サイト?>でこう書いていたので。。。

とのことでした。
もう本当に、お金取るならこういうところしっかり勉強させてあげて欲しいです。なんならお金もらって僕が教えてたい。

公式API読もうよ

この新人はSwiftでiOSアプリを学習をしていて、基本的には独学のようなもので(ちょっと失礼だけど)他にガリガリとコーディングできる言語もないみたいです。なので基本的にはコピペ(のような感覚)でアプリを一式作って実機インストール出来るようになって、それで最初のステップは問題ないと思います。その後の学習の進め方で、ドキュメントを読んだり、公式APIを見たりというところが大事になると思っています。

具体的にはライブラリを作らせたい。そしてiOSのAPIというものを理解させたいと思いました。

APIってわかる?

iOSやAndroidなど開発していてAPIという言葉は出て来ますが、主な使われ方はサーバと通信する窓口という理解になっている人が多いかなと思います。それもAPIですが、それだけじゃないことも理解して欲しいところです。ググってください。

たとえば

let frame: CGRect = CGRect(x: 0, y: 0, width: 100, height: 100)
let view: UIView = UIView(frame: frame)

上の2行では2つのAPIを呼び出しています。

1つ目、CGRectのイニシャライズ。

extension CGRect {
    public init(x: Int, y: Int, width: Int, height: Int)
}

2つ目、UIViewのイニシャライズ。

open class UIView : UIResponder, NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, CALayerDelegate {
    public init(frame: CGRect)
}

これもAPIです。それぞれCGRectというstructとUIViewというクラスのインスタンスを生成するためのAPIを呼び出している。

じゃあライブラリ作ろうか

プロジェクトを作成

プロジェクトを作成する。

iOSのFrameworkプロジェクトを選びます

適当にMyFirstFrameworkというプロダクト名を入力して、言語はSwiftにしました。

Nextで保存先ディレクトリを決めるとプロジェクトができました。

MyViewを作る

このままではFrameworkが空っぽなので適当にビューを作ります。

今回は必須ではないけど、MyView.hInfo.plistは邪魔なのでグループを作ってまとめます。グループ名はSupportingFilesにしました。

MyView.swiftを作る。

できました。

MyViewのソースを書き換えます。デフォルトのバックグラウンドカラーが赤のビューです。

class MyView: UIView {

    func red() {
        self.backgroundColor = UIColor.red
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.red()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)

        self.red()
    }

}

ここまで出来たらビルドしてエラーが無いことを確認します。ビルドが成功するとProductsフォルダの中のMyFirstFramework.frameworkがアクティブな色になります(クリーンするとフレームワークも削除されて参照切れで赤字の表示にかわります)

ライブラリの動作環境を作る

MyFramework.frameworkを作ることができました。次は動作させてみます。

ターゲットの追加

中央ペインの左下「+」ボタンをクリック

今回は通常のアプリケーションを作成するのでSingle View Applicationで。

とりあえず名前はMyFirstFrameworkExampleにしました。

できました。

Frameworkを取り込む

frameworkを取り込みます。

  1. プロジェクトファイルを選択
  2. 取り込むターゲットを選択
  3. Embedded Binariesで取り込むターゲットの一覧を表示
  4. 取り込みたいMyFirstFramework.frameworkiOS を選択
  5. Addする

取り込むとMyFirstFrameworkExampleターゲットのEmbedded BinariesLinked Frameworks and Librariesに追加されます

これで取り込みは完了です。importできる状態になりました。コマンドラインでPathを通す作業と同じようなイメージです。

Frameworkを使う

Storyboardを開いて、ViewController.viewUIViewを乗せる

乗せたビューを選択したら右ペインでIdentify Inspectorを表示して、クラス名をMyViewに書き換える。この時自動的にクラス名下のModuleMyFirstFrameworkに変わる(変わらない時は入力する)

実行するターゲットをMyFirstFrameworkExampleにしてRun

出た。コード0行。

プロパティ(インスタンス変数)にする

Storyboardで設定したMyViewViewController.myViewとして扱えるようにIBOutletをつなぐ。

import UIKit
import MyFirstFramework // 追加

class ViewController: UIViewController {

    @IBOutlet weak var myView: MyView! // 追加

}

これでビルドするとエラーが出ます。

エラーを解消する

クラスの宣言が問題のエラーです。

class MyView: UIView

これをこう。

public class MyView: UIView

あとこっちも。

required public init?(coder aDecoder: NSCoder)
required init?(coder aDecoder: NSCoder)

これでビルドし直すとエラーが解消されて、また実行できるようになります。

MyViewでできることを増やす

fund red()でビューを赤くしていますが、青くできるようにします。

MyViewに自身を青くするメソッドを追加

    func blue() {
        self.backgroundColor = UIColor.blue
    }

ViewControllerにボタンを追加

追加したボタンのTouch Up Insideアクションで、MyView.blue()を呼び出す。

    @IBAction func blueButtonPressed(_ sender: Any) {
        self.myView.blue()
    }

勘のいい人は気づいているかと思いますがエラーが出ます。blue()は補完候補にも出ません。
func blue()を修正します。publicを追加します。こう書きます。

public func blue() {...

これで実行したらシミュレータで青くなる動作が確認できます。
ここまででフレームワークとテスト環境の構築ができました。
長いけどここからが本番です。

実際の開発を想定したフレームワークの組み込み

実際にアプリを開発している時は、アプリはアプリで別のプロジェクトになると思うので、別アプリに組み込みます。

組み込む

新規プロジェクトを作成します。MyFirstFrameworkExampleと同様にSingle View Applicationで作成します。

プロジェクト名は適当にMyAppにしました。

XcodeのMyAppに、FinderのMyFirstFramework.xcodeprojをドラッグ&ドロップします。

するとMyAppプロジェクトがMyFirstFramework.xcodeprojを内包する形になります。

MyFirstFrameworkExampleの時と同じようにEmbedded FrameworkMyFirstFramework.frameworkiOSを追加します。

こうなる

実装する

実装自体はMyFirstFrameworkExampleと同じにします。ViewController.viewが白いとStoryboardでわかりにくいのでグレーにしました。

実行する

Exampleと同じように動くことが確認できたかと思います。

ソースコードのジャンプ

ViewControllerでimportしているMyFirstFrameworkをコマンド+クリックします。

するとMyFirstFrameworkが表示されますが、自分が実装したMyViewとは違うソースコードが表示されます。

この表示されたものがMyFirstFramework.frameworkiOSとしてモジュール化されたMyFirstFrameworkのAPI(インターフェース)になります。長かった。

モジュール化されているので上の画像の通り、Mのマークが表示されます。通常はSwiftファイルなどのアイコンが表示されます。

モジュール化されたMyFirstFrameworkを見る

モジュール化されたMyFirstFrameworkを見るとfunc red()が見えません。何故かというとpublicではない(公開されていない)からです。

public class MyView : UIView {

    public func blue()

    required public init?(coder aDecoder: NSCoder)
}

では見えるfunc blue()を読み解くと、引数の無いメソッドだとわかります。返り値もありません(Swift.Voidですね)。モジュール化されるとメソッドの中身は見えなくなるものの、インターフェースは見えます。func red()のように見えなくなるものもあります。

今回はimport MyFirstFrameworkをクリックしてモジュールにジャンプしましたが、UIKitなどでも同様のことが可能です。またクラス名や関数をコマンド+クリックしても該当のクラスは関数のインターフェースを見ることができます。

長くなりましたが、こうやって公開されているAPIを見ることができるので、どう書くか悩んだらAPIを見たりドキュメントを読んで開発してくださいということです(内容的にはiOSアプリ開発とXcodeの使い方に寄りすぎました


追記@2016/11/29

最後の方に書いている「フレームワークのインターフェース〜」というのが、Objective-CなどC言語系統で使われるヘッダファイル(*.hファイル)と同じものという理解をしておくとSwiftとObjective-Cの違いなど吸収しやすいかと思います。