summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2021-09-14 13:16:12 +0200
committerAndrej Mihajlov <and@mullvad.net>2021-09-15 11:03:47 +0200
commitd2987ab97492787acf84f8aff589101cd8219e0f (patch)
treeefa5b7f16229eebbb501df8b063235ec92b2b5f3
parent28d5e14abed7cab6dabfe2ec1e6d0c2a04ff5f3e (diff)
downloadmullvadvpn-d2987ab97492787acf84f8aff589101cd8219e0f.tar.xz
mullvadvpn-d2987ab97492787acf84f8aff589101cd8219e0f.zip
AppStoreReceipt: refactor
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj4
-rw-r--r--ios/MullvadVPN/AppStoreReceipt.swift92
-rw-r--r--ios/MullvadVPN/Operations/ReceiptRefreshOperation.swift46
3 files changed, 80 insertions, 62 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 15263a1696..12f9d22b1e 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -54,6 +54,7 @@
5845F843236CBDAB00B2D93C /* PacketTunnelIpc.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */; };
5846226726E0DF960035F7C2 /* Promise+OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846226626E0DF960035F7C2 /* Promise+OperationQueue.swift */; };
5846226826E0DF960035F7C2 /* Promise+OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846226626E0DF960035F7C2 /* Promise+OperationQueue.swift */; };
+ 5846226A26E0E6FA0035F7C2 /* ReceiptRefreshOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5846226926E0E6FA0035F7C2 /* ReceiptRefreshOperation.swift */; };
584789B8264D4A2A000E45FB /* old_le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 584789B4264D4A2A000E45FB /* old_le_root_cert.cer */; };
584789B9264D4A2A000E45FB /* old_le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 584789B4264D4A2A000E45FB /* old_le_root_cert.cer */; };
584789BE264D4A2A000E45FB /* new_le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 584789B7264D4A2A000E45FB /* new_le_root_cert.cer */; };
@@ -339,6 +340,7 @@
584592602639B4A200EF967F /* ConsentContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsentContentView.swift; sourceTree = "<group>"; };
5845F841236CBACD00B2D93C /* PacketTunnelIpc.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelIpc.swift; sourceTree = "<group>"; };
5846226626E0DF960035F7C2 /* Promise+OperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Promise+OperationQueue.swift"; sourceTree = "<group>"; };
+ 5846226926E0E6FA0035F7C2 /* ReceiptRefreshOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptRefreshOperation.swift; sourceTree = "<group>"; };
584789B4264D4A2A000E45FB /* old_le_root_cert.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = old_le_root_cert.cer; sourceTree = "<group>"; };
584789B7264D4A2A000E45FB /* new_le_root_cert.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = new_le_root_cert.cer; sourceTree = "<group>"; };
584789DF26529D72000E45FB /* SSLPinningURLSessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSLPinningURLSessionDelegate.swift; sourceTree = "<group>"; };
@@ -530,6 +532,7 @@
580EE22324B3243100F9D8A1 /* AsyncBlockOperation.swift */,
58E973DD24850EB600096F90 /* AsyncOperation.swift */,
580EE20524B3222200F9D8A1 /* ExclusivityController.swift */,
+ 5846226926E0E6FA0035F7C2 /* ReceiptRefreshOperation.swift */,
);
path = Operations;
sourceTree = "<group>";
@@ -1135,6 +1138,7 @@
58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */,
58FAEDEF245069C700CB0F5B /* KeychainAttributes.swift in Sources */,
58CB0EE024B86751001EF0D8 /* MullvadRest.swift in Sources */,
+ 5846226A26E0E6FA0035F7C2 /* ReceiptRefreshOperation.swift in Sources */,
58E1337526D2BEC400CC316B /* Promise+Optional.swift in Sources */,
58293FB125124117005D0BB5 /* CustomTextField.swift in Sources */,
582AE3102440A6CA00E6733A /* AccountTokenInput.swift in Sources */,
diff --git a/ios/MullvadVPN/AppStoreReceipt.swift b/ios/MullvadVPN/AppStoreReceipt.swift
index 9ced1bad7d..200d6ba1df 100644
--- a/ios/MullvadVPN/AppStoreReceipt.swift
+++ b/ios/MullvadVPN/AppStoreReceipt.swift
@@ -40,8 +40,26 @@ enum AppStoreReceipt {
return queue
}()
+ /// Read AppStore receipt from disk or refresh it from AppStore if it's missing.
+ /// This call may trigger a sign in with AppStore prompt to appear.
+ static func fetch(forceRefresh: Bool = false, receiptProperties: [String: Any]? = nil) -> Result<Data, Error>.Promise {
+ if forceRefresh {
+ return refreshReceipt(receiptProperties: receiptProperties)
+ } else {
+ return self.readFromDisk()
+ .asPromise()
+ .flatMapErrorThen { error in
+ if case .doesNotExist = error {
+ return refreshReceipt(receiptProperties: receiptProperties)
+ } else {
+ return .failure(error)
+ }
+ }
+ }
+ }
+
/// Read AppStore receipt from disk
- static func readFromDisk() -> Result<Data, Error> {
+ private static func readFromDisk() -> Result<Data, Error> {
guard let appStoreReceiptURL = Bundle.main.appStoreReceiptURL else {
return .failure(.doesNotExist)
}
@@ -56,70 +74,20 @@ enum AppStoreReceipt {
}
}
- /// Read AppStore receipt from disk or refresh it from the AppStore if it's missing
- /// This call may trigger a sign in with AppStore prompt to appear
- static func fetch(forceRefresh: Bool = false, receiptProperties: [String: Any]? = nil,
- completionHandler: @escaping (Result<Data, Error>) -> Void)
- {
- if forceRefresh {
- refreshReceipt(receiptProperties: receiptProperties,
- completionHandler: completionHandler)
- } else {
- switch self.readFromDisk() {
- case .success(let data):
- completionHandler(.success(data))
-
- case .failure(let error):
- // Refresh the receipt from AppStore if it's not on disk
- if case .doesNotExist = error {
- refreshReceipt(receiptProperties: receiptProperties,
- completionHandler: completionHandler)
- } else {
- completionHandler(.failure(error))
- }
+ /// Refresh receipt from AppStore
+ private static func refreshReceipt(receiptProperties: [String: Any]?) -> Result<Data, Error>.Promise {
+ return Result<(), Swift.Error>.Promise { resolver in
+ let operation = ReceiptRefreshOperation(receiptProperties: receiptProperties) { result in
+ resolver.resolve(value: result)
}
+ self.operationQueue.addOperation(operation)
}
- }
-
- private static func refreshReceipt(receiptProperties: [String: Any]?, completionHandler: @escaping (Result<Data, Error>) -> Void) {
- let refreshOperation = ReceiptRefreshOperation(receiptProperties: receiptProperties)
- refreshOperation.addDidFinishBlockObserver { (operation, result) in
- let result = result
- .mapError { Error.refresh($0) }
- .flatMap { Self.readFromDisk() }
- completionHandler(result)
+ .mapError { error in
+ return .refresh(error)
+ }
+ .flatMap {
+ return Self.readFromDisk()
}
-
- operationQueue.addOperation(refreshOperation)
}
}
-
-private class ReceiptRefreshOperation: AsyncOperation, OutputOperation, SKRequestDelegate {
- typealias Output = Result<(), Error>
-
- private let request: SKReceiptRefreshRequest
-
- init(receiptProperties: [String: Any]?) {
- request = SKReceiptRefreshRequest(receiptProperties: receiptProperties)
- }
-
- override func main() {
- request.delegate = self
- request.start()
- }
-
- override func operationDidCancel() {
- request.cancel()
- }
-
- // - MARK: SKRequestDelegate
-
- func requestDidFinish(_ request: SKRequest) {
- finish(with: .success(()))
- }
-
- func request(_ request: SKRequest, didFailWithError error: Error) {
- finish(with: .failure(error))
- }
-}
diff --git a/ios/MullvadVPN/Operations/ReceiptRefreshOperation.swift b/ios/MullvadVPN/Operations/ReceiptRefreshOperation.swift
new file mode 100644
index 0000000000..c386fc1cb1
--- /dev/null
+++ b/ios/MullvadVPN/Operations/ReceiptRefreshOperation.swift
@@ -0,0 +1,46 @@
+//
+// ReceiptRefreshOperation.swift
+// ReceiptRefreshOperation
+//
+// Created by pronebird on 02/09/2021.
+// Copyright © 2021 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import StoreKit
+
+class ReceiptRefreshOperation: AsyncOperation, SKRequestDelegate {
+ private let request: SKReceiptRefreshRequest
+ private let completionHandler: (Result<(), Error>) -> Void
+
+ init(receiptProperties: [String: Any]?, completionHandler: @escaping (Result<(), Error>) -> Void) {
+ request = SKReceiptRefreshRequest(receiptProperties: receiptProperties)
+ self.completionHandler = completionHandler
+
+ super.init()
+
+ request.delegate = self
+ }
+
+ override func main() {
+ request.start()
+ }
+
+ override func cancel() {
+ super.cancel()
+
+ request.cancel()
+ }
+
+ // - MARK: SKRequestDelegate
+
+ func requestDidFinish(_ request: SKRequest) {
+ completionHandler(.success(()))
+ finish()
+ }
+
+ func request(_ request: SKRequest, didFailWithError error: Error) {
+ completionHandler(.failure(error))
+ finish()
+ }
+}