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.h
とInfo.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を取り込みます。
- プロジェクトファイルを選択
- 取り込むターゲットを選択
Embedded Binaries
で取り込むターゲットの一覧を表示- 取り込みたい
MyFirstFramework.frameworkiOS
を選択 Add
する
取り込むとMyFirstFrameworkExample
ターゲットのEmbedded Binaries
とLinked Frameworks and Libraries
に追加されます
これで取り込みは完了です。importできる状態になりました。コマンドラインでPathを通す作業と同じようなイメージです。
Frameworkを使う
Storyboardを開いて、ViewController.view
にUIView
を乗せる
乗せたビューを選択したら右ペインでIdentify Inspector
を表示して、クラス名をMyView
に書き換える。この時自動的にクラス名下のModule
がMyFirstFramework
に変わる(変わらない時は入力する)
実行するターゲットをMyFirstFrameworkExample
にしてRun
出た。コード0行。
プロパティ(インスタンス変数)にする
Storyboardで設定したMyView
をViewController.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 Framework
にMyFirstFramework.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の違いなど吸収しやすいかと思います。