From f6691efc93b991432b9bcbafca39e2ee6350e13f Mon Sep 17 00:00:00 2001 From: Tim Schmelter Date: Mon, 4 Mar 2019 17:07:49 -0800 Subject: [PATCH] Fixed incorrect AWSCore dependency version in podspec (#191) - Added logging helpers, added logging around mutation queue actions - AppSyncLog's `message` argument is now an autoclosure - Removed unused DispatchGroup from send operation - Removed unused context arg to send --- AWSAppSync.podspec | 2 +- AWSAppSyncClient.xcodeproj/project.pbxproj | 4 ++ AWSAppSyncClient/AWSAppSyncClient.swift | 6 +- ...ncClientConflictResolutionExtensions.swift | 14 ++-- .../AWSAppSyncClientLogFormatter.swift | 47 ++++++++++++++ .../AWSAppSyncClientS3ObjectsExtensions.swift | 4 -- .../Apollo/Sources/Apollo/Record.swift | 2 +- .../AWSAppSyncSubscriptionMetadataCache.swift | 1 + .../Internal/AWSMutationCache.swift | 64 ++++++++++--------- .../Internal/AWSOfflineMutation.swift | 8 +-- .../AWSPerformMutationOperation.swift | 11 ++-- .../Internal/AWSPerformMutationQueue.swift | 27 ++++++-- .../AWSPerformOfflineMutationOperation.swift | 6 +- .../Internal/AWSSQLiteNormalizedCache.swift | 2 + AWSAppSyncClient/Internal/AppSyncLogHelper.h | 12 ++-- AWSAppSyncClient/Internal/AppSyncLogHelper.m | 60 ++--------------- .../Internal/AppSyncLogWrapper.swift | 31 ++++++--- CHANGELOG.md | 23 +++++++ 18 files changed, 196 insertions(+), 128 deletions(-) create mode 100644 AWSAppSyncClient/AWSAppSyncClientLogFormatter.swift diff --git a/AWSAppSync.podspec b/AWSAppSync.podspec index e6ba6fcc..5f0725bb 100644 --- a/AWSAppSync.podspec +++ b/AWSAppSync.podspec @@ -10,7 +10,7 @@ Pod::Spec.new do |s| s.requires_arc = true s.ios.deployment_target = '9.0' s.swift_version = '4.2' - s.dependency 'AWSCore', '~> 2.8.0' + s.dependency 'AWSCore', '~> 2.9.0' s.dependency 'SQLite.swift', '0.11.5' s.dependency 'ReachabilitySwift', '~> 4.3.0' s.source_files = 'AWSAppSyncClient/AWSAppSync.h', 'AWSAppSyncClient/*.swift', 'AWSAppSyncClient/Internal/*.swift', 'AWSAppSyncClient/Apollo/Sources/Apollo/*.swift', 'AWSAppSyncClient/MQTTSDK/*.{h,m}', 'AWSAppSyncClient/MQTTSDK/MQTTSDK/*.{h,m}', 'AWSAppSyncClient/MQTTSDK/SocketRocket/*.{h,m}', 'AWSAppSyncClient/Internal/*.{h,m}' diff --git a/AWSAppSyncClient.xcodeproj/project.pbxproj b/AWSAppSyncClient.xcodeproj/project.pbxproj index 26125ad1..3a952a13 100644 --- a/AWSAppSyncClient.xcodeproj/project.pbxproj +++ b/AWSAppSyncClient.xcodeproj/project.pbxproj @@ -126,6 +126,7 @@ FA1A620C21E6533A00AA54D0 /* AWSAppSyncRetryHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA1A620B21E6533A00AA54D0 /* AWSAppSyncRetryHandlerTests.swift */; }; FA2B4598221DDF2C00F68E6C /* CachePersistenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA2B4597221DDF2C00F68E6C /* CachePersistenceTests.swift */; }; FA2B459A221F436400F68E6C /* MutationQueuePerformanceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA2B4599221F436400F68E6C /* MutationQueuePerformanceTests.swift */; }; + FA2BE607222746EC0036FCD9 /* AWSAppSyncClientLogFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA2BE606222746EC0036FCD9 /* AWSAppSyncClientLogFormatter.swift */; }; FA38636E21DD8FF200DBA2BC /* MutationQueueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA38636D21DD8FF200DBA2BC /* MutationQueueTests.swift */; }; FA3E128121D5259800F2D19A /* AWSAppSyncClientConfigurationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3E128021D5259800F2D19A /* AWSAppSyncClientConfigurationError.swift */; }; FA3E128321D5290900F2D19A /* AWSAppSyncClientInfoError.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA3E128221D5290900F2D19A /* AWSAppSyncClientInfoError.swift */; }; @@ -446,6 +447,7 @@ FA1A620B21E6533A00AA54D0 /* AWSAppSyncRetryHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncRetryHandlerTests.swift; sourceTree = ""; }; FA2B4597221DDF2C00F68E6C /* CachePersistenceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CachePersistenceTests.swift; sourceTree = ""; }; FA2B4599221F436400F68E6C /* MutationQueuePerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutationQueuePerformanceTests.swift; sourceTree = ""; }; + FA2BE606222746EC0036FCD9 /* AWSAppSyncClientLogFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncClientLogFormatter.swift; sourceTree = ""; }; FA30F363219B353800B92938 /* SubscriptionStressTestHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionStressTestHelper.swift; sourceTree = ""; }; FA38636D21DD8FF200DBA2BC /* MutationQueueTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutationQueueTests.swift; sourceTree = ""; }; FA3E128021D5259800F2D19A /* AWSAppSyncClientConfigurationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncClientConfigurationError.swift; sourceTree = ""; }; @@ -767,6 +769,7 @@ CCEF79DC21DE7EED004AD64D /* AWSAppSyncClientError.swift */, FABD707222050FD100C99B47 /* AWSAppSyncClientInfo.swift */, FA3E128221D5290900F2D19A /* AWSAppSyncClientInfoError.swift */, + FA2BE606222746EC0036FCD9 /* AWSAppSyncClientLogFormatter.swift */, 178B31051FCDB34100EA4619 /* AWSAppSyncClientS3ObjectsExtensions.swift */, FA88834421E3C2D300DEBCB3 /* AWSAppSyncConnection.swift */, 17915B041F5F106600C4B73C /* AWSAppSyncHTTPNetworkTransport.swift */, @@ -1527,6 +1530,7 @@ 17E009C01FCAB234005031DB /* InMemoryNormalizedCache.swift in Sources */, CC96F8AB21BAF68300446EBD /* AWSRequestBuilder.swift in Sources */, 17E009CA1FCAB234005031DB /* GraphQLExecutor.swift in Sources */, + FA2BE607222746EC0036FCD9 /* AWSAppSyncClientLogFormatter.swift in Sources */, 17E009DC1FCAB234005031DB /* ApolloClient.swift in Sources */, 17E009CE1FCAB234005031DB /* GraphQLError.swift in Sources */, 17D2C2071F6F44A3006C6818 /* AWSOfflineMutation.swift in Sources */, diff --git a/AWSAppSyncClient/AWSAppSyncClient.swift b/AWSAppSyncClient/AWSAppSyncClient.swift index 457125c7..da2674a4 100644 --- a/AWSAppSyncClient/AWSAppSyncClient.swift +++ b/AWSAppSyncClient/AWSAppSyncClient.swift @@ -71,6 +71,9 @@ public class AWSAppSyncClient { init(appSyncConfig: AWSAppSyncClientConfiguration, reachabilityFactory: NetworkReachabilityProvidingFactory.Type? = nil) throws { + + AppSyncLog.info("Initializing AppSyncClient") + self.autoSubmitOfflineMutations = appSyncConfig.autoSubmitOfflineMutations self.store = appSyncConfig.store self.appSyncMQTTClient.allowCellularAccess = appSyncConfig.allowsCellularAccess @@ -102,6 +105,7 @@ public class AWSAppSyncClient { } deinit { + AppSyncLog.info("Releasing AppSyncClient") NetworkReachabilityNotifier.clearShared() } @@ -132,7 +136,7 @@ public class AWSAppSyncClient { /// - error: An error that indicates why the fetch failed, or `nil` if the fetch was succesful. /// - Returns: An object that can be used to cancel an in progress fetch. @discardableResult public func fetch(query: Query, cachePolicy: CachePolicy = .returnCacheDataElseFetch, queue: DispatchQueue = DispatchQueue.main, resultHandler: OperationResultHandler? = nil) -> Cancellable { - AppSyncLog.verbose("fetch: \(query)") + AppSyncLog.verbose("Fetching: \(query)") return apolloClient!.fetch(query: query, cachePolicy: cachePolicy, queue: queue, resultHandler: resultHandler) } diff --git a/AWSAppSyncClient/AWSAppSyncClientConflictResolutionExtensions.swift b/AWSAppSyncClient/AWSAppSyncClientConflictResolutionExtensions.swift index a75ed966..bb731d82 100644 --- a/AWSAppSyncClient/AWSAppSyncClientConflictResolutionExtensions.swift +++ b/AWSAppSyncClient/AWSAppSyncClientConflictResolutionExtensions.swift @@ -25,14 +25,13 @@ extension AWSAppSyncClient { func send( operation: Operation, - context: UnsafeMutableRawPointer?, conflictResolutionBlock: MutationConflictHandler?, - dispatchGroup: DispatchGroup?, handlerQueue: DispatchQueue, resultHandler: OperationResultHandler?) -> Cancellable { + AppSyncLog.verbose("Sending operation \(operation)") + func notifyResultHandler(result: GraphQLResult?, error: Error?) { - dispatchGroup?.leave() guard let resultHandler = resultHandler else { return } handlerQueue.async { @@ -40,7 +39,7 @@ extension AWSAppSyncClient { } } - return self.httpTransport!.send(operation: operation) { (response, error) in + return httpTransport!.send(operation: operation) { (response, error) in guard let response = response else { notifyResultHandler(result: nil, error: error) return @@ -61,7 +60,10 @@ extension AWSAppSyncClient { conflictResolutionBlock(serverState, taskCompletionSource, nil) taskCompletionSource.task.continueWith(block: { (task) -> Any? in if let mutation = task.result { - _ = self.send(operation: mutation, context: nil, conflictResolutionBlock: nil, dispatchGroup: dispatchGroup, handlerQueue: handlerQueue, resultHandler: resultHandler) + _ = self.send(operation: mutation, + conflictResolutionBlock: nil, + handlerQueue: handlerQueue, + resultHandler: resultHandler) } return nil }).waitUntilFinished() @@ -70,7 +72,7 @@ extension AWSAppSyncClient { notifyResultHandler(result: result, error: nil) if let records = records { - self.store?.publish(records: records, context: context).catch { error in + self.store?.publish(records: records).catch { error in preconditionFailure(String(describing: error)) } } diff --git a/AWSAppSyncClient/AWSAppSyncClientLogFormatter.swift b/AWSAppSyncClient/AWSAppSyncClientLogFormatter.swift new file mode 100644 index 00000000..72180eb0 --- /dev/null +++ b/AWSAppSyncClient/AWSAppSyncClientLogFormatter.swift @@ -0,0 +1,47 @@ +// +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Licensed under the Amazon Software License +// http://aws.amazon.com/asl/ +// + +import Foundation + +public final class AWSAppSyncClientLogFormatter: NSObject, AWSDDLogFormatter { + + static let dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + // 2019-02-27 15:09:34.624-0800 + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSZ" + return dateFormatter + }() + + public func format(message logMessage: AWSDDLogMessage) -> String? { + let logLevelPrefix: String + switch logMessage.flag { + case .error: + logLevelPrefix = "E" + case .warning: + logLevelPrefix = "W" + case .info: + logLevelPrefix = "I" + case .debug: + logLevelPrefix = "D" + default: + logLevelPrefix = "V" + } + + let date = AWSAppSyncClientLogFormatter.dateFormatter.string(from: logMessage.timestamp) + let file = AWSDDExtractFileNameWithoutExtension(logMessage.file, false) ?? "(no file)" + let line = String(describing: logMessage.line) + + var sourceSection = file + if let function = logMessage.function { + sourceSection += ".\(function)" + } + sourceSection += ", L\(line)" + + let message = "\(date) [\(logLevelPrefix) \(sourceSection)] \(logMessage.message)" + return message + } + +} diff --git a/AWSAppSyncClient/AWSAppSyncClientS3ObjectsExtensions.swift b/AWSAppSyncClient/AWSAppSyncClientS3ObjectsExtensions.swift index d54b7496..5e46e9ac 100644 --- a/AWSAppSyncClient/AWSAppSyncClientS3ObjectsExtensions.swift +++ b/AWSAppSyncClient/AWSAppSyncClientS3ObjectsExtensions.swift @@ -11,7 +11,6 @@ extension AWSAppSyncClient { operation: Operation, s3Object: InternalS3ObjectDetails, conflictResolutionBlock: MutationConflictHandler?, - dispatchGroup: DispatchGroup?, handlerQueue: DispatchQueue, resultHandler: OperationResultHandler?) { @@ -19,9 +18,7 @@ extension AWSAppSyncClient { if success { _ = self.send( operation: operation, - context: nil, conflictResolutionBlock: conflictResolutionBlock, - dispatchGroup: dispatchGroup, handlerQueue: handlerQueue, resultHandler: resultHandler) } else { @@ -33,7 +30,6 @@ extension AWSAppSyncClient { func performMutationWithS3Object( data: Data, s3Object: InternalS3ObjectDetails, - dispatchGroup: DispatchGroup?, resultHandler: ((JSONObject?, Error?) -> Void)?) { s3ObjectManager!.upload(s3Object: s3Object) { success, error in diff --git a/AWSAppSyncClient/Apollo/Sources/Apollo/Record.swift b/AWSAppSyncClient/Apollo/Sources/Apollo/Record.swift index f7397180..e0355b82 100644 --- a/AWSAppSyncClient/Apollo/Sources/Apollo/Record.swift +++ b/AWSAppSyncClient/Apollo/Sources/Apollo/Record.swift @@ -13,7 +13,7 @@ public struct Record { self.key = key self.fields = fields } - + public subscript(key: CacheKey) -> Value? { get { return fields[key] diff --git a/AWSAppSyncClient/Internal/AWSAppSyncSubscriptionMetadataCache.swift b/AWSAppSyncClient/Internal/AWSAppSyncSubscriptionMetadataCache.swift index c9b342e6..d770d57e 100644 --- a/AWSAppSyncClient/Internal/AWSAppSyncSubscriptionMetadataCache.swift +++ b/AWSAppSyncClient/Internal/AWSAppSyncSubscriptionMetadataCache.swift @@ -15,6 +15,7 @@ final class AWSSubscriptionMetaDataCache { private let lastSyncDate = Expression("lastSyncDate") init(fileURL: URL) throws { + AppSyncLog.verbose("Initializing subscription metadata cache at \(fileURL.absoluteString)") db = try Connection(.uri(fileURL.absoluteString), readonly: false) db.busyTimeout = sqlBusyTimeoutConstant try createTableIfNeeded() diff --git a/AWSAppSyncClient/Internal/AWSMutationCache.swift b/AWSAppSyncClient/Internal/AWSMutationCache.swift index 3687275a..efac70f9 100644 --- a/AWSAppSyncClient/Internal/AWSMutationCache.swift +++ b/AWSAppSyncClient/Internal/AWSMutationCache.swift @@ -7,6 +7,8 @@ import Foundation import SQLite +/// Although this class is currently public, it is not intended to be used by clients, and will be marked "internal" in +/// a future release of AppSync. public final class AWSMutationCache { private let db: Connection @@ -22,9 +24,9 @@ public final class AWSMutationCache { private let s3LocalUri = Expression("s3LocalUri") private let s3MimeType = Expression("s3MimeType") private let operationString = Expression("operationString") - private let priority = Expression("priority") public init(fileURL: URL) throws { + AppSyncLog.verbose("Initializing mutation cache at \(fileURL.absoluteString)") db = try Connection(.uri(fileURL.absoluteString), readonly: false) db.busyTimeout = sqlBusyTimeoutConstant try createTableIfNeeded() @@ -45,50 +47,48 @@ public final class AWSMutationCache { table.column(operationString) }) - do { - try db.run(mutationRecords.addColumn(priority)) - } catch {} - try db.run(mutationRecords.createIndex(recordIdentifier, unique: true, ifNotExists: true)) } internal func saveMutationRecord(record: AWSAppSyncMutationRecord) -> Promise { return Promise { - if let s3Object = record.s3ObjectInput { - let insert = mutationRecords.insert( - recordIdentifier <- record.recordIdentitifer, - data <- record.data!, - recordState <- record.recordState.rawValue, - timestamp <- record.timestamp, - s3Bucket <- s3Object.bucket, - s3Key <- s3Object.key, - s3Region <- s3Object.region, - s3LocalUri <- s3Object.localUri, - s3MimeType <- s3Object.mimeType, - operationString <- record.operationString!) - try db.run(insert) - } else { - let insert = mutationRecords.insert( - recordIdentifier <- record.recordIdentitifer, - data <- record.data!, - recordState <- record.recordState.rawValue, - timestamp <- record.timestamp, - operationString <- record.operationString!) - try db.run(insert) - } + AppSyncLog.verbose("\(record.recordIdentifier): saving") + if let s3Object = record.s3ObjectInput { + let insert = mutationRecords.insert( + recordIdentifier <- record.recordIdentifier, + data <- record.data!, + recordState <- record.recordState.rawValue, + timestamp <- record.timestamp, + s3Bucket <- s3Object.bucket, + s3Key <- s3Object.key, + s3Region <- s3Object.region, + s3LocalUri <- s3Object.localUri, + s3MimeType <- s3Object.mimeType, + operationString <- record.operationString!) + try db.run(insert) + } else { + let insert = mutationRecords.insert( + recordIdentifier <- record.recordIdentifier, + data <- record.data!, + recordState <- record.recordState.rawValue, + timestamp <- record.timestamp, + operationString <- record.operationString!) + try db.run(insert) + } } } internal func updateMutationRecord(record: AWSAppSyncMutationRecord) -> Promise { return Promise { - let sqlRecord = mutationRecords.filter(recordIdentifier == record.recordIdentitifer) + AppSyncLog.verbose("\(record.recordIdentifier): updating state \(record.recordState.rawValue)") + let sqlRecord = mutationRecords.filter(recordIdentifier == record.recordIdentifier) try db.run(sqlRecord.update(recordState <- record.recordState.rawValue)) } } internal func deleteMutationRecord(withIdentifier identifier: String) -> Promise { return Promise { - AppSyncLog.verbose("Deleting \(identifier)") + AppSyncLog.verbose("\(identifier): deleting") let sqlRecord = mutationRecords.filter(recordIdentifier == identifier) try db.run(sqlRecord.delete()) } @@ -96,6 +96,7 @@ public final class AWSMutationCache { internal func getStoredMutationRecordsInQueue() -> Promise<[AWSAppSyncMutationRecord]> { return Promise { + AppSyncLog.info("Retrieving stored mutation records") let sqlRecords = mutationRecords.filter(recordState == MutationRecordState.inQueue.rawValue).order(timestamp.asc) var mutationRecordQueue: [AWSAppSyncMutationRecord] = [] for record in try db.prepare(sqlRecords) { @@ -123,15 +124,16 @@ public final class AWSMutationCache { localUri: localUri) } } catch { - AppSyncLog.error("\(#function) failed \(error)") + AppSyncLog.error("Failed to retrieve S3Object from mutation record \(mutationRecord.recordIdentifier): \(error)") } mutationRecordQueue.append(mutationRecord) } catch { - AppSyncLog.error("\(#function) failed \(error)") + AppSyncLog.error("Failed to retrieve stored mutation records: \(error)") } } + AppSyncLog.debug("Retrieved \(mutationRecordQueue.count) mutation records from store") return mutationRecordQueue } } diff --git a/AWSAppSyncClient/Internal/AWSOfflineMutation.swift b/AWSAppSyncClient/Internal/AWSOfflineMutation.swift index c7723134..470123d7 100644 --- a/AWSAppSyncClient/Internal/AWSOfflineMutation.swift +++ b/AWSAppSyncClient/Internal/AWSOfflineMutation.swift @@ -10,7 +10,7 @@ final class AWSAppSyncMutationRecord { var jsonRecord: JSONObject? var data: Data? var contentMap: GraphQLMap? - var recordIdentitifer: String + var recordIdentifier: String var recordState: MutationRecordState = .inQueue var timestamp: Date var type: MutationType @@ -21,7 +21,7 @@ final class AWSAppSyncMutationRecord { recordIdentifier: String = UUID().uuidString, timestamp: Date = Date(), type: MutationType = .graphQLMutation) { - self.recordIdentitifer = recordIdentifier + self.recordIdentifier = recordIdentifier self.timestamp = timestamp self.type = type } @@ -32,8 +32,8 @@ final class AWSAppSyncMutationRecord { extension AWSAppSyncMutationRecord: CustomStringConvertible { var description: String { - var desc: String = "<\(self):\(recordIdentitifer)" - desc.append("\tID: \(recordIdentitifer)") + var desc: String = "<\(self):\(recordIdentifier)" + desc.append("\tID: \(recordIdentifier)") desc.append("\ttimestamp: \(timestamp)") desc.append("\thasS3Object: \(s3ObjectInput != nil ? true : false)") desc.append(">") diff --git a/AWSAppSyncClient/Internal/AWSPerformMutationOperation.swift b/AWSAppSyncClient/Internal/AWSPerformMutationOperation.swift index 221ad5a3..4283f6b4 100644 --- a/AWSAppSyncClient/Internal/AWSPerformMutationOperation.swift +++ b/AWSAppSyncClient/Internal/AWSPerformMutationOperation.swift @@ -13,6 +13,8 @@ final class AWSPerformMutationOperation: Asynchronous private let mutationConflictHandler: MutationConflictHandler? private let mutationResultHandler: OperationResultHandler? + private var networkTask: Cancellable? + var identifier: String? var operationCompletionBlock: ((AWSPerformMutationOperation, Error?) -> Void)? @@ -29,19 +31,22 @@ final class AWSPerformMutationOperation: Asynchronous self.mutationResultHandler = mutationResultHandler } - private var networkTask: Cancellable? + deinit { + AppSyncLog.verbose("\(identifier ?? "(no identifier)"): deinit") + } private func send(_ resultHandler: OperationResultHandler?) -> Cancellable? { guard let appSyncClient = appSyncClient else { return nil } + AppSyncLog.verbose("\(identifier ?? "(No identifier)"): sending") + if let s3Object = AWSRequestBuilder.s3Object(from: mutation.variables) { appSyncClient.performMutationWithS3Object( operation: mutation, s3Object: s3Object, conflictResolutionBlock: mutationConflictHandler, - dispatchGroup: nil, handlerQueue: handlerQueue, resultHandler: resultHandler) @@ -49,9 +54,7 @@ final class AWSPerformMutationOperation: Asynchronous } else { return appSyncClient.send( operation: mutation, - context: nil, conflictResolutionBlock: mutationConflictHandler, - dispatchGroup: nil, handlerQueue: handlerQueue, resultHandler: resultHandler) } diff --git a/AWSAppSyncClient/Internal/AWSPerformMutationQueue.swift b/AWSAppSyncClient/Internal/AWSPerformMutationQueue.swift index 4ed207b3..e0baa3fb 100644 --- a/AWSAppSyncClient/Internal/AWSPerformMutationQueue.swift +++ b/AWSAppSyncClient/Internal/AWSPerformMutationQueue.swift @@ -12,6 +12,7 @@ final class AWSPerformMutationQueue { private weak var networkClient: AWSNetworkTransport? private let persistentCache: AWSMutationCache? + /// The OperationQueue onto which we enqueue mutations to perform private let operationQueue: OperationQueue init( @@ -20,12 +21,14 @@ final class AWSPerformMutationQueue { reachabiltyChangeNotifier: NetworkReachabilityNotifier?, cacheFileURL: URL? = nil) { + AppSyncLog.verbose("Initializing AWSPerformMutationQueue") + self.appSyncClient = appSyncClient self.networkClient = networkClient - self.operationQueue = OperationQueue() - self.operationQueue.name = "com.amazonaws.service.appsync.MutationQueue" - self.operationQueue.maxConcurrentOperationCount = 1 + operationQueue = OperationQueue() + operationQueue.name = "com.amazonaws.service.appsync.AWSPerformMutationQueue.operations" + operationQueue.maxConcurrentOperationCount = 1 if let cacheFileURL = cacheFileURL { do { @@ -73,7 +76,7 @@ final class AWSPerformMutationQueue { mutationConflictHandler: mutationConflictHandler, mutationResultHandler: mutationResultHandler) - operation.identifier = offlineMutation?.recordIdentitifer + operation.identifier = offlineMutation?.recordIdentifier operation.operationCompletionBlock = { [weak self] operation, error in guard let identifier = operation.identifier else { return } @@ -86,10 +89,12 @@ final class AWSPerformMutationQueue { } func suspend() { + AppSyncLog.verbose("Suspending OperationQueue") operationQueue.isSuspended = true } func resume() { + AppSyncLog.verbose("Resuming OperationQueue") operationQueue.isSuspended = false } @@ -105,6 +110,8 @@ final class AWSPerformMutationQueue { private func loadMutations() throws { do { + AppSyncLog.info("Loading offline mutations") + guard let mutations = try persistentCache?.getStoredMutationRecordsInQueue().await() else { return } @@ -117,14 +124,21 @@ final class AWSPerformMutationQueue { mutation: mutation) operation.operationCompletionBlock = { [weak self] operation, error in - let identifier = operation.mutation.recordIdentitifer + let identifier = operation.mutation.recordIdentifier self?.deleteOfflineMutation(withIdentifier: identifier) } operationQueue.addOperation(operation) + AppSyncLog.verbose("\(mutation.recordIdentifier) loaded") } } catch { - AppSyncLog.error("error retrieving offline mutation from storage: \(error)") + AppSyncLog.error("Error retrieving offline mutation from storage: \(error)") + } + + if AppSyncLogHelper.shouldLog(flag: .verbose) { + operationQueue.addOperation { + AppSyncLog.verbose("Finished processing offline mutations") + } } } @@ -161,6 +175,7 @@ final class AWSPerformMutationQueue { .deleteMutationRecord(withIdentifier: identifier) .catch { error in AppSyncLog.error("\(#function) failure: \(error)") } } + } // MARK: - NetworkConnectionNotification diff --git a/AWSAppSyncClient/Internal/AWSPerformOfflineMutationOperation.swift b/AWSAppSyncClient/Internal/AWSPerformOfflineMutationOperation.swift index 2b305e89..a4c96994 100644 --- a/AWSAppSyncClient/Internal/AWSPerformOfflineMutationOperation.swift +++ b/AWSAppSyncClient/Internal/AWSPerformOfflineMutationOperation.swift @@ -25,6 +25,10 @@ final class AWSPerformOfflineMutationOperation: AsynchronousOperation, Cancellab self.mutation = mutation } + deinit { + AppSyncLog.verbose("\(mutation.recordIdentifier): deinit") + } + private func send(_ mutation: AWSAppSyncMutationRecord, completion: @escaping ((JSONObject?, Error?) -> Void)) { guard @@ -72,7 +76,7 @@ final class AWSPerformOfflineMutationOperation: AsynchronousOperation, Cancellab // call master delegate offlineMutationDelegate.mutationCallback( - recordIdentifier: self.mutation.recordIdentitifer, + recordIdentifier: self.mutation.recordIdentifier, operationString: self.mutation.operationString!, snapshot: result, error: error) diff --git a/AWSAppSyncClient/Internal/AWSSQLiteNormalizedCache.swift b/AWSAppSyncClient/Internal/AWSSQLiteNormalizedCache.swift index ab5438ac..01db7ee8 100644 --- a/AWSAppSyncClient/Internal/AWSSQLiteNormalizedCache.swift +++ b/AWSAppSyncClient/Internal/AWSSQLiteNormalizedCache.swift @@ -31,6 +31,7 @@ final class AWSSQLiteNormalizedCache: NormalizedCache { private let record = Expression("record") init(fileURL: URL) throws { + AppSyncLog.verbose("Initializing normalized cache at \(fileURL.absoluteString)") db = try Connection(.uri(fileURL.absoluteString), readonly: false) db.busyTimeout = sqlBusyTimeoutConstant try createTableIfNeeded() @@ -100,6 +101,7 @@ final class AWSSQLiteNormalizedCache: NormalizedCache { } private func mergeRecords(records: RecordSet) -> Promise> { + AppSyncLog.verbose("Merging \(records.storage.count) records") return Promise { var recordSet = try selectRecords(forKeys: records.keys) diff --git a/AWSAppSyncClient/Internal/AppSyncLogHelper.h b/AWSAppSyncClient/Internal/AppSyncLogHelper.h index c88399ae..2f8fdf7d 100644 --- a/AWSAppSyncClient/Internal/AppSyncLogHelper.h +++ b/AWSAppSyncClient/Internal/AppSyncLogHelper.h @@ -9,10 +9,12 @@ @interface AppSyncLogHelper : NSObject -+(void)logVerbose:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line; -+(void)logDebug:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line; -+(void)logInfo:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line; -+(void)logWarn:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line; -+(void)logError:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line; ++(BOOL)shouldLogFlag:(AWSDDLogFlag)flag NS_SWIFT_NAME(shouldLog(flag:)); + ++(void)log:(NSString *)message + flag:(AWSDDLogFlag)flag + file:(NSString *)file + function:(NSString *)function + line:(NSUInteger)line; @end diff --git a/AWSAppSyncClient/Internal/AppSyncLogHelper.m b/AWSAppSyncClient/Internal/AppSyncLogHelper.m index 2fc84a2f..517ad19c 100644 --- a/AWSAppSyncClient/Internal/AppSyncLogHelper.m +++ b/AWSAppSyncClient/Internal/AppSyncLogHelper.m @@ -8,69 +8,21 @@ @implementation AppSyncLogHelper -+(void)logVerbose:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line { - [AWSDDLog log: YES - level: [AWSDDLog sharedInstance].logLevel - flag: AWSDDLogFlagVerbose - context: 0 - file: [file cStringUsingEncoding:NSUTF8StringEncoding] - function: [function cStringUsingEncoding:NSUTF8StringEncoding] - line: line - tag: nil - format:message - args:nil]; -} - -+(void)logDebug:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line { - [AWSDDLog log: YES - level: [AWSDDLog sharedInstance].logLevel - flag: AWSDDLogFlagDebug - context: 0 - file: [file cStringUsingEncoding:NSUTF8StringEncoding] - function: [function cStringUsingEncoding:NSUTF8StringEncoding] - line: line - tag: nil - format:message - args:nil]; -} - -+(void)logInfo:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line { - [AWSDDLog log: YES - level: [AWSDDLog sharedInstance].logLevel - flag: AWSDDLogFlagInfo - context: 0 - file: [file cStringUsingEncoding:NSUTF8StringEncoding] - function: [function cStringUsingEncoding:NSUTF8StringEncoding] - line: line - tag: nil - format:message - args:nil]; -} - -+(void)logWarn:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line { - [AWSDDLog log: YES - level: [AWSDDLog sharedInstance].logLevel - flag: AWSDDLogFlagWarning - context: 0 - file: [file cStringUsingEncoding:NSUTF8StringEncoding] - function: [function cStringUsingEncoding:NSUTF8StringEncoding] - line: line - tag: nil - format:message - args:nil]; ++(BOOL)shouldLogFlag:(AWSDDLogFlag)flag { + return flag & [AWSDDLog sharedInstance].logLevel; } -+(void)logError:(NSString *)message file:(NSString *)file funcion:(NSString *)function line:(NSUInteger)line { ++(void)log:(NSString *)message flag:(AWSDDLogFlag)flag file:(NSString *)file function:(NSString *)function line:(NSUInteger)line { [AWSDDLog log: YES level: [AWSDDLog sharedInstance].logLevel - flag: AWSDDLogFlagError + flag: flag context: 0 file: [file cStringUsingEncoding:NSUTF8StringEncoding] function: [function cStringUsingEncoding:NSUTF8StringEncoding] line: line tag: nil - format:message - args:nil]; + format: message + args: nil]; } @end diff --git a/AWSAppSyncClient/Internal/AppSyncLogWrapper.swift b/AWSAppSyncClient/Internal/AppSyncLogWrapper.swift index 24359eab..6feea051 100644 --- a/AWSAppSyncClient/Internal/AppSyncLogWrapper.swift +++ b/AWSAppSyncClient/Internal/AppSyncLogWrapper.swift @@ -5,23 +5,34 @@ // final class AppSyncLog { - class func verbose(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - AppSyncLogHelper.logVerbose(message, file: file, funcion: function, line: UInt(line)) + class func verbose(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { + log(message, flag: .verbose, file: file, function: function, line: line) } - class func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - AppSyncLogHelper.logVerbose(message, file: file, funcion: function, line: UInt(line)) + class func debug(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { + log(message, flag: .debug, file: file, function: function, line: line) } - class func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - AppSyncLogHelper.logVerbose(message, file: file, funcion: function, line: UInt(line)) + class func info(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { + log(message, flag: .info, file: file, function: function, line: line) } - class func warn(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - AppSyncLogHelper.logVerbose(message, file: file, funcion: function, line: UInt(line)) + class func warn(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { + log(message, flag: .warning, file: file, function: function, line: line) } - class func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - AppSyncLogHelper.logVerbose(message, file: file, funcion: function, line: UInt(line)) + class func error(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) { + log(message, flag: .error, file: file, function: function, line: line) } + + private class func log(_ message: @autoclosure () -> String, flag: AWSDDLogFlag, file: String, function: String, line: Int) { + if AppSyncLogHelper.shouldLog(flag: flag) { + AppSyncLogHelper.log(message(), + flag: flag, + file: file, + function: function, + line: UInt(line)) + } + } + } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fd12dcb..91b8bf5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,29 @@ The AWS AppSync SDK for iOS enables you to access your AWS AppSync backend and p ### Bug fixes +- Fixed incorrect AWSCore dependency version in podspec ([Issue #190](https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues/190)) + +### Misc. Updates + +- Added `AWSAppSyncClientLogFormatter` utility class. Developers who want to use it can add it to the appropriate logger. For example, a configuration like: + ```swift + AWSDDLog.sharedInstance.logLevel = .verbose + AWSDDTTYLogger.sharedInstance.logFormatter = AWSAppSyncClientLogFormatter() + AWSDDLog.sharedInstance.add(AWSDDTTYLogger.sharedInstance) + ``` + would output log messages like: + ``` + 2019-03-04 07:21:32.131-0800 [I AWSAppSyncClient.init(appSyncConfig:reachabilityFactory:), L75] Initializing AppSyncClient + 2019-03-04 07:21:32.135-0800 [V AWSPerformMutationQueue.init(appSyncClient:networkClient:reachabiltyChangeNotifier:cacheFileURL:), L24] Initializing AWSPerformMutationQueue + 2019-03-04 07:21:32.135-0800 [V AWSPerformMutationQueue.resume(), L95] Resuming OperationQueue + ``` +- Added some verbose logging around mutation queue handling; minor log additions elsewhere +- Minor dead code removal & miscellaneous cleanup + +## 2.10.2 + +### Bug fixes + - Fixed a bug where queries with dots (`"."`) in the arguments were not being properly cached ([Issue #110](https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues/110), [#165](https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues/165)) - `AWSAppSyncClient.perform(mutation:queue:optimisticUpdate:conflictResolutionBlock:resultHandler:)` now properly invokes its result handler callbacks on the supplied `queue` instead of always using `DispatchQueue.main`