Skip to content

Commit

Permalink
feat: add swift concurrency (async/await) support for async intercept…
Browse files Browse the repository at this point in the history
…ors (#85)

Co-authored-by: Ian Saultz <52051793+atierian@users.noreply.github.com>
  • Loading branch information
ameter and atierian authored May 24, 2022
1 parent 73a48b0 commit 5b94284
Show file tree
Hide file tree
Showing 31 changed files with 1,503 additions and 42 deletions.
5 changes: 0 additions & 5 deletions .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@
--header "//\n// Copyright Amazon.com Inc. or its affiliates.\n// All Rights Reserved.\n//\n// SPDX-License-Identifier: Apache-2.0\n//"

--disable hoistPatternLet
--patternlet inline

--disable indent
--ifdef outdent
--indent 4
--indentcase false
--xcodeindentation disabled
Expand Down Expand Up @@ -45,7 +43,6 @@
--semicolons never

--disable sortedImports
--importgrouping testable-bottom

--enable spaceAroundOperators
--operatorfunc spaced
Expand All @@ -54,13 +51,11 @@
--trailingclosures

--disable trailingCommas
--commas inline

--enable trailingSpace
--trimwhitespace always

--disable unusedArguments
--stripunusedargs closure-only

--enable void
--empty void
Expand Down
76 changes: 72 additions & 4 deletions AppSyncRealTimeClient.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ extension AppSyncSubscriptionConnection {
else {
return
}
AppSyncLogger.debug("[AppSyncSubscriptionConnection]: Connection connected, start subscription \(subscriptionItem.identifier).")
AppSyncLogger.debug(
"[AppSyncSubscriptionConnection]: Connection connected, start subscription \(subscriptionItem.identifier)."
)
subscriptionState = .inProgress

guard let payload = convertToPayload(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ extension AppSyncSubscriptionConnection {

let retryAdvice = retryHandler.shouldRetryRequest(for: connectionError)
if retryAdvice.shouldRetry, let retryInterval = retryAdvice.retryInterval {
// swiftlint:disable:next line_length
AppSyncLogger.debug("[AppSyncSubscriptionConnection] Retrying subscription \(subscriptionItem.identifier) after \(retryInterval)")
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval) {
self.connectionProvider?.connect()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ public class AppSyncSubscriptionConnection: SubscriptionConnection, RetryableCon

connectionProvider.addListener(identifier: subscriptionItem.identifier) { [weak self] event in
guard let self = self else {
AppSyncLogger.debug("[AppSyncSubscriptionConnection]: Subscription (Self) is nil, connection event is not handled.")
AppSyncLogger.debug(
"[AppSyncSubscriptionConnection]: Subscription (Self) is nil, connection event is not handled."
)
return
}
switch event {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ extension RealtimeConnectionProvider {

/// Start a stale connection timer, first invalidating and destroying any existing timer
func startStaleConnectionTimer() {
AppSyncLogger.debug("[RealtimeConnectionProvider] Starting stale connection timer for \(staleConnectionTimer.interval)s")
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Starting stale connection timer for \(staleConnectionTimer.interval)s"
)

staleConnectionTimer.start(interval: RealtimeConnectionProvider.staleConnectionTimeout) {
self.disconnectStaleConnection()
Expand All @@ -36,13 +38,19 @@ extension RealtimeConnectionProvider {
guard let self = self else {
return
}
AppSyncLogger.debug("[RealtimeConnectionProvider] Status: \(self.status). Connectivity status: \(connectivity.status)")
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Status: \(self.status). Connectivity status: \(connectivity.status)"
)
if self.status == .connected && connectivity.status == .unsatisfied && !self.isStaleConnection {
AppSyncLogger.debug("[RealtimeConnectionProvider] Connetion is stale. Pending reconnect on connectivity.")
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Connetion is stale. Pending reconnect on connectivity."
)
self.isStaleConnection = true

} else if self.status == .connected && self.isStaleConnection && connectivity.status == .satisfied {
AppSyncLogger.debug("[RealtimeConnectionProvider] Connetion is stale. Disconnecting to begin reconnect.")
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Connetion is stale. Disconnecting to begin reconnect."
)
self.staleConnectionTimer.invalidate()
self.disconnectStaleConnection()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public class RealtimeConnectionProvider: ConnectionProvider {
if iLimitExceededSubject == nil {
iLimitExceededSubject = PassthroughSubject<ConnectionProviderError, Never>()
}
return iLimitExceededSubject as! PassthroughSubject<ConnectionProviderError, Never> // swiftlint:disable:this force_cast
return iLimitExceededSubject as! PassthroughSubject<ConnectionProviderError, Never> // swiftlint:disable:this force_cast line_length
}

public convenience init(for url: URL, websocket: AppSyncWebsocketProvider) {
Expand Down Expand Up @@ -169,7 +169,9 @@ public class RealtimeConnectionProvider: ConnectionProvider {
self.listeners.removeValue(forKey: identifier)

if self.listeners.isEmpty {
AppSyncLogger.debug("[RealtimeConnectionProvider] all subscriptions removed, disconnecting websocket connection.")
AppSyncLogger.debug(
"[RealtimeConnectionProvider] all subscriptions removed, disconnecting websocket connection."
)
self.status = .notConnected
self.websocket.disconnect()
self.invalidateStaleConnectionTimer()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

#if swift(>=5.5.2)

import Foundation

@available(iOS 13.0.0, *)
extension RealtimeConnectionProviderAsync: ConnectionInterceptableAsync {

public func addInterceptor(_ interceptor: ConnectionInterceptorAsync) {
connectionInterceptors.append(interceptor)
}

public func interceptConnection(
_ request: AppSyncConnectionRequest,
for endpoint: URL
) async -> AppSyncConnectionRequest {
var finalRequest = request
for interceptor in connectionInterceptors {
finalRequest = await interceptor.interceptConnection(finalRequest, for: endpoint)
}

return finalRequest
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

#if swift(>=5.5.2)

import Foundation

@available(iOS 13.0.0, *)
extension RealtimeConnectionProviderAsync: MessageInterceptableAsync {
public func addInterceptor(_ interceptor: MessageInterceptorAsync) {
messageInterceptors.append(interceptor)
}

public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) async -> AppSyncMessage {
var finalMessage = message
for interceptor in messageInterceptors {
finalMessage = await interceptor.interceptMessage(finalMessage, for: endpoint)
}

return finalMessage
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

#if swift(>=5.5.2)

import Foundation

/// Consolidates usage and parameters passed to the `staleConnectionTimer` methods.
@available(iOS 13.0, *)
extension RealtimeConnectionProviderAsync {

/// Start a stale connection timer, first invalidating and destroying any existing timer
func startStaleConnectionTimer() {
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Starting stale connection timer for \(staleConnectionTimer.interval)s"
)

staleConnectionTimer.start(interval: RealtimeConnectionProviderAsync.staleConnectionTimeout) {
self.disconnectStaleConnection()
}
}

/// Reset the stale connection timer in response to receiving a message from the websocket
func resetStaleConnectionTimer(interval: TimeInterval? = nil) {
AppSyncLogger.verbose("[RealtimeConnectionProvider] Resetting stale connection timer")
staleConnectionTimer.reset(interval: interval)
}

/// Stops the timer when disconnecting the websocket.
func invalidateStaleConnectionTimer() {
staleConnectionTimer.invalidate()
}

/// Handle updates from the ConnectivityMonitor
func handleConnectivityUpdates(connectivity: ConnectivityPath) {
taskQueue.async { [weak self] in
guard let self = self else {
return
}
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Status: \(self.status). Connectivity status: \(connectivity.status)"
)
if self.status == .connected && connectivity.status == .unsatisfied && !self.isStaleConnection {
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Connetion is stale. Pending reconnect on connectivity."
)
self.isStaleConnection = true

} else if self.status == .connected && self.isStaleConnection && connectivity.status == .satisfied {
AppSyncLogger.debug(
"[RealtimeConnectionProvider] Connetion is stale. Disconnecting to begin reconnect."
)
self.staleConnectionTimer.invalidate()
self.disconnectStaleConnection()
}
}
}

/// Fired when the stale connection timer expires
private func disconnectStaleConnection() {
taskQueue.async { [weak self] in
guard let self = self else {
return
}
AppSyncLogger.error("[RealtimeConnectionProvider] Realtime connection is stale, disconnecting.")
self.status = .notConnected
self.isStaleConnection = false
self.websocket.disconnect()
self.updateCallback(event: .error(ConnectionProviderError.connection))
}
}
}
#endif
Loading

0 comments on commit 5b94284

Please sign in to comment.