Commit 1989c7ee authored by tbren's avatar tbren
Browse files

Initial development of new SQLite-based database for iOS app.

Adapt existing schema and demo data to the database and implement import.
New setup screen design
Performance testing - able to import entire demo database in 100ms, and export the ENTIRE database to a sync json in 300ms
parent 95f6f576
Showing with 6338 additions and 6 deletions
+6338 -6
......@@ -3,10 +3,21 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
D714F284255AAD100072FE97 /* DatabaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D714F283255AAD100072FE97 /* DatabaseController.swift */; };
D714F28A255AAD890072FE97 /* GRDB in Frameworks */ = {isa = PBXBuildFile; productRef = D714F289255AAD890072FE97 /* GRDB */; };
D7362AAF257C81ED009F540F /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7362AAE257C81ED009F540F /* String.swift */; };
D7362AB7257C822A009F540F /* ActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7362AB6257C822A009F540F /* ActivityIndicatorView.swift */; };
D73BA2492574C1220025C753 /* schema.json in Resources */ = {isa = PBXBuildFile; fileRef = D73BA2482574C1220025C753 /* schema.json */; };
D73BA24E2574C1300025C753 /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BA24D2574C1300025C753 /* Schema.swift */; };
D73BA2532574CA2F0025C753 /* DemoData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D73BA2522574CA2F0025C753 /* DemoData.swift */; };
D73BA2582574CA460025C753 /* demo_database.json in Resources */ = {isa = PBXBuildFile; fileRef = D73BA2572574CA460025C753 /* demo_database.json */; };
D745D1622575C39D006BC24D /* SyncController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D745D1612575C39D006BC24D /* SyncController.swift */; };
D76EA501255DF01500AE2E20 /* DatabaseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = D76EA500255DF01500AE2E20 /* DatabaseTypes.swift */; };
D7A135C5257B562600EBEEF7 /* SetupScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7A135C4257B562600EBEEF7 /* SetupScreen.swift */; };
D7F136F8254E645D00881B25 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F136F7254E645D00881B25 /* AppDelegate.swift */; };
D7F136FA254E645D00881B25 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F136F9254E645D00881B25 /* SceneDelegate.swift */; };
D7F136FC254E645D00881B25 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7F136FB254E645D00881B25 /* ContentView.swift */; };
......@@ -35,6 +46,16 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
D714F283255AAD100072FE97 /* DatabaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseController.swift; sourceTree = "<group>"; };
D7362AAE257C81ED009F540F /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
D7362AB6257C822A009F540F /* ActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicatorView.swift; sourceTree = "<group>"; };
D73BA2482574C1220025C753 /* schema.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = schema.json; sourceTree = "<group>"; };
D73BA24D2574C1300025C753 /* Schema.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Schema.swift; sourceTree = "<group>"; };
D73BA2522574CA2F0025C753 /* DemoData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoData.swift; sourceTree = "<group>"; };
D73BA2572574CA460025C753 /* demo_database.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = demo_database.json; sourceTree = "<group>"; };
D745D1612575C39D006BC24D /* SyncController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncController.swift; sourceTree = "<group>"; };
D76EA500255DF01500AE2E20 /* DatabaseTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseTypes.swift; sourceTree = "<group>"; };
D7A135C4257B562600EBEEF7 /* SetupScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupScreen.swift; sourceTree = "<group>"; };
D7F136F4254E645D00881B25 /* MemriDatabase.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MemriDatabase.app; sourceTree = BUILT_PRODUCTS_DIR; };
D7F136F7254E645D00881B25 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
D7F136F9254E645D00881B25 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
......@@ -56,6 +77,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D714F28A255AAD890072FE97 /* GRDB in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -76,6 +98,94 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
D7362AAD257C81DB009F540F /* Extensions */ = {
isa = PBXGroup;
children = (
D7362AAE257C81ED009F540F /* String.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
D7A135B7257B55EC00EBEEF7 /* Database */ = {
isa = PBXGroup;
children = (
D7A135BF257B560700EBEEF7 /* Local */,
D7A135BB257B55FE00EBEEF7 /* Syncing */,
);
path = Database;
sourceTree = "<group>";
};
D7A135BB257B55FE00EBEEF7 /* Syncing */ = {
isa = PBXGroup;
children = (
D745D1612575C39D006BC24D /* SyncController.swift */,
);
path = Syncing;
sourceTree = "<group>";
};
D7A135BF257B560700EBEEF7 /* Local */ = {
isa = PBXGroup;
children = (
D714F283255AAD100072FE97 /* DatabaseController.swift */,
D76EA500255DF01500AE2E20 /* DatabaseTypes.swift */,
D73BA24D2574C1300025C753 /* Schema.swift */,
D73BA2522574CA2F0025C753 /* DemoData.swift */,
);
path = Local;
sourceTree = "<group>";
};
D7A135C3257B561500EBEEF7 /* UI */ = {
isa = PBXGroup;
children = (
D7A135C9257B564900EBEEF7 /* Setup */,
D7A135CA257B565000EBEEF7 /* Navigation */,
D7A135CB257B565D00EBEEF7 /* Settings */,
D7A135CC257B566800EBEEF7 /* Components */,
);
path = UI;
sourceTree = "<group>";
};
D7A135C9257B564900EBEEF7 /* Setup */ = {
isa = PBXGroup;
children = (
D7A135C4257B562600EBEEF7 /* SetupScreen.swift */,
);
path = Setup;
sourceTree = "<group>";
};
D7A135CA257B565000EBEEF7 /* Navigation */ = {
isa = PBXGroup;
children = (
);
path = Navigation;
sourceTree = "<group>";
};
D7A135CB257B565D00EBEEF7 /* Settings */ = {
isa = PBXGroup;
children = (
);
path = Settings;
sourceTree = "<group>";
};
D7A135CC257B566800EBEEF7 /* Components */ = {
isa = PBXGroup;
children = (
D7362AB6257C822A009F540F /* ActivityIndicatorView.swift */,
);
path = Components;
sourceTree = "<group>";
};
D7A135CD257B567A00EBEEF7 /* Assets */ = {
isa = PBXGroup;
children = (
D7F13702254E645E00881B25 /* LaunchScreen.storyboard */,
D7F136FD254E645E00881B25 /* Assets.xcassets */,
D73BA2482574C1220025C753 /* schema.json */,
D73BA2572574CA460025C753 /* demo_database.json */,
);
path = Assets;
sourceTree = "<group>";
};
D7F136EB254E645D00881B25 = {
isa = PBXGroup;
children = (
......@@ -102,8 +212,10 @@
D7F136F7254E645D00881B25 /* AppDelegate.swift */,
D7F136F9254E645D00881B25 /* SceneDelegate.swift */,
D7F136FB254E645D00881B25 /* ContentView.swift */,
D7F136FD254E645E00881B25 /* Assets.xcassets */,
D7F13702254E645E00881B25 /* LaunchScreen.storyboard */,
D7A135B7257B55EC00EBEEF7 /* Database */,
D7A135C3257B561500EBEEF7 /* UI */,
D7362AAD257C81DB009F540F /* Extensions */,
D7A135CD257B567A00EBEEF7 /* Assets */,
D7F13705254E645E00881B25 /* Info.plist */,
D7F136FF254E645E00881B25 /* Preview Content */,
);
......@@ -152,6 +264,9 @@
dependencies = (
);
name = MemriDatabase;
packageProductDependencies = (
D714F289255AAD890072FE97 /* GRDB */,
);
productName = MemriDatabase;
productReference = D7F136F4254E645D00881B25 /* MemriDatabase.app */;
productType = "com.apple.product-type.application";
......@@ -223,6 +338,9 @@
Base,
);
mainGroup = D7F136EB254E645D00881B25;
packageReferences = (
D714F288255AAD890072FE97 /* XCRemoteSwiftPackageReference "GRDB" */,
);
productRefGroup = D7F136F5254E645D00881B25 /* Products */;
projectDirPath = "";
projectRoot = "";
......@@ -239,7 +357,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D73BA2492574C1220025C753 /* schema.json in Resources */,
D7F13704254E645E00881B25 /* LaunchScreen.storyboard in Resources */,
D73BA2582574CA460025C753 /* demo_database.json in Resources */,
D7F13701254E645E00881B25 /* Preview Assets.xcassets in Resources */,
D7F136FE254E645E00881B25 /* Assets.xcassets in Resources */,
);
......@@ -266,8 +386,16 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D7A135C5257B562600EBEEF7 /* SetupScreen.swift in Sources */,
D745D1622575C39D006BC24D /* SyncController.swift in Sources */,
D7362AAF257C81ED009F540F /* String.swift in Sources */,
D73BA24E2574C1300025C753 /* Schema.swift in Sources */,
D7F136F8254E645D00881B25 /* AppDelegate.swift in Sources */,
D73BA2532574CA2F0025C753 /* DemoData.swift in Sources */,
D7362AB7257C822A009F540F /* ActivityIndicatorView.swift in Sources */,
D76EA501255DF01500AE2E20 /* DatabaseTypes.swift in Sources */,
D7F136FA254E645D00881B25 /* SceneDelegate.swift in Sources */,
D714F284255AAD100072FE97 /* DatabaseController.swift in Sources */,
D7F136FC254E645D00881B25 /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
......@@ -597,6 +725,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
D714F288255AAD890072FE97 /* XCRemoteSwiftPackageReference "GRDB" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/groue/GRDB.swift";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 5.1.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
D714F289255AAD890072FE97 /* GRDB */ = {
isa = XCSwiftPackageProductDependency;
package = D714F288255AAD890072FE97 /* XCRemoteSwiftPackageReference "GRDB" */;
productName = GRDB;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = D7F136EC254E645D00881B25 /* Project object */;
}
{
"object": {
"pins": [
{
"package": "GRDB",
"repositoryURL": "https://github.com/groue/GRDB.swift",
"state": {
"branch": null,
"revision": "2c51c047081da160698e818d6844af75f321f867",
"version": "5.1.0"
}
}
]
},
"version": 1
}
<?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">
<array/>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "A27FE82B-1838-4084-929E-9C9323F7E4D2"
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "4CE36434-2BB7-4DDC-BB72-9093FC1A9047"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "MemriDatabase/DemoData.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "54"
endingLineNumber = "54"
landmarkName = "test()"
landmarkType = "9">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>
......@@ -4,8 +4,160 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>Associations (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>5</integer>
</dict>
<key>Associations (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>6</integer>
</dict>
<key>Associations (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>CustomizedDecodingOfDatabaseRows (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>14</integer>
</dict>
<key>CustomizedDecodingOfDatabaseRows (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>15</integer>
</dict>
<key>CustomizedDecodingOfDatabaseRows (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>13</integer>
</dict>
<key>Format Code.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>19</integer>
</dict>
<key>GettingStarted (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>17</integer>
</dict>
<key>GettingStarted (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>18</integer>
</dict>
<key>GettingStarted (Playground) 3.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>GettingStarted (Playground) 4.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>5</integer>
</dict>
<key>GettingStarted (Playground) 5.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>6</integer>
</dict>
<key>GettingStarted (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>16</integer>
</dict>
<key>MemriDatabase.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>MyPlayground (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>11</integer>
</dict>
<key>MyPlayground (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>12</integer>
</dict>
<key>MyPlayground (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>10</integer>
</dict>
<key>Tour (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>8</integer>
</dict>
<key>Tour (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>9</integer>
</dict>
<key>Tour (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
</dict>
<key>TransactionObserver (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>TransactionObserver (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
</dict>
<key>TransactionObserver (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>0</integer>
</dict>
......
......@@ -13,7 +13,39 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
DatabaseController.shared.setupDatabase()
importDemoData()
try! SyncController.shared.syncUpload(maxItems: 1000000)
// try! SyncController.shared.syncDownload(json: (
// """
// {
// "update" : [
// {
// "testProperty1" : "testing",
// "dateModified" : 1606787421852,
// "uid" : "E2662EE0-254B-48BC-B097-85B6E2F51F99",
// "version" : 1,
// "dateCreated" : 1606787421852,
// "deleted" : false
// }
// ],
// "create" : [
// {
// "dateModified" : 1606787421850,
// "testPropertyNumber" : 542.13999999999999,
// "dateCreated" : 1606787421850,
// "uid" : "D7C756C6-3EEA-4DBC-AE25-F06DE5B56BA8",
// "version" : 1,
// "testProperty2" : "test this out",
// "testPropertyDate" : 1606787421853,
// "deleted" : false,
// "testProperty1" : "ok tastedsc"
// }
// ]
// }
// """.data(using: .utf8)!))
return true
}
......
This diff is collapsed.
This diff is collapsed.
......@@ -9,8 +9,7 @@ import SwiftUI
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
SetupScreen()
}
}
......
//
// GRDBTest.swift
// MemriDatabase
//
// Created by T Brennan on 10/11/20.
//
import Foundation
import GRDB
class DatabaseController {
static let shared = DatabaseController()
let databasePool: DatabasePool = {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
let url = documentsDirectory.appendingPathComponent("memri.sqlite")
print(url)
return try! DatabasePool(path: url.path)
}()
var schema = Schema.loadFromFile()
func setupDatabase() {
var migrator = DatabaseMigrator()
#if DEBUG
try! databasePool.erase()
// Speed up development by wiping the database when migrations change
// NEVER set this outside of debug mode (because it deletes everything)
migrator.eraseDatabaseOnSchemaChange = true
#endif
migrator.registerMigration("initialSetup") { db in
try db.create(table: "item") { t in
// 16 byte UUID that uniquely identifies this item
t.column("uid", .text).notNull().primaryKey()
// The type of this item (a string matching a type defined in the schema)
t.column("type", .text).notNull()
// Time of creation
t.column("dateCreated", .integer).notNull()
// Time last modified
t.column("dateModified", .integer).notNull()
// Version - starts at 1
t.column("version", .integer).defaults(to: 1).notNull(onConflict: .replace)
// Whether this item has been deleted (default = false)
t.column("deleted", .boolean).defaults(to: false).notNull(onConflict: .replace)
// iOS only (used for syncing decisions
t.column("syncState", .text).defaults(to: SyncState.create).notNull(onConflict: .replace)
t.column("syncHasPriority", .boolean).defaults(to: false).notNull(onConflict: .replace)
}
try db.create(table: "itemEdge") { t in
// The uid of the item this edge belongs to. Delete this row if the item is deleted
t.column("ownerUID", .blob).notNull().indexed().references("item", onDelete: .cascade)
// The name of this edge
t.column("name", .text).notNull()
// The target item of this edge. Delete this row if the target item is deleted
t.column("targetUID", .blob).notNull().indexed().references("item", onDelete: .cascade)
}
try db.create(table: "itemProperty") { t in
// The uid of the item this property belongs to. Delete this row if the item is deleted
t.column("itemUID", .blob).notNull().indexed().references("item", onDelete: .cascade)
// The name of this property
t.column("name", .text).notNull()
// The value of this property
t.column("value", .blob).notNull()
// There must be only one row for each property name per item - if we try to add another replace the old one
t.uniqueKey(["itemUID", "name"], onConflict: .replace)
}
try db.create(virtualTable: "itemProperty_search", using: FTS4()) { t in
t.synchronize(withTable: "itemProperty")
t.tokenizer = .porter
t.column("itemUID").notIndexed()
t.column("name").notIndexed()
t.column("value")
}
}
try! migrator.migrate(databasePool)
}
func searchTest() {
try! databasePool.read { (db) in
let searchQuery = FTS3Pattern(matchingAllTokensIn: "testing out")!
let refinedQuery = try! FTS3Pattern(rawPattern: "\(searchQuery.rawPattern)*")
let search = try! ItemRecord.search(db, pattern: refinedQuery)
print(search)
}
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
//
// SyncController.swift
// MemriDatabase
//
// Created by T Brennan on 1/12/20.
//
import Foundation
import GRDB
enum SyncState: String, Codable, Hashable, DatabaseValueConvertible {
case create
case update
case noChanges
case failed
}
class SyncController {
static let shared = SyncController()
let databaseController: DatabaseController = DatabaseController.shared
func syncUpload(maxItems: Int = 100) throws {
#if DEBUG
let startTime = CFAbsoluteTimeGetCurrent()
#endif
// Construct the JSON to send to the pod
let syncJsonData = try databaseController.databasePool.read { (db) -> Data in
/// Select the items to sync, giving priority to those marked as `syncHasPriority`
let itemsForSync = try ItemRecord.order(ItemRecord.Columns.syncHasPriority.desc).limit(maxItems).fetchAll(db).filter { ![SyncState.noChanges, SyncState.failed].contains($0.syncState) }
/// Construct a JSON-compatible representation for the items
let syncItems = itemsForSync.map { itemRecord -> SyncUploadItem in
let properties = itemRecord.syncDict(db: db)
return SyncUploadItem(operation: itemRecord.syncState, properties: properties)
}
/// Group by sync operation. eg. create, update
let itemsByOperation = Dictionary(grouping: syncItems) { $0.operation.rawValue }.mapValues { $0.map { $0.properties } }
return try JSONSerialization.data(withJSONObject: itemsByOperation, options: [.prettyPrinted])
}
#if DEBUG
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("Time elapsed to make sync JSON: \(timeElapsed) s.")
#endif
#if DEBUG
let writePath = "/Users/tbrennan/syncOutput.json"
try! syncJsonData.write(to: URL(fileURLWithPath: writePath))
#endif
// Start network task
// Handle network task completion (eg. mark items as synced or failed)
// Schedule another sync operation until all batches are complete
}
func syncDownload(json: Data) throws {
guard let dict = try JSONSerialization.jsonObject(with: json, options: []) as? [String: [[String: Any?]]] else {
// Throw here
return
}
let createItemDicts = dict[SyncState.create.rawValue] ?? []
let updateItemsDicts = dict[SyncState.update.rawValue] ?? []
let createItems = createItemDicts.compactMap(SyncDownloadItem.init)
let updateItems = updateItemsDicts.compactMap(SyncDownloadItem.init)
print("Sync download test: Create \(createItems.count) items, Update \(updateItems.count) items")
}
}
struct SyncUploadItem {
var operation: SyncState
var properties: [String: Any?]
}
struct SyncDownloadItem {
var item: ItemRecord
var properties: [ItemPropertyRecord]
init?(fromSyncItemDict dict: [String: Any?]) {
guard let item = try? ItemRecord(fromSyncDict: dict) else { return nil }
let properties = dict.filter { !ItemRecord.intrinsicProperties.contains($0.key) }
.compactMap { property in
ItemPropertyRecord(itemUID: item.uid, name: property.key, value: property.value)
}
self.item = item
self.properties = properties
}
}
This diff is collapsed.
This diff is collapsed.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment