storyboardを使わずSwiftでToDoアプリを作ったら、とても勉強になった。

storyboardを使わずにToDoアプリを作ってみたら、とても勉強になりましたので、投稿します。

storyboardを使うか否かは趣味の問題もあるし、コードの量を減らせること考えたら、使った方が良いかと思うのですが。

**僕はプログラム初心者なので、どうしてもstoryboard使うとプログラムの行間が分からなくなってしまうのです。なので一切storyboardを使わずにやりたくなってしまうのです。**

以下のサンプルは（変数名が適当／サイズ指定が手抜き／テーブルからの削除機能とか未実装）だったりするけど、そんなことはどうでもよいのです！！（恥）

僕はこの課題を通して、Swiftのことを少し身近に感じることができたのですから。**具体的にはいろんな機能とその使い方、ハマりどころなどを知る事ができたのですから。**

皆さんの参考になれば、幸いでございます。

### 使っている主なおいしい機能

* NavigationController
* TableView
* CoreData

これらはアプリ開発におけるとても汎用性の高い機能たちだと思います。


### 完成図

![todo1123.gif](https://qiita-image-store.s3.amazonaws.com/0/45822/3d3f33bd-366f-8502-7370-f733c4868015.gif)


### 参考情報

本投稿で全部書くと死んじゃうので、各機能の細かい部分について不明点などあれば以下の記事をご参照ください。


* [[Swift] storyboardを使わず、テーブルを表示させる短いサンプル（UITableView）](http://qiita.com/mochizukikotaro/items/3ba0a8cce0ee57bd7273)
* [storyboardを使わず、Swiftでナビゲーションバーをつくる（UINavigationControllerとか）](http://qiita.com/mochizukikotaro/items/f053495eb130e92e13e8)
* [SwiftでCoreDataを使ってみる（Stepごとに解説）](http://qiita.com/mochizukikotaro/items/0355131c0df268454673)
* [SwiftでCoreData「NSManagedObjectのプロパティが上手く取れない」ときの対処法](http://qiita.com/mochizukikotaro/items/b4da088b142c077cacb3)


# ソースコード

CoreDataを使うための`xcodemodeld`の設定以外は、**4つファイル書いたらおわりです。**

ちなみに、xcodemodeldは以下のようにしておき、class名も「ToDo1123.ToDoStore」とちゃんと書きます。

![スクリーンショット 2014-11-24 19.33.11.png](https://qiita-image-store.s3.amazonaws.com/0/45822/40e7396f-1152-a101-6979-2ed155b8b485.png)

以下ソースコードです。

```swift:ToDoStore.swift
import UIKit
import CoreData

class ToDoStore: NSManagedObject {
    
    @NSManaged var memo:String
    @NSManaged var date:NSDate
}
```

```swift:AppDelegate.swift
import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    
    /*
    以下１行追加
    */
    var myNavigationController: UINavigationController?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        
        /*
        以下５行追加
        */
        let first: ViewController = ViewController()
        myNavigationController = UINavigationController(rootViewController: first)
        self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
        self.window?.rootViewController = myNavigationController
        self.window?.makeKeyAndVisible()
        
        return true
    }

// 以下デフォルトのままなので、省略。

```

```swift:ViewController.swift
import UIKit
import CoreData

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    // ナビバーの右上ボタンを用意
    var addBtn: UIBarButtonItem!
    
    // テーブルを用意
    var piyo: UITableView!
    
    // テーブルに表示するアイテムの配列を用意
    var memos: [NSString] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "Home"
        
        // addBtnを設置
        addBtn = UIBarButtonItem(barButtonSystemItem: .Add, target: self, action: "onClick")
        self.navigationItem.rightBarButtonItem = addBtn

        // 画面サイズを取得
        let width: CGFloat! = self.view.bounds.width
        let height: CGFloat! = self.view.bounds.height
        
        // テーブルを用意して、表示
        piyo = UITableView(frame: CGRectMake(0, 0, width, height))
        piyo.registerClass(UITableViewCell.self, forCellReuseIdentifier: "data")
        piyo.dataSource = self
        piyo.delegate = self
        self.view.addSubview(piyo)
        
    }
    
    // viewDidLoadは最初の一回しか呼ばれないので、viewWillAppearを使うよ
    override func viewWillAppear(animated: Bool) {

        // CoreDataからデータを読み込んで配列memosに格納する
        let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        let toDoContext: NSManagedObjectContext = appDel.managedObjectContext!
        let toDoRequest: NSFetchRequest = NSFetchRequest(entityName: "ToDoStore")
        // 並び順をdateの、昇順としてみる
        toDoRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: true)]
        toDoRequest.returnsObjectsAsFaults = false
        var results = toDoContext.executeFetchRequest(toDoRequest, error: nil) as [ToDoStore]!
        memos = []
        for data in results {
            memos.append(data.memo)
        }
        
        // テーブル情報を更新する
        self.piyo.reloadData()
    
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    // addBtnをタップしたときのアクション
    func onClick() {
        let second = EditViewController()
        self.navigationController?.pushViewController(second, animated: true)
    }

    // セルの行数を指定
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return memos.count
    }
    
    // セルの値を設定
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCellWithIdentifier("data", forIndexPath: indexPath) as UITableViewCell
        cell.textLabel.text = memos[indexPath.row]
        return cell
    }
}
```

```swift:EditViewController.swift
import UIKit
import CoreData

class EditViewController: UIViewController {
    
    // TextFieldを用意
    var memoField: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.cyanColor()
        self.title = "Edit"
        
        // text field
        memoField = UITextField(frame: CGRectMake(100, 100, 200, 30))
        memoField.borderStyle = UITextBorderStyle.RoundedRect
        self.view.addSubview(memoField)
        
        // write button
        let writeBtn: UIButton = UIButton(frame: CGRectMake(100, 150, 200, 30))
        writeBtn.backgroundColor = UIColor.magentaColor()
        writeBtn.setTitle("メモを保存", forState: UIControlState.Normal)
        writeBtn.addTarget(self, action: "writeData", forControlEvents: UIControlEvents.TouchUpInside)
        self.view.addSubview(writeBtn)
        
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    // CoreDataへの書き込み処理（writeBtnのアクション）
    func writeData() {
        let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
        let toDoContext: NSManagedObjectContext = appDel.managedObjectContext!
        let toDoEntity: NSEntityDescription! = NSEntityDescription.entityForName("ToDoStore", inManagedObjectContext: toDoContext)
        var newData = ToDoStore(entity: toDoEntity, insertIntoManagedObjectContext: toDoContext)
        newData.memo = memoField.text
        newData.date = NSDate()
    }
}
```

以上です。全部コードで書いてもこれくらいなので、そんな長くは無いかと。。。


# 僕がハマったこと２つ！！

基本的には[Stack Overflow](http://stackoverflow.com/)とかで調べて進めていくと、なんとか動いたりするのですが、それでもよくわからなくて、心優しい達人に教わって解決したポイントのメモです。


## executeFetchRequestの書き方

結論以下のように書きましょう。

```swift:OK
var results = toDoContext.executeFetchRequest(toDoRequest, error: nil) as [ToDoStore]!
```

ちなみに、最初は以下のように書いておりました。

```swift:NG
var results: NSArray! = toDoContext.executeFetchRequest(toDoRequest, error: nil)
```

そして、Entityのclass名はモジュールをprefixにして、ちゃんと書いておきましょう。

そうしないと、なんか上手くいきません。すくなくとも、僕は上手くいきませんでした。`result.memo`がエラーになるのです。最初は苦肉の策で`result.valueForKey("memo") as String`とかで回避しておりました。


## テーブル情報の更新

結論以下で解決です。

```swift
override func viewWillAppear(animated: Bool) {
        // piyoはtableViewです。無意味な変数名つけてごめんなさい。
        self.piyo.reloadData()
}
```

最初僕はreloadDataの存在を知りませんでした。そのおかげで、いろんな場所にテーブル表示させるコード書いてみたりして、本当に無意味な時間を長く過ごすことができました。（涙）


以上でございます。お役に立てば幸いです。
ありがとうございました。