More than 1 year has passed since last update.


はじめに

Life is Tech !というIT教育会社のiPhoneアプリプログラミングコースでメンターをしています、しのきんです。

Life is Tech !のiPhoneアプリプログラミングコースではキャンプとスクールを通して、教科書を用いて基礎を学び、中高生がオリジナルのiPhoneアプリをつくる過程で、論理的思考能力や創造性を養っていくことを目指しています。

特に教科書を使って基礎を学ぶときに、どのようなスタイルでSwiftを記述するかをここでまとめます。主に、教科書のなかでの記法ですので、アプリのパフォーマンスの向上よりも、学習コストや学習過程を意識しています。

オリジナルでアプリを制作していたり、それぞれの中高生の学習段階に合わせて、パフォーマンスやよいコードを指導していくため、中高生のコーディングスキルによっては、より高度なコーディングスタイルを用います。

かなり初学者向けたコーディング規約になっているので、実務ではそうじゃないでしょみたいなのもたくさんあります

※内部で使用しているものを一部加筆・修正しています。

※以下で、メンバーと記述しているのは、Life is Tech !で指導している中高生を指します


内容

Life is Tech ! iPhoneアプリプログラミングコースで使用しているSwiftコーディング規約です。Swiftコーディング規約というタイトルですが、その他基本的な文法や指導方針なども含まれます。


コーディング規約について

Life is Tech ! では2014年6月のSwift勉強会から導入を開始し、2014年秋にLife is Tech ! Schoolにてβ版教科書の導入を行い、そして2015年3月の春キャンプからiOSアプリ開発コースで本格導入することになりました。過去にもObjective-CのModern記法の導入時にも議論になりましたが、Swiftは「こうも書ける、ああも書ける」という風に、例えば1つの変数宣言をするのにも多くの記法があります。そのため、教材作成や複数メンター(インストラクター)に渡る指導の際にコーディング規約を念頭に置いておくと色々捗ります。コーディング規約作成に当たって、WantedlyのSwiftコーディング規約を基本にしながら、エウレカのコーディングの規約を使って、このコーディング規約を作成したので、Wantedlyコーディング規約とエウレカコーディング規約を合わせて読んでおきましょう。「中高生に指導する」という文脈を除けば、Wantedlyのコーディング規約とエウレカコーディング規約をそのまま学ぶということでよいと思います。

Wantedlyコーディング規約

エウレカコーディング規約

Qiitaのコーディング規約まとめ記事


基本的指針

中高生の指導に当たり、Life is Tech ! iPhooneアプリプログラミングコースでは、以下の指針に基づいて一般的なコーディング規約やルールを中高生向けにカスタマイズしています。


  1. Simple「"何を教えるか"だけでなく"何を教えないか"」

  2. Student-Friendly「正しい(賢い)作り方≠生徒にとって良い作り方」

  3. To Be 「その場の小手先での指導ではなく、”どういう人間になってほしいか”というゴールからの逆算」

  4. Timing「学習のプロセスのなかで最適なときに最適な教え方で教える判断を」

すべては「For(4) Student」の精神です。抽象的事象よりも具体的事象が大事。いきなりクラスの概念を教えてもなかなか理解できませんが、その生徒の趣味感心のものに例えて教えれば理解は深まります。


Swiftの新機能について

Swiftには、Objective-CでのiOSアプリ開発には無かった新しい仕組みが多く採用されていますが、新しい機能を使わなくてもアプリは作れます。確かに新しい機能を採用することで効率良く書くことができたり、パフォーマンスが向上しますが、それよりも「いかに生徒にとってシンプルでわかりやすい情報を届けられるか」という観点で考えるようにしましょう。ただし、もちろん指導する側は新機能や業務的な記法について知っておくことは重要です。


コーディング規約の位置付け・方針


  1. 教科書作成・修正時はかならず準拠をお願いします。

  2. オリジナルアプリ作成時にはコーディング規約の遵守以上に、まずアプリを作成すること。成功体験を積んでくれることが重要です。

  3. ただし、特に教科書を進めている時やオリジナルアプリ作成時のエラーの際に、エラーに対しての感度や理解が深まることを目的として、教科書作成時だけでなくオリジナルアプリの作成時にもコーディング規約に沿うことが望ましい。

  4. メンバーは日々成長していることを考慮して、教育上のベストプラクティスあるいはカリキュラムのロードマップに合わせて、適切な指導をすることが望ましい。指導の参考となる「いつ教えるか」「どう教えるか」の目安を適宜記述する。


フォーマット

セミコロンなどの基本的なコードを書くときの作法


セミコロンはつけない。

理由:

Swiftではセミコロンをつけることに実用的なメリットがないため。

OK
NG


var number: Int = 0
func doSomething() {
// 処理
}


var number: Int = 0;
func doSomething() {
// 処理
};


メソッドとメソッドの間には空行をいれる。

理由:

メソッドのコードのブロックとブロックの間に余白を設け、可読性をあげるため。

メンバーにメソッドのブロックへの意識を持ってもらう。(Swiftでは、コードのブロックを波括弧{}で示します)

OK
NG


func doSomething() {
// 処理
}
// ここに1行挿入
func domeSomething2() {
// 処理
}



func doSomething() {
// 処理
} // 空行がないのでNG
func domeSomething2() {
// 処理
}


インデントは必ず揃える。(⌘+Aで全選択したあとに、Control+I)

理由:

インデントを揃えることで、コードの可読性をあげるため。

また波括弧でブロックが分かれることをメンバーに意識してもらうため。

OK
NG


var number: Int = 0

func doSomething() {
// 処理
}



var number: Int = 0
// インデントがひとつ前にずれている
func doSomething() {
// 処理
}


func doSomething() {
number = number + 1 // ブロック内のインデントが奥に入っている
}


func doSomething() {
number = number + 1 // ブロック内のインデントが奥に入っていない
}


コロンの左側にはスペースを入れず、コロンの右側にスペースをいれる。ただし、switch文の際にはコロンの右側にスペースを入れずに改行しても構わない。

理由:

コロンは型を示す役割があり、コロンは左側の変数・引数を説明するものであり、右側を説明するものではないため。

OK
NG


var number: Int = 0


var number : Int = 0
var number :Int = 0
var number:Int = 0


switch something {
case .something: doSomething() // 同じ行で書く
case .something:
doSomething() // 改行してもOK
}



コンマの左側にはスペースを入れず、右側にはスペースをいれる。

理由:

コンマで分けたものが、ひと目でわかるため。可読性をあげるため。

OK
NG


func doSomething(hoge: Int, piyo: Int) {
// 処理
}


func doSomething(hoge: Int , piyo: Int) {
// 処理
}

func doSomething(hoge: Int,piyo: Int) {
// 処理
}

func doSomething(hoge: Int ,piyo: Int) {
// 処理
}


波括弧の左側にはスペースを必ず入れる。

理由:

宣言部分と実装部分をしっかりと区別するため。



OK
NG





class Hoge { // 波括弧の左側にスペースがある。
}




class Hoge{ // 波括弧の左側にスペースがないので、NG
}




関数定義の際に、returnを示す -> の間には両側にスペースを入れる。

理由:

可読性をあげるため。

OK
NG


// ->の両側にスペースが入っている
func hoge() -> Piyo {
}


// ->の両側にスペースがないため誤り
func hoge()->Piyo {
}


// ->の左側にスペースがないため誤り
func hoge()-> Piyo {
}


// ->の右側にスペースがないため誤り
func hoge() ->Piyo {
}


selfは原則明示しない。コーディングスキルの向上のために必要と判断したメンバーに関してはこの限りでない。

理由:

変数のスコープやオブジェクト指向プログラミングにおけるインスタンスなどを理解している必要があるため、プログラミングをかなり理解していないと難しいため。

