From aa089be8e71c0e7c40e9fb52b0ac80fe619128e9 Mon Sep 17 00:00:00 2001 From: Dan Barela Date: Tue, 26 Jul 2022 15:10:38 -0600 Subject: [PATCH 1/4] timing logs --- MAGE.xcodeproj/project.pbxproj | 4 ++-- Mage/CoreData/Event.swift | 9 +++++++++ Mage/CoreData/Feed.swift | 15 +++++++++++++++ Mage/CoreData/Form.swift | 5 +++++ Mage/CoreData/Location.swift | 8 ++++++++ Mage/CoreData/Observation.swift | 6 ++++++ Mage/CoreData/Role.swift | 10 ++++++++-- Mage/CoreData/User.swift | 24 ++++++++++++++++++++++++ sdk/MageServer.swift | 3 +++ 9 files changed, 80 insertions(+), 4 deletions(-) diff --git a/MAGE.xcodeproj/project.pbxproj b/MAGE.xcodeproj/project.pbxproj index b895ce5b..3f14f185 100644 --- a/MAGE.xcodeproj/project.pbxproj +++ b/MAGE.xcodeproj/project.pbxproj @@ -3804,7 +3804,7 @@ CLANG_CXX_LIBRARY = "compiler-default"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = MAGE/MAGE.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ZL8G5D9G2H; @@ -3838,7 +3838,7 @@ CLANG_CXX_LIBRARY = "compiler-default"; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = MAGE/MAGE.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ZL8G5D9G2H; diff --git a/Mage/CoreData/Event.swift b/Mage/CoreData/Event.swift index b0f0fdd0..fe5d16e5 100644 --- a/Mage/CoreData/Event.swift +++ b/Mage/CoreData/Event.swift @@ -17,7 +17,14 @@ import CoreData } let url = "\(baseURL.absoluteURL)/api/events"; let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Events @ \(methodStart)") + let task = manager?.get_TASK(url, parameters: nil, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Events. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") + + let saveStart = Date() + NSLog("TIMING Saving Events @ \(saveStart)") MagicalRecord.save { localContext in let localUser = User.fetchCurrentUser(context: localContext); var eventsReturned: [NSNumber] = [] @@ -48,6 +55,8 @@ import CoreData } Event.mr_deleteAll(matching: NSPredicate(format: "NOT (\(EventKey.remoteId.key) IN %@)", eventsReturned), in: localContext); } completion: { contextDidSave, error in + NSLog("TIMING Saved Events. Elapsed: \(saveStart.timeIntervalSinceNow) seconds") + NotificationCenter.default.post(name: .MAGEEventsFetched, object:nil) if let error = error { diff --git a/Mage/CoreData/Feed.swift b/Mage/CoreData/Feed.swift index e861ceb2..886270ad 100644 --- a/Mage/CoreData/Feed.swift +++ b/Mage/CoreData/Feed.swift @@ -94,8 +94,14 @@ import CoreData var feedRemoteIds: [String] = []; let url = "\(baseURL.absoluteURL)/api/events/\(eventId)/feeds"; let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Feeds @ \(methodStart)") let task = manager?.get_TASK(url, parameters: nil, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Feeds. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") + + let saveStart = Date() + NSLog("TIMING Saving Feeds @ \(saveStart)") MagicalRecord.save({ localContext in if let feedsJson = responseObject as? [[AnyHashable : Any]] { feedRemoteIds = Feed.populateFeeds(feeds: feedsJson, eventId: eventId, context: localContext); @@ -105,6 +111,8 @@ import CoreData Feed.mr_deleteAll(matching: NSPredicate(format: "(NOT (\(FeedKey.remoteId.key) IN %@)) AND \(FeedKey.eventId.key) == %@", feedRemoteIds, eventId), in: localContext) } }, completion: { contextDidSave, error in + NSLog("TIMING Saved Feeds. Elapsed: \(saveStart.timeIntervalSinceNow) seconds") + if let error = error { if let failure = failure { failure(error); @@ -127,12 +135,19 @@ import CoreData } let url = "\(baseURL.absoluteURL)/api/events/\(eventId)/feeds/\(feedId)/content"; let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Feed Items /api/events/\(eventId)/feeds/\(feedId)/content @ \(methodStart)") let task = manager?.post_TASK(url, parameters: nil, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Feed Items /api/events/\(eventId)/feeds/\(feedId)/content. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") + + let saveStart = Date() + NSLog("TIMING Saving Feed Items /api/events/\(eventId)/feeds/\(feedId)/content @ \(saveStart)") MagicalRecord.save { localContext in if let json = responseObject as? [AnyHashable : Any], let items = json[FeedKey.items.key] as? [AnyHashable : Any], let features = items[FeedKey.features.key] as? [[AnyHashable : Any]] { Feed.populateFeedItems(feedItems: features, feedId: feedId, eventId: eventId, context: localContext); } } completion: { contextDidSave, error in + NSLog("TIMING Saved Feed Items. Elapsed: \(saveStart.timeIntervalSinceNow) seconds") if let error = error { if let failure = failure { failure(task, error); diff --git a/Mage/CoreData/Form.swift b/Mage/CoreData/Form.swift index 992cf503..0124d873 100644 --- a/Mage/CoreData/Form.swift +++ b/Mage/CoreData/Form.swift @@ -153,12 +153,17 @@ import CoreData let folderToUnzipTo = "\(getDocumentsDirectory())/events/icons-\(eventId)" do { + let methodStart = Date() + NSLog("TIMING Fetching Form for event \(eventId) @ \(methodStart)") + guard let request = try manager?.requestSerializer.request(withMethod: "GET", urlString: url, parameters: nil) else { return nil; } let task = manager?.downloadTask(with: request as URLRequest, progress: nil, destination: { targetPath, response in return URL(fileURLWithPath: stringPath); }, completionHandler: { response, filePath, error in + NSLog("TIMING Fetched Form for event \(eventId). Elapsed: \(methodStart.timeIntervalSinceNow) seconds") + if let error = error { NSLog("Error pulling icons and form \(error)") failure?(error); diff --git a/Mage/CoreData/Location.swift b/Mage/CoreData/Location.swift index 3f2e0fd5..b88c1fcd 100644 --- a/Mage/CoreData/Location.swift +++ b/Mage/CoreData/Location.swift @@ -115,7 +115,10 @@ import MagicalRecord parameters["startDate"] = ISO8601DateFormatter.string(from: lastLocationDate, timeZone: TimeZone(secondsFromGMT: 0)!, formatOptions: [.withDashSeparatorInDate, .withFullDate, .withFractionalSeconds, .withTime, .withColonSeparatorInTime, .withTimeZone]) } let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Locations /api/events/\(currentEventId)/locations/users @ \(methodStart)") let task = manager?.get_TASK(url, parameters: parameters, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Locations /api/events/\(currentEventId)/locations/users. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") guard let allUserLocations = responseObject as? [[AnyHashable : Any]] else { success?(task, nil); return; @@ -126,6 +129,9 @@ import MagicalRecord success?(task, responseObject) return; } + + let saveStart = Date() + NSLog("TIMING Saving Locations /api/events/\(currentEventId)/locations/users @ \(saveStart)") MagicalRecord.save { localContext in let currentUser = User.fetchCurrentUser(context: localContext); @@ -213,6 +219,8 @@ import MagicalRecord User.operationToFetchUsers(success: nil, failure: nil); } } completion: { contextDidSave, error in + NSLog("TIMING Saved Locations /api/events/\(currentEventId)/locations/users. Elapsed: \(saveStart.timeIntervalSinceNow) seconds") + if let error = error { failure?(task, error); } else if let success = success { diff --git a/Mage/CoreData/Observation.swift b/Mage/CoreData/Observation.swift index d5143834..4a489a66 100644 --- a/Mage/CoreData/Observation.swift +++ b/Mage/CoreData/Observation.swift @@ -155,7 +155,10 @@ enum State: Int, CustomStringConvertible { } let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Observations for event \(currentEventId) @ \(methodStart)") let task = manager?.get_TASK(url, parameters: parameters, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Observations for event \(currentEventId). Elapsed: \(methodStart.timeIntervalSinceNow) seconds") guard let features = responseObject as? [[AnyHashable : Any]] else { success?(task, nil); return; @@ -167,6 +170,8 @@ enum State: Int, CustomStringConvertible { return; } + let saveStart = Date() + NSLog("TIMING Saving Observations for event \(currentEventId) @ \(saveStart)") let rootSavingContext = NSManagedObjectContext.mr_rootSaving(); let localContext = NSManagedObjectContext.mr_context(withParent: rootSavingContext); localContext.perform { @@ -218,6 +223,7 @@ enum State: Int, CustomStringConvertible { NotificationRequester.observationPulled(observationToNotifyAbout); } + NSLog("TIMING Saved Observations for event \(currentEventId). Elapsed: \(saveStart.timeIntervalSinceNow) seconds") DispatchQueue.main.async { success?(task, responseObject); } diff --git a/Mage/CoreData/Role.swift b/Mage/CoreData/Role.swift index 55656380..1614e6cf 100644 --- a/Mage/CoreData/Role.swift +++ b/Mage/CoreData/Role.swift @@ -29,7 +29,10 @@ import CoreData } let url = "\(baseURL.absoluteURL)/api/roles"; let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Roles @ \(methodStart)") let task = manager?.get_TASK(url, parameters: nil, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Roles. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") if let responseData = responseObject as? Data { if responseData.count == 0 { print("Roles are empty"); @@ -42,9 +45,10 @@ import CoreData success?(task, nil); return; } - + let saveStart = Date() + NSLog("TIMING Saving Roles @ \(saveStart)") MagicalRecord.save { localContext in - + // Get the role ids to query var roleIds: [String] = []; for roleJson in roles { @@ -78,6 +82,8 @@ import CoreData } } } completion: { contextDidSave, error in + NSLog("TIMING inserted roles. Elapsed: \(saveStart.timeIntervalSinceNow) seconds") + if let error = error { if let failure = failure { failure(task, error); diff --git a/Mage/CoreData/User.swift b/Mage/CoreData/User.swift index 3c6a7b13..240c838a 100644 --- a/Mage/CoreData/User.swift +++ b/Mage/CoreData/User.swift @@ -85,7 +85,13 @@ import Kingfisher let url = "\(baseURL.absoluteURL)/api/users/myself"; let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Myself @ \(methodStart)") let task = manager?.get_TASK(url, parameters: nil, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Myself. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") + + let saveStart = Date() + NSLog("TIMING Saving Myself @ \(saveStart)") MagicalRecord.save { localContext in guard let myself = responseObject as? [AnyHashable : Any], let userId = myself["id"] as? String else { return; @@ -97,6 +103,8 @@ import Kingfisher } } completion: { contextDidSave, error in + NSLog("TIMING Saved Myself. Elapsed: \(saveStart.timeIntervalSinceNow) seconds") + if let error = error { if let failure = failure { failure(task, error); @@ -120,7 +128,13 @@ import Kingfisher } let url = "\(baseURL.absoluteURL)/api/users/\(userId)"; let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching User /api/users/\(userId) @ \(methodStart)") let task = manager?.get_TASK(url, parameters: nil, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched User /api/users/\(userId) . Elapsed: \(methodStart.timeIntervalSinceNow) seconds") + + let saveStart = Date() + NSLog("TIMING Saving User /api/users/\(userId) @ \(saveStart)") if let responseData = responseObject as? Data { if responseData.count == 0 { print("Users are empty"); @@ -148,6 +162,8 @@ import Kingfisher } } } completion: { contextDidSave, error in + NSLog("TIMING Saved User /api/users/\(userId). Elapsed: \(saveStart.timeIntervalSinceNow) seconds") + if let error = error { if let failure = failure { failure(task, error); @@ -171,7 +187,13 @@ import Kingfisher } let url = "\(baseURL.absoluteURL)/api/users"; let manager = MageSessionManager.shared(); + let methodStart = Date() + NSLog("TIMING Fetching Users @ \(methodStart)") let task = manager?.get_TASK(url, parameters: nil, progress: nil, success: { task, responseObject in + NSLog("TIMING Fetched Users. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") + + let saveStart = Date() + NSLog("TIMING Saving Users @ \(saveStart)") if let responseData = responseObject as? Data { if responseData.count == 0 { print("Users are empty"); @@ -228,6 +250,8 @@ import Kingfisher } } } completion: { contextDidSave, error in + NSLog("TIMING Saved Users. Elapsed: \(saveStart.timeIntervalSinceNow) seconds") + if let error = error { if let failure = failure { failure(task, error); diff --git a/sdk/MageServer.swift b/sdk/MageServer.swift index 0e71c20c..cbbb65ee 100644 --- a/sdk/MageServer.swift +++ b/sdk/MageServer.swift @@ -137,7 +137,10 @@ import Foundation let manager = MageSessionManager.shared() let apiURL = "\(url.absoluteString)/api" + let methodStart = Date() + NSLog("TIMING API @ \(methodStart)") let task = manager?.get_TASK(apiURL, parameters: nil, progress: nil, success: { task, response in + NSLog("TIMING Fetched API. Elapsed: \(methodStart.timeIntervalSinceNow) seconds") if let dataResponse = response as? Data { if dataResponse.count == 0 { failure?(NSError(domain: NSURLErrorDomain, code: NSURLErrorBadURL, userInfo: [NSLocalizedDescriptionKey: "Empty API response received from server."])) From 381d9a9d787bd4c1788af22972b05528f92e7459 Mon Sep 17 00:00:00 2001 From: Dan Barela Date: Tue, 26 Jul 2022 15:37:22 -0600 Subject: [PATCH 2/4] so much logging --- Mage/CoreData/Observation.swift | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Mage/CoreData/Observation.swift b/Mage/CoreData/Observation.swift index 4a489a66..aa39872c 100644 --- a/Mage/CoreData/Observation.swift +++ b/Mage/CoreData/Observation.swift @@ -175,17 +175,21 @@ enum State: Int, CustomStringConvertible { let rootSavingContext = NSManagedObjectContext.mr_rootSaving(); let localContext = NSManagedObjectContext.mr_context(withParent: rootSavingContext); localContext.perform { + NSLog("TIMING There are \(features.count) features to save, chunking into groups of 250") localContext.mr_setWorkingName(#function) var chunks = features.chunked(into: 250); var newObservationCount = 0; var observationToNotifyAbout: Observation?; + NSLog("TIMING we have \(chunks.count) groups to save") while (chunks.count > 0) { autoreleasepool { guard let features = chunks.last else { return; } chunks.removeLast(); - + let createObservationsDate = Date() + NSLog("TIMING creating \(features.count) observations for chunk \(chunks.count)") + for observation in features { if let newObservation = Observation.create(feature: observation, context: localContext) { newObservationCount = newObservationCount + 1; @@ -194,25 +198,34 @@ enum State: Int, CustomStringConvertible { } } } - print("Saved \(features.count) observations") + NSLog("TIMING created \(features.count) observations for chunk \(chunks.count) Elapsed: \(createObservationsDate.timeIntervalSinceNow) seconds") } // only save once per chunk + let localSaveDate = Date() do { + NSLog("TIMING saving \(features.count) observations on local context") try localContext.save() } catch { print("Error saving observations: \(error)") } + NSLog("TIMING saved \(features.count) observations on local context. Elapsed \(localSaveDate.timeIntervalSinceNow) seconds") rootSavingContext.perform { + let rootSaveDate = Date() + do { + NSLog("TIMING saving \(features.count) observations on root context") try rootSavingContext.save() } catch { print("Error saving observations: \(error)") } + NSLog("TIMING saved \(features.count) observations on root context. Elapsed \(rootSaveDate.timeIntervalSinceNow) seconds") + } localContext.reset(); + NSLog("TIMING reset the local context for chunk \(chunks.count)") NSLog("Saved chunk \(chunks.count)") } From dcb9d4d70c5bfe31513e92d126875f60fc516bf3 Mon Sep 17 00:00:00 2001 From: Dan Barela Date: Wed, 27 Jul 2022 12:08:19 -0600 Subject: [PATCH 3/4] Proposed fix for initial observation saves --- Mage/CoreData/Form.swift | 6 ++++ Mage/CoreData/Observation.swift | 52 +++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/Mage/CoreData/Form.swift b/Mage/CoreData/Form.swift index 0124d873..3ba1fbab 100644 --- a/Mage/CoreData/Form.swift +++ b/Mage/CoreData/Form.swift @@ -136,6 +136,12 @@ import CoreData return nil } + static func getFieldByNameFromJSONFields(json: [[String: AnyHashable]], name: String) -> [String: AnyHashable]? { + return json.first { field in + field[FieldKey.name.key] as? String == name + } + } + static func getDocumentsDirectory() -> String { let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) let documentsDirectory = paths[0] diff --git a/Mage/CoreData/Observation.swift b/Mage/CoreData/Observation.swift index aa39872c..ad56ae88 100644 --- a/Mage/CoreData/Observation.swift +++ b/Mage/CoreData/Observation.swift @@ -177,9 +177,19 @@ enum State: Int, CustomStringConvertible { localContext.perform { NSLog("TIMING There are \(features.count) features to save, chunking into groups of 250") localContext.mr_setWorkingName(#function) + var chunks = features.chunked(into: 250); var newObservationCount = 0; var observationToNotifyAbout: Observation?; + var eventFormDictionary: [NSNumber: [[String: AnyHashable]]] = [:] + if let event = Event.getEvent(eventId: currentEventId, context: localContext), let eventForms = event.forms { + for eventForm in eventForms { + if let formId = eventForm.formId, let json = eventForm.json?.json { + eventFormDictionary[formId] = json[FormKey.fields.key] as? [[String: AnyHashable]] + } + } + } + localContext.reset(); NSLog("TIMING we have \(chunks.count) groups to save") while (chunks.count > 0) { autoreleasepool { @@ -191,7 +201,7 @@ enum State: Int, CustomStringConvertible { NSLog("TIMING creating \(features.count) observations for chunk \(chunks.count)") for observation in features { - if let newObservation = Observation.create(feature: observation, context: localContext) { + if let newObservation = Observation.create(feature: observation, eventForms: eventFormDictionary, context: localContext) { newObservationCount = newObservationCount + 1; if (!initial) { observationToNotifyAbout = newObservation; @@ -569,12 +579,14 @@ enum State: Int, CustomStringConvertible { } @discardableResult - @objc public static func create(feature: [AnyHashable : Any], context:NSManagedObjectContext) -> Observation? { + @objc public static func create(feature: [AnyHashable : Any], eventForms: [NSNumber: [[String: AnyHashable]]]? = nil, context:NSManagedObjectContext) -> Observation? { var newObservation: Observation? = nil; let remoteId = Observation.idFromJson(json: feature); let state = Observation.stateFromJson(json: feature); +// NSLog("TIMING create the observation \(remoteId)") + if let remoteId = remoteId, let existingObservation = Observation.mr_findFirst(byAttribute: ObservationKey.remoteId.key, withValue: remoteId, in: context) { // if the observation is archived, delete it if state == .Archive { @@ -593,7 +605,7 @@ enum State: Int, CustomStringConvertible { } } - existingObservation.populate(json: feature); + existingObservation.populate(json: feature, eventForms: eventForms); if let userId = existingObservation.userId { if let user = User.mr_findFirst(byAttribute: ObservationKey.remoteId.key, withValue: userId, in: context) { existingObservation.user = user @@ -681,7 +693,7 @@ enum State: Int, CustomStringConvertible { if state != .Archive { // if the observation doesn't exist, insert it if let observation = Observation.mr_createEntity(in: context) { - observation.populate(json: feature); + observation.populate(json: feature, eventForms: eventForms); if let userId = observation.userId { if let user = User.mr_findFirst(byAttribute: UserKey.remoteId.key, withValue: userId, in: context) { observation.user = user @@ -771,7 +783,7 @@ enum State: Int, CustomStringConvertible { } @discardableResult - @objc public func populate(json: [AnyHashable : Any]) -> Observation { + @objc public func populate(json: [AnyHashable : Any], eventForms: [NSNumber: [[String: AnyHashable]]]? = nil) -> Observation { self.eventId = json[ObservationKey.eventId.key] as? NSNumber self.remoteId = Observation.idFromJson(json: json); self.userId = json[ObservationKey.userId.key] as? String @@ -779,7 +791,7 @@ enum State: Int, CustomStringConvertible { self.dirty = false if let properties = json[ObservationKey.properties.key] as? [String : Any] { - self.properties = self.generateProperties(propertyJson: properties); + self.properties = self.generateProperties(propertyJson: properties, eventForms: eventForms); } if let lastModified = json[ObservationKey.lastModified.key] as? String { @@ -804,7 +816,7 @@ enum State: Int, CustomStringConvertible { return self; } - func generateProperties(propertyJson: [String : Any]) -> [AnyHashable : Any] { + func generateProperties(propertyJson: [String : Any], eventForms: [NSNumber: [[String: AnyHashable]]]? = nil) -> [AnyHashable : Any] { var parsedProperties: [String : Any] = [:] if self.event == nil { @@ -817,19 +829,29 @@ enum State: Int, CustomStringConvertible { if let formsProperties = value as? [[String : Any]] { for formProperties in formsProperties { var parsedFormProperties:[String:Any] = formProperties; - if let formId = formProperties[EventKey.formId.key] as? NSNumber, let managedObjectContext = managedObjectContext, let form : Form = Form.mr_findFirst(byAttribute: "formId", withValue: formId, in: managedObjectContext) { - for (formKey, value) in formProperties { - if let field = form.getFieldByName(name: formKey) { - if let type = field[FieldKey.type.key] as? String, type == FieldType.geometry.key { - if let value = value as? [String: Any] { - let geometry = GeometryDeserializer.parseGeometry(json: value) - parsedFormProperties[formKey] = geometry; + + if let formId = formProperties[EventKey.formId.key] as? NSNumber { + var formFields: [[String: AnyHashable]]? = nil + if let eventForms = eventForms { + formFields = eventForms[formId] + } else if let managedObjectContext = managedObjectContext, let fetchedForm : Form = Form.mr_findFirst(byAttribute: "formId", withValue: formId, in: managedObjectContext) { + formFields = fetchedForm.json?.json?[FormKey.fields.key] as? [[String: AnyHashable]] + } + + if let formFields = formFields { + for (formKey, value) in formProperties { + if let field = Form.getFieldByNameFromJSONFields(json: formFields, name: formKey) { + if let type = field[FieldKey.type.key] as? String, type == FieldType.geometry.key { + if let value = value as? [String: Any] { + let geometry = GeometryDeserializer.parseGeometry(json: value) + parsedFormProperties[formKey] = geometry; + } } } } } + forms.append(parsedFormProperties); } - forms.append(parsedFormProperties); } } parsedProperties[ObservationKey.forms.key] = forms; From 6aaf23b422bc277f7231bd10c2882a3537184617 Mon Sep 17 00:00:00 2001 From: Dan Barela Date: Wed, 27 Jul 2022 14:53:37 -0600 Subject: [PATCH 4/4] changelog with speed improvement for observation fetch --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88f65bc0..faac9dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Adheres to [Semantic Versioning](http://semver.org/). * Tapping a local attachment does not show the attachment * Distance is not being calculated correctly when navigation lines are updated * Add more informative message when an attachment fails to open + * Significantly improved the speed of the initial observation fetch ## 4.0.1