Androidはワンツーパンチ 三歩進んで二歩下がる

プログラミングやどうでもいい話

ファイルを新規作成する時の「Swift class」と「 Cocoa Touch Class」の違いは何か

覚え書き

Xcodeでファイルを新規で作る時、「Swift class」と「 Cocoa Touch Class」どちらを選べばいいか迷った。

「Swift class」の方で作ると、「import Foundation」とだけある空のファイルが出来る。

Cocoa Touch Class」の方で作ると、どのスーパークラスのサブクラスとして作るか選択でき、
言語もSwiftかObjective-Cのどちらかを選択できる。

ココアタッチはiOSのUIフレームワークであり、UIKitをインポートし
選んだスーパークラスにより、いくつかのメソッドを含むクラスのひな型が作成される。

ひな型が必要なければ「Swift class」からファイルを作成すればよい。







Swift4で新しく追加されたDecodableプロトコルを使ってJSONデータをパースする

Android開発におけるGsonライブラリに似たものはないか

Androidアプリを作る際、Gsonというライブラリを使っていて、
JSONのデータとJavaのオブジェクトを相互に変換しておりました。
(当エントリではJSON→Swiftのモデルの変換をしますので、
以降はこの方向の変換の話とさせていただきます。)

Gsonではどのように変換するかと言いますと

JSONのデータ構造を表すJavaのモデルクラスを定義
 その際JSONのキーをフィールド名にしておく
JSONのデータが入っているStringオブジェクトをモデルクラスを使って変換
・変換が失敗した時の処理も書いておく

このような感じで、非常に短いコードで実現できます。

SwiftならDecodableが良さそう

これと似たようなことをiOSでもやろうとして調べたら、
Swift4で新しく追加された以下のプロトコルを実装すると大変便利に
エンコード・デコード出来ることがわかりました。

  • DecodableJSON→Swiftのオブジェクト
  • Encodable :Swiftのオブジェクト→JSON
  • Codable上記の両方

Encoding and Decoding Custom Types | Apple Developer Documentation

標準ライブラリに入っていることが嬉しいですし、
変換も非常に簡単です。
CodingKeyプロトコルを利用すれば
JSONのキーをそのままフィールド名にしなくても大丈夫です。
私にとってはGsonのやり方と似ているのでわかりやすいですし。

環境

サンプルコードの作成環境
Swift 4.0 Xcode 9.2

サンプルの仕様

f:id:sakura_bird1:20171226024346p:plain

  • Jsonファイルをローカルから読み込む
  • Array構造になっているデータなので、UITableViewで中身を表示する
  • データが部分的に欠けている場合"nil"と表示する

Jsonファイルはこちらです。
https://github.com/sakurabird/ios-example-swift4-json-parse/blob/master/ExamJsonToTable/colors.json

実装

Decodableを実装したモデルの定義をする

JSONの構造を表す構造体を定義するのですが、JSONを貼り付けると
Swiftのモデルクラスの形式で出力してくれるサービスがありました。
json4swift.com | Online JSON to Swift Models Generator

オンライン上で出来るのが嬉しいです。
他に似たようなことが出来るライブラリも見かけたことがあるので探してみるのも良いかもしれません。

上記のサービスの出力結果を参考に定義したのがこちらです。
全てのstructにDecodableを記述する必要があります。
入ってくるデータは値が欠けているものがある前提で作っていますので
"?"を付けてOptional型にしています。
付けないと値が欠けていたら実行時にクラッシュしてしまいます。

struct ColorModel: Decodable {
  
  struct Color: Decodable {
    let color: String?
    let category: String?
    let type: String?
    let code: Code?
  }
  struct Code: Decodable {
    let rgba: [Int]?
    let hex: String?
  }
  let colors: [Color]?
}

DataオブジェクトをパースしてSwiftのモデルオブジェクトを作成する