プログラミングがかなりできると判断したメンバーに関してはオブジェクト指向に対する理解などを深めてもらうためや、変数のスコープを意識してもらうために、selfを明示することは望ましい。

selfは自分のクラスのメソッドあるいは変数にアクセスしたいときに使用する

class TestClass {

var number: Int = 1

func testMethod() {

let number: Int = 2

// testMethodのなかにnumberという同じ名前の変数があるため、selfを明示しないとTestClassのnumberにアクセスできない。numberだけだと、testMethodのnumberとなる
print(String(self.number)) // 1がプリントされる
print(String(number)) // 2がプリントされる
}
func testMethod2() {

// 同じ名前がなければ"self"を省略可能
print(String(number)) // 1がプリントされる
}
}


アクセス修飾子はinternalとし、明示しない。

理由:

アクセス修飾子に対する理解は、綺麗なコードや効率的な開発・処理のためには必要であるが、教育上は学習コストと学習の優先順位により、少なくとも教科書の範囲では絶対入れない。コーディングスキルの向上のために必要と判断したメンバーには、アクセス修飾子をつけるように指導するのが望ましい。


アクセス修飾子のメリット


  • チーム開発でコールドリーディングのコスト小さくなる

  • 別クラスのメソッドを呼ぶ際にミスが減る

  • 別クラスとの関係性を考える際にラクチン

合わせて読むとアクセス修飾子の違いがよくわかると思います。

[Swift 3.0] アクセス修飾子にopenとfileprivateが追加された話

[Swift 4] privateとfileprivateの挙動が変わりますよというお話

修飾子
内容

open
一番広い。モジュール外からもアクセスできる。

public
モジュール外からもアクセスできる。 サブクラス化とoverrideができない。

internal
デフォルトの修飾子。アクセス修飾子を明示しないとinternalになる。モジュール内からアクセスできる。

fileprivate
ファイル内からアクセスできる。同一ファイルなら、別クラスからもアクセスできる。

private
一番狭い。同一クラスからアクセスできる(extensionも含む)。


変数定義

変数の定義の際の記法について


ローワーキャメルケースで記述する。ただし、URLなどのそもそも常に大文字で表記する単語に関しては、例外とする。

基本
小文字

語頭
小文字

区切り
大文字

OK
NG


// 基本が小文字、文頭が小文字、区切りが大文字になっているので、正しい
var hogePiyoHuga: Int = 0


// 文頭が大文字になっているので、誤り
var HogePiyoHuga: Int = 0


// URLは大文字で表記するものなので正しい
var hogeURL: URL = URL(string: "piyo")


// 基本が大文字になっているので、誤り
var HOGEPIYOHUGA: Int = 0


// 区切りが大文字になっていないので、誤り
var hogepiyohuga: Int = 0

理由:

Swiftでの書き方として、変数をローワーキャメルケースにすることが一般的なため。

一般的な記法と同じ記法にしておくことで、ネット上の記事などを見た際に、メンバーが記法の違いに混乱することがないようにするため。


型推論は極力使わず、 型を明記する。

OK
NG


var number: Int = 0 // スペースが入る場所があり、きちんと型を明記している。


var number = 0 // 型を明記していないため、誤り

理由:

メンバーに型に対する意識を持ってもらうため。

実務においては、開発速度を考えて型を明記しない場合や、型を明記することによるコンパイラーの型の一致のチェックを防ぐことによるビルド時間の伸びを考慮して型を明記しない場合があるが、Life is Tech ! iPhoneアプリプログラミングコースにおいては、原則型を明記する。


格納しているデータがなにかわかる名前、あるいは親クラスがなにがわかる接尾辞がついている名前をつける。

理由:

メンバー自身が、その変数がなんのために存在しているのか、どういうデータが入っているのか、きちんと意識してコーディングができるようになるため。

継承関係をわかりやすくするため。

OK
NG


// 接尾辞がImageViewなので、UIImageViewを格納しているとわかるので、正しい
var hogeImageView: UIImageView ...


// 接尾辞がImageなので、UIImageを格納していると勘違いしてしまう。UIImageViewを格納しているとわかりづらいので、誤り
var hogeImage: UIImageView ...



// 接尾辞がないので格納しているデータがわかりにくい名前の場合、格納しているデータがなにかわかりづらいため、誤り
var hoge: UIImageView ...


1文字の名前をつけない。(イテレータも含む)

理由:

③同様、メンバー自身が、その変数がなんのために存在しているのか、どういうデータが入っているのか、きちんと意識してコーディングができるようになるため。

OK
NG


// Int型と推測しやすい
var number: Int = 0


// 一文字だと格納しているデータがわかりにくい。
var n: Int = 0


// Int型と推測しやすく、for文のイテレータとわかりやすい。
for index in 0 ... 5 {
}


// イテレータも一文字にしない
for i in 0 ... 5 {
}


Bool値の変数に関しては、”is〇〇”, “can〇〇”という形にして、true/falseの値がわかりやすいようにする。

理由:

③とどうように、どのようなデータが入っているか、きちんと意識してコーディングができるようになるため。

OK
NG


// Bool型とisでわかる
var isFinished: Bool = false


// isがないので、Boolとわかりにくい
var finished: Bool = false


// canでBoolとわかりやすい
var canLoad: Bool = false


// canがないので、Boolとわかりにくい
var load: Bool = false


@IBOutletで宣言するものは、Implicitly unwrapped optional(暗黙的オプショナル型)で宣言する。

理由:

関連付けのミスを防ぎ、関連付けの習慣をメンバーにつけてもらうため。

実務においては、オプショナル型での記述でクラッシュする危険性を少なくするということが少なからずありますが、教育上はImplicitly unwrapped optionalで記述するのが望ましいため。

OK
NG


// Implicitly unwrapped optionalで記述
@IBOutlet var label: UILabel!


// Implicitly unwrapped optionalで記述しておらず、optionalで記述している
@IBOutlet var label: UILabel?


Optional型について

どこよりも分かりやすいSwiftの"?"と"!"

SwiftのOptional型を極める

SwiftのOptionalのベストプラクティス


@IBOutletの宣言では、weakで宣言しない。

理由:

Swiftではデフォルトで明示せずに@IBOutletを宣言すると、strongになるため参照カウンタや循環参照について学習するコストを削減するため。

画像処理や映像処理の際には、メモリリークを防ぐために必要に応じて弱参照にするのが望ましい。

OK
NG


// 強参照で記述
@IBOutlet var label: UILabel!


// 強参照で記述しておらず、弱参照で記述している
@IBOutlet weak var label: UILabel?


関数定義

関数の定義の際の記法について


関数名はローワーキャメルケースで記述する

理由:

Appleのガイドラインへの準拠。ローワーキャメルケースで書くことがSwiftでは一般的なため。

変数宣言に同じ

OK
NG


// 基本小文字で、文頭小文字、区切りが大文字
func hogePiyoHuga() {
}


// 区切りが大文字になっていない
func hogepiyoHuga() {
}


// 文頭が小文字になっていない
func HogePiyoHuga() {
}


// 基本が小文字になっていない
func hogePIYOHuga() {
}


外部引数名と内部引数名は区別しない。

理由:

Swift3からは、常にラベルを意識して外部引数名と内部引数名を区別すべきところは区別するのが、実務上望ましいが、学習コストと学習の優先順位から教科書の範囲では絶対入れない。コーディングスキルの向上のために必要と判断し、綺麗なコードを意識してほしいと思うメンバーには意識させるのが望ましい。

The Swift Programming Language (Swift 4): Functions - Apple Developer

[Swift 3.0] 関数ラベルのルールが変更になった話

OK
NG


// 外部引数名と内部引数名を区別しない
func hoge(piyo: Int) {
// 処理
}


