fix: Fix stale connection handling upon resume from background (#25)

Additional work:
- chore: Add SwiftLint and SwiftFormat build tools to all targets except HostApp
- chore: Fixed formatting & linting
- chore: Add interactive app to test subscribe, unsubscribe and disconnect behavior from clients
- chore: Add unit tests for RealTimeConnectionProvider
- chore: Add CountdownTimer support class
This commit is contained in:
Tim Schmelter 2020-07-31 12:47:58 -07:00 committed by GitHub
parent 43c94c3638
commit 74c5079ecd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
92 changed files with 5821 additions and 653 deletions

119
.swiftformat Normal file
View File

@ -0,0 +1,119 @@
# file options
--exclude Pods
# rules with format options
--enable braces
--allman false
--enable elseOnSameLine
--elseposition same-line
--enable fileHeader
--header "//\n// Copyright 2018-{created.year} Amazon.com,\n// Inc. or its affiliates. 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
--enable linebreaks
--linebreaks lf
--enable numberFormatting
--binarygrouping none
--decimalgrouping 3
--exponentcase lowercase
--exponentgrouping disabled
--fractiongrouping disabled
--hexgrouping none
--hexliteralcase lowercase
--octalgrouping none
--enable spaceAroundOperators
--operatorfunc spaced
--enable redundantSelf
--self init-only
--selfrequired
--enable semicolons
--semicolons never
--disable sortedImports
--importgrouping testable-bottom
--enable spaceAroundOperators
--operatorfunc spaced
--enable trailingClosures
--trailingclosures
--disable trailingCommas
--commas inline
--enable trailingSpace
--trimwhitespace always
--disable unusedArguments
--stripunusedargs closure-only
--enable void
--empty void
--enable wrapArguments
--closingparen balanced
--wraparguments before-first
--wrapcollections before-first
--wrapparameters before-first
# standalone rules
--disable andOperator
--enable anyObjectProtocol
--disable blankLinesAroundMark
--disable blankLinesAtEndOfScope
--disable blankLinesAtStartOfScope
--disable blankLinesBetweenScopes
--disable consecutiveBlankLines
--disable consecutiveSpaces
--disable duplicateImports
--disable emptyBraces
--enable isEmpty
--enable leadingDelimiters
--enable linebreakAtEndOfFile
--enable redundantBackticks
--enable redundantBreak
--enable redundantExtensionACL
--enable redundantFileprivate
--enable redundantGet
--disable redundantInit
--enable redundantLet
--enable redundantLetError
--enable redundantNilInit
--enable redundantObjc
--enable redundantParens
--enable redundantPattern
--enable redundantRawValues
--disable redundantReturn
--enable redundantVoidReturnType
--enable spaceAroundBraces
--enable spaceAroundBrackets
--enable spaceAroundComments
--enable spaceAroundGenerics
--enable spaceAroundParens
--disable spaceInsideBraces
--enable spaceInsideBrackets
--disable spaceInsideComments
--enable spaceInsideGenerics
--disable spaceInsideParens
--disable specifiers
--enable strongOutlets
--enable strongifiedSelf
--disable todos
--enable typeSugar
--disable wrapMultilineStatementBraces
--enable yodaConditions

38
.swiftlint.yml Normal file
View File

@ -0,0 +1,38 @@
# Do not specify an `included` section at this top-level file. Specify the
# `--config` option pointing to this file, and the `--path` option to the files
# you wish to lint
excluded:
- Pods
analyzer_rules:
- unused_import
- unused_declaration
opt_in_rules:
- empty_count
# configurable rules can be customized from this configuration file
closing_brace: error
colon:
severity: error
comma: error
empty_count: warning
empty_enum_arguments: error
function_body_length:
warning: 100
error: 150
identifier_name:
excluded:
- id
- of
- or
line_length:
warning: 120
error: 160
opening_brace: error
return_arrow_whitespace: error
statement_position:
severity: error
todo: warning
trailing_semicolon: error

View File

@ -0,0 +1,60 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import os.log
import UIKit
import AppSyncRealTimeClient
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Override point for customization after application launch.
// Initialize the subscription, don't do anything in particular with it
_ = AppSyncRTCProvider.default
return true
}
// MARK: UISceneSession Lifecycle
func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(
_ application: UIApplication,
didDiscardSceneSessions sceneSessions: Set<UISceneSession>
) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will
// be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded
// scenes, as they will not return.
}
func applicationWillResignActive(_ application: UIApplication) {
OSLog.subscription.log("\(#function), unsubscribing", log: .subscription)
AppSyncRTCProvider.default.unsubscribe()
}
func applicationDidEnterBackground(_ application: UIApplication) {
OSLog.subscription.log("\(#function), unsubscribing", log: .subscription)
AppSyncRTCProvider.default.unsubscribe()
}
}

View File

@ -0,0 +1,224 @@
//
// Copyright 2018-2020 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)
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,57 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import SwiftUI
struct ContentView: View {
@ObservedObject var provider: AppSyncRTCProvider = .default
var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .top) {
Text("Subscription state: ")
Text(provider.connectionState.description)
Spacer()
}
.padding(.bottom, 8)
HStack(alignment: .top) {
Text("Events received: ")
Text("\(provider.events.count)")
Spacer()
}
.padding(.bottom, 8)
HStack(alignment: .top) {
Text("Last data: ")
Text("\(provider.lastData?.description ?? "N/A")")
Spacer()
}
.padding(.bottom, 8)
HStack(alignment: .top) {
Text("Last error: ")
Text("\(provider.lastError?.localizedDescription ?? "N/A")")
Spacer()
}
.padding(.bottom, 24)
Button("Subscribe") { self.provider.subscribe() }
Button("Unsubscribe") { self.provider.unsubscribe() }
Button("Disconnect") { self.provider.disconnect() }
Spacer()
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

View File

@ -0,0 +1,60 @@
<?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">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,71 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import os.log
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
// Use this method to optionally configure and attach the UIWindow `window` to the
// provided UIWindowScene `scene`. If using a storyboard, the `window` property
// will automatically be initialized and attached to the scene. This delegate does
// not imply the connecting scene or session are new (see
// `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system. This occurs shortly after
// the scene enters the background, or when its session is discarded. Release any
// resources associated with this scene that can be re-created the next time the
// scene connects. The scene may re-connect later, as its session was not
// neccessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state. Use
// this method to restart any tasks that were paused (or not yet started) when the
// scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state. This
// may occur due to temporary interruptions (ex. an incoming phone call).
OSLog.subscription.log("\(#function), unsubscribing", log: .subscription)
AppSyncRTCProvider.default.unsubscribe()
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground. Use this
// method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background. Use this
// method to save data, release shared resources, and store enough scene-specific
// state information to restore the scene back to its current state.
}
}

View File