ローカルからJSONファイルを読み込みDataオブジェクトを作成します。
JSONDecoderのインスタンスよりdecodeメソッドを使用して変換します。

    // パスの取得
    guard let path = Bundle.main.path(forResource: "colors", ofType: "json") else { return }
    // URLの取得
    let url = URL(fileURLWithPath: path)

    do {
      // JSONファイルを読み込みDataオブジェクトに格納する
      let data = try Data(contentsOf: url)

     // Dataオブジェクトをモデルオブジェクトにパースする
      let colors = try
        JSONDecoder().decode(ColorModel.self, from: data)

      // オブジェクトの中身を表示
//      for color in (colors.colors)! {
//        print(color.color as Any)
//        print(color.category as Any)
//        print(color.type as Any)
//        print(color.code?.hex as Any)
//        print(color.code?.rgba?[0] as Any)
//        print(color.code?.rgba?[1] as Any)
//        print(color.code?.rgba?[2] as Any)
//        print(color.code?.rgba?[3] as Any)
//      }

      self.colors = colors.colors

    } catch  {
      print(error)
    }

サンプル全体

以上のようにJSONは変換できました。
サンプルでは画面に表示するところまで書いております。
プロジェクト全体はこちらにあります。

github.com

処理のメインである「ViewController.swift」の全体を貼り付けておきます。

Swiftの経験が浅すぎるのでおかしな部分や、もっと綺麗に書き直せる
部分などございましたらご指摘いただけますと嬉しいです。

//
//  ViewController.swift
//  ExamJsonToTable
//
//  Created by Sakura on 2017/12/19.
//  Copyright © 2017年 Sakura. All rights reserved.
//

import UIKit

// colors.json用のモデル
struct ColorModel: Decodable {
  
  struct Color: Decodable {
    let color: String?
    let category: String?
    let type: String?
    let code: Code?
  }
  struct Code: Decodable {
    let rgba: [Int]?
    let hex: String?
  }
  let colors: [Color]?
}

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

  @IBOutlet weak var tableView: UITableView!

  var colors: [ColorModel.Color]?
  
  // リユースするセルのid
  let cellReuseIdentifier = "cell"

  override func viewDidLoad() {
    super.viewDidLoad()

    loadJsonFile()

    tableView.delegate = self
    tableView.dataSource = self

  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

  // ローカルのJSONファイルを読み込みモデルオブジェクトにパースする
  func loadJsonFile() {
    // パスの取得
    guard let path = Bundle.main.path(forResource: "colors", ofType: "json") else { return }
    // URLの取得
    let url = URL(fileURLWithPath: path)

    do {
      // JSONファイルを読み込みDataオブジェクトに格納する
      let data = try Data(contentsOf: url)
//      print(data) // byte数が表示される

      // Dataオブジェクトをモデルオブジェクトにパースする
      let colors = try
        JSONDecoder().decode(ColorModel.self, from: data)

      // オブジェクトの中身を表示
//      for color in (colors.colors)! {
//        print(color.color as Any)
//        print(color.category as Any)
//        print(color.type as Any)
//        print(color.code?.hex as Any)
//        print(color.code?.rgba?[0] as Any)
//        print(color.code?.rgba?[1] as Any)
//        print(color.code?.rgba?[2] as Any)
//        print(color.code?.rgba?[3] as Any)
//      }

      self.colors = colors.colors

    } catch  {
      print(error)

    }
  }

  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return (colors!.count)
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    // セルを取得する
    guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier, for: indexPath) as? TableViewCell
      else {
        fatalError("The dequeued cell is not an instance of TableViewCell.")
    }

    // セルにjsonの中身を表示する。Viewに対応するフィールドに値がなければ"nil"と表示する。
    if let color =  colors![indexPath.row].color {
      cell.color!.text = color
    } else {
      cell.color!.text = "nil"
    }
    if let category =  colors![indexPath.row].category {
      cell.category!.text = category
    } else {
      cell.category!.text = "nil"
    }
    if let type =  colors![indexPath.row].type {
      cell.type!.text = type
    } else {
      cell.type!.text = "nil"
    }
    if let hex =  colors![indexPath.row].code?.hex {
      cell.hex!.text = hex
    } else {
      cell.hex!.text = "nil"
    }
    if let rgba1 =  colors![indexPath.row].code?.rgba![0] {
      cell.rgba1!.text = String(rgba1)
    } else {
      cell.rgba1!.text = "nil"
    }
    if let rgba2 =  colors![indexPath.row].code?.rgba![1] {
      cell.rgba2!.text = String(rgba2)
    } else {
      cell.rgba2!.text = "nil"
    }
    if let rgba3 =  colors![indexPath.row].code?.rgba![2] {
      cell.rgba3!.text = String(rgba3)
    } else {
      cell.rgba3!.text = "nil"
    }
    if let rgba4 =  colors![indexPath.row].code?.rgba![3] {
      cell.rgba4!.text = String(rgba4)
    } else {
      cell.rgba4!.text = "nil"
    }

    return cell
  }

}

