Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
日記アプリの
データ管理
集まれSwift好き!Swift愛好会 vol24 (2017/11/20)
Bitz Co., Ltd. 村上幸雄
• 埼玉県朝霞市でソフトハウスを起業。
• macOSやiOS、Androidのアプリケーション開発を主に請け負う。
• 自社アプリの製作。
• Twitter: @m_yukio
• Facebook: yukio.murakami
• Gi...
• BUKURO.swift
Cocoa勉強会とMOSAの合同勉強会。
毎月上旬の月曜日に池袋FORESTで開会。
https://cocoa-kanto.connpass.com/
• Swift勉強会 Developer's Tech La...
発端
• Day Oneという日記アプリを利用しているが、
今後に不安がある。
• データは、plist + 画像ファイル。
容易にアクセスできるので、独自の日記アプリ
を製作して、移行を考えている。
Day One
+- Journal.dayone
+- entries
| 1A573C0B559248A991C4AC0F0EA8B1E7.doentry
| 1FB23D983DF341079532B0F9381BCFCC.doentry...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/Pro...
データ管理は?
iCloud
• キー値ストレージ
UserDefaultsと同様。1アプリ1MB。
• ドキュメントストレージ
文書ファイル。iCloudアカウントの容量から。
• CoreDataストレージ
←廃止
• CloudKit
他ユーザと共有でき...
CloudKitを検討したが
• 10 GB Asset storage
• 100 MB Database storage
• 2 GB Data transfer
• 40 Requests per second
• Overage Fee...
コンテナ
コンテナID
デフォルトコンテナ
let container: CKContainer = CKContainer.default()
カスタムコンテナ
let container: CKContainer = CKContainer...
データベース
公開データベース
let publicDatabase: CKDatabase = container.publicCloudDatabase
非公開データベース
let privateDatabase: CKDatabase =...
アカウントの確認
CKContainer.default().accountStatus(completionHandler: {
accountStatus, error in
if error != nil {
print(error ??...
追加
/* レコードIDを生成 */
let uuidString = UUID.init().uuidString
let diaryRecordID: CKRecordID = CKRecordID(recordName: uuidStri...
更新
/* レコードIDからレコードを取得する */
database.fetch(withRecordID: diaryRecordID, completionHandler: { (record, error) in
if error !=...
削除
/* 追加で使ったレコードIDで取得 */
database.delete(withRecordID: diaryRecordID, completionHandler: { (recordId, error) in
if error !...
検索/* 検索条件 */
let now = Date()
var calendar = Calendar.current
calendar.locale = Locale(identifier: "ja")
var component = c...
サブスクリプション保存
/* 条件 */
let predicate = NSPredicate(format: "TRUEPREDICATE")
let subscription = CKSubscription(recordType: "D...
画像はアセットで
database.fetch(withRecordID: diaryRecordID, completionHandler: { (diaryRecord, error) in
print(#function)
if erro...
Assetフィールドにファイルを設定
レコードを保存するとファイルがアップロードされる
Assetを操作するとダウンロードされる
iCloud設計ガイド
• CloudKitを取り入れた設計
• アプリケーションのモデルオブジェクトの置き
換えおよびオブジェクトのローカル保存には
CloudKitフレームワークを使わないでください。
• このフレームワークは、iCloud...
ストレージを再検討
iCloudドキュメントを利用
iCloud Drive
+- KeepADiary
+- entries
| 1A573C0B559248A991C4AC0F0EA8B1E7.plist
| 1FB23D983DF341079532B0F93...
• 1エントリは1ファイル。
• データ破損の影響範囲を狭くする。
• アプリがなくなっても、ユーザはデータを取り出せる。
• 差分のみ同期。
• インデックスを用意する。
• 各アプリケーションローカルにインデックスを保持。
• CloudK...
ファイル数が増えた場
合
• 同一ディレクトリに大量のファイルを置かない
。
• 例えば、年月 の名前のディレクトリにおく。
• 例えば、名前が 年/月日 なら、plistとjpegは同一
ディレクトリに、そして、ディレクトリ配下の
個数が抑え...
iCloudドキュメントストレージ
• Document-Based Applicationなら、NSDocument
の恩恵を受けれるがファイル数は一個なので、ち
ょっと違う。
• アクセス先はiCloudコンテナ。CloudKitで用意し
...
書き込み
let containerURL = FileManager.default.url(forUbiquityContainerIdentifier:
“iCloud.com.example.KeepADiary”)
let docum...
• macOSで書き込み先のURLは、以下だった。
file:///Users/ユーザ名
/Library/Mobile%20Documents/iCloud~com~example~KeepADiary/Document
s/my.diary...
ありがとうございます
ありがとうございます
Upcoming SlideShare
Loading in …5
×

日記アプリのデータ管理

358 views

Published on

日記アプリのデータ管理

Published in: Technology
  • Be the first to comment

  • Be the first to like this

日記アプリのデータ管理

  1. 1. 日記アプリの データ管理 集まれSwift好き!Swift愛好会 vol24 (2017/11/20) Bitz Co., Ltd. 村上幸雄
  2. 2. • 埼玉県朝霞市でソフトハウスを起業。 • macOSやiOS、Androidのアプリケーション開発を主に請け負う。 • 自社アプリの製作。 • Twitter: @m_yukio • Facebook: yukio.murakami • GitHub: murakami • http://www.bitz.co.jp/weblog/
  3. 3. • BUKURO.swift Cocoa勉強会とMOSAの合同勉強会。 毎月上旬の月曜日に池袋FORESTで開会。 https://cocoa-kanto.connpass.com/ • Swift勉強会 Developer's Tech Lab. Cocoa勉強会 関東。 毎月下旬の土曜日に関東で開催。 https://cocoa-kanto.connpass.com/ • Cocoa Advent Calendar 2017 https://qiita.com/advent-calendar/2017/cocoa
  4. 4. 発端
  5. 5. • Day Oneという日記アプリを利用しているが、 今後に不安がある。 • データは、plist + 画像ファイル。 容易にアクセスできるので、独自の日記アプリ を製作して、移行を考えている。
  6. 6. Day One +- Journal.dayone +- entries | 1A573C0B559248A991C4AC0F0EA8B1E7.doentry | 1FB23D983DF341079532B0F9381BCFCC.doentry | : +- photos 1A573C0B559248A991C4AC0F0EA8B1E7.jpg 2D37E853754E43EFBA0FFBAEEB6A8B40.jpg :
  7. 7. <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Activity</key> <string>Automotive</string> <key>Creation Date</key> <date>2016-07-18T08:51:34Z</date> <key>Creator</key> <dict> <key>Device Agent</key> <string>iPhone/iPhone7,2</string> <key>Generation Date</key> <date>2016-07-18T08:51:34Z</date> <key>Host Name</key> <string>iPhone6GB128Gold</string> <key>OS Agent</key> <string>iOS/9.3.2</string> <key>Software Agent</key> <string>Day One iOS/1.17.9</string> </dict> <key>Entry Text</key> <string>AT教習のゼッケン 下で待つ。準備なし</string> :
  8. 8. データ管理は?
  9. 9. iCloud • キー値ストレージ UserDefaultsと同様。1アプリ1MB。 • ドキュメントストレージ 文書ファイル。iCloudアカウントの容量から。 • CoreDataストレージ ←廃止 • CloudKit 他ユーザと共有できるデータ
  10. 10. CloudKitを検討したが • 10 GB Asset storage • 100 MB Database storage • 2 GB Data transfer • 40 Requests per second • Overage Fees • Asset storage $0.03/GB • Database storage $3.00/GB • Data transfer $0.10/GB • Requests per sec $100 per 10 requests
  11. 11. コンテナ コンテナID デフォルトコンテナ let container: CKContainer = CKContainer.default() カスタムコンテナ let container: CKContainer = CKContainer(identifier: “iCloud.com.example.KeepADiary”)
  12. 12. データベース 公開データベース let publicDatabase: CKDatabase = container.publicCloudDatabase 非公開データベース let privateDatabase: CKDatabase = container.privateCloudDatabase 共有データベース let sharedDatabase: CKDatabase = container.sharedCloudDatabase
  13. 13. アカウントの確認 CKContainer.default().accountStatus(completionHandler: { accountStatus, error in if error != nil { print(error ?? "") } else if accountStatus == .noAccount { let alert = UIAlertController.init(title: "Sign in to iCloud", message: "Sign in to your iCloud account to write records.On the Home screen, launch Settings, tap iCloud, and enter your Apple ID.Turn iCloud Drive on.If you don't have an iCloud account, tap Create a new Apple ID.", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Okay", style: .cancel, handler: nil)) self.present(alert, animated: true, completion: nil) } else { print(“Insert your just-in-time schema code here”) } })
  14. 14. 追加 /* レコードIDを生成 */ let uuidString = UUID.init().uuidString let diaryRecordID: CKRecordID = CKRecordID(recordName: uuidString) /* レコード・オブジェクトを生成 */ let diaryRecord: CKRecord = CKRecord(recordType: "Diary", recordID: diaryRecordID) /* レコードのフィールドを設定 */ diaryRecord["UUID"] = uuidString as CKRecordValue diaryRecord.setObject("本文です。" as NSString, forKey: "EntryText") diaryRecord.setObject(Date() as NSDate, forKey: "DiaryDate") /* レコードを保存する */ database.save(diaryRecord, completionHandler: { (record, error) in if error != nil { print(error) } else { print(record) } })
  15. 15. 更新 /* レコードIDからレコードを取得する */ database.fetch(withRecordID: diaryRecordID, completionHandler: { (record, error) in if error != nil { print(error) } else { /* 値を更新する */ record.setObject("本文を更新します。" as NSString, forKey: "EntryText") /* レコードを保存する */ database.save(diaryRecord, completionHandler: { (record, error) in if error != nil { print(error) } else { print(record) } }) } })
  16. 16. 削除 /* 追加で使ったレコードIDで取得 */ database.delete(withRecordID: diaryRecordID, completionHandler: { (recordId, error) in if error != nil { print(error) } else { print(recordId) } })
  17. 17. 検索/* 検索条件 */ let now = Date() var calendar = Calendar.current calendar.locale = Locale(identifier: "ja") var component = calendar.dateComponents([.year, .month, .day], from: now) component.hour = 0 component.minute = 0 component.second = 0 let beginDate: NSDate = calendar.date(from:component)! as NSDate let endDate: NSDate = NSDate(timeInterval: (60.0 * 60.0 * 24.0), since: beginDate as Date) let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "ja_JP") dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss" let beginDateString = dateFormatter.string(from: beginDate as Date) let endDateString = dateFormatter.string(from: endDate as Date) let predicate = NSPredicate(format: "DiaryDate != %@", NSDate()) let query : CKQuery = CKQuery(recordType: "Diary", predicate: predicate) /* 検索する */ database.perform(query, inZoneWith: nil, completionHandler: { results, error in if error != nil { print(error) } else { print(results) /* 検索した結果 */ for record in results! { /* 削除する */ database.delete(withRecordID: record.recordID, completionHandler: { (recordId, error) in if error != nil { print(error) } else { print(recordId) } }) } } })
  18. 18. サブスクリプション保存 /* 条件 */ let predicate = NSPredicate(format: "TRUEPREDICATE") let subscription = CKSubscription(recordType: "Diary", predicate: predicate, options: [CKSubscriptionOptions.firesOnRecordCreation, CKSubscriptionOptions.firesOnRecordUpdate]) /* 通知 */ subscription.notificationInfo = CKNotificationInfo() subscription.notificationInfo?.alertBody = "Item has been added." subscription.notificationInfo?.alertLocalizationKey = "Diary record has changed!" subscription.notificationInfo?.shouldBadge = true /* 保存 */ database.save(subscription, completionHandler: { subscription, error in if error != nil { print(error) } else { print(subscription) } })
  19. 19. 画像はアセットで database.fetch(withRecordID: diaryRecordID, completionHandler: { (diaryRecord, error) in print(#function) if error != nil { print(error) } else { let asset = CKAsset(fileURL: fileUrl) diaryRecord!["photo"] = asset database.save(diaryRecord!, completionHandler: { (record, error) in if error != nil { print(error) } else { print(record) } }) } })
  20. 20. Assetフィールドにファイルを設定 レコードを保存するとファイルがアップロードされる Assetを操作するとダウンロードされる
  21. 21. iCloud設計ガイド • CloudKitを取り入れた設計 • アプリケーションのモデルオブジェクトの置き 換えおよびオブジェクトのローカル保存には CloudKitフレームワークを使わないでください。 • このフレームワークは、iCloudとデータをやり取 りしたり、アプリケーションのユーザ間でデー タを共有したりするためのサービスです。
  22. 22. ストレージを再検討
  23. 23. iCloudドキュメントを利用 iCloud Drive +- KeepADiary +- entries | 1A573C0B559248A991C4AC0F0EA8B1E7.plist | 1FB23D983DF341079532B0F9381BCFCC.plist | : +- photos 1A573C0B559248A991C4AC0F0EA8B1E7.jpg 2D37E853754E43EFBA0FFBAEEB6A8B40.jpg : CloudKit +- <インデックスの差分> ローカル・ストレージ +- <インデックス>
  24. 24. • 1エントリは1ファイル。 • データ破損の影響範囲を狭くする。 • アプリがなくなっても、ユーザはデータを取り出せる。 • 差分のみ同期。 • インデックスを用意する。 • 各アプリケーションローカルにインデックスを保持。 • CloudKitで同期する。
  25. 25. ファイル数が増えた場 合 • 同一ディレクトリに大量のファイルを置かない 。 • 例えば、年月 の名前のディレクトリにおく。 • 例えば、名前が 年/月日 なら、plistとjpegは同一 ディレクトリに、そして、ディレクトリ配下の 個数が抑えられる。
  26. 26. iCloudドキュメントストレージ • Document-Based Applicationなら、NSDocument の恩恵を受けれるがファイル数は一個なので、ち ょっと違う。 • アクセス先はiCloudコンテナ。CloudKitで用意し たものみたい。 • 複数のファイルを扱うが、ファイルパッケージで ひと塊りにできる。
  27. 27. 書き込み let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: “iCloud.com.example.KeepADiary”) let documentsURL = containerURL?.appendingPathComponent("Documents") let fileURL = documentsURL?.appendingPathComponent("my.diary") let todayText = Date().description do { try todayText.write(to: fileURL!, atomically: true, encoding: .utf8) } catch { print("write error") }
  28. 28. • macOSで書き込み先のURLは、以下だった。 file:///Users/ユーザ名 /Library/Mobile%20Documents/iCloud~com~example~KeepADiary/Document s/my.diary • 書き込まれたファイルは以下だった。 $ pwd /Users/ユーザ名/Library/Mobile Documents/iCloud~com~example~KeepADiary/Documents $ ls my 3.diary my 4.diary my 5.diary my 6.diary $ cat my 3.diary 2017-11-19 02:14:31 +0000
  29. 29. ありがとうございます ありがとうございます

×