[Swift] Header - Items形式のデータの読み書きを行う
ヘッダデータ[1]と明細データ[0...n]を1つのデータの塊として、
ファイルに保存したり、読み込んだりしたくて、試行錯誤しました。
DBにした方が楽なんじゃない? とか思ったけど、DBにするほどデータも無かったので 、意地でもファイルで実現してやる!
ってことで、なんとか動きました。

<ヘッダデータ> [1]
伝票番号 orderNo
仕入先コード vendorCode
仕入先名 vendorName
<明細データ> [0...1]
明細番号 itemNo
明細テキスト itemText
数量 quantity
ヘッダは必ず存在し、明細は0以上の可変を想定。
キーはStringで良いと思いますが、値は、String、Int、Item構造型など、様々な型があるので、AnyObjectとしました。
ヘッダデータは、キーと値を単純にセットしてます。
これにより明細数を動的にしてます。
Array配列の明細データを作り上げてから、ヘッダデータを格納した Dictionary型の変数に格納してます。
データは、アプリのDocuments内に、Test.plistで保存してます。
ヘッダデータは、キー指定で値を取り出してます。
nilの場合(キー値が存在しない場合)は、何もせず初期値のままとするため、nilの判定を入れてます。
明細データは、Array配列として一旦取り込んでから、1件毎に明細データの取り出しを行ってます。
配列は、0件の場合もあるため、nil判定を行ってます。
forループで明細データを1件毎 取り出してます。

ファイルからの読み込み値を検証するために、一旦、変数を初期化してます。
その後、データ内容をファイルから読み込む関数(readData)を呼び出してます。
取り込んだデータは、UITextViewに出力してます。
実装の参考になれば幸いです。
ファイルに保存したり、読み込んだりしたくて、試行錯誤しました。
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に出力してます。
最後に
エラーハンドリングなど甘いところはあると思いますが、実装の参考になれば幸いです。
コメント
コメントを投稿