// 外部引数名をアンダースコアにして、表示しないようにしている
func hoge(_ piyo: Int) {
// 処理
}


func hoge(with piyo: Int) {
// 処理
}

// 内部引数名と外部引数名を区別

func show1(in viewController: UIViewController, with message: String) {

// 内部では、viewControllerとmessageを使用する
}

// 呼び出しでは、外部引数名のinをwithのみを使用する
show1(in: HogeViewController(), with: "piyo")

// 内部引数名と外部引数名を区別しない
func show2(inViewController: UIViewController, message: String) {

// 内部でもinViewControllerをmessageを使用する
}

// 呼び出しでも内部引数名と外部引数名を区別しない
show2(inViewController: HogeViewController(), message: "piyo")

// 外部引数名をアンダースコア(_)で
func show3(_ viewController: UIViewController, _ message: String) {

// 内部では、viewControllerとmessageを使用する
}

// 呼び出しでは、外部引数名なしで使用する
show3(HogeViewController(), "piyo")


クラス定義

クラスを定義する際の記法について


クラスはアッパーキャメルケースで記述する。

理由:

Appleのガイドラインへの準拠。可読性の向上。

またガイドラインへの準拠から、書籍等でメンバーが学習した際に、無用な混乱を避けるため。

基本
小文字

語頭
大文字

区切り
大文字

OK
NG


class HogePiyoHuga { // 基本が小文字、文頭が大文字、区切りが大文字になっている
}


class hogePiyoHuga { // 文頭が大文字になっていない
}


class hogepiyohuga { // 区切りが大文字になっていない
}


class HOGEPIYOHUGA { // 基本が小文字になっていない
}


プロトコル(protocol)、構造体(struct)、列挙型(enum)の命名も同様にアッパーキャメルケースで記述する。

理由:

Appleのガイドラインへの準拠。可読性の向上。

またガイドラインへの準拠から、書籍等でメンバーが学習した際に、無用な混乱を避けるため。

クラスに同じ


親クラスがなにであるかわかる接尾辞につけること。

理由:

可読性の向上。継承関係を明示するため。

どういったクラスを継承しているかメンバーに意識してもらうため。

OK
NG


// 接尾辞にViewControllerなので、スーパークラスがUIViewControllerとわかる
class HogeViewController: UIViewController {

}



// 接尾辞にViewなので、スーパークラスがUIViewと勘違いしてしまう。UIViewControllerだとわかりにくい。
class HogeView: UIViewController {

}


// 接尾辞がないので、スーパークラスがUIViewControllerだとわかりにくい。
class Hoge: UIViewController {

}


条件分岐


if文の条件に丸括弧は使用しない。ただし等号の右辺や左辺をまとめるための丸括弧は除く

理由:

括弧をつけることによる実用的な理由がないため。

OK
NG


var number: Int = 0

// 丸括弧がないので正しい
if number == 0 {
}



var number: Int = 0

// 丸括弧があるので誤り
if (number == 0) {
}


var number: Int = 0
// 左辺を分かりやすくするための丸括弧は正しい
if (number + 1) == 0 {
}


基本的に不等号は、小なり(<)あるいは小なりイコール(<=)を使用する。ただし数直線で表せる条件の時は大なり(>)も積極的に使用することが望ましい。

理由:

if文においては、小なりでも大なりでも条件が書けるため、条件を理解することや瞬時に判別することが難しくなりやすいため。しかし、数値判定では、等号の左辺に右辺よりも小さい数字を置くことのほうが可読性があがるため、大なりの使用が有益であるため。

OK
NG


var number: Int = 0

// 小なりを用いているので、正しい
if number < 0 {
}



var number: Int = 0

// 大なりを用いているので、誤り
if 0 > number {
}


var number: Int = 0
// 小なりイコールを用いているので、正しい
if number <= 0 {
}


var number: Int = 0
// 大なりイコールを用いているので、誤り
if 0 >= number {
}


