225 lines
6.9 KiB
Swift
225 lines
6.9 KiB
Swift
//
|
|
// Copyright Amazon.com Inc. or its affiliates.
|
|
// All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
import os.log
|
|
|
|
import Foundation
|
|
import AppSyncRealTimeClient
|
|
|
|
class AppSyncRTCProvider: ObservableObject {
|
|
private static var instance: AppSyncRTCProvider?
|
|
|
|
public static var `default`: AppSyncRTCProvider {
|
|
guard let existingInstance = instance else {
|
|
// If we can't initialize the provider, we can't do anything meaningful in the
|
|
// app. A crash is appropriate here.
|
|
// swiftlint:disable:next force_try
|
|
let newInstance = try! AppSyncRTCProvider()
|
|
instance = newInstance
|
|
return newInstance
|
|
}
|
|
return existingInstance
|
|
}
|
|
|
|
@Published var connectionState: ConnectionState
|
|
@Published var events: [SubscriptionItemEvent]
|
|
|
|
@Published var lastData: String?
|
|
@Published var lastError: Error?
|
|
|
|
private let url: URL
|
|
private let apiKey: String
|
|
private let connectionProvider: ConnectionProvider
|
|
|
|
private let requestString = """
|
|
subscription onCreate {
|
|
onCreateTodo{
|
|
id
|
|
description
|
|
name
|
|
}
|
|
}
|
|
"""
|
|
|
|
private var subscriptionItem: SubscriptionItem?
|
|
private var subscriptionConnection: SubscriptionConnection?
|
|
|
|
init() throws {
|
|
os_log(#function, log: .subscription, type: .debug)
|
|
let json = try AppSyncRTCProvider.retrieveConfigurationJSON()
|
|
guard let data = json as? [String: Any],
|
|
let apiCategoryConfig = data["api"] as? [String: Any],
|
|
let plugins = apiCategoryConfig["plugins"] as? [String: Any],
|
|
let awsAPIPlugin = plugins["awsAPIPlugin"] as? [String: Any],
|
|
let pluginConfig = awsAPIPlugin.first?.value as? [String: Any],
|
|
let endpoint = pluginConfig["endpoint"] as? String,
|
|
let apiKey = pluginConfig["apiKey"] as? String,
|
|
let url = URL(string: endpoint)
|
|
else {
|
|
os_log(#function, log: .subscription, type: .fault)
|
|
throw "Could not retrieve endpoint configuration from amplifyconfiguration.json"
|
|
}
|
|
|
|
self.url = url
|
|
self.apiKey = apiKey
|
|
|
|
let authInterceptor = AppSyncRTCProvider.makeAuthInterceptor(for: apiKey)
|
|
self.connectionProvider = AppSyncRTCProvider.makeConnectionProvider(
|
|
for: url,
|
|
authInterceptor: authInterceptor
|
|
)
|
|
|
|
self.connectionState = .notConnected
|
|
self.events = []
|
|
self.lastData = nil
|
|
self.lastError = nil
|
|
}
|
|
|
|
static func retrieveConfigurationJSON() throws -> Any {
|
|
guard let path = Bundle.main.path(forResource: "amplifyconfiguration", ofType: "json") else {
|
|
throw "Could not retrieve configuration file `amplifyconfiguration.json`"
|
|
}
|
|
|
|
let url = URL(fileURLWithPath: path)
|
|
let data = try Data(contentsOf: url)
|
|
let json = try JSONSerialization.jsonObject(with: data)
|
|
return json
|
|
}
|
|
|
|
func handleEvent(event: SubscriptionItemEvent) {
|
|
events.append(event)
|
|
|
|
switch event {
|
|
case .connection(let connectionEvent):
|
|
switch connectionEvent {
|
|
case .connected:
|
|
connectionState = .connected
|
|
case .connecting:
|
|
connectionState = .inProgress
|
|
case .disconnected:
|
|
connectionState = .notConnected
|
|
}
|
|
|
|
case .failed(let error):
|
|
OSLog.subscription.log(
|
|
"event is error:\(error)",
|
|
log: .subscription,
|
|
type: .error
|
|
)
|
|
AppSyncSubscriptionConnection.logExtendedErrorInfo(for: error)
|
|
|
|
lastError = error
|
|
|
|
case .data(let data):
|
|
OSLog.subscription.log(
|
|
"event is data:\(data)",
|
|
log: .subscription,
|
|
type: .debug
|
|
)
|
|
lastData = String(data: data, encoding: .utf8)
|
|
}
|
|
}
|
|
|
|
func subscribe() {
|
|
os_log(#function, log: .subscription, type: .debug)
|
|
if subscriptionConnection != nil, subscriptionItem != nil {
|
|
unsubscribe()
|
|
}
|
|
|
|
let subscriptionConnection = AppSyncRTCProvider
|
|
.makeSubscriptionConnection(using: connectionProvider)
|
|
|
|
self.subscriptionConnection = subscriptionConnection
|
|
DispatchQueue.global().async {
|
|
self.subscriptionItem = subscriptionConnection.subscribe(
|
|
requestString: self.requestString,
|
|
variables: nil
|
|
) { event, item in
|
|
OSLog.subscription.log(
|
|
"received event:\(event), item:\(item)",
|
|
log: .subscription,
|
|
type: .debug
|
|
)
|
|
|
|
DispatchQueue.main.async {
|
|
self.handleEvent(event: event)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func unsubscribe() {
|
|
os_log(#function)
|
|
guard
|
|
let subscriptionConnection = subscriptionConnection,
|
|
let subscriptionItem = subscriptionItem
|
|
else {
|
|
return
|
|
}
|
|
OSLog.subscription.log(
|
|
"unsubscribe: connection=\(subscriptionConnection); subscriptionItem=\(subscriptionItem)",
|
|
log: .subscription,
|
|
type: .debug
|
|
)
|
|
subscriptionConnection.unsubscribe(item: subscriptionItem)
|
|
self.subscriptionConnection = nil
|
|
self.subscriptionItem = nil
|
|
}
|
|
|
|
func disconnect() {
|
|
connectionProvider.disconnect()
|
|
}
|
|
|
|
private static func makeSubscriptionConnection(
|
|
using connectionProvider: ConnectionProvider
|
|
) -> SubscriptionConnection {
|
|
os_log(#function)
|
|
return AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
}
|
|
|
|
private static func makeConnectionProvider(
|
|
for url: URL,
|
|
authInterceptor: AuthInterceptor
|
|
) -> ConnectionProvider {
|
|
os_log(#function)
|
|
return ConnectionProviderFactory.createConnectionProvider(
|
|
for: url,
|
|
authInterceptor: authInterceptor,
|
|
connectionType: .appSyncRealtime
|
|
)
|
|
}
|
|
|
|
private static func makeAuthInterceptor(for apiKey: String) -> AuthInterceptor {
|
|
os_log(#function)
|
|
return APIKeyAuthInterceptor(apiKey)
|
|
}
|
|
|
|
}
|
|
|
|
extension String: Error { }
|
|
|
|
extension ConnectionState: CustomStringConvertible {
|
|
public var description: String {
|
|
switch self {
|
|
case .connected: return "connected"
|
|
case .inProgress: return "inProgress"
|
|
case .notConnected: return "notConnected"
|
|
}
|
|
}
|
|
}
|
|
|
|
extension OSLog {
|
|
private static var subsystem = Bundle.main.bundleIdentifier!
|
|
|
|
/// Logs the view cycles like viewDidLoad.
|
|
static let subscription = OSLog(subsystem: subsystem, category: "subscription")
|
|
|
|
func log(_ message: String, log: OSLog = .default, type: OSLogType = .default) {
|
|
os_log("%@", log: log, type: type, message)
|
|
}
|
|
}
|