今日からswiftを使ったiOSアプリ開発を勉強していきます! swiftの文法もまだ全然覚えてないが、とりあえず機能を実装しながら習っていきたいと思う。(グーグル頼り)

webエンジニア出身なので、とりあえずswiftでリモートのJSONをパースしてModelに変換してみる

Gloss(https://github.com/hkellaway/Gloss)という便利なフレームワークもあるようだが、swiftに関して自分はかなり初心者なので、とりあえずswiftだけでやってみる。

JSONはこんな感じ

エンドポイントはhttp://zoo.dev/apiとする(ローカルサーバ)

{
    "state": "ok",
    "data": {
        "entries": [
            {
                "title": "Title1",
                "body": "body1",
                "published_at": "2017-10-11 15:00:00"
            },
            {
                "title": "Title2",
                "body": "body2",
                "published_at": "2017-10-18 15:00:00"
            }
        ]
    }
}

まずEntryというデータ構造を作る:

下記の内容でModel/Entry.swiftを作成:

import Foundation

struct Entry
{
    let name: String
    let body: String
    let published_at: Date? // `?`マークはnilの可能性もあるってこと

    public init?(json: [String:Any]) {
        self.name = json["title"] as! String
        self.body = json["body"] as! String

        // 時間の文字列をDate型に変換
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "YYYY-MM-DD HH:mm:ss"
        let published_at_str = json["published_at"] as! String;
        self.published_at = dateFormatter.date(from: published_at_str)
    }
}

swiftの文法ってすごい直感的で、プログラミングの経験者なら少しでも読めると思う。ちょっと特別なのは、initというメソード、名前の通り初期化用のメソードである

エンドポイントにリクエストを出す

swiftでHttpリクエストを出す方法はこのリンクhttps://stackoverflow.com/questions/43047016/urlsession-shared-datatask-with-body-payloadを参考した:

// エラーの種類を定義
enum JSONError : Error {
    case invalid // 不正のJSON
}

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let api_endpoint = URL(string: "http://zoo.dev/api")
        let task = URLSession.shared.dataTask(with: api_endpoint!) {(data, response, error) in
            do {
                let json_str = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
                print(json_str) // JSON文字列を出してみる
                guard let json = try? JSONSerialization.jsonObject(with: data!) as? [String: Any] else {
                    throw JSONError.invalid
                }
                guard let root = json?["data"] as? [String: Any],
                    let entry_json_arr = root["entries"] as? [[String: Any]] else {
                        throw JSONError.invalid
                }

                // JSONデータをEntry型に変換
                let entries = entry_json_arr.map{(json: [String: Any]) -> Entry in
                    return Entry(json: json)!
                }

                for entry in entries {
                    print(entry)
                }
            } catch JSONError.invalid {
                print("invalid json format")
            } catch let error as NSError {
                print(error.debugDescription)
            }
        }

        task.resume()
    }
    ...

実行すると:

{"state":"ok","data":{"entries":[{"title":"Title1","body":"body1","published_at":"2017-10-11 15:00:00"},{"title":"Title2","body":"body2","published_at":"2017-10-18 15:00:00"}]}}
Entry(name: "Title1", body: "body1", published_at: Optional(2017-01-11 06:00:00 +0000))
Entry(name: "Title2", body: "body2", published_at: Optional(2017-01-18 06:00:00 +0000))

printはオブジェクトも綺麗にプリントアウトしてくるのが嬉しい!
で、ここまでちょっと面白いと思うのは:

1. swiftでのエラー処理(throwとcatch)

swiftでエラー処理するには、まず下記のようにenum型でErrorを継承する:

enum CustomError : Error {
    case ops
    case errorType1
    case errorType2
}

ロジック部分をdo { ... } catch { ... }で例外をキャッチし、処理する:

do {
    // ...エラーが出るかもしれない処理
    // throw CustomError.errorType1
} catch CustomError.errorType1 {
    // errorType1の処理
} catch CustomError.errorType2 {
    // errorType2の処理
} catch error as NSError {
    // それ以外のエラー
}

2. guard

guardは何かの原因で処理を失敗し、フォールバックを提供するに使える便利なキーワード。PHPでいうとtry { ... } catch (\Exception $e) { ... }に近い。構文は下記のように:

guard ... else { ... }

簡単な例:

// エラーを定義
enum CustomError : Error {
    case ops
}

guard let a = retrieveAValue(),
      let b = retrieveBValue() else {
    throw CustomError.ops
}

まとめ

さすがswiftはまだ全然慣れてないので、今日はこれぐらいにします。ただswiftの魅力はなんとなくわかってきました。コンパイル言語でありながら使いやすいkotlinと似てるとは言えないが、kotlinともモダンな言語だなという所感です。

次はもうちょっとiOSを掘ってみます!