@ -14,7 +14,6 @@
217F39CF2406E98400F1A0B3 /* AppSyncMessage+Encodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39AD2406E98300F1A0B3 /* AppSyncMessage+Encodable.swift */; };
217F39D02406E98400F1A0B3 /* AppSyncMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39AE2406E98300F1A0B3 /* AppSyncMessage.swift */; };
217F39D12406E98400F1A0B3 /* AppSyncConnectionRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39AF2406E98300F1A0B3 /* AppSyncConnectionRequest.swift */; };
217F39D22406E98400F1A0B3 /* RealtimeConnectionProvider+KeepAlive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39B12406E98300F1A0B3 /* RealtimeConnectionProvider+KeepAlive.swift */; };
217F39D32406E98400F1A0B3 /* RealtimeConnectionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39B22406E98300F1A0B3 /* RealtimeConnectionProvider.swift */; };
217F39D42406E98400F1A0B3 /* RealtimeConnectionProvider+Websocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39B32406E98300F1A0B3 /* RealtimeConnectionProvider+Websocket.swift */; };
217F39D52406E98400F1A0B3 /* RealtimeConnectionProviderResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39B42406E98300F1A0B3 /* RealtimeConnectionProviderResponse.swift */; };
@ -38,7 +37,7 @@
217F39E72406E98400F1A0B3 /* SubscriptionConnectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39CB2406E98400F1A0B3 /* SubscriptionConnectionType.swift */; };
217F39F02406EA4000F1A0B3 /* AppSyncSubscriptionConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39E92406EA3F00F1A0B3 /* AppSyncSubscriptionConnectionTests.swift */; };
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */; };
217F39F22406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39ED2406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift */; };
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */; };
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */; };
21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */; };
21D38B432409AFBD00EC2A8D /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; };
@ -50,7 +49,6 @@
21D38B5C2409B94100EC2A8D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B5B2409B94100EC2A8D /* Assets.xcassets */; };
21D38B5F2409B94100EC2A8D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B5E2409B94100EC2A8D /* Preview Assets.xcassets */; };
21D38B622409B94100EC2A8D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B602409B94100EC2A8D /* LaunchScreen.storyboard */; };
21D38B692409B95B00EC2A8D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; };
21D38B6D240A262800EC2A8D /* AppSyncJSONHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B6C240A262800EC2A8D /* AppSyncJSONHelper.swift */; };
21D38B7C240A2A1300EC2A8D /* OIDCAuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B78240A2A1300EC2A8D /* OIDCAuthInterceptor.swift */; };
21D38B83240A392B00EC2A8D /* APIKeyAuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B82240A392B00EC2A8D /* APIKeyAuthInterceptor.swift */; };
@ -65,7 +63,24 @@
259F9EB83B9F67D0413A6D4C /* Pods_AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AFCBED5A97E398A4BD9D06 /* Pods_AppSyncRealTimeClient.framework */; };
450019BB151D701382536BD0 /* Pods_HostApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29CDD85F44666C7241232D29 /* Pods_HostApp.framework */; };
4A3EAC9B20D96D81CC3A7EF4 /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */; };
9617CE1692296AC6B7336E65 /* Pods_AppSyncRTCSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2CF92F54E085ECA65E1932A6 /* Pods_AppSyncRTCSample.framework */; };
DCFE701B5D1380E566694A48 /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */; };
FA67507824D3244A005A1345 /* MockWebsocketProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507724D3244A005A1345 /* MockWebsocketProvider.swift */; };
FA67507B24D338CB005A1345 /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507A24D338C6005A1345 /* Error+Extension.swift */; };
FA67507E24D33976005A1345 /* RealtimeConnectionProviderTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507C24D338FA005A1345 /* RealtimeConnectionProviderTestBase.swift */; };
FA67508024D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507F24D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift */; };
FA67508224D33A7A005A1345 /* CountdownTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67508124D33A7A005A1345 /* CountdownTimer.swift */; };
FA67508424D33ACC005A1345 /* CountdownTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67508324D33ACC005A1345 /* CountdownTimerTests.swift */; };
FA91B1FE24D3063E0017404D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA91B1FD24D3063E0017404D /* AppDelegate.swift */; };
FA91B20024D3063E0017404D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA91B1FF24D3063E0017404D /* SceneDelegate.swift */; };
FA91B20224D3063E0017404D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA91B20124D3063E0017404D /* ContentView.swift */; };
FA91B20424D306420017404D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FA91B20324D306420017404D /* Assets.xcassets */; };
FA91B20724D306430017404D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FA91B20624D306430017404D /* Preview Assets.xcassets */; };
FA91B20A24D306430017404D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FA91B20824D306430017404D /* LaunchScreen.storyboard */; };
FA91B20F24D306550017404D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; };
FA91B21024D306730017404D /* AppSyncRTCProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAB5AA1D24D1CD31001F370F /* AppSyncRTCProvider.swift */; };
FAB5AA1F24D1CD84001F370F /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; };
FAB7E91224D2644E00DF1EA1 /* RealtimeConnectionProvider+StaleConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39B12406E98300F1A0B3 /* RealtimeConnectionProvider+StaleConnection.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -93,6 +108,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
106B0590055335CE3655E19D /* Pods-AppSyncRTCSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRTCSample.debug.xcconfig"; path = "Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample.debug.xcconfig"; sourceTree = "<group>"; };
18D6E56CE03BAC33493CC19B /* Pods-HostApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp.debug.xcconfig"; path = "Target Support Files/Pods-HostApp/Pods-HostApp.debug.xcconfig"; sourceTree = "<group>"; };
217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppSyncRealTimeClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
217F39932405D9D500F1A0B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -104,7 +120,7 @@
217F39AD2406E98300F1A0B3 /* AppSyncMessage+Encodable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppSyncMessage+Encodable.swift"; sourceTree = "<group>"; };
217F39AE2406E98300F1A0B3 /* AppSyncMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncMessage.swift; sourceTree = "<group>"; };
217F39AF2406E98300F1A0B3 /* AppSyncConnectionRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncConnectionRequest.swift; sourceTree = "<group>"; };
217F39B12406E98300F1A0B3 /* RealtimeConnectionProvider+KeepAlive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RealtimeConnectionProvider+KeepAlive.swift"; sourceTree = "<group>"; };
217F39B12406E98300F1A0B3 /* RealtimeConnectionProvider+StaleConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RealtimeConnectionProvider+StaleConnection.swift"; sourceTree = "<group>"; };
217F39B22406E98300F1A0B3 /* RealtimeConnectionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeConnectionProvider.swift; sourceTree = "<group>"; };
217F39B32406E98300F1A0B3 /* RealtimeConnectionProvider+Websocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RealtimeConnectionProvider+Websocket.swift"; sourceTree = "<group>"; };
217F39B42406E98300F1A0B3 /* RealtimeConnectionProviderResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeConnectionProviderResponse.swift; sourceTree = "<group>"; };
@ -128,7 +144,7 @@
217F39CB2406E98400F1A0B3 /* SubscriptionConnectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubscriptionConnectionType.swift; sourceTree = "<group>"; };
217F39E92406EA3F00F1A0B3 /* AppSyncSubscriptionConnectionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncSubscriptionConnectionTests.swift; sourceTree = "<group>"; };
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockConnectionProvider.swift; sourceTree = "<group>"; };
217F39ED2406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeConnectionProviderTests.swift; sourceTree = "<group>"; };
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProviderTests.swift; sourceTree = "<group>"; };
217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeGatewayURLInterceptorTests.swift; sourceTree = "<group>"; };
21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientIntegrationTests.swift; sourceTree = "<group>"; };
@ -155,9 +171,11 @@
21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationHelper.swift; sourceTree = "<group>"; };
21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCommonConstants.swift; sourceTree = "<group>"; };
29CDD85F44666C7241232D29 /* Pods_HostApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HostApp.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2CF92F54E085ECA65E1932A6 /* Pods_AppSyncRTCSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRTCSample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
356411189EAD2C776D250FB7 /* Pods-HostApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp.release.xcconfig"; path = "Target Support Files/Pods-HostApp/Pods-HostApp.release.xcconfig"; sourceTree = "<group>"; };
3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7729985A4AC28DABC48ED0E7 /* Pods-AppSyncRTCSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRTCSample.release.xcconfig"; path = "Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample.release.xcconfig"; sourceTree = "<group>"; };
7D4F451B830A5837CE5274A3 /* Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.release.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.release.xcconfig"; sourceTree = "<group>"; };
81AFCBED5A97E398A4BD9D06 /* Pods_AppSyncRealTimeClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRealTimeClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
93AA1C96F1B8D428CCC24CF0 /* Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.debug.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.debug.xcconfig"; sourceTree = "<group>"; };
@ -165,6 +183,21 @@
B07A87133C24AA78AF59093C /* Pods-AppSyncRealTimeClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient.debug.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient/Pods-AppSyncRealTimeClient.debug.xcconfig"; sourceTree = "<group>"; };
C73EA796F50FB0196FDF4865 /* Pods-AppSyncRealTimeClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient.release.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient/Pods-AppSyncRealTimeClient.release.xcconfig"; sourceTree = "<group>"; };
E65A19C826699DA299A49284 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-HostApp-AppSyncRealTimeClientIntegrationTests/Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig"; sourceTree = "<group>"; };
FA67507724D3244A005A1345 /* MockWebsocketProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockWebsocketProvider.swift; sourceTree = "<group>"; };
FA67507A24D338C6005A1345 /* Error+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Extension.swift"; sourceTree = "<group>"; };
FA67507C24D338FA005A1345 /* RealtimeConnectionProviderTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealtimeConnectionProviderTestBase.swift; sourceTree = "<group>"; };
FA67507F24D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionProviderStaleConnectionTests.swift; sourceTree = "<group>"; };
FA67508124D33A7A005A1345 /* CountdownTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownTimer.swift; sourceTree = "<group>"; };
FA67508324D33ACC005A1345 /* CountdownTimerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountdownTimerTests.swift; sourceTree = "<group>"; };
FA91B1FB24D3063E0017404D /* AppSyncRTCSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AppSyncRTCSample.app; sourceTree = BUILT_PRODUCTS_DIR; };
FA91B1FD24D3063E0017404D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
FA91B1FF24D3063E0017404D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
FA91B20124D3063E0017404D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
FA91B20324D306420017404D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
FA91B20624D306430017404D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
FA91B20924D306430017404D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
FA91B20B24D306430017404D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
FAB5AA1D24D1CD31001F370F /* AppSyncRTCProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRTCProvider.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -202,6 +235,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
FA91B1F824D3063E0017404D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9617CE1692296AC6B7336E65 /* Pods_AppSyncRTCSample.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@ -211,6 +252,7 @@
217F39912405D9D500F1A0B3 /* AppSyncRealTimeClient */,
217F399C2405D9D500F1A0B3 /* AppSyncRealTimeClientTests */,
21D38B3F2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */,
FA91B1FC24D3063E0017404D /* AppSyncRTCSample */,
21D38B542409B93F00EC2A8D /* HostApp */,
217F39902405D9D500F1A0B3 /* Products */,
D146D6DDE2251CCA7A4ECD7F /* Pods */,
@ -225,6 +267,7 @@
217F39982405D9D500F1A0B3 /* AppSyncRealTimeClientTests.xctest */,
21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */,
21D38B532409B93F00EC2A8D /* HostApp.app */,
FA91B1FB24D3063E0017404D /* AppSyncRTCSample.app */,
);
name = Products;
sourceTree = "<group>";
@ -232,9 +275,9 @@
217F39912405D9D500F1A0B3 /* AppSyncRealTimeClient */ = {
isa = PBXGroup;
children = (
217F39932405D9D500F1A0B3 /* Info.plist */,
217F39B82406E98300F1A0B3 /* Connection */,
217F39A92406E98300F1A0B3 /* ConnectionProvider */,
217F39932405D9D500F1A0B3 /* Info.plist */,
21D38B6E240A272F00EC2A8D /* Interceptor */,
217F39C62406E98400F1A0B3 /* Support */,
217F39C12406E98400F1A0B3 /* Websocket */,
@ -245,12 +288,13 @@
217F399C2405D9D500F1A0B3 /* AppSyncRealTimeClientTests */ = {
isa = PBXGroup;
children = (
217F399F2405D9D500F1A0B3 /* Info.plist */,
217F39E82406EA3F00F1A0B3 /* Connection */,
217F39EC2406EA4000F1A0B3 /* ConnectionProvider */,
217F399F2405D9D500F1A0B3 /* Info.plist */,
21D38B84240A39E400EC2A8D /* Interceptor */,
217F39EA2406EA3F00F1A0B3 /* Mocks */,
217F39EE2406EA4000F1A0B3 /* Support */,
FA67507924D338BF005A1345 /* TestSupport */,
);
path = AppSyncRealTimeClientTests;
sourceTree = "<group>";
@ -261,12 +305,12 @@
217F39AF2406E98300F1A0B3 /* AppSyncConnectionRequest.swift */,
217F39AE2406E98300F1A0B3 /* AppSyncMessage.swift */,
217F39AD2406E98300F1A0B3 /* AppSyncMessage+Encodable.swift */,
217F39B02406E98300F1A0B3 /* AppsyncRealtimeConnection */,
217F39AA2406E98300F1A0B3 /* AppSyncResponse.swift */,
21D38B8D240A3C2300EC2A8D /* ConnectionProviderFactory.swift */,
217F39B72406E98300F1A0B3 /* ConnectionProvider.swift */,
217F39AC2406E98300F1A0B3 /* ConnectionProviderError.swift */,
21D38B8D240A3C2300EC2A8D /* ConnectionProviderFactory.swift */,
217F39AB2406E98300F1A0B3 /* InterceptableConnection.swift */,
217F39B02406E98300F1A0B3 /* AppsyncRealtimeConnection */,
);
path = ConnectionProvider;
sourceTree = "<group>";
@ -276,8 +320,8 @@
children = (
217F39B22406E98300F1A0B3 /* RealtimeConnectionProvider.swift */,
217F39B62406E98300F1A0B3 /* RealtimeConnectionProvider+ConnectionInterceptable.swift */,
217F39B12406E98300F1A0B3 /* RealtimeConnectionProvider+KeepAlive.swift */,
217F39B52406E98300F1A0B3 /* RealtimeConnectionProvider+MessageInterceptable.swift */,
217F39B12406E98300F1A0B3 /* RealtimeConnectionProvider+StaleConnection.swift */,
217F39B32406E98300F1A0B3 /* RealtimeConnectionProvider+Websocket.swift */,
217F39B42406E98300F1A0B3 /* RealtimeConnectionProviderResponse.swift */,
);
@ -287,10 +331,10 @@
217F39B82406E98300F1A0B3 /* Connection */ = {
isa = PBXGroup;
children = (
217F39BB2406E98300F1A0B3 /* AppSyncConnection */,
217F39BA2406E98300F1A0B3 /* RetryableConnection.swift */,
217F39B92406E98300F1A0B3 /* SubscriptionConnection.swift */,
217F39C02406E98300F1A0B3 /* SubscriptionItem.swift */,
217F39BB2406E98300F1A0B3 /* AppSyncConnection */,
);
path = Connection;
sourceTree = "<group>";
@ -330,6 +374,7 @@
21D38B6C240A262800EC2A8D /* AppSyncJSONHelper.swift */,
217F39C92406E98400F1A0B3 /* AppSyncJSONValue.swift */,
217F39C72406E98400F1A0B3 /* AppSyncLogger.swift */,
FA67508124D33A7A005A1345 /* CountdownTimer.swift */,
21D38B93240C4A2A00EC2A8D /* OIDCAuthProvider.swift */,
217F39CB2406E98400F1A0B3 /* SubscriptionConnectionType.swift */,
217F39CA2406E98400F1A0B3 /* SubscriptionConstants.swift */,
@ -349,6 +394,7 @@
isa = PBXGroup;
children = (
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */,
FA67507724D3244A005A1345 /* MockWebsocketProvider.swift */,
);
path = Mocks;
sourceTree = "<group>";
@ -356,7 +402,9 @@
217F39EC2406EA4000F1A0B3 /* ConnectionProvider */ = {
isa = PBXGroup;
children = (
217F39ED2406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift */,
FA67507F24D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift */,
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */,
FA67507C24D338FA005A1345 /* RealtimeConnectionProviderTestBase.swift */,
);
path = ConnectionProvider;
sourceTree = "<group>";
@ -365,6 +413,7 @@
isa = PBXGroup;
children = (
217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */,
FA67508324D33ACC005A1345 /* CountdownTimerTests.swift */,
);
path = Support;
sourceTree = "<group>";
@ -373,9 +422,9 @@
isa = PBXGroup;
children = (
21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */,
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */,
21D38B422409AFBD00EC2A8D /* Info.plist */,
21D38B4D2409B8B200EC2A8D /* README.md */,
21D38B422409AFBD00EC2A8D /* Info.plist */,
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */,
21D38B95240C4DC200EC2A8D /* Support */,
);
path = AppSyncRealTimeClientIntegrationTests;
@ -416,9 +465,9 @@
21D38B84240A39E400EC2A8D /* Interceptor */ = {
isa = PBXGroup;
children = (
21D38B85240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift */,
21D38B87240A39E400EC2A8D /* AppSyncJSONHelperTests.swift */,
21D38B88240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift */,
21D38B87240A39E400EC2A8D /* AppSyncJSONHelperTests.swift */,
21D38B85240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift */,
);
path = Interceptor;
sourceTree = "<group>";
@ -426,8 +475,8 @@
21D38B95240C4DC200EC2A8D /* Support */ = {
isa = PBXGroup;
children = (
21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */,
21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */,
21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */,
21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */,
);
path = Support;
@ -440,6 +489,7 @@
3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */,
29CDD85F44666C7241232D29 /* Pods_HostApp.framework */,
43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */,
2CF92F54E085ECA65E1932A6 /* Pods_AppSyncRTCSample.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -455,10 +505,43 @@
356411189EAD2C776D250FB7 /* Pods-HostApp.release.xcconfig */,
A843B4589131E0D2DB13ADC7 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.debug.xcconfig */,
E65A19C826699DA299A49284 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig */,
106B0590055335CE3655E19D /* Pods-AppSyncRTCSample.debug.xcconfig */,
7729985A4AC28DABC48ED0E7 /* Pods-AppSyncRTCSample.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
FA67507924D338BF005A1345 /* TestSupport */ = {
isa = PBXGroup;
children = (
FA67507A24D338C6005A1345 /* Error+Extension.swift */,
);
path = TestSupport;
sourceTree = "<group>";
};
FA91B1FC24D3063E0017404D /* AppSyncRTCSample */ = {
isa = PBXGroup;
children = (
FA91B20B24D306430017404D /* Info.plist */,
FA91B1FD24D3063E0017404D /* AppDelegate.swift */,
FAB5AA1D24D1CD31001F370F /* AppSyncRTCProvider.swift */,
FA91B20124D3063E0017404D /* ContentView.swift */,
FA91B1FF24D3063E0017404D /* SceneDelegate.swift */,
FA91B20324D306420017404D /* Assets.xcassets */,
FA91B20824D306430017404D /* LaunchScreen.storyboard */,
FA91B20524D306420017404D /* Preview Content */,
);
path = AppSyncRTCSample;
sourceTree = "<group>";
};
FA91B20524D306420017404D /* Preview Content */ = {
isa = PBXGroup;
children = (
FA91B20624D306430017404D /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -479,6 +562,8 @@
F78B4AC87B22D691D620684A /* [CP] Check Pods Manifest.lock */,
217F398A2405D9D500F1A0B3 /* Headers */,
217F398B2405D9D500F1A0B3 /* Sources */,
FA91B21124D308330017404D /* SwiftFormat */,
FA91B21224D308550017404D /* SwiftLint */,
217F398C2405D9D500F1A0B3 /* Frameworks */,
217F398D2405D9D500F1A0B3 /* Resources */,
);
@ -497,6 +582,8 @@
buildPhases = (
4F32EBC5131508484DC03565 /* [CP] Check Pods Manifest.lock */,
217F39942405D9D500F1A0B3 /* Sources */,
FA91B21324D308800017404D /* SwiftFormat */,
FA91B21424D308B00017404D /* SwiftLint */,
217F39952405D9D500F1A0B3 /* Frameworks */,
217F39962405D9D500F1A0B3 /* Resources */,
9CB3A44F02BD117B9687BF03 /* [CP] Embed Pods Frameworks */,
@ -517,6 +604,8 @@
buildPhases = (
6996A5923AF9E7D35CA85369 /* [CP] Check Pods Manifest.lock */,
21D38B3A2409AFBD00EC2A8D /* Sources */,
FA91B21524D308C70017404D /* SwiftFormat */,
FA91B21624D308D90017404D /* SwiftLint */,
21D38B3B2409AFBD00EC2A8D /* Frameworks */,
21D38B3C2409AFBD00EC2A8D /* Resources */,
D3A1E54210CC83949895F543 /* [CP] Embed Pods Frameworks */,
@ -550,13 +639,34 @@
productReference = 21D38B532409B93F00EC2A8D /* HostApp.app */;
productType = "com.apple.product-type.application";
};
FA91B1FA24D3063E0017404D /* AppSyncRTCSample */ = {
isa = PBXNativeTarget;
buildConfigurationList = FA91B20E24D306430017404D /* Build configuration list for PBXNativeTarget "AppSyncRTCSample" */;
buildPhases = (
29B508C3BFEFCAE624464616 /* [CP] Check Pods Manifest.lock */,
FA91B1F724D3063E0017404D /* Sources */,
FA91B21724D308EB0017404D /* SwiftFormat */,
FA91B21824D308FD0017404D /* SwiftLint */,
FA91B1F824D3063E0017404D /* Frameworks */,
FA91B1F924D3063E0017404D /* Resources */,
B225C44A936D8399256AB27A /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = AppSyncRTCSample;
productName = AppSyncRTCSample;
productReference = FA91B1FB24D3063E0017404D /* AppSyncRTCSample.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
217F39862405D9D500F1A0B3 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1130;
LastSwiftUpdateCheck = 1160;
LastUpgradeCheck = 1130;
ORGANIZATIONNAME = amazonaws;
TargetAttributes = {
@ -573,6 +683,9 @@
21D38B522409B93F00EC2A8D = {
CreatedOnToolsVersion = 11.3.1;
};
FA91B1FA24D3063E0017404D = {
CreatedOnToolsVersion = 11.6;
};
};
};
buildConfigurationList = 217F39892405D9D500F1A0B3 /* Build configuration list for PBXProject "AppSyncRealTimeClient" */;
@ -591,6 +704,7 @@
217F398E2405D9D500F1A0B3 /* AppSyncRealTimeClient */,
217F39972405D9D500F1A0B3 /* AppSyncRealTimeClientTests */,
21D38B3D2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */,
FA91B1FA24D3063E0017404D /* AppSyncRTCSample */,
21D38B522409B93F00EC2A8D /* HostApp */,
);
};
@ -624,16 +738,49 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
21D38B692409B95B00EC2A8D /* amplifyconfiguration.json in Resources */,
21D38B622409B94100EC2A8D /* LaunchScreen.storyboard in Resources */,
21D38B5F2409B94100EC2A8D /* Preview Assets.xcassets in Resources */,
21D38B5C2409B94100EC2A8D /* Assets.xcassets in Resources */,
FAB5AA1F24D1CD84001F370F /* amplifyconfiguration.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FA91B1F924D3063E0017404D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FA91B20F24D306550017404D /* amplifyconfiguration.json in Resources */,
FA91B20A24D306430017404D /* LaunchScreen.storyboard in Resources */,
FA91B20724D306430017404D /* Preview Assets.xcassets in Resources */,
FA91B20424D306420017404D /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
29B508C3BFEFCAE624464616 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-AppSyncRTCSample-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
4F32EBC5131508484DC03565 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -695,6 +842,23 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
B225C44A936D8399256AB27A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
B4AC537B815B7AF42413C854 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@ -756,6 +920,150 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
FA91B21124D308330017404D /* SwiftFormat */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftFormat;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" --config \"${SRCROOT}/.swiftformat\" --swiftversion \"$SWIFT_VERSION\" \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
FA91B21224D308550017404D /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\" --config \"${SRCROOT}/.swiftlint.yml\" --path \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
FA91B21324D308800017404D /* SwiftFormat */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftFormat;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" --config \"${SRCROOT}/.swiftformat\" --swiftversion \"$SWIFT_VERSION\" \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
FA91B21424D308B00017404D /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\" --config \"${SRCROOT}/.swiftlint.yml\" --path \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
FA91B21524D308C70017404D /* SwiftFormat */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftFormat;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" --config \"${SRCROOT}/.swiftformat\" --swiftversion \"$SWIFT_VERSION\" \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
FA91B21624D308D90017404D /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\" --config \"${SRCROOT}/.swiftlint.yml\" --path \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
FA91B21724D308EB0017404D /* SwiftFormat */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftFormat;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" --config \"${SRCROOT}/.swiftformat\" --swiftversion \"$SWIFT_VERSION\" \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
FA91B21824D308FD0017404D /* SwiftLint */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = SwiftLint;
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/SwiftLint/swiftlint\" --config \"${SRCROOT}/.swiftlint.yml\" --path \"${SRCROOT}/${PRODUCT_NAME}\"\n";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -771,6 +1079,7 @@
217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */,
21D38B8E240A3C2300EC2A8D /* ConnectionProviderFactory.swift in Sources */,
217F39E02406E98400F1A0B3 /* AppSyncWebsocketProvider.swift in Sources */,
FAB7E91224D2644E00DF1EA1 /* RealtimeConnectionProvider+StaleConnection.swift in Sources */,
217F39D32406E98400F1A0B3 /* RealtimeConnectionProvider.swift in Sources */,
217F39D12406E98400F1A0B3 /* AppSyncConnectionRequest.swift in Sources */,
217F39DD2406E98400F1A0B3 /* AppSyncSubscriptionConnection+Connection.swift in Sources */,
@ -779,6 +1088,7 @@
217F39E22406E98400F1A0B3 /* StarscreamAdapter+Delegate.swift in Sources */,
217F39D52406E98400F1A0B3 /* RealtimeConnectionProviderResponse.swift in Sources */,
217F39D62406E98400F1A0B3 /* RealtimeConnectionProvider+MessageInterceptable.swift in Sources */,
FA67508224D33A7A005A1345 /* CountdownTimer.swift in Sources */,
217F39E62406E98400F1A0B3 /* SubscriptionConstants.swift in Sources */,
217F39D02406E98400F1A0B3 /* AppSyncMessage.swift in Sources */,
217F39E32406E98400F1A0B3 /* AppSyncLogger.swift in Sources */,
@ -795,7 +1105,6 @@
217F39E12406E98400F1A0B3 /* StarscreamAdapter.swift in Sources */,
217F39D72406E98400F1A0B3 /* RealtimeConnectionProvider+ConnectionInterceptable.swift in Sources */,
217F39DE2406E98400F1A0B3 /* AppSyncSubscriptionConnection+ErrorHandler.swift in Sources */,
217F39D22406E98400F1A0B3 /* RealtimeConnectionProvider+KeepAlive.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -804,11 +1113,16 @@
buildActionMask = 2147483647;
files = (
21D38B89240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift in Sources */,
217F39F22406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift in Sources */,
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */,
FA67507824D3244A005A1345 /* MockWebsocketProvider.swift in Sources */,
217F39F02406EA4000F1A0B3 /* AppSyncSubscriptionConnectionTests.swift in Sources */,
FA67507B24D338CB005A1345 /* Error+Extension.swift in Sources */,
21D38B8C240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift in Sources */,
21D38B8B240A39E400EC2A8D /* AppSyncJSONHelperTests.swift in Sources */,
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */,
FA67507E24D33976005A1345 /* RealtimeConnectionProviderTestBase.swift in Sources */,
FA67508424D33ACC005A1345 /* CountdownTimerTests.swift in Sources */,
FA67508024D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift in Sources */,
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -834,6 +1148,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
FA91B1F724D3063E0017404D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
FA91B1FE24D3063E0017404D /* AppDelegate.swift in Sources */,
FA91B21024D306730017404D /* AppSyncRTCProvider.swift in Sources */,
FA91B20024D3063E0017404D /* SceneDelegate.swift in Sources */,
FA91B20224D3063E0017404D /* ContentView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@ -863,6 +1188,14 @@
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
FA91B20824D306430017404D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
FA91B20924D306430017404D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@ -1164,6 +1497,48 @@
};
name = Release;
};
FA91B20C24D306430017404D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 106B0590055335CE3655E19D /* Pods-AppSyncRTCSample.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"AppSyncRTCSample/Preview Content\"";
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = AppSyncRTCSample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.amazonaws.AppSyncRTCSample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
FA91B20D24D306430017404D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7729985A4AC28DABC48ED0E7 /* Pods-AppSyncRTCSample.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"AppSyncRTCSample/Preview Content\"";
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = AppSyncRTCSample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.amazonaws.AppSyncRTCSample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -1212,6 +1587,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
FA91B20E24D306430017404D /* Build configuration list for PBXNativeTarget "AppSyncRTCSample" */ = {
isa = XCConfigurationList;
buildConfigurations = (
FA91B20C24D306430017404D /* Debug */,
FA91B20D24D306430017404D /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 217F39862405D9D500F1A0B3 /* Project object */;

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1160"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FA91B1FA24D3063E0017404D"
BuildableName = "AppSyncRTCSample.app"
BlueprintName = "AppSyncRTCSample"
ReferencedContainer = "container:AppSyncRealTimeClient.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FA91B1FA24D3063E0017404D"
BuildableName = "AppSyncRTCSample.app"
BlueprintName = "AppSyncRTCSample"
ReferencedContainer = "container:AppSyncRealTimeClient.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "FA91B1FA24D3063E0017404D"
BuildableName = "AppSyncRTCSample.app"
BlueprintName = "AppSyncRTCSample"
ReferencedContainer = "container:AppSyncRealTimeClient.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -8,7 +8,6 @@
import Foundation
extension AppSyncSubscriptionConnection {
func handleConnectionEvent(connectionState: ConnectionState) {
// If we get back not connected during an inprogress subscription connection
// we should retry the connection
@ -24,16 +23,19 @@ extension AppSyncSubscriptionConnection {
}
}
// MARK: -
// MARK: - Private implementations
private func startSubscription() {
guard subscriptionState == .notSubscribed else {
return
}
subscriptionState = .inProgress
let payload = convertToPayload(for: subscriptionItem.requestString, variables: subscriptionItem.variables)
let message = AppSyncMessage(id: subscriptionItem.identifier,
payload: payload,
type: .subscribe("start"))
let message = AppSyncMessage(
id: subscriptionItem.identifier,
payload: payload,
type: .subscribe("start")
)
connectionProvider?.write(message)
}

View File

@ -6,9 +6,9 @@
//
import Foundation
import Starscream
extension AppSyncSubscriptionConnection {
func handleError(error: Error) {
// If the error identifier is not for the this connection
// we return immediately without handling the error.
@ -16,12 +16,15 @@ extension AppSyncSubscriptionConnection {
identifier != subscriptionItem.identifier {
return
}
AppSyncLogger.error(error)
AppSyncSubscriptionConnection.logExtendedErrorInfo(for: error)
subscriptionState = .notSubscribed
guard let retryHandler = retryHandler,
let connectionError = error as? ConnectionProviderError else {
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
return
let connectionError = error as? ConnectionProviderError
else {
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
return
}
let retryAdvice = retryHandler.shouldRetryRequest(for: connectionError)
@ -34,4 +37,74 @@ extension AppSyncSubscriptionConnection {
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
}
}
public static func logExtendedErrorInfo(for error: Error) {
switch error {
case let typedError as ConnectionProviderError:
logExtendedErrorInfo(for: typedError)
case let typedError as WSError:
logExtendedErrorInfo(for: typedError)
case let typedError as NSError:
logExtendedErrorInfo(for: typedError)
default:
AppSyncLogger.error(error)
}
}
private static func logExtendedErrorInfo(for error: ConnectionProviderError) {
switch error {
case .connection:
AppSyncLogger.error("ConnectionProviderError.connection")
case .jsonParse(let identifier, let underlyingError):
AppSyncLogger.error(
"""
ConnectionProviderError.jsonParse; \
identifier=\(identifier ?? "(N/A)"); \
underlyingError=\(underlyingError?.localizedDescription ?? "(N/A)")
"""
)
case .limitExceeded(let identifier):
AppSyncLogger.error(
"""
ConnectionProviderError.limitExceeded; \
identifier=\(identifier ?? "(N/A)");
"""
)
case .subscription(let identifier, let errorPayload):
AppSyncLogger.error(
"""
ConnectionProviderError.jsonParse; \
identifier=\(identifier); \
additionalInfo=\(String(describing: errorPayload))
"""
)
case .other:
AppSyncLogger.error("ConnectionProviderError.other")
}
}
private static func logExtendedErrorInfo(for error: WSError) {
AppSyncLogger.error(error)
}
private static func logExtendedErrorInfo(for error: NSError) {
AppSyncLogger.error(
"""
NSError:\(error.domain); \
code:\(error.code); \
userInfo:\(error.userInfo)
"""
)
}
}
extension WSError: CustomStringConvertible {
public var description: String {
"""
WSError:\(message); \
code:\(code); \
type:\(type)
"""
}
}

View File

@ -8,7 +8,6 @@
import Foundation
enum SubscriptionState {
case notSubscribed
case inProgress
@ -17,7 +16,6 @@ enum SubscriptionState {
}
public class AppSyncSubscriptionConnection: SubscriptionConnection, RetryableConnection {
/// Connection provider that connects with the service
weak var connectionProvider: ConnectionProvider?
@ -34,12 +32,16 @@ public class AppSyncSubscriptionConnection: SubscriptionConnection, RetryableCon
self.connectionProvider = provider
}
public func subscribe(requestString: String,
variables: [String: Any?]?,
eventHandler: @escaping (SubscriptionItemEvent, SubscriptionItem) -> Void) -> SubscriptionItem {
subscriptionItem = SubscriptionItem(requestString: requestString,
variables: variables,
eventHandler: eventHandler)
public func subscribe(
requestString: String,
variables: [String: Any?]?,
eventHandler: @escaping (SubscriptionItemEvent, SubscriptionItem) -> Void
) -> SubscriptionItem {
subscriptionItem = SubscriptionItem(
requestString: requestString,
variables: variables,
eventHandler: eventHandler
)
addListener()
subscriptionItem.subscriptionEventHandler(.connection(.connecting), subscriptionItem)
connectionProvider?.connect()
@ -48,8 +50,7 @@ public class AppSyncSubscriptionConnection: SubscriptionConnection, RetryableCon
public func unsubscribe(item: SubscriptionItem) {
AppSyncLogger.debug("Unsubscribe - \(item.identifier)")
let message = AppSyncMessage(id: item.identifier,
type: .unsubscribe("stop"))
let message = AppSyncMessage(id: item.identifier, type: .unsubscribe("stop"))
connectionProvider?.write(message)
connectionProvider?.removeListener(identifier: subscriptionItem.identifier)
}

View File

@ -14,9 +14,11 @@ public protocol SubscriptionConnection {
/// - Parameter variables: variables for the subscription
/// - Parameter requestString: query for the subscription
/// - Parameter eventHandler: event handler
func subscribe(requestString: String,
variables: [String: Any?]?,
eventHandler: @escaping SubscriptionEventHandler) -> SubscriptionItem
func subscribe(
requestString: String,
variables: [String: Any?]?,
eventHandler: @escaping SubscriptionEventHandler
) -> SubscriptionItem
/// Unsubscribe from the subscription
/// - Parameter item: item to be unsubscribed

View File

@ -22,9 +22,11 @@ public struct SubscriptionItem {
// Subscription related events will be send to this handler.
let subscriptionEventHandler: SubscriptionEventHandler
public init(requestString: String,
variables: [String: Any?]?,
eventHandler: @escaping SubscriptionEventHandler) {
public init(
requestString: String,
variables: [String: Any?]?,
eventHandler: @escaping SubscriptionEventHandler
) {
self.identifier = UUID().uuidString
self.variables = variables

View File

@ -22,9 +22,7 @@ public struct AppSyncMessage {
/// Message type
public let messageType: AppSyncMessageType
public init(id: String? = nil,
payload: Payload? = nil,
type: AppSyncMessageType) {
public init(id: String? = nil, payload: Payload? = nil, type: AppSyncMessageType) {
self.id = id
self.payload = payload
self.messageType = type

View File

@ -15,9 +15,11 @@ public struct AppSyncResponse {
let responseType: AppSyncResponseType
init(id: String? = nil,
payload: [String: AppSyncJSONValue]? = nil,
type: AppSyncResponseType) {
init(
id: String? = nil,
payload: [String: AppSyncJSONValue]? = nil,
type: AppSyncResponseType
) {
self.id = id
self.responseType = type
self.payload = payload

View File

@ -13,7 +13,10 @@ extension RealtimeConnectionProvider: ConnectionInterceptable {
connectionInterceptors.append(interceptor)
}
public func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) -> AppSyncConnectionRequest {
public func interceptConnection(
_ request: AppSyncConnectionRequest,
for endpoint: URL
) -> AppSyncConnectionRequest {
let finalRequest = connectionInterceptors.reduce(request) { $1.interceptConnection($0, for: endpoint) }
return finalRequest
}

View File

@ -1,42 +0,0 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
extension RealtimeConnectionProvider {
/// Check if the we got a keep alive message within the given timeout window.
/// If we did not get the keepalive, disconnect the connection and return an error.
func disconnectIfStale() {
// Validate the connection only when it is connected or inprogress state.
guard status != .notConnected else {
return
}
AppSyncLogger.verbose("Validating connection")
let staleThreshold = lastKeepAliveTime + staleConnectionTimeout
let currentTime = DispatchTime.now()
if staleThreshold < currentTime {
serialConnectionQueue.async {[weak self] in
guard let self = self else {
return
}
self.status = .notConnected
self.websocket.disconnect()
AppSyncLogger.error("Realtime connection is stale, disconnected.")
self.updateCallback(event: .error(ConnectionProviderError.connection))
}
} else {
DispatchQueue.global().asyncAfter(deadline: currentTime + staleConnectionTimeout) { [weak self] in
self?.disconnectIfStale()
}
}
}
}

View File

@ -0,0 +1,49 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
extension RealtimeConnectionProvider {
/// Start a stale connection timer, first invalidating and destroying any existing timer
func startStaleConnectionTimer() {
AppSyncLogger.debug("Starting stale connection timer for \(staleConnectionTimeout)s")
if staleConnectionTimer != nil {
stopStaleConnectionTimer()
}
staleConnectionTimer = CountdownTimer(interval: staleConnectionTimeout) {
self.disconnectStaleConnection()
}
}
/// Stop and destroy any existing stale connection timer
func stopStaleConnectionTimer() {
AppSyncLogger.debug("Stopping and destroying stale connection timer")
staleConnectionTimer?.invalidate()
staleConnectionTimer = nil
}
/// Reset the stale connection timer in response to receiving a message
func resetStaleConnectionTimer() {
AppSyncLogger.debug("Resetting stale connection timer")
staleConnectionTimer?.resetCountdown()
}
/// Fired when the stale connection timer expires
private func disconnectStaleConnection() {
serialConnectionQueue.async {[weak self] in
guard let self = self else {
return
}
self.status = .notConnected
self.websocket.disconnect()
AppSyncLogger.error("Realtime connection is stale, disconnected.")
self.updateCallback(event: .error(ConnectionProviderError.connection))
}
}
}

View File

@ -14,11 +14,11 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
// Inform the callback when ack gives back a response.
AppSyncLogger.debug("WebsocketDidConnect, sending init message...")
sendConnectionInitMessage()
disconnectIfStale()
startStaleConnectionTimer()
}
public func websocketDidDisconnect(provider: AppSyncWebsocketProvider, error: Error?) {
serialConnectionQueue.async {[weak self] in
serialConnectionQueue.async { [weak self] in
guard let self = self else {
return
}
@ -43,13 +43,13 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
// MARK: - Handle websocket response
func handleResponse(_ response: RealtimeConnectionProviderResponse) {
lastKeepAliveTime = DispatchTime.now()
resetStaleConnectionTimer()
switch response.responseType {
case .connectionAck:
/// Only from in progress state, the connection can transition to connected state.
/// The below guard statement make sure that. If we get connectionAck in other state means that
/// we have initiated a disconnect parallely.
// Only from in progress state, the connection can transition to connected state.
// The below guard statement make sure that. If we get connectionAck in other
// state means that we have initiated a disconnect parallely.
guard status == .inProgress else {
return
}
@ -60,14 +60,24 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
self.status = .connected
self.updateCallback(event: .connection(self.status))
/// If the service returns a connection timeout, use that instead of the default
// If the service returns a connection timeout, use that instead of the default
if case let .number(value) = response.payload?["connectionTimeoutMs"] {
self.staleConnectionTimeout = DispatchTimeInterval.milliseconds(Int(value))
let interval = value / 1_000
if interval != self.staleConnectionTimer?.interval {
AppSyncLogger.debug(
"""
Resetting keep alive timer in response to service timeout \
instructions \(interval)s
"""
)
self.staleConnectionTimeout = interval
self.startStaleConnectionTimer()
}
}
}
case .error:
/// If we get an error in connection inprogress state, return back as connection error.
// If we get an error in connection inprogress state, return back as connection error.
if status == .inProgress {
serialConnectionQueue.async {[weak self] in
guard let self = self else {
@ -79,14 +89,14 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
return
}
/// Return back as generic error if there is no identifier.
// Return back as generic error if there is no identifier.
guard let identifier = response.id else {
let genericError = ConnectionProviderError.other
updateCallback(event: .error(genericError))
return
}
/// Map to limit exceed error if we get MaxSubscriptionsReachedException
// Map to limit exceed error if we get MaxSubscriptionsReachedException
if let errorType = response.payload?["errorType"],
errorType == "MaxSubscriptionsReachedException" {
let limitExceedError = ConnectionProviderError.limitExceeded(identifier)
@ -103,7 +113,7 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
updateCallback(event: .data(appSyncResponse))
}
case .keepAlive:
print("")
AppSyncLogger.debug("\(self) received keepAlive")
}
}
}
@ -111,7 +121,7 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
extension RealtimeConnectionProviderResponse {
func toAppSyncResponse() -> AppSyncResponse? {
guard let appSyncType = self.responseType.toAppSyncResponseType() else {
guard let appSyncType = responseType.toAppSyncResponseType() else {
return nil
}
return AppSyncResponse(id: id, payload: payload, type: appSyncType)

View File

@ -18,20 +18,26 @@ public class RealtimeConnectionProvider: ConnectionProvider {
var messageInterceptors: [MessageInterceptor] = []
var connectionInterceptors: [ConnectionInterceptor] = []
var staleConnectionTimeout = DispatchTimeInterval.seconds(5 * 60)
var lastKeepAliveTime = DispatchTime.now()
/// Maximum number of seconds a connection may go without receiving a keep alive
/// message before we consider it stale and force a disconnect
var staleConnectionTimeout: TimeInterval = 5 * 60
/// A timer that automatically disconnects the current connection if it goes longer
/// than `staleConnectionTimeout` without activity. Receiving any data or "keep
/// alive" message will cause the timer to be reset to the full interval.
var staleConnectionTimer: CountdownTimer?
/// Serial queue for websocket connection.
///
/// Each connection request will be send to this queue. Connection request are handled one at a time.
/// Each connection request will be sent to this queue. Connection request are
/// handled one at a time.
let serialConnectionQueue = DispatchQueue(label: "com.amazonaws.AppSyncRealTimeConnectionProvider.serialQueue")
let serialCallbackQueue = DispatchQueue(label: "com.amazonaws.AppSyncRealTimeConnectionProvider.callbackQueue")
let serialWriteQueue = DispatchQueue(label: "com.amazonaws.AppSyncRealTimeConnectionProvider.writeQueue")
public init(for url: URL,
websocket: AppSyncWebsocketProvider) {
public init(for url: URL, websocket: AppSyncWebsocketProvider) {
self.url = url
self.websocket = websocket
}
@ -52,9 +58,11 @@ public class RealtimeConnectionProvider: ConnectionProvider {
let request = AppSyncConnectionRequest(url: self.url)
let signedRequest = self.interceptConnection(request, for: self.url)
DispatchQueue.global().async {
self.websocket.connect(url: signedRequest.url,
protocols: ["graphql-ws"],
delegate: self)
self.websocket.connect(
url: signedRequest.url,
protocols: ["graphql-ws"],
delegate: self
)
}
}
}
@ -91,6 +99,8 @@ public class RealtimeConnectionProvider: ConnectionProvider {
public func disconnect() {
websocket.disconnect()
staleConnectionTimer?.invalidate()
staleConnectionTimer = nil
}
public func addListener(identifier: String, callback: @escaping ConnectionProviderCallback) {
@ -107,13 +117,14 @@ public class RealtimeConnectionProvider: ConnectionProvider {
self.listeners.removeValue(forKey: identifier)
if self.listeners.count == 0 {
if self.listeners.isEmpty {
AppSyncLogger.debug("All listeners removed, disconnecting")
self.serialConnectionQueue.async { [weak self] in
guard let self = self else {
return
}
self.status = .notConnected
self.websocket.disconnect()
self.disconnect()
}
}
}
@ -132,7 +143,7 @@ public class RealtimeConnectionProvider: ConnectionProvider {
}
func receivedConnectionInit() {
self.serialConnectionQueue.async {[weak self] in
serialConnectionQueue.async { [weak self] in
guard let self = self else {
return
}

View File

@ -17,9 +17,11 @@ struct RealtimeConnectionProviderResponse {
let responseType: RealtimeConnectionProviderResponseType
init(id: String? = nil,
payload: [String: AppSyncJSONValue]? = nil,
type: RealtimeConnectionProviderResponseType) {
init(
id: String? = nil,
payload: [String: AppSyncJSONValue]? = nil,
type: RealtimeConnectionProviderResponseType
) {
self.id = id
self.responseType = type
self.payload = payload

View File

@ -7,7 +7,7 @@
import Foundation
public protocol ConnectionProvider: class {
public protocol ConnectionProvider: AnyObject {
func connect()

View File

@ -18,8 +18,9 @@ public enum ConnectionProviderError: Error {
/// Caused when a limit exceeded error occurs. The optional String will have the identifier if available.
case limitExceeded(String?)
/// Caused when any other subscription related error occurs. The optional String will have the identifier if available.
/// The second optional value is the error payload in dictionary format.
/// Caused when any other subscription related error occurs. The optional String
/// will have the identifier if available. The second optional value is the error
/// payload in dictionary format.
case subscription(String, [String: Any]?)
/// Any other error is identified by this type

View File

@ -10,9 +10,11 @@ import Foundation
/// Create connection providers to connect to the websocket endpoint of the AppSync endpoint.
public struct ConnectionProviderFactory {
public static func createConnectionProvider(for url: URL,
authInterceptor: AuthInterceptor,
connectionType: SubscriptionConnectionType) -> ConnectionProvider {
public static func createConnectionProvider(
for url: URL,
authInterceptor: AuthInterceptor,
connectionType: SubscriptionConnectionType
) -> ConnectionProvider {
let provider = ConnectionProviderFactory.createConnectionProvider(for: url, connectionType: connectionType)
if let messageInterceptable = provider as? MessageInterceptable {
@ -26,8 +28,10 @@ public struct ConnectionProviderFactory {
return provider
}
static func createConnectionProvider(for url: URL,
connectionType: SubscriptionConnectionType) -> ConnectionProvider {
static func createConnectionProvider(
for url: URL,
connectionType: SubscriptionConnectionType
) -> ConnectionProvider {
switch connectionType {
case .appSyncRealtime:
let websocketProvider = StarscreamAdapter()

View File

@ -24,8 +24,10 @@ public class APIKeyAuthInterceptor: AuthInterceptor {
/// * "x-api-key": <string> : Api key configured for AppSync API
/// The value of payload is {}
/// - Parameter request: Signed request
public func interceptConnection(_ request: AppSyncConnectionRequest,
for endpoint: URL) -> AppSyncConnectionRequest {
public func interceptConnection(
_ request: AppSyncConnectionRequest,
for endpoint: URL
) -> AppSyncConnectionRequest {
let host = endpoint.host!
let authHeader = APIKeyAuthenticationHeader(apiKey: apiKey, host: host)
let base64Auth = AppSyncJSONHelper.base64AuthenticationBlob(authHeader)
@ -54,9 +56,11 @@ public class APIKeyAuthInterceptor: AuthInterceptor {
var payload = message.payload ?? AppSyncMessage.Payload()
payload.authHeader = authHeader
let signedMessage = AppSyncMessage(id: message.id,
payload: payload,
type: message.messageType)
let signedMessage = AppSyncMessage(
id: message.id,
payload: payload,
type: message.messageType
)
return signedMessage
default:
AppSyncLogger.debug("Message type does not need signing - \(message.messageType)")

View File

@ -11,7 +11,7 @@ public class OIDCAuthInterceptor: AuthInterceptor {
let authProvider: OIDCAuthProvider
public init (_ authProvider: OIDCAuthProvider) {
public init(_ authProvider: OIDCAuthProvider) {
self.authProvider = authProvider
}
@ -30,9 +30,11 @@ public class OIDCAuthInterceptor: AuthInterceptor {
var payload = message.payload ?? AppSyncMessage.Payload()
payload.authHeader = authHeader
let signedMessage = AppSyncMessage(id: message.id,
payload: payload,
type: message.messageType)
let signedMessage = AppSyncMessage(
id: message.id,
payload: payload,
type: message.messageType
)
return signedMessage
default:
AppSyncLogger.debug("Message type does not need signing - \(message.messageType)")
@ -40,7 +42,10 @@ public class OIDCAuthInterceptor: AuthInterceptor {
return message
}
public func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) -> AppSyncConnectionRequest {
public func interceptConnection(
_ request: AppSyncConnectionRequest,
for endpoint: URL
) -> AppSyncConnectionRequest {
let host = endpoint.host!
let jwtToken: String
switch authProvider.getLatestAuthToken() {

View File

@ -9,13 +9,14 @@ import Foundation
/// Connection interceptor for real time connection provider
public class RealtimeGatewayURLInterceptor: ConnectionInterceptor {
public init() {
// Do nothing
}
public func interceptConnection(_ request: AppSyncConnectionRequest,
for endpoint: URL) -> AppSyncConnectionRequest {
public func interceptConnection(
_ request: AppSyncConnectionRequest,
for endpoint: URL
) -> AppSyncConnectionRequest {
guard let host = endpoint.host else {
return request
}
@ -23,8 +24,10 @@ public class RealtimeGatewayURLInterceptor: ConnectionInterceptor {
return request
}
urlComponents.scheme = SubscriptionConstants.realtimeWebsocketScheme
urlComponents.host = host.replacingOccurrences(of: SubscriptionConstants.appsyncHostPart,
with: SubscriptionConstants.appsyncRealtimeHostPart)
urlComponents.host = host.replacingOccurrences(
of: SubscriptionConstants.appsyncHostPart,
with: SubscriptionConstants.appsyncRealtimeHostPart
)
guard let url = urlComponents.url else {
return request
}

View File

@ -49,6 +49,7 @@ struct AppSyncLogger {
NSLog("%@", log)
}
}
static func error(_ error: Error) {
if #available(iOS 10.0, *) {
os_log("%@", type: .error, error.localizedDescription)

View File

@ -0,0 +1,60 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
/// A resettable timer that executes `onCountdownComplete` closure after
/// `interval`.
///
/// The timer will execute the closure on a background queue. If the closure
/// includes work that must be performed on a specific queue, make sure to dispatch
/// it inside the closure.
class CountdownTimer {
/// The interval after which the timer will fire
let interval: TimeInterval
private var workItem: DispatchWorkItem?
private let onCountdownComplete: () -> Void
init(interval: TimeInterval, onCountdownComplete: @escaping () -> Void) {
self.interval = interval
self.onCountdownComplete = onCountdownComplete
createAndScheduleTimer()
}
/// Resets the countdown of the timer to `interval`
func resetCountdown() {
invalidate()
createAndScheduleTimer()
}
/// Invalidates the timer
func invalidate() {
workItem?.cancel()
workItem = nil
}
/// Invoked by the timer. Do not execute this method directly.
private func timerFired() {
defer {
workItem = nil
}
guard let workItem = workItem, !workItem.isCancelled else {
return
}
onCountdownComplete()
}
private func createAndScheduleTimer() {
let workItem = DispatchWorkItem { self.timerFired() }
self.workItem = workItem
DispatchQueue.global().asyncAfter(deadline: .now() + interval, execute: workItem)
}
}

View File

@ -6,6 +6,5 @@
//
public enum SubscriptionConnectionType {
case appSyncRealtime
}

View File

@ -22,7 +22,6 @@ public struct SubscriptionConstants {
}
public struct RealtimeProviderConstants {
public static let header = "header"
public static let payload = "payload"

View File

@ -28,7 +28,7 @@ public protocol AppSyncWebsocketProvider {
}
/// Delegate method to get callbacks on websocket provider connection
public protocol AppSyncWebsocketDelegate: class {
public protocol AppSyncWebsocketDelegate: AnyObject {
func websocketDidConnect(provider: AppSyncWebsocketProvider)

View File

@ -9,8 +9,8 @@ import Foundation
import Starscream
public class StarscreamAdapter: AppSyncWebsocketProvider {
public init() {
// Do nothing
}
var socket: WebSocket?

View File

@ -45,8 +45,8 @@ class AppSyncRealTimeClientIntegrationTests: XCTestCase {
}
}
/// Simple integration test against an AppSync service provisioned with a simple Todo model generated by the
/// GraphQL Transform on the `model` directive.
/// Simple integration test against an AppSync service provisioned with a simple
/// Todo model generated by the GraphQL Transform on the `model` directive.
///
/// - Given: A subscription connection on an AppSync endpoint with Todo model provisioned
/// - When:
@ -57,11 +57,16 @@ class AppSyncRealTimeClientIntegrationTests: XCTestCase {
func testSubscribeWithSubscriptionConnection() {
let subscribeSuccess = expectation(description: "subscribe successfully")
let authInterceptor = APIKeyAuthInterceptor(apiKey)
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(for: url,
authInterceptor: authInterceptor,
connectionType: .appSyncRealtime)
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
for: url,
authInterceptor: authInterceptor,
connectionType: .appSyncRealtime
)
let subscriptionConnection = AppSyncSubscriptionConnection(provider: connectionProvider)
_ = subscriptionConnection.subscribe(requestString: requestString, variables: nil) { (event, item) in
_ = subscriptionConnection.subscribe(
requestString: requestString,
variables: nil
) { event, _ in
switch event {
case .connection(let subscriptionConnectionEvent):
@ -83,8 +88,11 @@ class AppSyncRealTimeClientIntegrationTests: XCTestCase {
wait(for: [subscribeSuccess], timeout: TestCommonConstants.networkTimeout)
}
/// The purpose of this test is to ensure that all websockets can be successfully created, exercised and terminated
/// while keeping a single connection provider in memory. Specifically, the following test exercises the following:
/// The purpose of this test is to ensure that all websockets can be successfully
/// created, exercised and terminated while keeping a single connection provider in
/// memory.
///
/// Specifically, the following test exercises the following:
/// 1. Create a new connection provider
/// 2. Create multiple subscriptions
/// 3. Unsubscribe the subscriptions
@ -102,11 +110,16 @@ class AppSyncRealTimeClientIntegrationTests: XCTestCase {
connectedInvoked.expectedFulfillmentCount = 3
let authInterceptor = APIKeyAuthInterceptor(apiKey)
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(for: url,
authInterceptor: authInterceptor,
connectionType: .appSyncRealtime)
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
for: url,
authInterceptor: authInterceptor,
connectionType: .appSyncRealtime
)
let subscriptionConnection1 = AppSyncSubscriptionConnection(provider: connectionProvider)
let item1 = subscriptionConnection1.subscribe(requestString: requestString, variables: nil) { (event, item) in
let item1 = subscriptionConnection1.subscribe(
requestString: requestString,
variables: nil
) { event, _ in
if case let .connection(state) = event {
if case .connected = state {
connectedInvoked.fulfill()
@ -114,7 +127,10 @@ class AppSyncRealTimeClientIntegrationTests: XCTestCase {
}
}
let subscriptionConnection2 = AppSyncSubscriptionConnection(provider: connectionProvider)
let item2 = subscriptionConnection2.subscribe(requestString: requestString, variables: nil) { (event, item) in
let item2 = subscriptionConnection2.subscribe(
requestString: requestString,
variables: nil
) { event, _ in
if case let .connection(state) = event {
if case .connected = state {
connectedInvoked.fulfill()
@ -122,7 +138,10 @@ class AppSyncRealTimeClientIntegrationTests: XCTestCase {
}
}
let subscriptionConnection3 = AppSyncSubscriptionConnection(provider: connectionProvider)
let item3 = subscriptionConnection3.subscribe(requestString: requestString, variables: nil) { (event, item) in
let item3 = subscriptionConnection3.subscribe(
requestString: requestString,
variables: nil
) { event, _ in
if case let .connection(state) = event {
if case .connected = state {
connectedInvoked.fulfill()
@ -145,15 +164,19 @@ class AppSyncRealTimeClientIntegrationTests: XCTestCase {
subscriptionConnection2.unsubscribe(item: item2)
subscriptionConnection3.unsubscribe(item: item3)
// Sleep is required here as disconnecting the connection provider is done asynchronously on the connection
// queue for the very last unsubscribe. This means we need to "pull" for the status to ensure the system is operating correctly by sleeping
// and checking that the status is .notConnected
// Sleep is required here as disconnecting the connection provider is done
// asynchronously on the connection queue for the very last unsubscribe. This
// means we need to "pull" for the status to ensure the system is operating
// correctly by sleeping and checking that the status is .notConnected
sleep(5)
XCTAssertEqual(realTimeConnectionProvider.status, .notConnected)
let newConnectedInvoked = expectation(description: "Connection established")
let subscriptionConnection4 = AppSyncSubscriptionConnection(provider: connectionProvider)
let newItem = subscriptionConnection4.subscribe(requestString: requestString, variables: nil) { (event, item) in
let newItem = subscriptionConnection4.subscribe(
requestString: requestString,
variables: nil
) { event, _ in
if case let .connection(state) = event {
if case .connected = state {
newConnectedInvoked.fulfill()

View File

@ -10,7 +10,17 @@ import XCTest
class AppSyncSubscriptionConnectionTests: XCTestCase {
let mockRequestString = "subscription OnCreateMessage {\n onCreateMessage {\n __typename\n id\n message\n createdAt\n }\n}"
let mockRequestString = """
subscription OnCreateMessage {
onCreateMessage {
__typename
id
message
createdAt
}
}
"""
let variables = [String: Any]()
/// Test to check if subscription works
@ -28,7 +38,10 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
let connectingMessageExpectation = expectation(description: "Connecting event should be fired")
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { (event, item) in
let item = connection.subscribe(
requestString: mockRequestString,
variables: variables
) { event, _ in
switch event {
case .connection(let status):
@ -63,7 +76,10 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
let connectingMessageExpectation = expectation(description: "Connecting event should be fired")
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
let unsubscribeAckExpectation = expectation(description: "Not connected event should be fired")
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { (event, item) in
let item = connection.subscribe(
requestString: mockRequestString,
variables: variables
) { event, _ in
switch event {
case .connection(let status):
@ -104,7 +120,10 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
let connectingMessageExpectation = expectation(description: "Connecting event should be fired")
let errorEventExpectation = expectation(description: "Error event should be fired")
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { (event, item) in
let item = connection.subscribe(
requestString: mockRequestString,
variables: variables
) { event, _ in
switch event {
case .connection(let status):
@ -141,7 +160,10 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
let connectingMessageExpectation = expectation(description: "Connecting event should be fired")
let errorEventExpectation = expectation(description: "Error event should be fired")
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { (event, item) in
let item = connection.subscribe(
requestString: mockRequestString,
variables: variables
) { event, _ in
switch event {
case .connection(let status):
@ -178,7 +200,10 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
let dataEventExpectation = expectation(description: "Data event should be fired")
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { (event, item) in
let item = connection.subscribe(
requestString: mockRequestString,
variables: variables
) { event, _ in
switch event {
case .connection(let status):
@ -198,22 +223,27 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
XCTAssertNotNil(item, "Subscription item should not be nil")
wait(for: [connectingMessageExpectation, connectedMessageExpectation], timeout: 5, enforceOrder: true)
let mockResponse = AppSyncResponse(id: item.identifier,
payload: ["data" : "testData"], type: .data)
let mockResponse = AppSyncResponse(
id: item.identifier,
payload: ["data": "testData"],
type: .data
)
connectionProvider.sendDataResponse(mockResponse)
wait(for: [dataEventExpectation], timeout: 2)
}
func testNilDataInVariables() {
let variablesWithNil = ["key": nil] as [String : Any?]
let variablesWithNil = ["key": nil] as [String: Any?]
let connectionProvider = MockConnectionProvider()
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
let connectingMessageExpectation = expectation(description: "Connecting event should be fired")
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
let item = connection.subscribe(requestString: mockRequestString,
variables: variablesWithNil) { (event, item) in
let item = connection.subscribe(
requestString: mockRequestString,
variables: variablesWithNil
) { event, _ in
switch event {
case .connection(let status):

View File

@ -0,0 +1,65 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import XCTest
@testable import AppSyncRealTimeClient
class ConnectionProviderStaleConnectionTests: RealtimeConnectionProviderTestBase {
/// Stale connection test
///
/// Given:
/// - A provider configured with a default stale connection timeout
/// When:
/// - The service sends a message containing an override timeout value
/// Then:
/// - The provider updates its stale connection timeout to the service-provided value
func testServiceOverridesStaleConnectionTimeout() {
receivedNotConnected.isInverted = true
receivedError.isInverted = true
let expectedTimeoutInSeconds = 60.0
let timeoutInMilliseconds = Int(expectedTimeoutInSeconds) * 1_000
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
self.websocketDelegate = delegate
DispatchQueue.global().async {
delegate?.websocketDidConnect(provider: self.websocket)
}
}
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
let connectionAckMessage = RealtimeConnectionProviderTestBase
.makeConnectionAckMessage(withTimeout: timeoutInMilliseconds)
let onWrite: MockWebsocketProvider.OnWrite = { message in
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
XCTFail("Incoming message did not have 'connection_init' type")
return
}
self.websocketDelegate.websocketDidReceiveData(
provider: self.websocket,
data: connectionAckMessage
)
}
websocket = MockWebsocketProvider(
onConnect: onConnect,
onDisconnect: onDisconnect,
onWrite: onWrite
)
let provider = createProviderAndConnect()
wait(for: [receivedConnected], timeout: 0.05)
XCTAssertEqual(provider.staleConnectionTimeout, expectedTimeoutInSeconds)
waitForExpectations(timeout: 0.05)
}
}

View File

@ -0,0 +1,163 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import XCTest
@testable import AppSyncRealTimeClient
class ConnectionProviderTests: RealtimeConnectionProviderTestBase {
/// Provider test
///
/// Given:
/// - A configured subscriber -> provider -> websocket chain
/// When:
/// - I invoke `provider.connect()`
/// - And the websocket properly connects
/// Then:
/// - The subscriber is notified of the successful connection
func testSuccessfulConnection() {
receivedNotConnected.isInverted = true
receivedError.isInverted = true
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
self.websocketDelegate = delegate
DispatchQueue.global().async {
delegate?.websocketDidConnect(provider: self.websocket)
}
}
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
let onWrite: MockWebsocketProvider.OnWrite = { message in
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
XCTFail("Incoming message did not have 'connection_init' type")
return
}
self.websocketDelegate.websocketDidReceiveData(
provider: self.websocket,
data: RealtimeConnectionProviderTestBase.makeConnectionAckMessage()
)
}
websocket = MockWebsocketProvider(
onConnect: onConnect,
onDisconnect: onDisconnect,
onWrite: onWrite
)
// Retain the provider so it doesn't release prior to executing callbacks
let provider = createProviderAndConnect()
// Get rid of "written to, but never read" compiler warnings
print(provider)
waitForExpectations(timeout: 0.05)
}
/// Provider test
///
/// Given:
/// - A configured subscriber -> provider -> websocket chain
/// When:
/// - I invoke `provider.connect()`
/// - And the websocket reports a connection error
/// Then:
/// - The subscriber is notified of the unsuccessful connection
func testConnectionError() {
receivedConnected.isInverted = true
receivedNotConnected.isInverted = true
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
self.websocketDelegate = delegate
DispatchQueue.global().async {
delegate?.websocketDidConnect(provider: self.websocket)
}
}
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
let onWrite: MockWebsocketProvider.OnWrite = { message in
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
XCTFail("Incoming message did not have 'connection_init' type")
return
}
self.websocketDelegate.websocketDidDisconnect(
provider: self.websocket,
error: "test error"
)
}
websocket = MockWebsocketProvider(
onConnect: onConnect,
onDisconnect: onDisconnect,
onWrite: onWrite
)
// Retain the provider so it doesn't release prior to executing callbacks
let provider = createProviderAndConnect()
// Get rid of "written to, but never read" compiler warnings
print(provider)
waitForExpectations(timeout: 0.05)
}
/// Stale connection test
///
/// Given:
/// - A provider configured with a default stale connection timeout
/// When:
/// - The service sends a message containing an override timeout value
/// Then:
/// - The provider updates its stale connection timeout to the service-provided value
func testServiceOverridesStaleConnectionTimeout() {
receivedNotConnected.isInverted = true
receivedError.isInverted = true
let expectedTimeoutInSeconds = 60.0
let timeoutInMilliseconds = Int(expectedTimeoutInSeconds) * 1_000
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
self.websocketDelegate = delegate
DispatchQueue.global().async {
delegate?.websocketDidConnect(provider: self.websocket)
}
}
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
let connectionAckMessage = RealtimeConnectionProviderTestBase
.makeConnectionAckMessage(withTimeout: timeoutInMilliseconds)
let onWrite: MockWebsocketProvider.OnWrite = { message in
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
XCTFail("Incoming message did not have 'connection_init' type")
return
}
self.websocketDelegate.websocketDidReceiveData(
provider: self.websocket,
data: connectionAckMessage
)
}
websocket = MockWebsocketProvider(
onConnect: onConnect,
onDisconnect: onDisconnect,
onWrite: onWrite
)
let provider = createProviderAndConnect()
wait(for: [receivedConnected], timeout: 0.05)
XCTAssertEqual(provider.staleConnectionTimeout, expectedTimeoutInSeconds)
waitForExpectations(timeout: 0.05)
}
}

View File

@ -0,0 +1,91 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import XCTest
@testable import AppSyncRealTimeClient
class RealtimeConnectionProviderTestBase: XCTestCase {
let url = URL(string: "https://www.appsyncrealtimeclient.test/")!
var websocket: MockWebsocketProvider!
//swiftlint:disable:next weak_delegate
var websocketDelegate: AppSyncWebsocketDelegate!
// Shared test expectations. Set expected fulfillment counts and inversions as
// needed in the body of the test.
var receivedInProgress: XCTestExpectation!
var receivedConnected: XCTestExpectation!
var receivedNotConnected: XCTestExpectation!
var receivedError: XCTestExpectation!
override func setUp() {
receivedInProgress = expectation(description: "receivedInProgress")
receivedConnected = expectation(description: "receivedConnected")
receivedNotConnected = expectation(description: "receivedNotConnected")
receivedError = expectation(description: "receivedError")
}
// MARK: - Utilities
/// Creates a RealtimeConnectionProvider, adds a listener that fulfills the shared
/// expectations as appropriate, and invokes `connect()`. Returns the provider for
/// subsequent testing.
///
/// Preconditions:
/// - `self.websocket` must be initialized in the mock provider's `onConnect`
func createProviderAndConnect() -> RealtimeConnectionProvider {
let provider = RealtimeConnectionProvider(for: url, websocket: websocket)
provider.addListener(identifier: "testListener") { event in
switch event {
case .connection(let connectionState):
switch connectionState {
case .inProgress:
self.receivedInProgress.fulfill()
case .connected:
self.receivedConnected.fulfill()
case .notConnected:
self.receivedNotConnected.fulfill()
}
case .error:
self.receivedError.fulfill()
default:
break
}
}
provider.connect()
return provider
}
/// Given a Stringified AppSyncMessage, validates the `type` is equal to `expectedType`
/// - Parameter message: a string representation of a websocket message
/// - Parameter expectedType: the expected value of the type
/// - Returns: type `type` field of the message, if present
static func messageType(of message: String, equals expectedType: String) -> Bool {
guard
let messageData = message.data(using: .utf8),
let dict = try? JSONSerialization.jsonObject(with: messageData) as? [String: String]
else {
return false
}
guard let type = dict["type"] else {
return false
}
return type == expectedType
}
/// Creates a connection acknowledgement message with the specified timeout
/// - Parameter timeout: stale connection timeout, in milliseconds (defaults to 300,000)
static func makeConnectionAckMessage(withTimeout timeout: Int = 300_000) -> Data {
#"{"type":"connection_ack","payload":{"connectionTimeoutMs":\#(timeout)}}"#
.data(using: .utf8)!
}
}

View File

@ -1,31 +0,0 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import XCTest
class RealtimeConnectionProviderTests: XCTestCase {
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

View File

@ -25,8 +25,8 @@ class APIKeyAuthInterceptorTests: XCTestCase {
assertionFailure("Query parameters should not be nil")
return
}
XCTAssertTrue(queries.contains{ $0.name == "header"}, "Should contain the header query")
XCTAssertTrue(queries.contains{ $0.name == "payload"}, "Should contain the payload query")
XCTAssertTrue(queries.contains { $0.name == "header"}, "Should contain the header query")
XCTAssertTrue(queries.contains { $0.name == "payload"}, "Should contain the payload query")
}
func testInterceptMessage() {

View File

@ -41,7 +41,6 @@ class AppSyncJSONHelperTests: XCTestCase {
}
}
private class MockInvalidHeader: AuthenticationHeader {
private enum CodingKeys: String, CodingKey {

View File

@ -25,8 +25,8 @@ class OIDCAuthInterceptorTests: XCTestCase {
assertionFailure("Query parameters should not be nil")
return
}
XCTAssertTrue(queries.contains{ $0.name == "header"}, "Should contain the header query")
XCTAssertTrue(queries.contains{ $0.name == "payload"}, "Should contain the payload query")
XCTAssertTrue(queries.contains { $0.name == "header"}, "Should contain the header query")
XCTAssertTrue(queries.contains { $0.name == "payload"}, "Should contain the payload query")
}
func testInterceptMessage() {

View File

@ -1,7 +1,8 @@
//
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// Licensed under the Amazon Software License
// http://aws.amazon.com/asl/
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
@ -15,7 +16,7 @@ class MockConnectionProvider: ConnectionProvider {
/// If this boolean is set, all the request to this provider will return `notConnected` event
var isConnected: Bool = true
init (validConnection: Bool = true) {
init(validConnection: Bool = true) {
self.validConnection = validConnection
}
@ -49,16 +50,20 @@ class MockConnectionProvider: ConnectionProvider {
switch message.messageType {
case .connectionInit:
print("")
break
case .subscribe:
let response = AppSyncResponse(id: message.id,
payload: [:],
type: .subscriptionAck)
let response = AppSyncResponse(
id: message.id,
payload: [:],
type: .subscriptionAck
)
listener?(.data(response))
case .unsubscribe:
let response = AppSyncResponse(id: message.id,
payload: [:],
type: .unsubscriptionAck)
let response = AppSyncResponse(
id: message.id,
payload: [:],
type: .unsubscriptionAck
)
listener?(.data(response))
}
}

View File

@ -0,0 +1,45 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
import AppSyncRealTimeClient
class MockWebsocketProvider: AppSyncWebsocketProvider {
typealias OnConnect = (URL, [String], AppSyncWebsocketDelegate?) -> Void
typealias OnDisconnect = () -> Void
typealias OnWrite = (String) -> Void
var isConnected: Bool
let onConnect: OnConnect
let onDisconnect: OnDisconnect
let onWrite: OnWrite
init(
onConnect: @escaping OnConnect,
onDisconnect: @escaping OnDisconnect,
onWrite: @escaping OnWrite
) {
self.isConnected = false
self.onConnect = onConnect
self.onDisconnect = onDisconnect
self.onWrite = onWrite
}
func connect(url: URL, protocols: [String], delegate: AppSyncWebsocketDelegate?) {
onConnect(url, protocols, delegate)
}
func disconnect() {
onDisconnect()
}
func write(message: String) {
onWrite(message)
}
}

View File

@ -0,0 +1,141 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import XCTest
@testable import AppSyncRealTimeClient
/// Tests for the CountdownTimer internal support type. We do not expose this as
/// part of the public API, but its behavior is critical to the RTC's ability to
/// handle stale connections.
///
/// Clearly many of these tests are timing dependent, and could vary based on the
/// execution environment. We'll try these tests now, and if they turn out to be
/// unstable then we'll remove it and try a different approach.
class CountdownTimerTests: XCTestCase {
func testTimerFires() {
let timerFired = expectation(description: "timerFired")
let timer = CountdownTimer(interval: 0.1) { timerFired.fulfill() }
waitForExpectations(timeout: 1.0)
timer.invalidate()
}
func testTimerDoesNotFireEarly() {
let timerFired = expectation(description: "timerFired")
timerFired.isInverted = true
let timer = CountdownTimer(interval: 0.5) { timerFired.fulfill() }
waitForExpectations(timeout: 0.1)
timer.invalidate()
}
func testTimerFiresOnBackgroundQueue() {
let timerFired = expectation(description: "timerFired")
timerFired.isInverted = true
XCTAssert(Thread.isMainThread)
let timer = CountdownTimer(interval: 1.0) {
timerFired.fulfill()
XCTAssertFalse(Thread.isMainThread)
}
waitForExpectations(timeout: 0.1)
timer.invalidate()
}
func testTimerDoesNotFireAfterInvalidate() {
let timerFired = expectation(description: "timerFired")
timerFired.isInverted = true
let timer = CountdownTimer(interval: 0.1) { timerFired.fulfill() }
timer.invalidate()
waitForExpectations(timeout: 0.2)
}
/// Timing test
///
/// Given:
/// - A timer set to fire at a specific interval
/// When:
/// - The the interval elapses
/// Then:
/// - The timer fires then and only then
///
/// Test timing in ms:
/// - 000: Set up a timer with a .2 sec interval
/// - 100: Ensure timer has not yet fired
/// - 300: Ensure timer has fired
func testTimerFiresOnSchedule() {
var timer: CountdownTimer!
var timerHasFired = false
let timerShouldHaveFired = expectation(description: "the timer should have fired by now")
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(0)) {
timer = CountdownTimer(interval: 0.200) {
timerHasFired = true
}
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(100)) {
XCTAssertFalse(timerHasFired, "The timer should not have fired yet")
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(300)) {
XCTAssert(timerHasFired, "The timer should have fired by now")
timerShouldHaveFired.fulfill()
}
waitForExpectations(timeout: 1.0)
timer.invalidate()
}
/// Timing test
///
/// Given:
/// - A timer set to fire at a specific interval
/// When:
/// - We `resetCountdown` the timer
/// Then:
/// - The timer does not fire until the interval has elapsed from the moment of
/// `resetCountdown`
///
/// Test timing in ms:
/// - 000: Set up a timer with a .3 sec interval
/// - 100: Ensure timer has not yet fired
/// - 200: Issue a `resetCountdown`
/// - 400: Ensure timer has not yet fired
/// - 600: Ensure timer has yet fired
func testTimerResets() {
var timer: CountdownTimer!
var timerHasFired = false
let timerShouldHaveFired = expectation(description: "the timer should have fired by now")
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(0)) {
timer = CountdownTimer(interval: 0.300) { timerHasFired = true }
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(100)) {
XCTAssertFalse(timerHasFired, "The timer should not have fired yet")
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(200)) {
timer.resetCountdown()
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(400)) {
XCTAssertFalse(timerHasFired, "The timer should not have fired yet")
}
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(600)) {
XCTAssert(timerHasFired, "The timer should have fired by now")
timerShouldHaveFired.fulfill()
}
waitForExpectations(timeout: 1.0)
timer.invalidate()
}
}

View File

@ -21,8 +21,10 @@ class RealtimeGatewayURLInterceptorTests: XCTestCase {
let request = AppSyncConnectionRequest(url: url)
let changedRequest = realtimeGatewayURLInterceptor.interceptConnection(request, for: url)
XCTAssertEqual(changedRequest.url.scheme, "wss", "Scheme should be wss")
XCTAssertTrue(changedRequest.url.absoluteString.contains("appsync-realtime-api"),
"URL should contain the realtime part")
XCTAssertTrue(
changedRequest.url.absoluteString.contains("appsync-realtime-api"),
"URL should contain the realtime part"
)
}
}

View File

@ -0,0 +1,8 @@
//
// Copyright 2018-2020 Amazon.com,
// Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
extension String: Error { }

22
Podfile
View File

@ -1,6 +1,11 @@
# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
def include_build_tools!
pod 'SwiftFormat/CLI'
pod 'SwiftLint'
end
target 'AppSyncRealTimeClient' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
@ -8,15 +13,32 @@ target 'AppSyncRealTimeClient' do
# Pods for AppSyncRealTimeClient
pod "Starscream", "~> 3.1.0"
include_build_tools!
target 'AppSyncRealTimeClientTests' do
# Pods for testing
end
end
target "AppSyncRTCSample" do
use_frameworks!
pod 'AppSyncRealTimeClient', :path => '.'
include_build_tools!
end
target "HostApp" do
use_frameworks!
# Intentionally not including build tools here--HostApp has no content or
# purpose beyond giving the integration tests a device-like execution
# context.
target "AppSyncRealTimeClientIntegrationTests" do
inherit! :complete
include_build_tools!
end
end

View File

@ -1,16 +1,32 @@
PODS:
- AppSyncRealTimeClient (1.3.0):
- Starscream (~> 3.1.0)
- Starscream (3.1.1)
- SwiftFormat/CLI (0.45.1)
- SwiftLint (0.39.2)
DEPENDENCIES:
- AppSyncRealTimeClient (from `.`)
- Starscream (~> 3.1.0)
- SwiftFormat/CLI
- SwiftLint
SPEC REPOS:
trunk:
- Starscream
- SwiftFormat
- SwiftLint
EXTERNAL SOURCES:
AppSyncRealTimeClient:
:path: "."
SPEC CHECKSUMS:
AppSyncRealTimeClient: 22189199c4ec10b27347ee4dd6ec44b1a26aaf5d
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
SwiftFormat: 69db45cf8e30eda564431e0241322773b2d975fc
SwiftLint: 22ccbbe3b8008684be5955693bab135e0ed6a447
PODFILE CHECKSUM: 7d5b1efb53eedb756e92a520f3941d2fd2eb88fd
PODFILE CHECKSUM: aba5eb379392784071bfa9b6e559469cfd8cd8c9
COCOAPODS: 1.9.3

View File

@ -0,0 +1,27 @@
{
"name": "AppSyncRealTimeClient",
"version": "1.3.0",
"summary": "Amazon Web Services AppSync RealTime Client for iOS.",
"description": "AppSync RealTime Client provides subscription connections to AppSync websocket endpoints",
"homepage": "https://aws.amazon.com/amplify/",
"license": "Apache License, Version 2.0",
"authors": {
"Amazon Web Services": "amazonwebservices"
},
"platforms": {
"ios": "9.0"
},
"source": {
"git": "https://github.com/aws-amplify/aws-appsync-realtime-client-ios.git",
"tag": "1.3.0"
},
"swift_versions": "5.1",
"requires_arc": true,
"source_files": "AppSyncRealTimeClient/**/*.swift",
"dependencies": {
"Starscream": [
"~> 3.1.0"
]
},
"swift_version": "5.1"
}

18
Pods/Manifest.lock generated
View File

@ -1,16 +1,32 @@
PODS:
- AppSyncRealTimeClient (1.3.0):
- Starscream (~> 3.1.0)
- Starscream (3.1.1)
- SwiftFormat/CLI (0.45.1)
- SwiftLint (0.39.2)
DEPENDENCIES:
- AppSyncRealTimeClient (from `.`)
- Starscream (~> 3.1.0)
- SwiftFormat/CLI
- SwiftLint
SPEC REPOS:
trunk:
- Starscream
- SwiftFormat
- SwiftLint
EXTERNAL SOURCES:
AppSyncRealTimeClient:
:path: "."
SPEC CHECKSUMS:
AppSyncRealTimeClient: 22189199c4ec10b27347ee4dd6ec44b1a26aaf5d
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
SwiftFormat: 69db45cf8e30eda564431e0241322773b2d975fc
SwiftLint: 22ccbbe3b8008684be5955693bab135e0ed6a447
PODFILE CHECKSUM: 7d5b1efb53eedb756e92a520f3941d2fd2eb88fd
PODFILE CHECKSUM: aba5eb379392784071bfa9b6e559469cfd8cd8c9
COCOAPODS: 1.9.3

File diff suppressed because it is too large Load Diff

BIN
Pods/SwiftFormat/CommandLineTool/swiftformat generated Executable file

Binary file not shown.

21
Pods/SwiftFormat/LICENSE.md generated Executable file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

758
Pods/SwiftFormat/README.md generated Normal file
View File

@ -0,0 +1,758 @@
![](EditorExtension/Application/Assets.xcassets/AppIcon.appiconset/AppIcon256.png)
[![PayPal](https://img.shields.io/badge/paypal-donate-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9ZGWNK5FEZFF6&source=url)
[![Travis](https://api.travis-ci.org/nicklockwood/SwiftFormat.svg?branch=master)](https://travis-ci.org/nicklockwood/SwiftFormat)
[![Coveralls](https://coveralls.io/repos/github/nicklockwood/SwiftFormat/badge.svg)](https://coveralls.io/github/nicklockwood/SwiftFormat)
[![Swift 4.2](https://img.shields.io/badge/swift-4.2-red.svg?style=flat)](https://developer.apple.com/swift)
[![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](https://opensource.org/licenses/MIT)
[![Twitter](https://img.shields.io/badge/twitter-@nicklockwood-blue.svg)](http://twitter.com/nicklockwood)
Table of Contents
-----------------
- [What?](#what-is-this)
- [Why?](#why-would-i-want-to-do-that)
- [How?](#how-do-i-install-it)
- [Command-line tool](#command-line-tool)
- [Xcode source editor extension](#xcode-source-editor-extension)
- [Xcode build phase](#xcode-build-phase)
- [Via Applescript](#via-applescript)
- [VSCode plugin](#vscode-plugin)
- [Sublime Text plugin](#sublime-text-plugin)
- [Git pre-commit hook](#git-pre-commit-hook)
- [On CI using Danger](#on-ci-using-danger)
- [Configuration](#configuration)
- [Options](#options)
- [Rules](#rules)
- [Swift version](#swift-version)
- [Config file](#config-file)
- [Globs](#globs)
- [Linting](#linting)
- [Cache](#cache)
- [File headers](#file-headers)
- [FAQ](#faq)
- [Known issues](#known-issues)
- [Tip Jar](#tip-jar)
- [Credits](#credits)
What is this?
----------------
SwiftFormat is a code library and command-line tool for reformatting Swift code on macOS or Linux.
SwiftFormat goes above and beyond what you might expect from a code formatter. In addition to adjusting white space it can insert or remove implicit `self`, remove redundant parentheses, and correct many other deviations from the standard Swift idioms.
Why would I want to do that?
-----------------------------
Many programmers have a preferred style for formatting their code, and others seem entirely blind to the existing formatting conventions of a project (to the enragement of their colleagues).
When collaborating on a project, it can be helpful to agree on a common coding style, but enforcing that manually is tedious and error-prone, and can lead to arguments if some participants take it more seriously than others.
Having a tool to automatically enforce a common style eliminates those issues, and lets you focus on the behavior of the code, not its presentation.
How do I install it?
---------------------
That depends - There are several ways you can use SwiftFormat:
1. As a command-line tool that you run manually, or as part of some other toolchain
2. As a Source Editor Extension that you can invoke via the Editor > SwiftFormat menu within Xcode
3. As a build phase in your Xcode project, so that it runs every time you press Cmd-R or Cmd-B, or
4. As a Git pre-commit hook, so that it runs on any files you've changed before you check them in
Command-line tool
-------------------
**NOTE:** if you are using any of the following methods to install SwiftFormat on macOS 10.14.3 or earlier and are experiencing a crash on launch, you may need to install the [Swift 5 Runtime Support for Command Line Tools](https://support.apple.com/kb/DL1998). See [known issues](#known-issues) for details.
**Installation:**
You can install the `swiftformat` command-line tool on macOS using [Homebrew](http://brew.sh/). Assuming you already have Homebrew installed, just type:
```bash
$ brew install swiftformat
```
To update to the latest version once installed:
```bash
$ brew upgrade swiftformat
```
Alternatively, you can install the tool on macOS or Linux by using [Mint](https://github.com/yonaskolb/Mint) as follows:
```bash
$ mint install nicklockwood/SwiftFormat
```
And then run it using:
```bash
$ mint run swiftformat
```
Or if you prefer, you can check out and build SwiftFormat manually on macOS or Linux as follows:
```bash
$ git clone https://github.com/nicklockwood/SwiftFormat
$ cd SwiftFormat
$ swift build -c release
```
If you are installing SwiftFormat into your project directory, you can use [CocoaPods](https://cocoapods.org/) on macOS to automatically install the swiftformat binary along with your other pods - see the Xcode build phase instructions below for details.
If you would prefer not to use a package manager, you can build the command-line app manually:
1. open `SwiftFormat.xcodeproj` and build the `SwiftFormat (Application)` scheme.
2. Drag the `swiftformat` binary into `/usr/local/bin/` (this is a hidden folder, but you can use the Finder's `Go > Go to Folder...` menu to open it).
3. Open `~/.bash_profile` in your favorite text editor (this is a hidden file, but you can type `open ~/.bash_profile` in the terminal to open it).
4. Add the following line to the file: `alias swiftformat="/usr/local/bin/swiftformat --indent 4"` (you can omit the `--indent 4`, or replace it with something else. Run `swiftformat --help` to see the available options).
5. Save the `.bash_profile` file and run the command `source ~/.bash_profile` for the changes to take effect.
**Usage:**
If you followed the installation instructions above, you can now just type
```bash
$ swiftformat .
```
(that's a space and then a period after the command) in the terminal to format any Swift files in the current directory. In place of the `.`, you can instead type an absolute or relative path to the file or directory that you want to format.
**WARNING:** `swiftformat .` will overwrite any Swift files it finds in the current directory, and any subfolders therein. If you run it in your home directory, it will probably reformat every Swift file on your hard drive.
To use it safely, do the following:
1. Choose a file or directory that you want to apply the changes to.
2. Make sure that you have committed all your changes to that code safely in git (or whatever source control system you use).
3. (Optional) In Terminal, type `swiftformat --inferoptions "/path/to/your/code/"`. This will suggest a set of formatting options to use that match your existing project style (but you are free to ignore these and use the defaults, or your own settings if you prefer).
The path can point to either a single Swift file or a directory of files. It can be either be absolute, or relative to the current directory. The `""` quotes around the path are optional, but if the path contains spaces then you either need to use quotes, or escape each space with `\`. You may include multiple paths separated by spaces.
4. In Terminal, type `swiftformat "/path/to/your/code/"`. The same rules apply as above with respect to paths, and multiple space-delimited paths are allowed.
If you used `--inferoptions` to generate a suggested set of options in step 3, you should copy and paste them into the command, either before or after the path(s) to your source files.
If you have created a [config file](#config-file), you can specify its path using `--config "/path/to/your/config-file/"`. Alternatively, if you name the file `.swiftformat` and place it inside the project you are formatting, it will be picked up automatically.
5. Press enter to begin formatting. Once the formatting is complete, use your source control system to check the changes, and verify that no undesirable changes have been introduced. If they have, revert the changes, tweak the options and try again.
6. (Optional) commit the changes.
Following these instructions *should* ensure that you avoid catastrophic data loss, but in the unlikely event that it wipes your hard drive, **please note that I accept no responsibility**.
**Using Standard Input/Output:**
If you prefer, you can use unix pipes to include SwiftFormat as part of a command chain. For example, this is an alternative way to format a file:
```bash
$ cat /path/to/file.swift | swiftformat --output /path/to/file.swift
```
Omitting the `--output /path/to/file.swift` will print the formatted file to Standard Output (stdout). You can also pass "stdout" explicitly as the output path:
```bash
$ cat /path/to/file.swift | swiftformat --output stdout
```
Or you can use `>` to specify the output path as follows:
```bash
$ cat /path/to/file.swift | swiftformat > /path/to/file.swift
```
If you do not supply an input file, SwiftFormat will automatically take its input from Standard Input (stdin), but will time-out if no input is received immediately and display the help screen. To make it explicit, pass "stdin" as the input path:
```bash
$ cat /path/to/file.swift | swiftformat stdin
```
Xcode source editor extension
-----------------------------
**Installation:**
Like the command-line tool, you can install the SwiftFormat for Xcode extension application via [Homebrew](http://brew.sh/). Assuming you already have Homebrew installed, type:
```bash
$ brew cask install swiftformat-for-xcode
```
This will install SwiftFormat for Xcode in your Applications folder. Double-click the app to launch it, and then follow the on-screen instructions.
**NOTE:** The app should be correctly signed, but if you get a Gatekeeper warning when trying to open it you can bypass this by right-clicking (or control-clicking) the app and selecting `Open`.
To update to the latest version once installed use:
```bash
$ brew cask upgrade swiftformat-for-xcode
```
Alternatively, if you prefer not to use Homebrew, you'll find the latest version of the SwiftFormat for Xcode application inside the EditorExtension folder included in the SwiftFormat repository. Download and unpack the zip archive, then drag `SwiftFormat for Xcode.app` into your `Applications` folder.
**Usage:**
Once you have launched the app and restarted Xcode, you'll find a SwiftFormat option under Xcode's Editor menu.
You can configure the formatting [rules](#rules) and [options](#options) using the SwiftFormat for Xcode host application. There is currently no way to override these per-project, however, you can import and export different configurations using the File menu. You will need to do this again each time you switch projects.
The format of the configuration file is described in the [Config section](#config-file) below.
**Note:** SwiftFormat for Xcode cannot automatically detect changes to an imported configuration file. If you update the `.swiftformat` file for your project, you will need to manually re-import that file into SwiftFormat for Xcode in order for the Xcode source editor extension to use the new configuration.
Xcode build phase
-------------------
**NOTE:** Adding this script will overwrite your source files as you work on them, which has the annoying side-effect of clearing the undo history. You may wish to add the script to your test target rather than your main target, so that it is invoked only when you run the unit tests, and not every time you build the app.
Alternatively, you might want to consider running SwiftFormat in [lint](#linting) mode as part of your normal build, and then running a formatting pass manually, or as part of a less-frequent build target (such as the tests).
### Using Swift Package Manager
To set up SwiftFormat as an Xcode build phase, do the following:
#### 1) Create a BuildTools folder & Package.swift
1. Create a folder called `BuildTools` in the same folder as your xcodeproj file
2. In this folder, create a file called `Package.swift`, with the following contents:
```swift
// swift-tools-version:5.1
import PackageDescription
let package = Package(
name: "BuildTools",
platforms: [.macOS(.v10_11)],
dependencies: [
.package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.41.2"),
],
targets: [.target(name: "BuildTools", path: "")]
)
```
3. If you are running Xcode 11.4 or later, in the `BuildTools` folder create a file called `Empty.swift` with nothing in it. This is to satisfy a change in Swift Package Manager.
#### 2) Add a Build phases to your app target
1. Click on your project in the file list, choose your target under `TARGETS`, click the `Build Phases` tab
2. Add a `New Run Script Phase` by clicking the little plus icon in the top left
3. Drag the new `Run Script` phase **above** the `Compile Sources` phase, expand it and paste the following script:
```bash
cd BuildTools
SDKROOT=macosx
#swift package update #Uncomment this line temporarily to update the version used to the latest matching your BuildTools/Package.swift file
swift run -c release swiftformat "$SRCROOT"
```
You can also use `swift run -c release --package-path BuildTools swiftformat "$SRCROOT"` if you need a more complex script and `cd BuildTools` breaks stuff.
**NOTE:** You may wish to check BuildTools/Package.swift into your source control so that the version used by your run-script phase is kept in version control. It is recommended to add the following to your .gitignore file: `BuildTools/.build` and `BuildTools/.swiftpm`.
### Using Cocoapods
#### 1) Add the SwiftFormat CLI to your Podfile
1. Add the `swiftformat` binary to your project directory via [CocoaPods](https://cocoapods.org/), by adding the following line to your Podfile then running `pod install`:
```ruby
pod 'SwiftFormat/CLI'
```
**NOTE:** This will only install the pre-built command-line app, not the source code for the SwiftFormat framework.
#### 2) Add a Build phase to your app target
1. Click on your project in the file list, choose your target under `TARGETS`, click the `Build Phases` tab
2. Add a `New Run Script Phase` by clicking the little plus icon in the top left
3. Drag the new `Run Script` phase **above** the `Compile Sources` phase, expand it and paste the following script:
```bash
"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat" "$SRCROOT"
```
### Alternative: Locally installed SwiftFormat
Alternatively, you could use a locally installed swiftformat command-line tool instead by putting the following in your Run Script build phase:
```bash
if which swiftformat >/dev/null; then
swiftformat .
else
echo "warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat"
fi
```
This is not recommended for shared projects however, as different team members using different versions of SwiftFormat may result in noise in the commit history as code gets reformatted inconsistently.
Via AppleScript
----------------
To run SwiftFormat on the frontmost Xcode document (project or workspace) you can use the following AppleScript:
```applescript
tell application "Xcode"
set frontWindow to the first window
set myPath to path of document of frontWindow
do shell script "cd " & myPath & ";cd ..; /usr/local/bin/swiftformat ."
end tell
```
Some Apps you can trigger this from are [BetterTouchTool](https://folivora.ai), [Alfred](https://www.alfredapp.com) or [Keyboard Maestro](https://www.keyboardmaestro.com/main/). Another option is to define a QuickAction for Xcode via Automator and then assign a keyboard shortcut for it in the System Preferences.
VSCode plugin
--------------
If you prefer to use Microsoft's [VSCode](https://code.visualstudio.com) editor for writing Swift, [Valentin Knabel](https://github.com/vknabel) has created a [VSCode plugin](https://marketplace.visualstudio.com/items?itemName=vknabel.vscode-swiftformat) for SwiftFormat.
Sublime Text plugin
--------------------
If you prefer to use the [Sublime Text](https://www.sublimetext.com) editor, try the [Sublime-Swift-Format plugin](https://github.com/aerobounce/Sublime-Swift-Format) by [Aerobounce](https://github.com/aerobounce).
Git pre-commit hook
---------------------
1. Follow the instructions for installing the SwiftFormat command-line tool.
2. Install [git-format-staged](https://github.com/hallettj/git-format-staged).
3. Edit or create a `.git/hooks/pre-commit` file in your project folder. The .git folder is hidden but should already exist if you are using Git with your project, so open it with the terminal, or the Finder's `Go > Go to Folder...` menu.
4. Add the following line in the pre-commit file. The `{}` will be replaced automatically by the path to the Swift file being formatted:
```bash
#!/bin/bash
git-format-staged --formatter "swiftformat stdin --stdinpath {}" "*.swift"
```
(Note that this example uses your locally installed version of SwiftFormat, not a separate copy in your project repository. You can replace `swiftformat` with the path to a copy inside your project if you prefer.)
5. enable the hook by typing `chmod +x .git/hooks/pre-commit` in the terminal.
The pre-commit hook will now run whenever you run `git commit`. Running `git commit --no-verify` will skip the pre-commit hook.
**NOTE:** If you are using Git via a GUI client such as [Tower](https://www.git-tower.com), [additional steps](https://www.git-tower.com/help/mac/faq-and-tips/faq/hook-scripts) may be needed.
**NOTE (2):** Unlike the Xcode build phase approach, git pre-commit hook won't be checked in to source control, and there's no way to guarantee that all users of the project are using the same version of SwiftFormat. For a collaborative project, you might want to consider a *post*-commit hook instead, which would run on your continuous integration server.
On CI using Danger
-------------------
To setup SwiftFormat to be used by your continuous integration system using [Danger](http://danger.systems/ruby/), do the following:
1. Follow the [`instructions`](http://danger.systems/guides/getting_started.html) to setup Danger.
2. Add the [`danger-swiftformat`](https://github.com/garriguv/danger-ruby-swiftformat) plugin to your `Gemfile`.
3. Add the following to your `Dangerfile`:
```ruby
swiftformat.binary_path = "/path/to/swiftformat" # optional
swiftformat.additional_args = "--indent tab --self insert" # optional
swiftformat.check_format(fail_on_error: true)
```
**NOTE:** It is recommended to add the `swiftformat` binary to your project directory to ensure the same version is used each time (see the [Xcode build phase](#xcode-build-phase) instructions above).
Configuration
-------------
SwiftFormat's configuration is split between **rules** and **options**. Rules are functions in the SwiftFormat library that apply changes to the code. Options are settings that control the behavior of the rules.
Options
-------
The options available in SwiftFormat can be displayed using the `--options` command-line argument. The default value for each option is indicated in the help text.
Rules are configured by adding `--[option_name] [value]` to your command-line arguments, or by creating a `.swiftformat` [config file](#config-file) and placing it in your project directory.
A given option may affect multiple rules. Use `--ruleinfo [rule_name]` command for details about which options affect a given rule, or see the [Rules.md](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md) file.
Rules
-----
SwiftFormat includes over 50 rules, and new ones are added all the time. An up-to-date list can be found in [Rules.md](https://github.com/nicklockwood/SwiftFormat/blob/master/Rules.md) along with documentation for how they are used.
The list of available rules can be displayed within the command-line app using the `--rules` argument. Rules can be either enabled or disabled. Most are enabled by default. Disabled rules are marked with "(disabled)" when displayed using `--rules`.
You can use the `--ruleinfo [rule_name]` command to get information about a specific rule. Pass a comma-delimited list of rule names to get information for multiple rules at once, or use `--ruleinfo` with no argument for info on all rules.
You can disable rules individually using `--disable` followed by a list of one or more comma-delimited rule names, or enable opt-in rules using `--enable` followed by the rule names:
```bash
--disable redundantSelf,trailingClosures
--enable isEmpty
```
If you prefer, you can place your enabled/disabled rules on separate lines instead of using commas:
```bash
--disable indent
--disable linebreaks
--disable redundantSelf
```
To avoid automatically opting-in to new rules when SwiftFormat is updated, you can use the`--rules` argument to *only* enable the rules you specify:
```bash
--rules indent,linebreaks
```
To see exactly which rules were applied to a given file, you can use the `--verbose` command-line option to force SwiftFormat to print a more detailed log as it applies the formatting. **NOTE:** running in verbose mode is slower than the default mode.
You can disable rules for specific files or code ranges by using `swiftformat:` directives in comments inside your Swift files. To temporarily disable one or more rules inside a source file, use:
```swift
// swiftformat:disable <rule1> [<rule2> [rule<3> ...]]
```
To enable the rule(s) again, use:
```swift
// swiftformat:enable <rule1> [<rule2> [rule<3> ...]]
```
To disable all rules use:
```swift
// swiftformat:disable all
```
And to enable them all again, use:
```swift
// swiftformat:enable all
```
To temporarily prevent one or more rules being applied to just the next line, use:
```swift
// swiftformat:disable:next <rule1> [<rule2> [rule<3> ...]]
let foo = bar // rule(s) will be disabled for this line
let bar = baz // rule(s) will be re-enabled for this line
```
There is no need to manually re-enable a rule after using the `next` directive.
**NOTE:** The `swiftformat:enable` directives only serves to counter a previous `swiftformat:disable` directive in the same file. It is not possible to use `swiftformat:enable` to enable a rule that was not already enabled when formatting started.
Swift version
-------------
Most SwiftFormat rules are version-agnostic, but some are applicable only to newer Swift versions. These rules will be disabled automatically if the Swift version is not specified, so to make sure that the full functionality is available you should specify the version of Swift that is used by your project.
You can specify the Swift version in one of two ways:
The preferred option is to add a `.swift-version` file to your project directory. This is a text file that should contain the minimum Swift version supported by your project, and is a standard already used by other tools.
The `.swift-version` file applies hierarchically; If you have submodules in your project that use a different Swift version, you can add separate `.swift-version` files to those directories.
The other option to specify the Swift version using the `--swiftversion` command line argument. Note that this will be overridden by any `.swift-version` files encountered while processing.
Config file
-----------
Although it is possible to configure SwiftFormat directly by using the command-line [options](#options) and [rules](#rules) detailed above, it is sometimes more convenient to create a configuration file, which can be added to your project and shared with other developers.
A SwiftFormat configuration file consists of one or more command-line options, split onto separate lines, e.g:
```
--allman true
--indent tab
--disable elseOnSameLine,semicolons
```
While formatting, SwiftFormat will automatically check inside each subdirectory for the presence of a `.swiftformat` file and will apply any options that it finds there to the files in that directory.
This allows you to override certain rules or formatting options just for a particular directory of files. You can also specify excluded files relative to that directory using `--exclude`, which may be more convenient than specifying them at the top-level:
```
--exclude Pods,Generated
```
The `--exclude` option takes a comma-delimited list of file or directory paths to exclude from formatting. Excluded paths are relative to the config file containing the `--exclude` command. The excluded paths can include wildcards, specified using Unix "Glob" syntax, as [documented below](#globs).
Config files named ".swiftformat" will be processed automatically, however, you can select an additional configuration file to use for formatting using the `--config "path/to/config/file"` command-line argument. A configuration file selected using `--config` does not need to be named ".swiftformat", and can be located outside of the project directory.
The config file format is designed to be edited by hand. You may include blank lines for readability, and can also add comments using a hash prefix (#), e.g.
```
# format options
--allman true
--indent tab # tabs FTW!
# file options
--exclude Pods
# rules
--disable elseOnSameLine,semicolons
```
If you would prefer not to edit the configuration file by hand, you can use the [SwiftFormat for Xcode](#xcode-source-editor-extension) app to edit the configuration and export a configuration file. You can also use the swiftformat command-line-tool's `--inferoptions` command to generate a config file from your existing project, like this:
```bash
$ cd /path/to/project
$ swiftformat --inferoptions . --output .swiftformat
```
Globs
-----
When excluding files from formatting using the `--exclude` option, you may wish to make use of wildcard paths (aka "Globs") to match all files that match a particular naming convention without having to manually list them all.
SwiftFormat's glob syntax is based on Ruby's implementation, which varies slightly from the Unix standard. The following patterns are supported:
* `*` - A single star matches zero or more characters in a filename, but *not* a `/`.
* `**` - A double star will match anything, including one or more `/`.
* `?` - A question mark will match any single character except `/`.
* `[abc]` - Matches any single character inside the brackets.
* `[a-z]` - Matches a single character in the specified range in the brackets.
* `{foo,bar}` - Matches any one of the comma-delimited strings inside the braces.
Examples:
* `foo.swift` - Matches the file "foo.swift" in the same directory as the config file.
* `*.swift` - Matches any Swift file in the same directory as the config file.
* `foo/bar.swift` - Matches the file "bar.swift" in the directory "foo".
* `**/foo.swift` - Matches any file named "foo.swift" in the project.
* `**/*.swift` - Matches any Swift file in the project.
* `**/Generated` - Matches any folder called `Generated` in the project.
* `**/*_generated.swift` - Matches any Swift file with the suffix "_generated" in the project.
Linting
-------
SwiftFormat is primarily designed as a formatter rather than a linter, i.e. it is designed to fix your code rather than tell you what's wrong with it. However, sometimes it can be useful to verify that code has been formatted in a context where it is not desirable to actually change it.
A typical example would be as part of a CI (Continuous Integration) process, where you may wish to have an automated script that checks committed code for style violations. While you can use a separate tool such as [SwiftLint](https://github.com/realm/SwiftLint) for this, it makes sense to be able to validate the formatting against the exact same rules as you are using to apply it.
In order to run SwiftFormat as a linter, you can use the `--lint` command-line option:
```bash
$ swiftformat --lint path/to/project
```
This runs the same rules as format mode, and all the same configuration options apply, however, no files will be modified. Instead, SwiftFormat will format each file in memory and then compare the result against the input and report the lines that required changes.
The `--lint` option is similar to `--dryrun`, but `--lint` returns warnings for every line that required changes, and will return a nonzero error code if any changes are detected, which is useful if you want it to fail a build step on your CI server.
If you would prefer `--lint` not to fail your build, you can use the `--lenient` option to force SwiftFormat to return success in `--lint` mode even when formatting issues were detected.
```bash
$ swiftformat --lint --lenient path/to/project
```
By default, `--lint` will only report lines that require formatting, but you can use the additional `--verbose` flag to display additional info about which files were checked, even if there were no changes needed.
If you would prefer not to see a warning for each and every formatting change, you can use the `--quiet` flag to suppress all output except errors.
Cache
------
SwiftFormat uses a cache file to avoid reformatting files that haven't changed. For a large project, this can significantly reduce processing time.
By default, the cache is stored in `~/Library/Caches/com.charcoaldesign.swiftformat` on macOS, or `/var/tmp/com.charcoaldesign.swiftformat` on Linux. Use the command-line option `--cache ignore` to ignore the cached version and re-apply formatting to all files. Alternatively, you can use `--cache clear` to delete the cache (or you can just manually delete the cache file).
The cache is shared between all projects. The file is fairly small, as it only stores the path and size for each file, not the contents. If you do start experiencing slowdown due to the cache growing too large, you might want to consider using a separate cache file for each project.
You can specify a custom cache file location by passing a path as the `--cache` option value. For example, you might want to store the cache file inside your project directory. It is fine to check in the cache file if you want to share it between different users of your project, as the paths stored in the cache are relative to the location of the formatted files.
File headers
-------------
SwiftFormat can be configured to strip or replace the header comments in every file with a template. The "header comment" is defined as a comment block that begins on the first nonblank line in the file, and is followed by at least one blank line. This may consist of a single comment body, or multiple comments on consecutive lines:
```swift
// This is a header comment
```
```swift
// This is a regular comment
func foo(bar: Int) -> Void { ... }
```
The header template is a string that you provide using the `--header` command-line option. Passing a value of `ignore` (the default) will leave the header comments unmodified. Passing `strip` or an empty string `""` will remove them. If you wish to provide a custom header template, the format is as follows:
For a single-line template: `--header "Copyright (c) 2017 Foobar Industries"`
For a multiline comment, mark linebreaks with `\n`: `--header "First line\nSecond line"`
You can optionally include Swift comment markup in the template if you wish: `--header "/*--- Header comment ---*/"`
If you do not include comment markup, each line in the template will be prepended with `//` and a single space.
It is common practice to include the file name, creation date and/or the current year in a comment header copyright notice. To do that, you can use the following placeholders:
* `{file}` - the name of the file
* `{year}` - the current year
* `{created}` - the date on which the file was created
* `{created.year}` - the year in which the file was created
For example, a header template of:
```bash
--header "{file}\nCopyright (c) {year} Foobar Industries\nCreated by John Smith on {created}."
```
Will be formatted as:
```swift
// SomeFile.swift
// Copyright (c) 2019 Foobar Industries
// Created by John Smith on 01/02/2016.
```
**NOTE:** the `{year}` value and `{created}` date format are determined from the current locale and timezone of the machine running the script.
FAQ
-----
*Q. How is this different from SwiftLint?*
> A. SwiftLint is primarily designed to find and report code smells and style violations in your code. SwiftFormat is designed to fix them. While SwiftLint can autocorrect some issues, and SwiftFormat has some support for [linting](#linting), their primary functions are different.
*Q. Can SwiftFormat and SwiftLint be used together?*
> A. Absolutely! The style rules encouraged by both tools are quite similar, and SwiftFormat even fixes some style violations that SwiftLint warns about but can't currently autocorrect.
*Q. What platforms does SwiftFormat support?*
> A. SwiftFormat works on macOS 10.13 (High Sierra) and above, and also runs on Ubuntu Linux.
*Q. What versions of Swift are supported?*
> A. The SwiftFormat framework and command-line tool can be compiled using Swift 4.2 and above, and can format programs written in Swift 4.x or 5. Swift 3.x is no longer actively supported. If you are still using Swift 3.x or earlier and find that SwiftFormat breaks your code, the best solution is probably to revert to an earlier SwiftFormat release, or enable only a small subset of rules. Use the `--swiftversion` argument to enable additional rules specific to later Swift versions.
*Q. SwiftFormat made changes I didn't want it to. How can I find out which rules to disable?*
> A. If you run SwiftFormat using the `--verbose` option, it will tell you which rules were applied to each file. You can then selectively disable certain rules using the `--disable` argument (see below).
*Q. People on my team have different SwiftFormat versions installed. How can we ensure consistent formatting?
> A. You can specify a `--minversion` argument in your project's .swiftformat` file to fail the build if developers attempt to use an older SwiftFormat version.
*Q. How can I modify the formatting rules?*
> A. Many configuration options are exposed in the command-line interface or `.swiftformat` configuration file. You can either set these manually, or use the `--inferoptions` argument to automatically generate the configuration from your existing project.
> If there is a rule that you don't like, and which cannot be configured to your liking via the command-line options, you can disable one or more rules by using the `--disable` argument, followed by the name of the rules, separated by commas. You can display a list of all supported rules using the `--rules` argument, and their behaviors are documented above this section in the README.
> If you are using the Xcode source editor extension, rules and options can be configured using the [SwiftFormat for Xcode](#xcode-source-editor-extension) host application. Unfortunately, due to limitation of the Extensions API, there is no way to configure these on a per-project basis.
> If the options you want aren't exposed, and disabling the rule doesn't solve the problem, the rules are implemented in the file `Rules.swift`, so you can modify them and build a new version of the command-line tool. If you think your changes might be generally useful, make a pull request.
Q. I don't want to be surprised by new rules added when I upgrade SwiftFormat. How can I prevent this?
> A. You can use the `--rules` argument to specify an exclusive list of rules to run. If new rules are added, they won't be enabled if you have specified a `--rules` list in your SwiftFormat configuration.
*Q. Why can't I set the indent width or choose between tabs/spaces in the [SwiftFormat for Xcode](#xcode-source-editor-extension) options?*
> Indent width and tabs/spaces can be configured in Xcode on a per project-basis. You'll find the option under "Text Settings" in the Files inspector of the right-hand sidebar.
*Q. After applying SwiftFormat, my code won't compile. Is that a bug?*
> A. SwiftFormat should ideally never break your code. Check the [known issues](#known-issues), and if it's not already listed there, or the suggested workaround doesn't solve your problem, please [open an issue on Github](https://github.com/nicklockwood/SwiftFormat/issues).
*Q. Can I use SwiftFormat to lint my code without changing it?*
> A. Yes, see the [linting](#linting) section above for details.
*Q. Can I use the `SwiftFormat.framework` inside another app?*
> A. Yes, the SwiftFormat framework can be included in an app or test target, and used for many kinds of parsing and processing of Swift source code besides formatting. The SwiftFormat framework is available as a [CocoaPod](https://cocoapods.org/pods/SwiftFormat) for easy integration.
Known issues
---------------
* When running a version of SwiftFormat built using Xcode 10.2 on macOS 10.14.3 or earlier, you may experience a crash with the error "dyld: Library not loaded: @rpath/libswiftCore.dylib". To fix this, you need to install the [Swift 5 Runtime Support for Command Line Tools](https://support.apple.com/kb/DL1998). These tools are included by default in macOS 10.14.4 and later.
* When using the `--self remove` option, the `redundantSelf` rule will remove references to `self` in autoclosure arguments, which may change the meaning of the code, or cause it not to compile. To work around this issue, use the `--selfrequired` option to provide a comma-delimited list of methods to be excluded from the rule. The `expect()` function from the popular [Nimble](https://github.com/Quick/Nimble) unit testing framework is already excluded by default. If you are using the `--self insert` option then this is not an issue.
* If you assign `SomeClass.self` to a variable and then instantiate an instance of the class using that variable, Swift requires that you use an explicit `.init()`, however, the `redundantInit` rule is not currently capable of detecting this situation and will remove the `.init`. To work around this issue, use the `// swiftformat:disable:next redundantInit` comment directive to disable the rule for any affected lines of code (or just disable the `redundantInit` rule completely).
* The `--self insert` option can only recognize locally declared member variables, not ones inherited from superclasses or extensions in other files, so it cannot insert missing `self` references for those. Note that the reverse is not true: `--self remove` should remove *all* redundant `self` references.
* The `trailingClosures` rule can generate ambiguous code if a function has multiple optional closure arguments, or if multiple functions have signatures differing only by the name of the closure argument. For this reason, the rule is limited to anonymous closure arguments by default, with a whitelist for exceptions.
* The `isEmpty` rule will convert `count == 0` to `isEmpty` even for types that do not have an `isEmpty` method, such as `NSArray`/`NSDictionary`/etc. Use of Foundation collections in Swift code is pretty rare, but just in case, the rule is disabled by default.
* If a file begins with a comment, the `stripHeaders` rule will remove it if it is followed by a blank line. To avoid this, make sure that the first comment is directly followed by a line of code.
* The formatted file cache is based on a hash of the file contents, so it's possible (though unlikely) that an edited file will have the exact same hash as the previously formatted version, causing SwiftFormat to incorrectly identify it as not having changed, and fail to update it.
To fix this, you can use the command-line option `--cache ignore` to force SwiftFormat to ignore the cache for this run, or just type an extra space in the file (which SwiftFormat will then remove again when it applies the correct formatting).
* When running on Linux, the `--symlinks` option has no effect, and some of the `fileHeader` placeholders are not supported.
Tip Jar
-----------
SwiftFormat is not a commercially-funded product, it's a labour of love given freely to the community. If you find it useful, please consider making a donation.
[![Donate via PayPal](https://www.paypalobjects.com/en_GB/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9ZGWNK5FEZFF6&source=url)
Credits
------------
* [Tony Arnold](https://github.com/tonyarnold) - SwiftFormat for Xcode
* [Vincent Bernier](https://github.com/vinceburn) - SwiftFormat for Xcode settings UI
* [Vikram Kriplaney](https://github.com/markiv) - SwiftFormat for Xcode icon
* [Maxime Marinel](https://github.com/bourvill) - Git pre-commit hook script
* [Romain Pouclet](https://github.com/palleas) - Homebrew formula
* [Aerobounce](https://github.com/aerobounce) - Homebrew cask and Sublime Text plugin
* [Ali Akhtarzada](https://github.com/aliak00) - Several path-related CLI enhancements
* [Yonas Kolb](https://github.com/yonaskolb) - Swift Package Manager integration
* [Wolfgang Lutz](https://github.com/Lutzifer) - AppleScript integration instructions
* [Balázs Kilvády](https://github.com/balitm) - Xcode lint warning integration
* [Anthony Miller](https://github.com/AnthonyMDev) - Improvements to wrap/indent logic
* [Shingo Takagi](https://github.com/zizi4n5) - Several brace-related bug fixes
* [Juri Pakaste](https://github.com/juri) - Filelist feature
* [Nick Lockwood](https://github.com/nicklockwood) - Everything else
([Full list of contributors](https://github.com/nicklockwood/SwiftFormat/graphs/contributors))

21
Pods/SwiftLint/LICENSE generated Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

BIN
Pods/SwiftLint/swiftlint generated Executable file

Binary file not shown.

View File

@ -0,0 +1,26 @@
<?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">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.3.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_AppSyncRealTimeClient : NSObject
@end
@implementation PodsDummy_AppSyncRealTimeClient
@end

View File

@ -0,0 +1,12 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

View File

@ -0,0 +1,16 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double AppSyncRealTimeClientVersionNumber;
FOUNDATION_EXPORT const unsigned char AppSyncRealTimeClientVersionString[];

View File

@ -0,0 +1,11 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AppSyncRealTimeClient
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Starscream"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/..
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,6 @@
framework module AppSyncRealTimeClient {
umbrella header "AppSyncRealTimeClient-umbrella.h"
export *
module * { export * }
}

View File

@ -0,0 +1,11 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/AppSyncRealTimeClient
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Starscream"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/..
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,26 @@
<?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">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,332 @@
# Acknowledgements
This application makes use of the following third party libraries:
## Starscream
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2014-2016 Dalton Cherry.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
## SwiftFormat
MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## SwiftLint
The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## AppSyncRealTimeClient
Amazon Software License 1.0
This Amazon Software License ("License") governs your use, reproduction, and
distribution of the accompanying software as specified below.
1. Definitions
"Licensor" means any person or entity that distributes its Work.
"Software" means the original work of authorship made available under this
License.
"Work" means the Software and any additions to or derivative works of the
Software that are made available under this License.
The terms "reproduce," "reproduction," "derivative works," and
"distribution" have the meaning as provided under U.S. copyright law;
provided, however, that for the purposes of this License, derivative works
shall not include works that remain separable from, or merely link (or bind
by name) to the interfaces of, the Work.
Works, including the Software, are "made available" under this License by
including in or with the Work either (a) a copyright notice referencing the
applicability of this License to the Work, or (b) a copy of this License.
2. License Grants
2.1 Copyright Grant. Subject to the terms and conditions of this License,
each Licensor grants to you a perpetual, worldwide, non-exclusive,
royalty-free, copyright license to reproduce, prepare derivative works of,
publicly display, publicly perform, sublicense and distribute its Work and
any resulting derivative works in any form.
2.2 Patent Grant. Subject to the terms and conditions of this License, each
Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free
patent license to make, have made, use, sell, offer for sale, import, and
otherwise transfer its Work, in whole or in part. The foregoing license
applies only to the patent claims licensable by Licensor that would be
infringed by Licensor's Work (or portion thereof) individually and
excluding any combinations with any other materials or technology.
3. Limitations
3.1 Redistribution. You may reproduce or distribute the Work only if
(a) you do so under this License, (b) you include a complete copy of this
License with your distribution, and (c) you retain without modification
any copyright, patent, trademark, or attribution notices that are present
in the Work.
3.2 Derivative Works. You may specify that additional or different terms
apply to the use, reproduction, and distribution of your derivative works
of the Work ("Your Terms") only if (a) Your Terms provide that the use
limitation in Section 3.3 applies to your derivative works, and (b) you
identify the specific derivative works that are subject to Your Terms.
Notwithstanding Your Terms, this License (including the redistribution
requirements in Section 3.1) will continue to apply to the Work itself.
3.3 Use Limitation. The Work and any derivative works thereof only may be
used or intended for use with the web services, computing platforms or
applications provided by Amazon.com, Inc. or its affiliates, including
Amazon Web Services, Inc.
3.4 Patent Claims. If you bring or threaten to bring a patent claim against
any Licensor (including any claim, cross-claim or counterclaim in a
lawsuit) to enforce any patents that you allege are infringed by any Work,
then your rights under this License from such Licensor (including the
grants in Sections 2.1 and 2.2) will terminate immediately.
3.5 Trademarks. This License does not grant any rights to use any
Licensor's or its affiliates' names, logos, or trademarks, except as
necessary to reproduce the notices described in this License.
3.6 Termination. If you violate any term of this License, then your rights
under this License (including the grants in Sections 2.1 and 2.2) will
terminate immediately.
4. Disclaimer of Warranty.
THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR
NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER
THIS LICENSE. SOME STATES' CONSUMER LAWS DO NOT ALLOW EXCLUSION OF AN
IMPLIED WARRANTY, SO THIS DISCLAIMER MAY NOT APPLY TO YOU.
5. Limitation of Liability.
EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL
THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE
SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT,
INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR
RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING
BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS
OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER COMM ERCIAL DAMAGES
OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
Generated by CocoaPods - https://cocoapods.org

View File

@ -0,0 +1,382 @@
<?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">
<dict>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>FooterText</key>
<string>This application makes use of the following third party libraries:</string>
<key>Title</key>
<string>Acknowledgements</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string> Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2014-2016 Dalton Cherry.
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.</string>
<key>License</key>
<string>Apache License, Version 2.0</string>
<key>Title</key>
<string>Starscream</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftFormat</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftLint</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Amazon Software License 1.0
This Amazon Software License ("License") governs your use, reproduction, and
distribution of the accompanying software as specified below.
1. Definitions
"Licensor" means any person or entity that distributes its Work.
"Software" means the original work of authorship made available under this
License.
"Work" means the Software and any additions to or derivative works of the
Software that are made available under this License.
The terms "reproduce," "reproduction," "derivative works," and
"distribution" have the meaning as provided under U.S. copyright law;
provided, however, that for the purposes of this License, derivative works
shall not include works that remain separable from, or merely link (or bind
by name) to the interfaces of, the Work.
Works, including the Software, are "made available" under this License by
including in or with the Work either (a) a copyright notice referencing the
applicability of this License to the Work, or (b) a copy of this License.
2. License Grants
2.1 Copyright Grant. Subject to the terms and conditions of this License,
each Licensor grants to you a perpetual, worldwide, non-exclusive,
royalty-free, copyright license to reproduce, prepare derivative works of,
publicly display, publicly perform, sublicense and distribute its Work and
any resulting derivative works in any form.
2.2 Patent Grant. Subject to the terms and conditions of this License, each
Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free
patent license to make, have made, use, sell, offer for sale, import, and
otherwise transfer its Work, in whole or in part. The foregoing license
applies only to the patent claims licensable by Licensor that would be
infringed by Licensor's Work (or portion thereof) individually and
excluding any combinations with any other materials or technology.
3. Limitations
3.1 Redistribution. You may reproduce or distribute the Work only if
(a) you do so under this License, (b) you include a complete copy of this
License with your distribution, and (c) you retain without modification
any copyright, patent, trademark, or attribution notices that are present
in the Work.
3.2 Derivative Works. You may specify that additional or different terms
apply to the use, reproduction, and distribution of your derivative works
of the Work ("Your Terms") only if (a) Your Terms provide that the use
limitation in Section 3.3 applies to your derivative works, and (b) you
identify the specific derivative works that are subject to Your Terms.
Notwithstanding Your Terms, this License (including the redistribution
requirements in Section 3.1) will continue to apply to the Work itself.
3.3 Use Limitation. The Work and any derivative works thereof only may be
used or intended for use with the web services, computing platforms or
applications provided by Amazon.com, Inc. or its affiliates, including
Amazon Web Services, Inc.
3.4 Patent Claims. If you bring or threaten to bring a patent claim against
any Licensor (including any claim, cross-claim or counterclaim in a
lawsuit) to enforce any patents that you allege are infringed by any Work,
then your rights under this License from such Licensor (including the
grants in Sections 2.1 and 2.2) will terminate immediately.
3.5 Trademarks. This License does not grant any rights to use any
Licensor's or its affiliates' names, logos, or trademarks, except as
necessary to reproduce the notices described in this License.
3.6 Termination. If you violate any term of this License, then your rights
under this License (including the grants in Sections 2.1 and 2.2) will
terminate immediately.
4. Disclaimer of Warranty.
THE WORK IS PROVIDED "AS IS" WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR
NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER
THIS LICENSE. SOME STATES' CONSUMER LAWS DO NOT ALLOW EXCLUSION OF AN
IMPLIED WARRANTY, SO THIS DISCLAIMER MAY NOT APPLY TO YOU.
5. Limitation of Liability.
EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL
THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE
SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT,
INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR
RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING
BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS
OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER COMM ERCIAL DAMAGES
OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
</string>
<key>License</key>
<string>Apache License, Version 2.0</string>
<key>Title</key>
<string>AppSyncRealTimeClient</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>
<key>Title</key>
<string></string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
</array>
<key>StringsTable</key>
<string>Acknowledgements</string>
<key>Title</key>
<string>Acknowledgements</string>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_Pods_AppSyncRTCSample : NSObject
@end
@implementation PodsDummy_Pods_AppSyncRTCSample
@end

View File

@ -0,0 +1,3 @@
${PODS_ROOT}/Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample-frameworks.sh
${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework
${BUILT_PRODUCTS_DIR}/AppSyncRealTimeClient/AppSyncRealTimeClient.framework

View File

@ -0,0 +1,2 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppSyncRealTimeClient.framework

View File

@ -0,0 +1,3 @@
${PODS_ROOT}/Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample-frameworks.sh
${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework
${BUILT_PRODUCTS_DIR}/AppSyncRealTimeClient/AppSyncRealTimeClient.framework

View File

@ -0,0 +1,2 @@
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AppSyncRealTimeClient.framework

View File

@ -0,0 +1,209 @@
#!/bin/sh
set -e
set -u
set -o pipefail
function on_error {
echo "$(realpath -mq "${0}"):$1: error: Unexpected failure"
}
trap 'on_error $LINENO' ERR
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
# frameworks to, so exit 0 (signalling the script phase was successful).
exit 0
fi
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
# Used as a return value for each invocation of `strip_invalid_archs` function.
STRIP_BINARY_RETVAL=0
# This protects against multiple targets copying the same framework dependency at the same time. The solution
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
# Copies and strips a vendored framework
install_framework()
{
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
local source="${BUILT_PRODUCTS_DIR}/$1"
elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then
local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")"
elif [ -r "$1" ]; then
local source="$1"
fi
local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L "${source}" ]; then
echo "Symlinked..."
source="$(readlink "${source}")"
fi
# Use filter instead of exclude so missing patterns don't throw errors.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
local basename
basename="$(basename -s .framework "$1")"
binary="${destination}/${basename}.framework/${basename}"
if ! [ -r "$binary" ]; then
binary="${destination}/${basename}"
elif [ -L "${binary}" ]; then
echo "Destination binary is symlinked..."
dirname="$(dirname "${binary}")"
binary="${dirname}/$(readlink "${binary}")"
fi
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then
strip_invalid_archs "$binary"
fi
# Resign the code if required by the build settings to avoid unstable apps
code_sign_if_enabled "${destination}/$(basename "$1")"
# Embed linked Swift runtime libraries. No longer necessary as of Xcode 7.
if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then
local swift_runtime_libs
swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u)
for lib in $swift_runtime_libs; do
echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\""
rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
code_sign_if_enabled "${destination}/${lib}"
done
fi
}
# Copies and strips a vendored dSYM
install_dsym() {
local source="$1"
warn_missing_arch=${2:-true}
if [ -r "$source" ]; then
# Copy the dSYM into the targets temp dir.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
local basename
basename="$(basename -s .dSYM "$source")"
binary_name="$(ls "$source/Contents/Resources/DWARF")"
binary="${DERIVED_FILES_DIR}/${basename}.dSYM/Contents/Resources/DWARF/${binary_name}"
# Strip invalid architectures so "fat" simulator / device frameworks work on device
if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then
strip_invalid_archs "$binary" "$warn_missing_arch"
fi
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
# Move the stripped file into its final destination.
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --links --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
else
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.dSYM"
fi
fi
}
# Copies the bcsymbolmap files of a vendored framework
install_bcsymbolmap() {
local bcsymbolmap_path="$1"
local destination="${BUILT_PRODUCTS_DIR}"
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}""
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"
}
# Signs a framework with the provided identity
code_sign_if_enabled() {
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
# Use the current code_sign_identity
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
code_sign_cmd="$code_sign_cmd &"
fi
echo "$code_sign_cmd"
eval "$code_sign_cmd"
fi
}
# Strip invalid architectures
strip_invalid_archs() {
binary="$1"
warn_missing_arch=${2:-true}
# Get architectures for current target binary
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
# Intersect them with the architectures we are building for
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
# If there are no archs supported by this binary then warn the user
if [[ -z "$intersected_archs" ]]; then
if [[ "$warn_missing_arch" == "true" ]]; then
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
fi
STRIP_BINARY_RETVAL=0
return
fi
stripped=""
for arch in $binary_archs; do
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
# Strip non-valid architectures in-place
lipo -remove "$arch" -output "$binary" "$binary"
stripped="$stripped $arch"
fi
done
if [[ "$stripped" ]]; then
echo "Stripped $binary of architectures:$stripped"
fi
STRIP_BINARY_RETVAL=1
}
install_artifact() {
artifact="$1"
base="$(basename "$artifact")"
case $base in
*.framework)
install_framework "$artifact"
;;
*.dSYM)
# Suppress arch warnings since XCFrameworks will include many dSYM files
install_dsym "$artifact" "false"
;;
*.bcsymbolmap)
install_bcsymbolmap "$artifact"
;;
*)
echo "error: Unrecognized artifact "$artifact""
;;
esac
}
copy_artifacts() {
file_list="$1"
while read artifact; do
install_artifact "$artifact"
done <$file_list
}
ARTIFACT_LIST_FILE="${BUILT_PRODUCTS_DIR}/cocoapods-artifacts-${CONFIGURATION}.txt"
if [ -r "${ARTIFACT_LIST_FILE}" ]; then
copy_artifacts "${ARTIFACT_LIST_FILE}"
fi
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework"
install_framework "${BUILT_PRODUCTS_DIR}/AppSyncRealTimeClient/AppSyncRealTimeClient.framework"
fi
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework"
install_framework "${BUILT_PRODUCTS_DIR}/AppSyncRealTimeClient/AppSyncRealTimeClient.framework"
fi
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
wait
fi

View File

@ -0,0 +1,16 @@
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
FOUNDATION_EXPORT double Pods_AppSyncRTCSampleVersionNumber;
FOUNDATION_EXPORT const unsigned char Pods_AppSyncRTCSampleVersionString[];

View File

@ -0,0 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AppSyncRealTimeClient" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AppSyncRealTimeClient/AppSyncRealTimeClient.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream/Starscream.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "AppSyncRealTimeClient" -framework "Starscream"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,6 @@
framework module Pods_AppSyncRTCSample {
umbrella header "Pods-AppSyncRTCSample-umbrella.h"
export *
module * { export * }
}

View File

@ -0,0 +1,12 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AppSyncRealTimeClient" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AppSyncRealTimeClient/AppSyncRealTimeClient.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream/Starscream.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -framework "AppSyncRealTimeClient" -framework "Starscream"
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
PODS_ROOT = ${SRCROOT}/Pods
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -179,4 +179,54 @@ This application makes use of the following third party libraries:
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
## SwiftFormat
MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## SwiftLint
The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Generated by CocoaPods - https://cocoapods.org

View File

@ -197,6 +197,68 @@
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftFormat</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftLint</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>

View File

@ -179,4 +179,54 @@ This application makes use of the following third party libraries:
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
## SwiftFormat
MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## SwiftLint
The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Generated by CocoaPods - https://cocoapods.org

View File

@ -197,6 +197,68 @@
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftFormat</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftLint</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>Generated by CocoaPods - https://cocoapods.org</string>

View File

@ -1,6 +1,56 @@
# Acknowledgements
This application makes use of the following third party libraries:
## SwiftFormat
MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## SwiftLint
The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## Starscream
Apache License

View File

@ -12,6 +12,68 @@
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>MIT License
Copyright (c) 2016 Nick Lockwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftFormat</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2020 Realm Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>SwiftLint</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string> Apache License

View File

@ -1,10 +1,9 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Starscream
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftFormat
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Starscream
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftFormat
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,9 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftFormat
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftFormat
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,9 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftLint
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftLint
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES

View File

@ -0,0 +1,9 @@
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftLint
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftLint
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES