[Swift] 再入門したメモ


Swiftが出た当初、色々記事やメモを書きましたが、しばらくたってだいぶ仕様が変化しているのと、単純に忘れてる部分があるので改めて再入門してみます。

ただ、アップデートが激しく、過去の記事を見るとすでに仕様が変わってるように見えるものもあるので調べるのが大変・・。
ドキュメント見てもそれっぽいの見つからなかったりするし( ;´Д`)

-------------------------------------------

## CoreData

CoreDataは色々ハマりそうなので個別にメモ。

### 変数宣言

変数を宣言する場合は以下のようにします。

```swift
class EntityAccount : NSManagedObject {
	@NSManaged var name: String?
	@NSManaged var address: String?
}
```


### Class名の設定

![Class名の設定](https://qiita-image-store.s3.amazonaws.com/0/5616/e9fba908-7488-f359-d3f5-76d9b6613140.png "Class名の設定")

こんな感じで`モジュール名.エンティティ名`という形でClass名を設定してやる必要があります。
なおモジュール名はアプリのターゲット名を利用するようです。（上の例では`Hoge`）

参考: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/WritingSwiftClassesWithObjective-CBehavior.html


-------------------------------------------

## ちょっとしたメモ

### リソースからJSONファイルを読み出す

ローカルに保存したJSONファイルを読み込む方法です。

```swift
let path = NSBundle.mainBundle().pathForResource("hoge", ofType: "json")
let fileHandle = NSFileHandle(forReadingAtPath: path)

let data = fileHandle?.readDataToEndOfFile()

// SwiftJSONとか使ってる場合はそれ使う
let jsonString = NSString(data: data!, encoding: NSUTF8StringEncoding) as? String
let json = JSON.parse(jsonString)

// NSJSONSerializationを使う場合
// var json: NSDictionary? = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments, error: nil) as? NSDictionary
```

-------------------------------------------

## Objective-Cとの互換など

### `objc_setAssociatedObject`、`objc_getAssociatedObject`を使う

Swiftでも上記ランタイム関数が使用できます。

#### `<objc/runtime.h>`は`import ObjectiveC`

```swift
import ObjectiveC
```

を指定する。

#### サンプルコード

```swift:runtime
import UIKit
import ObjectiveC

class Hoge {
    init() {
        //
    }
}

var pointer = Selector("pointer")
var str = "test"
var hoge = Hoge()

objc_setAssociatedObject(hoge, &pointer, str, UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))

var str2 = objc_getAssociatedObject(hoge, &pointer) as! String
print(str2) // => "test"
```

#### Closureを保持する

Swiftでは、`objc_setAssociatedObject`の値の部分は`AnyObject!`になっているので、そのままではClosureを渡すことができません。

その場合はラッパークラスを作って対応すると持たせることができるようです。
こちらを参考にしました（[How do I pass in a void block to objc_setAssociatedObject in swift | Stackoverflow](http://stackoverflow.com/questions/29106891/how-do-i-pass-in-a-void-block-to-objc-setassociatedobject-in-swift)）

```swift:closure-associated
class AnyBlock {
    var closure: ((Int) -> ())?
    
    init(_ closure: ((Int) -> ())?) {
        self.closure = closure
    }
}

let block: (Int) -> () { (a: Int) in
	// do something.
}

