mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Fixes session expiring when downloading apps on slow connection
This commit is contained in:
@@ -137,6 +137,8 @@
|
|||||||
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4A22DD137F008935CF /* NavigationBar.swift */; };
|
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4A22DD137F008935CF /* NavigationBar.swift */; };
|
||||||
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4C22DD16DE008935CF /* PillButton.swift */; };
|
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4C22DD16DE008935CF /* PillButton.swift */; };
|
||||||
BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */; };
|
BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */; };
|
||||||
|
BFA8172923C56042001B5953 /* ServerConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA8172823C56042001B5953 /* ServerConnection.swift */; };
|
||||||
|
BFA8172B23C5633D001B5953 /* FetchAnisetteDataOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */; };
|
||||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB11691229322E400BB457C /* DatabaseManager.swift */; };
|
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB11691229322E400BB457C /* DatabaseManager.swift */; };
|
||||||
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */; };
|
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */; };
|
||||||
BFB3645A2325985F00CD0EB1 /* FindServerOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB364592325985F00CD0EB1 /* FindServerOperation.swift */; };
|
BFB3645A2325985F00CD0EB1 /* FindServerOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB364592325985F00CD0EB1 /* FindServerOperation.swift */; };
|
||||||
@@ -449,6 +451,8 @@
|
|||||||
BF9ABA4C22DD16DE008935CF /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = "<group>"; };
|
BF9ABA4C22DD16DE008935CF /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = "<group>"; };
|
||||||
BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = "<group>"; };
|
BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = "<group>"; };
|
||||||
BF9B63C5229DD44D002F0A62 /* AltSign.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AltSign.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BF9B63C5229DD44D002F0A62 /* AltSign.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AltSign.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
BFA8172823C56042001B5953 /* ServerConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConnection.swift; sourceTree = "<group>"; };
|
||||||
|
BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchAnisetteDataOperation.swift; sourceTree = "<group>"; };
|
||||||
BFB11691229322E400BB457C /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
|
BFB11691229322E400BB457C /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
|
||||||
BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+ManagedObjectContext.swift"; sourceTree = "<group>"; };
|
BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+ManagedObjectContext.swift"; sourceTree = "<group>"; };
|
||||||
BFB1169C22932DB100BB457C /* apps.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = apps.json; sourceTree = "<group>"; };
|
BFB1169C22932DB100BB457C /* apps.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = apps.json; sourceTree = "<group>"; };
|
||||||
@@ -915,6 +919,7 @@
|
|||||||
children = (
|
children = (
|
||||||
BFD52BD322A0800A000B7ED1 /* ServerManager.swift */,
|
BFD52BD322A0800A000B7ED1 /* ServerManager.swift */,
|
||||||
BF770E5522BC3C02002A40FE /* Server.swift */,
|
BF770E5522BC3C02002A40FE /* Server.swift */,
|
||||||
|
BFA8172823C56042001B5953 /* ServerConnection.swift */,
|
||||||
);
|
);
|
||||||
path = Server;
|
path = Server;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1130,6 +1135,7 @@
|
|||||||
BFDB6A0E22AB2776007EA6D6 /* SendAppOperation.swift */,
|
BFDB6A0E22AB2776007EA6D6 /* SendAppOperation.swift */,
|
||||||
BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */,
|
BF770E5022BB1CF6002A40FE /* InstallAppOperation.swift */,
|
||||||
BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */,
|
BFE338DE22F0EADB002E24B9 /* FetchSourceOperation.swift */,
|
||||||
|
BFA8172A23C5633D001B5953 /* FetchAnisetteDataOperation.swift */,
|
||||||
);
|
);
|
||||||
path = Operations;
|
path = Operations;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1677,6 +1683,7 @@
|
|||||||
BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */,
|
BF6F439223644C6E00A0B879 /* RefreshAltStoreViewController.swift in Sources */,
|
||||||
BFE60742231B07E6002B0E8E /* SettingsHeaderFooterView.swift in Sources */,
|
BFE60742231B07E6002B0E8E /* SettingsHeaderFooterView.swift in Sources */,
|
||||||
BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */,
|
BFE338E822F10E56002E24B9 /* LaunchViewController.swift in Sources */,
|
||||||
|
BFA8172B23C5633D001B5953 /* FetchAnisetteDataOperation.swift in Sources */,
|
||||||
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */,
|
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */,
|
||||||
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */,
|
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */,
|
||||||
BF3D648822E79A3700E9056B /* AppPermission.swift in Sources */,
|
BF3D648822E79A3700E9056B /* AppPermission.swift in Sources */,
|
||||||
@@ -1714,6 +1721,7 @@
|
|||||||
BFDB5B1622EE90D300F74113 /* Date+RelativeDate.swift in Sources */,
|
BFDB5B1622EE90D300F74113 /* Date+RelativeDate.swift in Sources */,
|
||||||
BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */,
|
BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */,
|
||||||
BF770E5622BC3C03002A40FE /* Server.swift in Sources */,
|
BF770E5622BC3C03002A40FE /* Server.swift in Sources */,
|
||||||
|
BFA8172923C56042001B5953 /* ServerConnection.swift in Sources */,
|
||||||
BF43003022A71C960051E2BC /* UserDefaults+AltStore.swift in Sources */,
|
BF43003022A71C960051E2BC /* UserDefaults+AltStore.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@@ -96,14 +96,13 @@ extension AppManager
|
|||||||
case .success(let server): group.server = server
|
case .success(let server): group.server = server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.operationQueue.addOperation(findServerOperation)
|
||||||
|
|
||||||
let authenticationOperation = AuthenticationOperation(group: group, presentingViewController: presentingViewController)
|
let authenticationOperation = AuthenticationOperation(group: group, presentingViewController: presentingViewController)
|
||||||
authenticationOperation.addDependency(findServerOperation)
|
|
||||||
authenticationOperation.resultHandler = { (result) in
|
authenticationOperation.resultHandler = { (result) in
|
||||||
completionHandler(result)
|
completionHandler(result)
|
||||||
}
|
}
|
||||||
|
authenticationOperation.addDependency(findServerOperation)
|
||||||
self.operationQueue.addOperation(findServerOperation)
|
|
||||||
self.operationQueue.addOperation(authenticationOperation)
|
self.operationQueue.addOperation(authenticationOperation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,6 +236,17 @@ private extension AppManager
|
|||||||
authenticationOperation = nil
|
authenticationOperation = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let refreshAnisetteDataOperation = FetchAnisetteDataOperation(group: group)
|
||||||
|
refreshAnisetteDataOperation.resultHandler = { (result) in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): group.error = error
|
||||||
|
case .success(let anisetteData): group.session?.anisetteData = anisetteData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refreshAnisetteDataOperation.addDependency(authenticationOperation ?? findServerOperation)
|
||||||
|
operations.append(refreshAnisetteDataOperation)
|
||||||
|
|
||||||
for app in apps
|
for app in apps
|
||||||
{
|
{
|
||||||
let context = AppOperationContext(bundleIdentifier: app.bundleIdentifier, group: group)
|
let context = AppOperationContext(bundleIdentifier: app.bundleIdentifier, group: group)
|
||||||
@@ -249,7 +259,7 @@ private extension AppManager
|
|||||||
guard let resignedApp = self.process(result, context: context) else { return }
|
guard let resignedApp = self.process(result, context: context) else { return }
|
||||||
context.resignedApp = resignedApp
|
context.resignedApp = resignedApp
|
||||||
}
|
}
|
||||||
resignAppOperation.addDependency(authenticationOperation ?? findServerOperation)
|
resignAppOperation.addDependency(refreshAnisetteDataOperation)
|
||||||
progress.addChild(resignAppOperation.progress, withPendingUnitCount: 20)
|
progress.addChild(resignAppOperation.progress, withPendingUnitCount: 20)
|
||||||
operations.append(resignAppOperation)
|
operations.append(resignAppOperation)
|
||||||
|
|
||||||
@@ -296,8 +306,8 @@ private extension AppManager
|
|||||||
/* Send */
|
/* Send */
|
||||||
let sendAppOperation = SendAppOperation(context: context)
|
let sendAppOperation = SendAppOperation(context: context)
|
||||||
sendAppOperation.resultHandler = { (result) in
|
sendAppOperation.resultHandler = { (result) in
|
||||||
guard let connection = self.process(result, context: context) else { return }
|
guard let installationConnection = self.process(result, context: context) else { return }
|
||||||
context.connection = connection
|
context.installationConnection = installationConnection
|
||||||
}
|
}
|
||||||
progress.addChild(sendAppOperation.progress, withPendingUnitCount: 10)
|
progress.addChild(sendAppOperation.progress, withPendingUnitCount: 10)
|
||||||
sendAppOperation.addDependency(resignAppOperation)
|
sendAppOperation.addDependency(resignAppOperation)
|
||||||
@@ -341,6 +351,12 @@ private extension AppManager
|
|||||||
group.set(progress, for: app)
|
group.set(progress, for: app)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refresh anisette data after downloading all apps to prevent session from expiring.
|
||||||
|
for case let downloadOperation as DownloadAppOperation in operations
|
||||||
|
{
|
||||||
|
refreshAnisetteDataOperation.addDependency(downloadOperation)
|
||||||
|
}
|
||||||
|
|
||||||
group.addOperations(operations)
|
group.addOperations(operations)
|
||||||
|
|
||||||
return group
|
return group
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class AppOperationContext
|
|||||||
var app: ALTApplication?
|
var app: ALTApplication?
|
||||||
var resignedApp: ALTApplication?
|
var resignedApp: ALTApplication?
|
||||||
|
|
||||||
var connection: NWConnection?
|
var installationConnection: ServerConnection?
|
||||||
|
|
||||||
var installedApp: InstalledApp? {
|
var installedApp: InstalledApp? {
|
||||||
didSet {
|
didSet {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
|
|||||||
private var signer: ALTSigner?
|
private var signer: ALTSigner?
|
||||||
private var session: ALTAppleAPISession?
|
private var session: ALTAppleAPISession?
|
||||||
|
|
||||||
private let dispatchQueue = DispatchQueue(label: "com.altstore.AuthenticationOperation")
|
private let operationQueue = OperationQueue()
|
||||||
|
|
||||||
private var submitCodeAction: UIAlertAction?
|
private var submitCodeAction: UIAlertAction?
|
||||||
|
|
||||||
@@ -66,6 +66,7 @@ class AuthenticationOperation: ResultOperation<(ALTSigner, ALTAppleAPISession)>
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.operationQueue.name = "com.altstore.AuthenticationOperation"
|
||||||
self.progress.totalUnitCount = 3
|
self.progress.totalUnitCount = 3
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,33 +222,6 @@ private extension AuthenticationOperation
|
|||||||
|
|
||||||
private extension AuthenticationOperation
|
private extension AuthenticationOperation
|
||||||
{
|
{
|
||||||
func connect(to server: Server, completionHandler: @escaping (Result<NWConnection, Error>) -> Void)
|
|
||||||
{
|
|
||||||
let connection = NWConnection(to: .service(name: server.service.name, type: server.service.type, domain: server.service.domain, interface: nil), using: .tcp)
|
|
||||||
|
|
||||||
connection.stateUpdateHandler = { [unowned connection] (state) in
|
|
||||||
switch state
|
|
||||||
{
|
|
||||||
case .failed(let error):
|
|
||||||
print("Failed to connect to service \(server.service.name).", error)
|
|
||||||
completionHandler(.failure(ConnectionError.connectionFailed))
|
|
||||||
|
|
||||||
case .cancelled:
|
|
||||||
completionHandler(.failure(OperationError.cancelled))
|
|
||||||
|
|
||||||
case .ready:
|
|
||||||
completionHandler(.success(connection))
|
|
||||||
|
|
||||||
case .waiting: break
|
|
||||||
case .setup: break
|
|
||||||
case .preparing: break
|
|
||||||
@unknown default: break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.start(queue: self.dispatchQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func signIn(completionHandler: @escaping (Result<(ALTAccount, ALTAppleAPISession), Swift.Error>) -> Void)
|
func signIn(completionHandler: @escaping (Result<(ALTAccount, ALTAppleAPISession), Swift.Error>) -> Void)
|
||||||
{
|
{
|
||||||
func authenticate()
|
func authenticate()
|
||||||
@@ -307,98 +281,73 @@ private extension AuthenticationOperation
|
|||||||
|
|
||||||
func authenticate(appleID: String, password: String, completionHandler: @escaping (Result<(ALTAccount, ALTAppleAPISession), Swift.Error>) -> Void)
|
func authenticate(appleID: String, password: String, completionHandler: @escaping (Result<(ALTAccount, ALTAppleAPISession), Swift.Error>) -> Void)
|
||||||
{
|
{
|
||||||
guard let server = self.group.server else { return completionHandler(.failure(OperationError.invalidParameters)) }
|
let fetchAnisetteDataOperation = FetchAnisetteDataOperation(group: self.group)
|
||||||
|
fetchAnisetteDataOperation.resultHandler = { (result) in
|
||||||
self.connect(to: server) { (result) in
|
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): completionHandler(.failure(error))
|
case .failure(let error): completionHandler(.failure(error))
|
||||||
case .success(let connection):
|
case .success(let anisetteData):
|
||||||
|
let verificationHandler: ((@escaping (String?) -> Void) -> Void)?
|
||||||
|
|
||||||
let request = AnisetteDataRequest()
|
if let presentingViewController = self.presentingViewController
|
||||||
server.send(request, via: connection) { (result) in
|
{
|
||||||
switch result
|
verificationHandler = { (completionHandler) in
|
||||||
{
|
DispatchQueue.main.async {
|
||||||
case .failure(let error): completionHandler(.failure(error))
|
let alertController = UIAlertController(title: NSLocalizedString("Please enter the 6-digit verification code that was sent to your Apple devices.", comment: ""), message: nil, preferredStyle: .alert)
|
||||||
case .success:
|
alertController.addTextField { (textField) in
|
||||||
|
textField.autocorrectionType = .no
|
||||||
server.receiveResponse(from: connection) { (result) in
|
textField.autocapitalizationType = .none
|
||||||
switch result
|
textField.keyboardType = .numberPad
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(AuthenticationOperation.textFieldTextDidChange(_:)), name: UITextField.textDidChangeNotification, object: textField)
|
||||||
|
}
|
||||||
|
|
||||||
|
let submitAction = UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default) { (action) in
|
||||||
|
let textField = alertController.textFields?.first
|
||||||
|
|
||||||
|
let code = textField?.text ?? ""
|
||||||
|
completionHandler(code)
|
||||||
|
}
|
||||||
|
submitAction.isEnabled = false
|
||||||
|
alertController.addAction(submitAction)
|
||||||
|
self.submitCodeAction = submitAction
|
||||||
|
|
||||||
|
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("Cancel"), style: .cancel) { (action) in
|
||||||
|
completionHandler(nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
if self.navigationController.presentingViewController != nil
|
||||||
{
|
{
|
||||||
case .failure(let error):
|
self.navigationController.present(alertController, animated: true, completion: nil)
|
||||||
completionHandler(.failure(error))
|
}
|
||||||
|
else
|
||||||
case .success(.error(let response)):
|
{
|
||||||
completionHandler(.failure(response.error))
|
presentingViewController.present(alertController, animated: true, completion: nil)
|
||||||
|
|
||||||
case .success(.anisetteData(let response)):
|
|
||||||
let verificationHandler: ((@escaping (String?) -> Void) -> Void)?
|
|
||||||
|
|
||||||
if let presentingViewController = self.presentingViewController
|
|
||||||
{
|
|
||||||
verificationHandler = { (completionHandler) in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let alertController = UIAlertController(title: NSLocalizedString("Please enter the 6-digit verification code that was sent to your Apple devices.", comment: ""),
|
|
||||||
message: nil, preferredStyle: .alert)
|
|
||||||
alertController.addTextField { (textField) in
|
|
||||||
textField.autocorrectionType = .no
|
|
||||||
textField.autocapitalizationType = .none
|
|
||||||
textField.keyboardType = .numberPad
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(AuthenticationOperation.textFieldTextDidChange(_:)), name: UITextField.textDidChangeNotification, object: textField)
|
|
||||||
}
|
|
||||||
|
|
||||||
let submitAction = UIAlertAction(title: NSLocalizedString("Continue", comment: ""), style: .default) { (action) in
|
|
||||||
let textField = alertController.textFields?.first
|
|
||||||
|
|
||||||
let code = textField?.text ?? ""
|
|
||||||
completionHandler(code)
|
|
||||||
}
|
|
||||||
submitAction.isEnabled = false
|
|
||||||
alertController.addAction(submitAction)
|
|
||||||
self.submitCodeAction = submitAction
|
|
||||||
|
|
||||||
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("Cancel"), style: .cancel) { (action) in
|
|
||||||
completionHandler(nil)
|
|
||||||
})
|
|
||||||
|
|
||||||
if self.navigationController.presentingViewController != nil
|
|
||||||
{
|
|
||||||
self.navigationController.present(alertController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
presentingViewController.present(alertController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No view controller to present security code alert, so don't provide verificationHandler.
|
|
||||||
verificationHandler = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ALTAppleAPI.shared.authenticate(appleID: appleID, password: password, anisetteData: response.anisetteData,
|
|
||||||
verificationHandler: verificationHandler) { (account, session, error) in
|
|
||||||
if let account = account, let session = session
|
|
||||||
{
|
|
||||||
completionHandler(.success((account, session)))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
completionHandler(.failure(error ?? OperationError.unknown))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case .success:
|
|
||||||
completionHandler(.failure(ALTServerError(.unknownRequest)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No view controller to present security code alert, so don't provide verificationHandler.
|
||||||
|
verificationHandler = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ALTAppleAPI.shared.authenticate(appleID: appleID, password: password, anisetteData: anisetteData,
|
||||||
|
verificationHandler: verificationHandler) { (account, session, error) in
|
||||||
|
if let account = account, let session = session
|
||||||
|
{
|
||||||
|
completionHandler(.success((account, session)))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completionHandler(.failure(error ?? OperationError.unknown))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.operationQueue.addOperation(fetchAnisetteDataOperation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Swift.Error>) -> Void)
|
func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Swift.Error>) -> Void)
|
||||||
|
|||||||
65
AltStore/Operations/FetchAnisetteDataOperation.swift
Normal file
65
AltStore/Operations/FetchAnisetteDataOperation.swift
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// FetchAnisetteDataOperation.swift
|
||||||
|
// AltStore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/7/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import AltSign
|
||||||
|
import AltKit
|
||||||
|
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
@objc(FetchAnisetteDataOperation)
|
||||||
|
class FetchAnisetteDataOperation: ResultOperation<ALTAnisetteData>
|
||||||
|
{
|
||||||
|
let group: OperationGroup
|
||||||
|
|
||||||
|
init(group: OperationGroup)
|
||||||
|
{
|
||||||
|
self.group = group
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func main()
|
||||||
|
{
|
||||||
|
super.main()
|
||||||
|
|
||||||
|
if let error = self.group.error
|
||||||
|
{
|
||||||
|
self.finish(.failure(error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let server = self.group.server else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||||
|
|
||||||
|
ServerManager.shared.connect(to: server) { (result) in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): self.finish(.failure(error))
|
||||||
|
case .success(let connection):
|
||||||
|
let request = AnisetteDataRequest()
|
||||||
|
connection.send(request) { (result) in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): self.finish(.failure(error))
|
||||||
|
case .success:
|
||||||
|
connection.receiveResponse() { (result) in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(let error): self.finish(.failure(error))
|
||||||
|
case .success(.error(let response)): self.finish(.failure(response.error))
|
||||||
|
case .success(.anisetteData(let response)): self.finish(.success(response.anisetteData))
|
||||||
|
case .success: self.finish(.failure(ALTServerError(.unknownRequest)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,8 +41,7 @@ class InstallAppOperation: ResultOperation<InstalledApp>
|
|||||||
|
|
||||||
guard
|
guard
|
||||||
let resignedApp = self.context.resignedApp,
|
let resignedApp = self.context.resignedApp,
|
||||||
let connection = self.context.connection,
|
let connection = self.context.installationConnection
|
||||||
let server = self.context.group.server
|
|
||||||
else { return self.finish(.failure(OperationError.invalidParameters)) }
|
else { return self.finish(.failure(OperationError.invalidParameters)) }
|
||||||
|
|
||||||
let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
|
||||||
@@ -73,13 +72,13 @@ class InstallAppOperation: ResultOperation<InstalledApp>
|
|||||||
self.context.group.beginInstallationHandler?(installedApp)
|
self.context.group.beginInstallationHandler?(installedApp)
|
||||||
|
|
||||||
let request = BeginInstallationRequest()
|
let request = BeginInstallationRequest()
|
||||||
server.send(request, via: connection) { (result) in
|
connection.send(request) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): self.finish(.failure(error))
|
case .failure(let error): self.finish(.failure(error))
|
||||||
case .success:
|
case .success:
|
||||||
|
|
||||||
self.receive(from: connection, server: server) { (result) in
|
self.receive(from: connection) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .success:
|
case .success:
|
||||||
@@ -107,9 +106,9 @@ class InstallAppOperation: ResultOperation<InstalledApp>
|
|||||||
|
|
||||||
private extension InstallAppOperation
|
private extension InstallAppOperation
|
||||||
{
|
{
|
||||||
func receive(from connection: NWConnection, server: Server, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
func receive(from connection: ServerConnection, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
||||||
{
|
{
|
||||||
server.receiveResponse(from: connection) { (result) in
|
connection.receiveResponse() { (result) in
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let response = try result.get()
|
let response = try result.get()
|
||||||
@@ -126,7 +125,7 @@ private extension InstallAppOperation
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
self.progress.completedUnitCount = Int64(response.progress * 100)
|
self.progress.completedUnitCount = Int64(response.progress * 100)
|
||||||
self.receive(from: connection, server: server, completionHandler: completionHandler)
|
self.receive(from: connection, completionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
case .error(let response):
|
case .error(let response):
|
||||||
|
|||||||
@@ -12,13 +12,13 @@ import Network
|
|||||||
import AltKit
|
import AltKit
|
||||||
|
|
||||||
@objc(SendAppOperation)
|
@objc(SendAppOperation)
|
||||||
class SendAppOperation: ResultOperation<NWConnection>
|
class SendAppOperation: ResultOperation<ServerConnection>
|
||||||
{
|
{
|
||||||
let context: AppOperationContext
|
let context: AppOperationContext
|
||||||
|
|
||||||
private let dispatchQueue = DispatchQueue(label: "com.altstore.SendAppOperation")
|
private let dispatchQueue = DispatchQueue(label: "com.altstore.SendAppOperation")
|
||||||
|
|
||||||
private var connection: NWConnection?
|
private var serverConnection: ServerConnection?
|
||||||
|
|
||||||
init(context: AppOperationContext)
|
init(context: AppOperationContext)
|
||||||
{
|
{
|
||||||
@@ -45,21 +45,21 @@ class SendAppOperation: ResultOperation<NWConnection>
|
|||||||
let fileURL = InstalledApp.refreshedIPAURL(for: app)
|
let fileURL = InstalledApp.refreshedIPAURL(for: app)
|
||||||
|
|
||||||
// Connect to server.
|
// Connect to server.
|
||||||
self.connect(to: server) { (result) in
|
ServerManager.shared.connect(to: server) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): self.finish(.failure(error))
|
case .failure(let error): self.finish(.failure(error))
|
||||||
case .success(let connection):
|
case .success(let serverConnection):
|
||||||
self.connection = connection
|
self.serverConnection = serverConnection
|
||||||
|
|
||||||
// Send app to server.
|
// Send app to server.
|
||||||
self.sendApp(at: fileURL, via: connection, server: server) { (result) in
|
self.sendApp(at: fileURL, via: serverConnection) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): self.finish(.failure(error))
|
case .failure(let error): self.finish(.failure(error))
|
||||||
case .success:
|
case .success:
|
||||||
self.progress.completedUnitCount += 1
|
self.progress.completedUnitCount += 1
|
||||||
self.finish(.success(connection))
|
self.finish(.success(serverConnection))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,34 +69,7 @@ class SendAppOperation: ResultOperation<NWConnection>
|
|||||||
|
|
||||||
private extension SendAppOperation
|
private extension SendAppOperation
|
||||||
{
|
{
|
||||||
func connect(to server: Server, completionHandler: @escaping (Result<NWConnection, Error>) -> Void)
|
func sendApp(at fileURL: URL, via connection: ServerConnection, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
||||||
{
|
|
||||||
let connection = NWConnection(to: .service(name: server.service.name, type: server.service.type, domain: server.service.domain, interface: nil), using: .tcp)
|
|
||||||
|
|
||||||
connection.stateUpdateHandler = { [unowned connection] (state) in
|
|
||||||
switch state
|
|
||||||
{
|
|
||||||
case .failed(let error):
|
|
||||||
print("Failed to connect to service \(server.service.name).", error)
|
|
||||||
completionHandler(.failure(ConnectionError.connectionFailed))
|
|
||||||
|
|
||||||
case .cancelled:
|
|
||||||
completionHandler(.failure(OperationError.cancelled))
|
|
||||||
|
|
||||||
case .ready:
|
|
||||||
completionHandler(.success(connection))
|
|
||||||
|
|
||||||
case .waiting: break
|
|
||||||
case .setup: break
|
|
||||||
case .preparing: break
|
|
||||||
@unknown default: break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.start(queue: self.dispatchQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendApp(at fileURL: URL, via connection: NWConnection, server: Server, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -106,14 +79,14 @@ private extension SendAppOperation
|
|||||||
let request = PrepareAppRequest(udid: udid, contentSize: appData.count)
|
let request = PrepareAppRequest(udid: udid, contentSize: appData.count)
|
||||||
|
|
||||||
print("Sending request \(request)")
|
print("Sending request \(request)")
|
||||||
server.send(request, via: connection) { (result) in
|
connection.send(request) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): completionHandler(.failure(error))
|
case .failure(let error): completionHandler(.failure(error))
|
||||||
case .success:
|
case .success:
|
||||||
|
|
||||||
print("Sending app data (\(appData.count) bytes)")
|
print("Sending app data (\(appData.count) bytes)")
|
||||||
server.send(appData, via: connection, prependSize: false) { (result) in
|
connection.send(appData, prependSize: false) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error): completionHandler(.failure(error))
|
case .failure(let error): completionHandler(.failure(error))
|
||||||
|
|||||||
@@ -57,126 +57,4 @@ struct Server: Equatable
|
|||||||
self.identifier = identifier
|
self.identifier = identifier
|
||||||
self.service = service
|
self.service = service
|
||||||
}
|
}
|
||||||
|
|
||||||
func send<T: Encodable>(_ payload: T, via connection: NWConnection, prependSize: Bool = true, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let data: Data
|
|
||||||
|
|
||||||
if let payload = payload as? Data
|
|
||||||
{
|
|
||||||
data = payload
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data = try JSONEncoder().encode(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func process(_ error: Error?) -> Bool
|
|
||||||
{
|
|
||||||
if error != nil
|
|
||||||
{
|
|
||||||
completionHandler(.failure(ConnectionError.connectionDropped))
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if prependSize
|
|
||||||
{
|
|
||||||
let requestSize = Int32(data.count)
|
|
||||||
let requestSizeData = withUnsafeBytes(of: requestSize) { Data($0) }
|
|
||||||
|
|
||||||
connection.send(content: requestSizeData, completion: .contentProcessed { (error) in
|
|
||||||
guard process(error) else { return }
|
|
||||||
|
|
||||||
connection.send(content: data, completion: .contentProcessed { (error) in
|
|
||||||
guard process(error) else { return }
|
|
||||||
completionHandler(.success(()))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
connection.send(content: data, completion: .contentProcessed { (error) in
|
|
||||||
guard process(error) else { return }
|
|
||||||
completionHandler(.success(()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
print("Invalid request.", error)
|
|
||||||
completionHandler(.failure(ALTServerError(.invalidRequest)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func receiveResponse(from connection: NWConnection, completionHandler: @escaping (Result<ServerResponse, Error>) -> Void)
|
|
||||||
{
|
|
||||||
let size = MemoryLayout<Int32>.size
|
|
||||||
|
|
||||||
connection.receive(minimumIncompleteLength: size, maximumLength: size) { (data, _, _, error) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let data = try self.process(data: data, error: error, from: connection)
|
|
||||||
|
|
||||||
let expectedBytes = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
|
|
||||||
connection.receive(minimumIncompleteLength: expectedBytes, maximumLength: expectedBytes) { (data, _, _, error) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let data = try self.process(data: data, error: error, from: connection)
|
|
||||||
|
|
||||||
let response = try JSONDecoder().decode(ServerResponse.self, from: data)
|
|
||||||
completionHandler(.success(response))
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
completionHandler(.failure(ALTServerError(error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
completionHandler(.failure(ALTServerError(error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension Server
|
|
||||||
{
|
|
||||||
func process(data: Data?, error: NWError?, from connection: NWConnection) throws -> Data
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
guard let data = data else { throw error ?? ALTServerError(.unknown) }
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
catch let error as NWError
|
|
||||||
{
|
|
||||||
print("Error receiving data from connection \(connection)", error)
|
|
||||||
|
|
||||||
throw ALTServerError(.lostConnection)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch let error as ALTServerError
|
|
||||||
{
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
preconditionFailure("A non-ALTServerError should never be thrown from this method.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
146
AltStore/Server/ServerConnection.swift
Normal file
146
AltStore/Server/ServerConnection.swift
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
//
|
||||||
|
// ServerConnection.swift
|
||||||
|
// AltStore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/7/20.
|
||||||
|
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Network
|
||||||
|
|
||||||
|
import AltKit
|
||||||
|
|
||||||
|
class ServerConnection
|
||||||
|
{
|
||||||
|
var server: Server
|
||||||
|
var connection: NWConnection
|
||||||
|
|
||||||
|
init(server: Server, connection: NWConnection)
|
||||||
|
{
|
||||||
|
self.server = server
|
||||||
|
self.connection = connection
|
||||||
|
}
|
||||||
|
|
||||||
|
func send<T: Encodable>(_ payload: T, prependSize: Bool = true, completionHandler: @escaping (Result<Void, Error>) -> Void)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let data: Data
|
||||||
|
|
||||||
|
if let payload = payload as? Data
|
||||||
|
{
|
||||||
|
data = payload
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = try JSONEncoder().encode(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
func process(_ error: Error?) -> Bool
|
||||||
|
{
|
||||||
|
if error != nil
|
||||||
|
{
|
||||||
|
completionHandler(.failure(ConnectionError.connectionDropped))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if prependSize
|
||||||
|
{
|
||||||
|
let requestSize = Int32(data.count)
|
||||||
|
let requestSizeData = withUnsafeBytes(of: requestSize) { Data($0) }
|
||||||
|
|
||||||
|
self.connection.send(content: requestSizeData, completion: .contentProcessed { (error) in
|
||||||
|
guard process(error) else { return }
|
||||||
|
|
||||||
|
self.connection.send(content: data, completion: .contentProcessed { (error) in
|
||||||
|
guard process(error) else { return }
|
||||||
|
completionHandler(.success(()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connection.send(content: data, completion: .contentProcessed { (error) in
|
||||||
|
guard process(error) else { return }
|
||||||
|
completionHandler(.success(()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print("Invalid request.", error)
|
||||||
|
completionHandler(.failure(ALTServerError(.invalidRequest)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveResponse(completionHandler: @escaping (Result<ServerResponse, Error>) -> Void)
|
||||||
|
{
|
||||||
|
let size = MemoryLayout<Int32>.size
|
||||||
|
|
||||||
|
self.connection.receive(minimumIncompleteLength: size, maximumLength: size) { (data, _, _, error) in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let data = try self.process(data: data, error: error)
|
||||||
|
|
||||||
|
let expectedBytes = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
|
||||||
|
self.connection.receive(minimumIncompleteLength: expectedBytes, maximumLength: expectedBytes) { (data, _, _, error) in
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let data = try self.process(data: data, error: error)
|
||||||
|
|
||||||
|
let response = try JSONDecoder().decode(ServerResponse.self, from: data)
|
||||||
|
completionHandler(.success(response))
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
completionHandler(.failure(ALTServerError(error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
completionHandler(.failure(ALTServerError(error)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ServerConnection
|
||||||
|
{
|
||||||
|
func process(data: Data?, error: NWError?) throws -> Data
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
guard let data = data else { throw error ?? ALTServerError(.unknown) }
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
catch let error as NWError
|
||||||
|
{
|
||||||
|
print("Error receiving data from connection \(connection)", error)
|
||||||
|
|
||||||
|
throw ALTServerError(.lostConnection)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch let error as ALTServerError
|
||||||
|
{
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
preconditionFailure("A non-ALTServerError should never be thrown from this method.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,8 @@ class ServerManager: NSObject
|
|||||||
|
|
||||||
private var services = Set<NetService>()
|
private var services = Set<NetService>()
|
||||||
|
|
||||||
|
private let dispatchQueue = DispatchQueue(label: "io.altstore.ServerManager")
|
||||||
|
|
||||||
private override init()
|
private override init()
|
||||||
{
|
{
|
||||||
super.init()
|
super.init()
|
||||||
@@ -50,6 +52,34 @@ extension ServerManager
|
|||||||
self.services.removeAll()
|
self.services.removeAll()
|
||||||
self.serviceBrowser.stop()
|
self.serviceBrowser.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func connect(to server: Server, completion: @escaping (Result<ServerConnection, Error>) -> Void)
|
||||||
|
{
|
||||||
|
let connection = NWConnection(to: .service(name: server.service.name, type: server.service.type, domain: server.service.domain, interface: nil), using: .tcp)
|
||||||
|
|
||||||
|
connection.stateUpdateHandler = { [unowned connection] (state) in
|
||||||
|
switch state
|
||||||
|
{
|
||||||
|
case .failed(let error):
|
||||||
|
print("Failed to connect to service \(server.service.name).", error)
|
||||||
|
completion(.failure(ConnectionError.connectionFailed))
|
||||||
|
|
||||||
|
case .cancelled:
|
||||||
|
completion(.failure(OperationError.cancelled))
|
||||||
|
|
||||||
|
case .ready:
|
||||||
|
let connection = ServerConnection(server: server, connection: connection)
|
||||||
|
completion(.success(connection))
|
||||||
|
|
||||||
|
case .waiting: break
|
||||||
|
case .setup: break
|
||||||
|
case .preparing: break
|
||||||
|
@unknown default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
connection.start(queue: self.dispatchQueue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ServerManager
|
private extension ServerManager
|
||||||
|
|||||||
Reference in New Issue
Block a user