Appleの公式Swiftチュートリアル「A Swift Tour」を勉強した感想。「FoodTracker」より先にやるべきだった!

iOSアプリを作るためにSwiftを勉強中です。
Apple公式の学習用ドキュメントに用意されている「A Swift Tour」を使用したので感想です。

f:id:sakura_bird1:20171215020027p:plain

「A Swift Tour」って?

Swiftのドキュメントの中にあるチュートリアルです。
developer.apple.com

「Playground」というXcodeの実行環境を使用して手を動かしながら学んでいけます。

ダウンロードのリンクから落としたGuidedTour.playgroundというファイルを
ダブルクリックすれば始まります。(Xcodeがインストールされてる前提)
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/GuidedTour.playground.zip

学んだこと

Swiftの基本的な文法を一通り触ることが出来ました。
Playgroundが実行途中の計算結果を表示してくれるので、自分で
コードを変更することでどのように動くかわかりやすく
役に立つチュートリアルだったと思います。

ただし、詳しいことはあまり書いておらず概要を説明しているのに過ぎないので
違うサイトで情報の補足をしていました。

もっと詳しい情報は「Language Guide」を読むべきでしょうね。

大体章の区切りで、「Experiment」という課題が用意されています。
これに結構頭を悩まされました。
読んでる時はうっすらわかったような気がしても、
書けと言われると書けない!
身につけたかったら手を動かしてアウトプットするって大事ですね。

Playgroundを触った感想

書くそばから実行されて結果が表示されるので、
いちいちビルド→実行しなくてもよくて
とてもいいです。

画像表示や複雑な処理も出来るみたいなんで
アプリ作成中も役に立ちそうです。

ゴチャゴチャ書いてるそばからエラーが出ますw
ちょっと処理が重い感じがしました。

学習の順序など

ついこの間まで、Appleの公式iOSチュートリアル「FoodTracker」というものを使って
アプリ作りの基礎を学んでいました。

「FoodTracker」公式サイトはこちらです。
Start Developing iOS Apps (Swift): Jump Right In

勉強した感想の記事はここです。
Appleの公式iOSチュートリアル「FoodTracker」をやってみた感想と勉強の動機 - Androidはワンツーパンチ 三歩進んで二歩下がる

このチュートリアルもすごく良かったんですが、swift文法を知らないまま
最後まで進めてしまいました(笑)
これはちょっと失敗だったかなと思っています。

FoodTracker進行中もクラス名でググったりはしてましたが、
「A Swift Tour」を先にやっていればもっとアプリ作成方法に集中できたのに!
と思います。

今後の予定

Apple公式のプログラミングガイドから気になるやつを学習できればと思っています。
日本語ドキュメント - Apple Developer

しかし、そんなに丁寧にやらずにざっと目を通すぐらいかな。

ミニサンプルみたいなプロジェクトをいくつか作りながら
プログラミングガイドや言語リファレンスや他の参考サイト様などを
行ったり来たり、でしょうか。

並行して優れたswiftのコードを読んでいけたらと思います。
きっと読んでも最初のうちは何が何だかわからないとは思いますが、
しばらくやってれば少しは見慣れてバラバラの知識がまとまって
くるのではないでしょうか?

本家コード!(※これを読むという宣言ではありません)
github.com