objc_setAssociatedObject(alertView, &blockKey, AnyBlock(block), UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
```


-------------------------------------------

## 言語仕様的なの

### subscript

`hoge[0]`とか`hoge["name"]`とか、ArrayやDictionaryで要素にアクセスするのに使われる書式を、自作のクラスでも使えるようにするものです。

百聞は一見にしかずということでサンプルコードを見てもらえると分かるかと思います。

```swift
class Test {
    private var name: String?
    init(name: String?) {
        self.name = name
    }
    
    subscript(key: String) -> String? {
        get {
            if key == "name" {
                return self.name
            }
            
            return ""
        }
        set(name) {
            if key == "name" {
                self.name = name
            }
        }
    }
}

// How to use
var test = Test(name: "edo")
test["name"] = "aaa"
print(test["name"])
```

ちなみに複数の値を受けることもできるようです。

```swift
// 中略
subscript(key: String, index: Int) -> String? {
    if key == "name" {
        if index == 0 {
            return "Wow"
        }

        return self.name
    }
    
    return ""
}
// 中略

// How to use
var test = Test(name: "edo")
print(test["name", 0]) // => Wow
print(test["name", 1]) // => edo
```


### NSData <-> Stringの変換

```swift:String->NSData
var str = "Hoge"
let data = str.dataUsingEncoding(NSUTF8StringEncoding)
```

```swift:NSData->String
var str: String = NSString(data: data, encoding:NSUTF8StringEncoding)
```

### Closureをtypealiasで定義する

ObjCではBlocksを`typedef`で定義して使いやすくしていましたが、Swiftでも同じようなことをClosureと`typealias`を使って実現することができます。

```swifth:typealias-closure
typealias closureType = (paramType) -> (returnType)

// 例
typealias anyBlock = (String, Int) -> (Int)
```

ちなみに戻り値がない場合は以下のように書く。

```swift:typealias-noreturn
typealias closureTyep = (Int) -> ()

// 例
var hoge: closureType = { (a: Int) in
	// do something.
}
```

-------------------------------------------

### `as?`と`as!`

詳細は[Appleのドキュメント](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-ID338)を参考に。

基本的には`Optional`な扱いの延長です。
主な違いは **Down Cast** で使われる、ということです。

Down Castの成功が確実なら`!`、失敗するかもしれない場合はOptionalな状態で評価（`as?`）し、もし失敗した場合は`nil`が返ります。

-------------------------------------------

### Namespace風に分解する

Swiftはnamespaceがある、というのが発表当時の内容でした。
実際、Module単位でnamespaceが分かれていますが、ちょっとしたプロジェクトでそれをやるのは大変なので、`extension`を使うことでそれっぽいものが作れるようです。

```swift:A.swift
class Namespace {
    class Hoge {
        func test() {
            print("hoge")
        }
    }
}
```

```swift:B.swift
extension Namespace {
    class Fuga {
        func test() {
            print("fuga")
        }
    }
}
```

```swift:C.swift
var hoge = Namespace.Hoge()
hoge.test() // => "hoge"

var fuga = Namespace.Fuga()
fuga.test() // => "fuga"
```

-------------------------------------------

### funcのアンダースコア（_）とシャープ（#）

アンダースコアはラベルを必要としないような場合に指定するもの。つまり無名ラベルと考えるといいかも。
*→と思ったけど、`func`についてはそもそも現状は明示的にラベル（外部引数名）を設定しないと一般的な言語のように関数呼び出せるっぽい。*


シャープはラベルと引数名を同じにしたい場合に使う。
サンプルは以下。

```swift
// アンダースコアはいらないかも。
func add(a: Int, b: Int) -> Int {
	return a + b
}

// 呼び出し
print(add(1, 2)) // => 3
```

```swift
// #は外部・内部引数名を一緒にしたい場合に使う。
// イニシャライザではこれがデフォルト挙動っぽい？
func add2(#a: Int, #b: Int) -> Int {
	return a + b
}

// 呼び出し
print(add2(a: 1, b: 2)) // => 3
```

#### イニシャライザとfuncは異なる？

ちなみにイニシャライザについてはまた別ルールがありそう。
ドキュメントあたってもなんかそれらしい記述が見当たりません；
（Stackoverflowとか見てると過去にはそういう記述があったふうのものは見かけますが・・）

**`func`はデフォルトが`_`、イニシャライザはデフォルトが`#`になってるように見える。**

-------------------------------------------

### The init! Failable Initializer

[Initialization | Apple Developer](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html)

`Failable`と書かれているだけあって、fail、つまり`nil`を返すことができるイニシャライザ、ということのよう。（英語の解釈間違ってたら指摘ください）

なので`init!`や`init?`として、それが`nil`を返す可能性があることを明示できる、というわけですね。
開発していると確かに`nil`くんの？　こないの？
ってことがありますが、それを明示できるのは利点です。
（ただ、初見だと「ん？」ってなりますが）


-------------------------------------------

## 細かいメモ

### #pragma mark を使う

Swiftでは`#pragma mark`がそのままでは使えません。
以下のようにします。

```swift
// MARK: hoge

// セパレータを入れたい場合

// MARK: - hoge
```


-------------------------------------------

## 参考記事

- [Swift の Subscript について](http://qiita.com/happy_ryo/items/72b68859ed8ace9f5fb4)
- [[Swift] クラスのイニシャライザ１： イニシャライザの引数の書き方](http://qiita.com/cotrpepe/items/bfebe21e9f75c1fce60c)
- [[Swift] クラスのイニシャライザ２： プロパティの初期化方法](http://qiita.com/cotrpepe/items/db842f74b4c8ce16052c)
- [[Swift] クラスのイニシャライザ３： Designated Initializer と Convenience Initializer](http://qiita.com/cotrpepe/items/3052d91468f1b852582a)
- [[Swift] クラスのイニシャライザ４： override 修飾子](http://qiita.com/cotrpepe/items/8d197a9a288eb24c19fb)
- [[Swift] クラスのイニシャライザ５： required 修飾子](http://qiita.com/cotrpepe/items/3931c0c20ef43f4a18ac)