switch文は極力使用しない。

理由:

switch文は全ての条件を網羅する必要があり、実務上は非常に有益である。一方で、教育上は条件が網羅できているか考える習慣や考えられる論理性を身につけてもらうため、また成長のステップとして、まずif文を使えるようになってもらいたいため。


switch文について

Swiftのswitch文はかなり便利です。

SwiftにおけるSwitchまとめ

switch文でも、クラスの比較、複数比較、範囲指定ができます。

switch文では、すべての条件を網羅する必要があるので、defaultを使用しない限り、条件の漏れを防ぐことができます。

なので、堅牢なアプリケーションを制作したい場合は、switch文を駆使するといいと思います。


guard文は極力使用しない

理由:

guard文はネストが深くならず、Optionalを剥がすときに非常に有益ですが、switch文と同じく、まずは成長のステップとしてif文を使えるようになってもらいたいため。

Swift の guard は正しく使いましょう

Swift 2.0 で追加された guard のいいところ


var number: Int = 0

// numberが0のとき、number == 0がtrueのときに処理A
if number == 0 {
// 処理A
}

// numberが0のとき、number == 0がtrueのときに処理A、そうでないとき処理B、
// なにも処理しない場合は、returnもしくはassert
guard number == 0 else {
// 処理B
}

// 処理A


Optionalについて


Optionalは極力使用しない

理由:

optional chaining, optional bindingやnilの許容について、実務上非常に有益であるが、学習コストが高いため。

コーディングスキルが高く、コーディングスキルの向上のために必要と判断したメンバーについては教えていくことが望ましい。


optional chaining

Swiftでオプショナルチェーン(Optional Chaining)を使う方法


optional binding

SwiftのOptional BindingのTips


nilを許容したい場合は、Implicitly Unwrapped Optional(暗黙的オプショナル)を使用する。nilチェックをしたいときは、if文でnilかどうかチェックする

理由:

Implicitly Unwrapped Optionalはoptional bindingを行う必要がないため。

また、nilチェックは以下のようにして行う。

var number: Int!

if number != nil {
}

実務においてはif letあるいはguard letでoptional bindingまたはnilチェックを行うが、単純な条件式で判定するほうが学習コストが高くなく、比較演算子を用いた条件が身につきやすいため。


Optionalの使い分け・使いどころ


Implicitly Unwrapped Optional(暗黙的オプショナル型)の使いどころ

Implicitly Unwrapped Optional(暗黙的オプショナル型)は必ず値が初期に入る変数に使用する。

例えば、画面遷移で前画面から値を受け取ったり、viewDidLoadでnilではなく値が必ず入る時

なぜなら、画面のインスタンスが生成されるときにすぐに値が入らないと、Implicitly Unwrapped Optional(暗黙的オプショナル型)は使用するときに、強制的にオプショナルがアンラップされクラッシュしてしまうため。

初期値がなく、「必ずnilでないと保障できない」あるいは「いつ値が入るかわからない」ときは、オプショナル型を使う


オプショナル型の使いどころ

上述したとおり、初期値がなく、「必ずnilでないと保障できない」あるいは「いつ値が入るかわからない」ときに使う

強制的にアンラップされないため、optional bindingとoptional chainingを利用して、nil安全・null安全に処理が行えるため。


NS接頭辞について

NSがつくクラスについて


NSLogを除くNS接頭辞は極力使わない。

理由:

NS接頭辞のクラスなどを使う実用的な理由があまりないため。ただし、Swift3においてはStringの処理が複雑で面倒なため、場合に応じてNSStringを使用するのが望ましい。


教育上の教え方ベスト・プラクティス

指導の際に気をつける項目


「これ」「それ」などの指示代名詞ではなく、「〇〇行目の△△という変数」などのように具体的なことばで指導する。

理由:

メンターが見ているものと同じものをメンバーが見ているわけではないので、メンバーが混乱しやすいため。