[Swift] Header - Items形式のデータの読み書きを行う

ヘッダデータ[1]と明細データ[0...n]を1つのデータの塊として、
ファイルに保存したり、読み込んだりしたくて、試行錯誤しました。

DBにした方が楽なんじゃない? とか思ったけど、DBにするほどデータも無かったので 、意地でもファイルで実現してやる!
ってことで、なんとか動きました。

扱うデータのイメージ



<ヘッダデータ> [1]
 伝票番号   orderNo
 仕入先コード vendorCode
 仕入先名   vendorName

<明細データ> [0...1]
 明細番号   itemNo
 明細テキスト itemText
 数量     quantity

ヘッダは必ず存在し、明細は0以上の可変を想定。

データ受け渡し用の構造


struct DataEntity {
    // Header
    var orderNo: Int!
    var vendorCode: String!
    var vendorName: String!

    // Items
    var items: [Item]!
    
    init(){
        orderNo = 0
        vendorCode = ""
        vendorName = ""
        items = []
    }
}

struct Item {
    var itemNo: Int!
    var itemText: String!
    var quantity: Int!
    
    init(){
        itemNo = 0;
        itemText = ""
        quantity = 0
    }
}


コード

データをファイルに保存する


    func writeData(dataEntity: DataEntity){
        
        var data:Dictionary = [String:AnyObject]()
        // Header
        data["orderNo"] = dataEntity.orderNo as AnyObject
        data["vendorCode"] = dataEntity.vendorCode as AnyObject
        data["vendorName"] = dataEntity.vendorName as AnyObject

        // Items
        var dataItem  :Dictionary = [String:Any]()
        var dataItems:Array = []
        for item in dataEntity.items {
            dataItem.removeAll()
            dataItem["itemNo"]   = item.itemNo
            dataItem["itemText"] = item.itemText
            dataItem["quantity"] = item.quantity
            dataItems.append(dataItem as AnyObject)
        }
        data["items"] = dataItems as AnyObject

        // ファイルへ書き込み
        let manager = FileManager.default
        let documentDir = manager.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let url = documentDir.appendingPathComponent("Test.plist")
        let dic = NSDictionary(dictionary: data)
        dic.write(to: url, atomically: true)
        
    }

説明

Dictionary型を使うと便利そうだったでので、こちらを使ってます。
キーはStringで良いと思いますが、値は、String、Int、Item構造型など、様々な型があるので、AnyObjectとしました。
        var data:Dictionary = [String:AnyObject]()

ヘッダデータは、キーと値を単純にセットしてます。
        // Header
        data["orderNo"] = dataEntity.orderNo as AnyObject
        data["vendorCode"] = dataEntity.vendorCode as AnyObject
        data["vendorName"] = dataEntity.vendorName as AnyObject
明細については、明細データをDictionary型で格納し、Dictionary型をArray配列に格納てます。
これにより明細数を動的にしてます。
        // Items
        var dataItem  :Dictionary = [String:Any]()
        var dataItems:Array = []
        for item in dataEntity.items {
            dataItem.removeAll()
            dataItem["itemNo"]   = item.itemNo
            dataItem["itemText"] = item.itemText
            dataItem["quantity"] = item.quantity
            dataItems.append(dataItem as AnyObject)
        }


Array配列の明細データを作り上げてから、ヘッダデータを格納した Dictionary型の変数に格納してます。

       data["items"] = dataItems as AnyObject

データは、アプリのDocuments内に、Test.plistで保存してます。


ファイルからデータを読み込む


    func readData() -> DataEntity {
        // ファイル読み込み
        let manager = FileManager.default
        let documentDir = manager.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let url = documentDir.appendingPathComponent("Test.plist")
        let plist = NSMutableDictionary(contentsOf: url)
        
        var dataEntity = DataEntity()
        if plist == nil {
            return dataEntity
        }
        
        // Header
        if let _orderNo = plist?["orderNo"] as? Int {
           dataEntity.orderNo = _orderNo
        }
        if let _vendorCode = plist?["vendorCode"] as? String {
            dataEntity.vendorCode = _vendorCode
        }
        if let _vendorName = plist?["vendorName"] as? String {
            dataEntity.vendorName = _vendorName
        }
        
        // Items
        if let _items = plist?["items"] as? Array {
            let dataItems = _items
            for dataItem in dataItems {
                var item = Item()
                if let _itemNo = dataItem["itemNo"] as? Int {
                    item.itemNo = _itemNo
                }
                if let _itemText = dataItem["itemText"] as? String {
                    item.itemText = _itemText
                }
                if let _quantity = dataItem["quantity"] as? Int {
                    item.quantity = _quantity
                }
                dataEntity.items.append(item)
            }
        }
        
        return dataEntity

    }

説明

アプリ内のDocumentsフォルダ内のTest.plistファイルを読み込みます。

ヘッダデータは、キー指定で値を取り出してます。
nilの場合(キー値が存在しない場合)は、何もせず初期値のままとするため、nilの判定を入れてます。
        if let _orderNo = plist?["orderNo"] as? Int {
           dataEntity.orderNo = _orderNo
        }


明細データは、Array配列として一旦取り込んでから、1件毎に明細データの取り出しを行ってます。
配列は、0件の場合もあるため、nil判定を行ってます。
if let _items = plist?["items"] as? Array {

forループで明細データを1件毎 取り出してます。
for dataItem in dataItems {

サンプルコード(全体)


import UIKit

class ViewController: UIViewController {

    let txtView = UITextView()
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.view.backgroundColor = UIColor.lightGray
        txtView.frame = self.view.frame
        view.addSubview(txtView)

        // -----------------------------
        var data = DataEntity()
        // Test Data
        data.orderNo = 100
        data.vendorCode = "VEN-123"
        data.vendorName = "Test Vendor Name"

        var item = Item()
        // item 1
        item.itemNo = 10
        item.itemText = "Item Text 1"
        item.quantity = 2
        data.items.append(item)
        // item 2
        item.itemNo = 20
        item.itemText = "Item Text 2"
        item.quantity = 7
        data.items.append(item)

        // Write to File
        writeData(dataEntity: data)
        
        // -----------------------------
        // Initialize
        data = DataEntity()
        // Read from File
        data = readData()
        
        txtView.text.append("Order = " + String(data.orderNo) + "\n")
        txtView.text.append("Vendor Code = " + data.vendorCode + "\n")
        txtView.text.append("Vendor Name = " + data.vendorName + "\n")
        
        for item in data.items {
            txtView.text.append("----------\n")
            txtView.text.append("ItemNo: " + String(item.itemNo) + "\n")
            txtView.text.append("Item Text: " + item.itemText + "\n")
            txtView.text.append("Quantity: " + String(item.quantity) + "\n")
        }

    }

    func writeData(dataEntity: DataEntity){
        
        var data:Dictionary = [String:AnyObject]()
        // Header
        data["orderNo"] = dataEntity.orderNo as AnyObject
        data["vendorCode"] = dataEntity.vendorCode as AnyObject
        data["vendorName"] = dataEntity.vendorName as AnyObject

        // Items
        var dataItem  :Dictionary = [String:Any]()
        var dataItems:Array = []
        for item in dataEntity.items {
            dataItem.removeAll()
            dataItem["itemNo"]   = item.itemNo
            dataItem["itemText"] = item.itemText
            dataItem["quantity"] = item.quantity
            dataItems.append(dataItem as AnyObject)
        }
        data["items"] = dataItems as AnyObject

        // ファイルへ書き込み
        let manager = FileManager.default
        let documentDir = manager.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let url = documentDir.appendingPathComponent("Test.plist")
        let dic = NSDictionary(dictionary: data)
        dic.write(to: url, atomically: true)
        
    }
    
    func readData() -> DataEntity {
        // ファイル読み込み
        let manager = FileManager.default
        let documentDir = manager.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let url = documentDir.appendingPathComponent("Test.plist")
        let plist = NSMutableDictionary(contentsOf: url)
        
        var dataEntity = DataEntity()
        if plist == nil {
            return dataEntity
        }
        
        // Header
        if let _orderNo = plist?["orderNo"] as? Int {
           dataEntity.orderNo = _orderNo
        }
        if let _vendorCode = plist?["vendorCode"] as? String {
            dataEntity.vendorCode = _vendorCode
        }
        if let _vendorName = plist?["vendorName"] as? String {
            dataEntity.vendorName = _vendorName
        }
        
        // Items
        if let _items = plist?["items"] as? Array {
            let dataItems = _items
            for dataItem in dataItems {
                var item = Item()
                if let _itemNo = dataItem["itemNo"] as? Int {
                    item.itemNo = _itemNo
                }
                if let _itemText = dataItem["itemText"] as? String {
                    item.itemText = _itemText
                }
                if let _quantity = dataItem["quantity"] as? Int {
                    item.quantity = _quantity
                }
                dataEntity.items.append(item)
            }
        }
        
        return dataEntity

    }
}

実行結果




説明

パラメータをセットしてファイル書き込み用の関数(writeData)を呼び出してます。
ファイルからの読み込み値を検証するために、一旦、変数を初期化してます。
その後、データ内容をファイルから読み込む関数(readData)を呼び出してます。
取り込んだデータは、UITextViewに出力してます。

最後に

エラーハンドリングなど甘いところはあると思いますが、
実装の参考になれば幸いです。

コメント

このブログの人気の投稿

[Music] DTM初心者のためのドラム打ち込み その2

[Swift] StoryBoardを使用しない - UITextFieldで編集不可にする方法

[Swift] UISliderをカスタマイズしてみる