diff --git a/apple/InfoPlists/MacOmnivore.plist b/apple/InfoPlists/MacOmnivore.plist
index 7224e9b7f..d18829a1d 100644
--- a/apple/InfoPlists/MacOmnivore.plist
+++ b/apple/InfoPlists/MacOmnivore.plist
@@ -28,6 +28,14 @@
omnivore
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLSchemes
+
+ com.googleusercontent.apps.267918240109-bdghlau7nsq2480c4l8gdgh6mrarokta
+
+
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
diff --git a/apple/InfoPlists/Omnivore.plist b/apple/InfoPlists/Omnivore.plist
index 947099c90..c9a47cec4 100644
--- a/apple/InfoPlists/Omnivore.plist
+++ b/apple/InfoPlists/Omnivore.plist
@@ -32,6 +32,14 @@
omnivore
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLSchemes
+
+ com.googleusercontent.apps.267918240109-bdghlau7nsq2480c4l8gdgh6mrarokta
+
+
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
diff --git a/apple/Omnivore.xcodeproj/project.pbxproj b/apple/Omnivore.xcodeproj/project.pbxproj
index 473fe264d..4625c4ea4 100644
--- a/apple/Omnivore.xcodeproj/project.pbxproj
+++ b/apple/Omnivore.xcodeproj/project.pbxproj
@@ -1244,6 +1244,7 @@
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.ShareExtension-Mac";
PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
@@ -1274,6 +1275,7 @@
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.ShareExtension-Mac";
PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
@@ -1560,6 +1562,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.Omnivore-Extension";
PRODUCT_NAME = SafariExtension;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
@@ -1597,6 +1600,7 @@
);
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.Omnivore-Extension";
PRODUCT_NAME = SafariExtension;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_COMPILATION_MODE = wholemodule;
@@ -1664,7 +1668,8 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = Entitlements/ShareExtension.entitlements;
- CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/ShareExtension.plist;
@@ -1677,6 +1682,7 @@
MARKETING_VERSION = 1.10.0;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension";
PRODUCT_NAME = ShareExtension;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_COMPILATION_MODE = wholemodule;
@@ -1746,7 +1752,8 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = Entitlements/ShareExtension.entitlements;
- CODE_SIGN_IDENTITY = "iPhone Developer";
+ CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = QJF2XZ86HB;
INFOPLIST_FILE = InfoPlists/ShareExtension.plist;
@@ -1759,6 +1766,7 @@
MARKETING_VERSION = 1.10.0;
PRODUCT_BUNDLE_IDENTIFIER = "app.omnivore.app.share-extension";
PRODUCT_NAME = ShareExtension;
+ PROVISIONING_PROFILE_SPECIFIER = "";
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
diff --git a/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved b/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved
index fa6f4cbb3..7d1eeae0b 100644
--- a/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/apple/Omnivore.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -23,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/openid/AppAuth-iOS.git",
"state" : {
- "revision" : "01131d68346c8ae552961c768d583c715fbe1410",
- "version" : "1.4.0"
+ "revision" : "33660c271c961f8ce1084cc13f2ea8195e864f7d",
+ "version" : "1.5.0"
}
},
{
@@ -72,6 +72,15 @@
"version" : "9.1.2"
}
},
+ {
+ "identity" : "googlesignin-ios",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GoogleSignIn-iOS",
+ "state" : {
+ "revision" : "9450e779619fc184d360c9f7ce61023587f7e1f4",
+ "version" : "6.2.2"
+ }
+ },
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
@@ -99,6 +108,15 @@
"version" : "1.7.0"
}
},
+ {
+ "identity" : "gtmappauth",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/google/GTMAppAuth.git",
+ "state" : {
+ "revision" : "b9d1683be336ba8c8d1c6867bafeb056a5399700",
+ "version" : "1.3.0"
+ }
+ },
{
"identity" : "intercom-ios",
"kind" : "remoteSourceControl",
diff --git a/apple/OmnivoreKit/Package.swift b/apple/OmnivoreKit/Package.swift
index 6fafc0cb0..f5ef78249 100644
--- a/apple/OmnivoreKit/Package.swift
+++ b/apple/OmnivoreKit/Package.swift
@@ -32,7 +32,7 @@ let package = Package(
.target(
name: "Services",
dependencies: [
- .product(name: "AppAuth", package: "AppAuth-iOS"),
+ .product(name: "GoogleSignIn", package: "GoogleSignIn-iOS"),
"Valet",
.product(name: "SwiftGraphQL", package: "swift-graphql"),
"Models",
@@ -63,11 +63,11 @@ var appPackageDependencies: [Target.Dependency] {
var dependencies: [Package.Dependency] {
var deps: [Package.Dependency] = [
- .package(url: "https://github.com/openid/AppAuth-iOS.git", .upToNextMajor(from: "1.4.0")),
.package(url: "https://github.com/Square/Valet", from: "4.1.2"),
.package(url: "https://github.com/maticzav/swift-graphql", from: "2.3.1"),
.package(url: "https://github.com/siteline/SwiftUI-Introspect.git", from: "0.1.4"),
- .package(url: "git@github.com:segmentio/analytics-swift.git", .upToNextMajor(from: "1.0.0"))
+ .package(url: "git@github.com:segmentio/analytics-swift.git", .upToNextMajor(from: "1.0.0")),
+ .package(url: "https://github.com/google/GoogleSignIn-iOS", from: "6.2.2")
]
// #if canImport(UIKit)
deps.append(.package(url: "https://github.com/PSPDFKit/PSPDFKit-SP", branch: "master"))
diff --git a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift
index 1b3225cfe..1d262edb7 100644
--- a/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift
+++ b/apple/OmnivoreKit/Sources/App/AppExtensions/Share/ShareExtensionScene.swift
@@ -1,4 +1,3 @@
-import Combine
import Foundation
import Models
import Services
diff --git a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift
index 7d8fc2a40..cde19d366 100644
--- a/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/LinkItemDetailView.swift
@@ -1,4 +1,3 @@
-import Combine
import CoreData
import Models
import Services
@@ -11,8 +10,6 @@ import Views
let item: LinkedItem?
@Published var webAppWrapperViewModel: WebAppWrapperViewModel?
- var subscriptions = Set()
-
init(linkedItemObjectID: NSManagedObjectID, dataService: DataService) {
if let linkedItem = dataService.viewContext.object(with: linkedItemObjectID) as? LinkedItem {
self.pdfItem = PDFItem.make(item: linkedItem)
diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift
index e218f96b2..2b8e71003 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Registration/CreateProfileView.swift
@@ -5,7 +5,7 @@ import SwiftUI
import Utils
import Views
-final class CreateProfileViewModel: ObservableObject {
+@MainActor final class CreateProfileViewModel: ObservableObject {
private(set) var initialUserProfile = UserProfile(username: "", name: "", bio: nil)
var isConfigured = false
@@ -39,7 +39,9 @@ final class CreateProfileViewModel: ObservableObject {
switch profileOrError {
case let .left(userProfile):
- submitProfile(userProfile: userProfile, authenticator: authenticator)
+ Task {
+ await submitProfile(userProfile: userProfile, authenticator: authenticator)
+ }
case let .right(errorMessage):
validationErrorMessage = errorMessage
}
@@ -51,41 +53,37 @@ final class CreateProfileViewModel: ObservableObject {
return
}
- dataService.validateUsernamePublisher(username: username).sink(
- receiveCompletion: { [weak self] completion in
- guard case let .failure(usernameError) = completion else { return }
+ Task {
+ do {
+ try await dataService.validateUsernamePublisher(username: username)
+ } catch {
+ let usernameError = (error as? UsernameAvailabilityError) ?? .unknown
switch usernameError {
case .tooShort:
- self?.potentialUsernameStatus = .tooShort
+ potentialUsernameStatus = .tooShort
case .tooLong:
- self?.potentialUsernameStatus = .tooLong
+ potentialUsernameStatus = .tooLong
case .invalidPattern:
- self?.potentialUsernameStatus = .invalidPattern
+ potentialUsernameStatus = .invalidPattern
case .nameUnavailable:
- self?.potentialUsernameStatus = .unavailable
+ potentialUsernameStatus = .unavailable
case .internalServer, .unknown:
- self?.loginError = .unknown
+ loginError = .unknown
case .network:
- self?.loginError = .network
+ loginError = .network
}
- },
- receiveValue: { [weak self] in
- self?.potentialUsernameStatus = .available
}
- )
- .store(in: &subscriptions)
+ }
}
- func submitProfile(userProfile: UserProfile, authenticator: Authenticator) {
- authenticator
- .createAccount(userProfile: userProfile).sink(
- receiveCompletion: { [weak self] completion in
- guard case let .failure(loginError) = completion else { return }
- self?.loginError = loginError
- },
- receiveValue: { _ in }
- )
- .store(in: &subscriptions)
+ func submitProfile(userProfile: UserProfile, authenticator: Authenticator) async {
+ do {
+ try await authenticator.createAccount(userProfile: userProfile)
+ } catch {
+ if let error = error as? LoginError {
+ loginError = error
+ }
+ }
}
func configure(profile: UserProfile, dataService: DataService) {
diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift
index d24298116..3bb561ac5 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Registration/NewAppleSignupView.swift
@@ -1,27 +1,22 @@
-import Combine
import Models
import Services
import SwiftUI
import Utils
import Views
-final class NewAppleSignupViewModel: ObservableObject {
+@MainActor final class NewAppleSignupViewModel: ObservableObject {
@Published var loginError: LoginError?
- var subscriptions = Set()
-
init() {}
- func submitProfile(userProfile: UserProfile, authenticator: Authenticator) {
- authenticator
- .createAccount(userProfile: userProfile).sink(
- receiveCompletion: { [weak self] completion in
- guard case let .failure(loginError) = completion else { return }
- self?.loginError = loginError
- },
- receiveValue: { _ in }
- )
- .store(in: &subscriptions)
+ func submitProfile(userProfile: UserProfile, authenticator: Authenticator) async {
+ do {
+ try await authenticator.createAccount(userProfile: userProfile)
+ } catch {
+ if let error = error as? LoginError {
+ loginError = error
+ }
+ }
}
}
@@ -48,7 +43,11 @@ struct NewAppleSignupView: View {
VStack {
Button(
- action: { viewModel.submitProfile(userProfile: userProfile, authenticator: authenticator) },
+ action: {
+ Task {
+ await viewModel.submitProfile(userProfile: userProfile, authenticator: authenticator)
+ }
+ },
label: { Text("Continue") }
)
.buttonStyle(SolidCapsuleButtonStyle(color: .appDeepBackground, width: 300))
diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift
index f7d5a496c..863ac8751 100644
--- a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift
@@ -1,12 +1,11 @@
import AuthenticationServices
-import Combine
import Models
import Services
import SwiftUI
import Utils
import Views
-final class RegistrationViewModel: ObservableObject {
+@MainActor final class RegistrationViewModel: ObservableObject {
enum RegistrationState {
case createProfile(userProfile: UserProfile)
case newAppleSignUp(userProfile: UserProfile)
@@ -15,12 +14,10 @@ final class RegistrationViewModel: ObservableObject {
@Published var loginError: LoginError?
@Published var registrationState: RegistrationState?
- var subscriptions = Set()
-
func handleAppleSignInCompletion(result: Result, authenticator: Authenticator) {
switch AppleSigninPayload.parse(authResult: result) {
case let .success(payload):
- handleAppleToken(payload: payload, authenticator: authenticator)
+ Task { await handleAppleToken(payload: payload, authenticator: authenticator) }
case let .failure(error):
switch error {
case .unauthorized, .unknown:
@@ -31,56 +28,48 @@ final class RegistrationViewModel: ObservableObject {
}
}
- private func handleAppleToken(payload: AppleSigninPayload, authenticator: Authenticator) {
- authenticator.submitAppleToken(token: payload.token).sink(
- receiveCompletion: { [weak self] completion in
- guard case let .failure(loginError) = completion else { return }
- switch loginError {
- case .unauthorized, .unknown:
- self?.handleAppleSignUp(authenticator: authenticator, payload: payload)
- case .network:
- self?.loginError = loginError
- }
- },
- receiveValue: { _ in }
- )
- .store(in: &subscriptions)
+ private func handleAppleToken(payload: AppleSigninPayload, authenticator: Authenticator) async {
+ do {
+ try await authenticator.submitAppleToken(token: payload.token)
+ } catch {
+ let submitTokenError = (error as? LoginError) ?? .unknown
+ switch submitTokenError {
+ case .unauthorized, .unknown:
+ await handleAppleSignUp(authenticator: authenticator, payload: payload)
+ case .network:
+ loginError = submitTokenError
+ }
+ }
}
- private func handleAppleSignUp(authenticator: Authenticator, payload: AppleSigninPayload) {
- authenticator
- .createPendingAccountUsingApple(token: payload.token, name: payload.fullName)
- .sink(
- receiveCompletion: { [weak self] completion in
- guard case let .failure(loginError) = completion else { return }
- self?.loginError = loginError
- },
- receiveValue: { [weak self] userProfile in
- if userProfile.name.isEmpty {
- self?.registrationState = .createProfile(userProfile: userProfile)
- } else {
- self?.registrationState = .newAppleSignUp(userProfile: userProfile)
- }
- }
+ private func handleAppleSignUp(authenticator: Authenticator, payload: AppleSigninPayload) async {
+ do {
+ let pendingUserProfile = try await authenticator.createPendingAccountUsingApple(
+ token: payload.token,
+ name: payload.fullName
)
- .store(in: &subscriptions)
+ if pendingUserProfile.name.isEmpty {
+ registrationState = .createProfile(userProfile: pendingUserProfile)
+ } else {
+ registrationState = .newAppleSignUp(userProfile: pendingUserProfile)
+ }
+ } catch {
+ loginError = (error as? LoginError) ?? .unknown
+ }
}
- func handleGoogleAuth(authenticator: Authenticator) {
- authenticator
- .handleGoogleAuth(presentingViewController: presentingViewController())
- .sink(
- receiveCompletion: { [weak self] completion in
- guard case let .failure(loginError) = completion else { return }
- self?.loginError = loginError
- },
- receiveValue: { [weak self] isNewAccount in
- if isNewAccount {
- self?.registrationState = .createProfile(userProfile: UserProfile(username: "", name: ""))
- }
- }
- )
- .store(in: &subscriptions)
+ func handleGoogleAuth(authenticator: Authenticator) async {
+ guard let presentingViewController = presentingViewController() else { return }
+ let googleAuthResponse = await authenticator.handleGoogleAuth(presenting: presentingViewController)
+
+ switch googleAuthResponse {
+ case let .loginError(error):
+ loginError = error
+ case .newOmnivoreUser:
+ registrationState = .createProfile(userProfile: UserProfile(username: "", name: ""))
+ case .existingOmnivoreUser:
+ break
+ }
}
}
diff --git a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift
index a946c7eb7..77f6c2982 100644
--- a/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift
@@ -97,6 +97,7 @@ struct InnerRootView: View {
}
}
#endif
+ .onOpenURL { Authenticator.handleGoogleURL(url: $0) }
}
#if os(iOS)
diff --git a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift
index 4a45a70e3..7ba4f8307 100644
--- a/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift
+++ b/apple/OmnivoreKit/Sources/App/Views/WelcomeView.swift
@@ -1,4 +1,3 @@
-import Combine
import Models
import Services
import SwiftUI
@@ -139,7 +138,9 @@ struct WelcomeView: View {
if AppKeys.sharedInstance?.iosClientGoogleId != nil {
GoogleAuthButton {
- viewModel.handleGoogleAuth(authenticator: authenticator)
+ Task {
+ await viewModel.handleGoogleAuth(authenticator: authenticator)
+ }
}
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift b/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift
index 468cfa4d2..a0e044364 100644
--- a/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift
+++ b/apple/OmnivoreKit/Sources/Services/Authentication/AccountCreator.swift
@@ -1,4 +1,3 @@
-import Combine
import Foundation
import Models
@@ -6,12 +5,12 @@ public extension Authenticator {
func createPendingAccountUsingApple(
token: String,
name: PersonNameComponents?
- ) -> AnyPublisher {
+ ) async throws -> UserProfile {
let params = CreatePendingAccountParams(token: token, provider: .apple, fullName: name)
- return createPendingAccount(params: params)
+ return try await createPendingAccount(params: params)
}
- func createAccount(userProfile: UserProfile) -> AnyPublisher {
+ func createAccount(userProfile: UserProfile) async throws {
let params = CreateAccountParams(
pendingUserToken: pendingUserToken ?? "",
userProfile: userProfile
@@ -19,36 +18,29 @@ public extension Authenticator {
let encodedParams = (try? JSONEncoder().encode(params)) ?? Data()
- return networker
- .createAccount(params: encodedParams)
- .tryMap { [weak self] in
- try ValetKey.authCookieString.setValue($0.commentedAuthCookieString)
- try ValetKey.authToken.setValue($0.authToken)
- self?.pendingUserToken = nil
- self?.isLoggedIn = true
+ do {
+ let authPayload = try await networker.createAccount(params: encodedParams)
+ try ValetKey.authCookieString.setValue(authPayload.commentedAuthCookieString)
+ try ValetKey.authToken.setValue(authPayload.authToken)
+ DispatchQueue.main.async {
+ self.pendingUserToken = nil
+ self.isLoggedIn = true
}
- .mapError { error in
- let serverError = (error as? ServerError) ?? ServerError.unknown
- return LoginError.make(serverError: serverError)
- }
- .eraseToAnyPublisher()
+ } catch {
+ let serverError = (error as? ServerError) ?? ServerError.unknown
+ throw LoginError.make(serverError: serverError)
+ }
}
}
extension Authenticator {
- func createPendingAccount(params: CreatePendingAccountParams) -> AnyPublisher {
- let encodedParams = (try? JSONEncoder().encode(params)) ?? Data()
-
- return networker
- .createPendingUser(params: encodedParams)
- .tryMap { [weak self] in
- self?.pendingUserToken = $0.pendingUserToken
- return $0.pendingUserProfile
- }
- .mapError { error in
- let serverError = (error as? ServerError) ?? ServerError.unknown
- return LoginError.make(serverError: serverError)
- }
- .eraseToAnyPublisher()
+ func createPendingAccount(params: CreatePendingAccountParams) async throws -> UserProfile {
+ do {
+ let encodedParams = (try? JSONEncoder().encode(params)) ?? Data()
+ let pendingUserAuthPayload = try await networker.createPendingUser(params: encodedParams)
+ return pendingUserAuthPayload.pendingUserProfile
+ } catch {
+ throw LoginError.make(serverError: (error as? ServerError) ?? .unknown)
+ }
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift b/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift
index 22475dc5e..751c53558 100644
--- a/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift
+++ b/apple/OmnivoreKit/Sources/Services/Authentication/AppleAuth.swift
@@ -1,20 +1,18 @@
-import Combine
import Foundation
import Models
public extension Authenticator {
- func submitAppleToken(token: String) -> AnyPublisher {
- networker
- .submitAppleToken(token: token)
- .tryMap { [weak self] in
- try ValetKey.authCookieString.setValue($0.commentedAuthCookieString)
- try ValetKey.authToken.setValue($0.authToken)
- self?.isLoggedIn = true
+ func submitAppleToken(token: String) async throws {
+ do {
+ let authPayload = try await networker.submitAppleToken(token: token)
+ try ValetKey.authCookieString.setValue(authPayload.commentedAuthCookieString)
+ try ValetKey.authToken.setValue(authPayload.authToken)
+ DispatchQueue.main.async {
+ self.isLoggedIn = true
}
- .mapError { error in
- let serverError = (error as? ServerError) ?? ServerError.unknown
- return LoginError.make(serverError: serverError)
- }
- .eraseToAnyPublisher()
+ } catch {
+ let serverError = (error as? ServerError) ?? ServerError.unknown
+ throw LoginError.make(serverError: serverError)
+ }
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift b/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift
index a31c56ba3..cd2f0c66b 100644
--- a/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift
+++ b/apple/OmnivoreKit/Sources/Services/Authentication/Authenticator.swift
@@ -1,6 +1,5 @@
-import AppAuth
-import Combine
import Foundation
+import GoogleSignIn
import Models
import Utils
import WebKit
@@ -8,6 +7,10 @@ import WebKit
public final class Authenticator: ObservableObject {
public static var unregisterIntercomUser: (() -> Void)?
+ public static func handleGoogleURL(url: URL) {
+ GIDSignIn.sharedInstance.handle(url)
+ }
+
public enum AuthStatus {
case loggedOut
case pendingUser
@@ -15,18 +18,11 @@ public final class Authenticator: ObservableObject {
}
@Published public internal(set) var isLoggedIn: Bool
- @Published public var pendinguserProfile = UserProfile(username: "", name: "", bio: nil)
let networker: Networker
- var subscriptions = Set()
- var currentAuthorizationFlow: OIDExternalUserAgentSession?
var pendingUserToken: String?
- #if os(macOS)
- var authRedirectHandler: OIDRedirectHTTPHandler?
- #endif
-
public init(networker: Networker) {
self.networker = networker
self.isLoggedIn = ValetKey.authToken.exists
diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift b/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift
index be2429db2..1302aae7a 100644
--- a/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift
+++ b/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift
@@ -1,123 +1,73 @@
-import AppAuth
-import Combine
import Foundation
+import GoogleSignIn
import Models
import Utils
-#if os(iOS)
- import UIKit
+public enum GoogleAuthResponse {
+ case loginError(error: LoginError)
+ case newOmnivoreUser
+ case existingOmnivoreUser
+}
- public extension Authenticator {
- func handleGoogleAuth(presentingViewController: PlatformViewController?) -> AnyPublisher {
- Future { [weak self] promise in
- guard let self = self, let presenting = presentingViewController else { return }
+extension Authenticator {
+ public func handleGoogleAuth(presenting: PlatformViewController) async -> GoogleAuthResponse {
+ let idToken = try? await googleSignIn(presenting: presenting)
+ guard let idToken = idToken else { return .loginError(error: .unauthorized) }
- // swiftlint:disable:next line_length
- self.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: self.googleAuthRequest(redirectURL: nil), presenting: presenting) { authState, authError in
- self.resolveAuthResponse(promise: promise, authState: authState, authError: authError)
- }
+ do {
+ let authPayload = try await networker.submitGoogleToken(idToken: idToken)
+ try ValetKey.authCookieString.setValue(authPayload.commentedAuthCookieString)
+ try ValetKey.authToken.setValue(authPayload.authToken)
+ DispatchQueue.main.async {
+ self.isLoggedIn = true
}
- .eraseToAnyPublisher()
- }
- }
-#endif
+ return .existingOmnivoreUser
+ } catch {
+ let loginError = (error as? LoginError) ?? .unknown
-#if os(macOS)
- import AppKit
-
- public extension Authenticator {
- func handleGoogleAuth(presentingViewController _: PlatformViewController?) -> AnyPublisher {
- authRedirectHandler = OIDRedirectHTTPHandler(
- successURL: URL(string: "https://omnivore.app")!
- )
- let redirectURL = authRedirectHandler?.startHTTPListener(nil)
- let authRequest = googleAuthRequest(redirectURL: redirectURL)
-
- return Future { [weak self] promise in
- guard let self = self else { return }
-
- // swiftlint:disable:next line_length
- self.authRedirectHandler?.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: authRequest) { authState, authError in
- NSRunningApplication.current.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])
- self.resolveAuthResponse(promise: promise, authState: authState, authError: authError)
- }
+ switch loginError {
+ case .unauthorized, .unknown:
+ return await createPendingUser(idToken: idToken)
+ case .network:
+ return .loginError(error: .network)
}
- .eraseToAnyPublisher()
- }
- }
-#endif
-
-private extension Authenticator {
- func resolveAuthResponse(
- promise: @escaping (Result) -> Void,
- authState: OIDAuthState?,
- authError: Error?
- ) {
- if let idToken = authState?.lastTokenResponse?.idToken {
- Task {
- do {
- let authPayload = try await networker.submitGoogleToken(idToken: idToken)
- try ValetKey.authCookieString.setValue(authPayload.commentedAuthCookieString)
- try ValetKey.authToken.setValue(authPayload.authToken)
- DispatchQueue.main.async {
- self.isLoggedIn = true
- }
- } catch {
- if let error = error as? LoginError {
- switch error {
- case .unauthorized, .unknown:
- self.resolveAuthResponseForAccountCreation(promise: promise, authState: authState, authError: authError)
- case .network:
- promise(.failure(error))
- }
- self.resolveAuthResponseForAccountCreation(promise: promise, authState: authState, authError: authError)
- }
- }
- }
- } else {
- resolveAuthResponseForAccountCreation(promise: promise, authState: authState, authError: authError)
}
}
- func resolveAuthResponseForAccountCreation(
- promise: @escaping (Result) -> Void,
- authState: OIDAuthState?,
- authError _: Error?
- ) {
- if let idToken = authState?.lastTokenResponse?.idToken {
+ func createPendingUser(idToken: String) async -> GoogleAuthResponse {
+ do {
let params = CreatePendingAccountParams(token: idToken, provider: .google, fullName: nil)
let encodedParams = (try? JSONEncoder().encode(params)) ?? Data()
-
- networker
- .createPendingUser(params: encodedParams)
- .sink { completion in
- guard case let .failure(serverError) = completion else { return }
- promise(.failure(LoginError.make(serverError: serverError)))
- } receiveValue: { [weak self] in
- self?.pendingUserToken = $0.pendingUserToken
- promise(.success(true))
- }
- .store(in: &subscriptions)
- } else {
- promise(.failure(.unauthorized))
+ let pendingUserAuthPayload = try await networker.createPendingUser(params: encodedParams)
+ pendingUserToken = pendingUserAuthPayload.pendingUserToken
+ return .newOmnivoreUser
+ } catch {
+ let loginError = LoginError.make(serverError: (error as? ServerError) ?? .unknown)
+ return .loginError(error: loginError)
}
}
- func googleAuthRequest(redirectURL: URL?) -> OIDAuthorizationRequest {
- let authEndpoint = URL(string: "https://accounts.google.com/o/oauth2/v2/auth")!
- let tokenEndpoint = URL(string: "https://www.googleapis.com/oauth2/v4/token")!
- let iosClientGoogleId = AppKeys.sharedInstance?.iosClientGoogleId ?? ""
- let scopes = ["profile", "email"]
+ func googleSignIn(presenting: PlatformViewController) async throws -> String {
+ try await withCheckedThrowingContinuation { continuation in
+ let clientID = "\(AppKeys.sharedInstance?.iosClientGoogleId ?? "").apps.googleusercontent.com"
+ GIDSignIn.sharedInstance.signIn(
+ with: GIDConfiguration(clientID: clientID),
+ presenting: presenting
+ ) { user, error in
+ guard let user = user, error == nil else {
+ continuation.resume(throwing: LoginError.unauthorized)
+ return
+ }
- return OIDAuthorizationRequest(
- configuration: OIDServiceConfiguration(authorizationEndpoint: authEndpoint, tokenEndpoint: tokenEndpoint),
- clientId: "\(iosClientGoogleId).apps.googleusercontent.com",
- scopes: scopes,
- redirectURL: redirectURL ?? URL(
- string: "com.googleusercontent.apps.\(iosClientGoogleId):/oauth2redirect/google"
- )!,
- responseType: "code",
- additionalParameters: nil
- )
+ user.authentication.do { authentication, error in
+ guard let idToken = authentication?.idToken, error == nil else {
+ continuation.resume(throwing: LoginError.unauthorized)
+ return
+ }
+
+ continuation.resume(returning: idToken)
+ }
+ }
+ }
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift
index b35582b0d..0eb0d41ca 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/DataService.swift
@@ -1,4 +1,3 @@
-import Combine
import CoreData
import CoreImage
import Foundation
@@ -19,7 +18,6 @@ public final class DataService: ObservableObject {
var persistentContainer: PersistentContainer
public var backgroundContext: NSManagedObjectContext
- var subscriptions = Set()
public var viewContext: NSManagedObjectContext {
persistentContainer.viewContext
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SaveArticle.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SaveArticle.swift
deleted file mode 100644
index 2c0766899..000000000
--- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SaveArticle.swift
+++ /dev/null
@@ -1,224 +0,0 @@
-import Combine
-import Foundation
-import Models
-import SwiftGraphQL
-
-public enum SaveArticleStatus {
- case succeeeded
- case processing(jobId: String)
- case failed
-
- static func make(jobId: String, savingStatus: Enums.ArticleSavingRequestStatus) -> SaveArticleStatus {
- switch savingStatus {
- case .processing:
- return .processing(jobId: jobId)
- case .succeeded:
- return .succeeeded
- case .failed:
- return .failed
- }
- }
-}
-
-public extension Networker {
- func articleSaveStatus(jobId: String) -> AnyPublisher {
- enum QueryResult {
- case saved(status: SaveArticleStatus)
- case error(errorCode: Enums.ArticleSavingRequestErrorCode)
- }
-
- let selection = Selection {
- try $0.on(
- articleSavingRequestError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .notFound) },
- articleSavingRequestSuccess: .init {
- .saved(
- status: try $0.articleSavingRequest(
- selection: .init {
- SaveArticleStatus.make(
- jobId: try $0.id(),
- savingStatus: try $0.status()
- )
- }
- )
- )
- }
- )
- }
-
- let query = Selection.Query {
- try $0.articleSavingRequest(id: jobId, selection: selection)
- }
-
- let path = appEnvironment.graphqlPath
- let headers = defaultHeaders
-
- return Deferred {
- Future { promise in
- send(query, to: path, headers: headers) { result in
- switch result {
- case let .success(payload):
- if let graphqlError = payload.errors {
- promise(.failure(.unknown(description: graphqlError.first.debugDescription)))
- }
-
- switch payload.data {
- case let .saved(status):
- promise(.success(status))
- case let .error(errorCode: errorCode):
- switch errorCode {
- case .unauthorized:
- promise(.failure(.unauthorized))
- case .notFound:
- promise(.failure(.badData))
- }
- }
- case let .failure(error):
- promise(.failure(SaveArticleError.make(from: error)))
- }
- }
- }
- }
- .receive(on: DispatchQueue.main)
- .eraseToAnyPublisher()
- }
-}
-
-public extension DataService {
- // swiftlint:disable:next function_body_length
- func saveArticlePublisher(
- pageScrapePayload: PageScrapePayload,
- uploadFileId: String?
- ) -> AnyPublisher {
- enum MutationResult {
- case saved(created: Bool)
- case error(errorCode: Enums.CreateArticleErrorCode)
- }
-
- let preparedDocument: InputObjects.PreparedDocumentInput? = {
- if case let .html(html, title, _) = pageScrapePayload.contentType {
- return InputObjects.PreparedDocumentInput(
- document: html,
- pageInfo: InputObjects.PageInfoInput(title: OptionalArgument(title))
- )
- }
- return nil
- }()
-
- let input = InputObjects.CreateArticleInput(
- preparedDocument: OptionalArgument(preparedDocument),
- uploadFileId: uploadFileId != nil ? .present(uploadFileId!) : .null(),
- url: pageScrapePayload.url
- )
-
- let selection = Selection {
- try $0.on(
- createArticleError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .unableToParse) },
- createArticleSuccess: .init { .saved(created: try $0.created()) }
- )
- }
-
- let mutation = Selection.Mutation {
- try $0.createArticle(input: input, selection: selection)
- }
-
- let path = appEnvironment.graphqlPath
- let headers = networker.defaultHeaders
-
- return Deferred {
- Future { promise in
- send(mutation, to: path, headers: headers) { result in
- switch result {
- case let .success(payload):
- if let graphqlError = payload.errors {
- promise(.failure(.unknown(description: graphqlError.first.debugDescription)))
- }
-
- switch payload.data {
- case .saved:
- promise(.success(()))
- case let .error(errorCode: errorCode):
- switch errorCode {
- case .unauthorized:
- promise(.failure(.unauthorized))
- default:
- promise(.failure(.unknown(description: errorCode.rawValue)))
- }
- }
- case let .failure(error):
- promise(.failure(SaveArticleError.make(from: error)))
- }
- }
- }
- }
- .receive(on: DispatchQueue.main)
- .eraseToAnyPublisher()
- }
-
- func saveArticlePublisher(articleURL: URL) -> AnyPublisher {
- saveArticlePublisher(articleURLString: articleURL.absoluteString)
- }
-
- func saveArticlePublisher(articleURLString: String) -> AnyPublisher {
- enum MutationResult {
- case saved(status: SaveArticleStatus)
- case error(errorCode: Enums.CreateArticleSavingRequestErrorCode)
- }
-
- let selection = Selection {
- try $0.on(
- createArticleSavingRequestError: .init { .error(errorCode: (try? $0.errorCodes().first) ?? .badData) },
- createArticleSavingRequestSuccess: .init {
- .saved(
- status: try $0.articleSavingRequest(
- selection: .init {
- SaveArticleStatus.make(
- jobId: try $0.id(),
- savingStatus: try $0.status()
- )
- }
- )
- )
- }
- )
- }
-
- let mutation = Selection.Mutation {
- try $0.createArticleSavingRequest(
- input: InputObjects.CreateArticleSavingRequestInput(url: articleURLString),
- selection: selection
- )
- }
-
- let path = appEnvironment.graphqlPath
- let headers = networker.defaultHeaders
-
- return Deferred {
- Future { promise in
- send(mutation, to: path, headers: headers) { result in
- switch result {
- case let .success(payload):
- if let graphqlError = payload.errors {
- promise(.failure(.unknown(description: graphqlError.first.debugDescription)))
- }
-
- switch payload.data {
- case let .saved(status):
- promise(.success(status))
- case let .error(errorCode: errorCode):
- switch errorCode {
- case .unauthorized:
- promise(.failure(.unauthorized))
- case .badData:
- promise(.failure(.badData))
- }
- }
- case let .failure(error):
- promise(.failure(SaveArticleError.make(from: error)))
- }
- }
- }
- }
- .receive(on: DispatchQueue.main)
- .eraseToAnyPublisher()
- }
-}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift
index 3e9d7787f..b6cf76259 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Mutations/SavePDF.swift
@@ -1,4 +1,3 @@
-import Combine
import Foundation
import Models
import SwiftGraphQL
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift
index 5da9691de..f9d442616 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/Networker.swift
@@ -39,7 +39,7 @@ extension Networker {
)
do {
- let authVerification = try await urlSession.performReq(resource: resource)
+ let authVerification = try await urlSession.performRequest(resource: resource)
return authVerification.authStatus.isAuthenticated
} catch {
return false
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResource.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResource.swift
index 5bdb206c7..331bac709 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResource.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResource.swift
@@ -1,4 +1,3 @@
-import Combine
import Foundation
import Models
import Utils
@@ -37,7 +36,7 @@ extension ServerResponse {
struct EmptyResponse: Decodable {}
extension URLSession {
- func performReq(
+ func performRequest(
resource: ServerResource
) async throws -> ResponseModel {
do {
@@ -61,30 +60,6 @@ extension URLSession {
throw ServerError(serverResponse: serverResponse)
}
}
-
- // TODO: remove performRequest
- // swiftlint:disable:next line_length
- func performRequest(resource: ServerResource) -> AnyPublisher {
- let request = resource.urlRequest
-
- return dataTaskPublisher(for: resource.urlRequest)
- .tryMap { data, response -> ResponseModel in
- let serverResponse = ServerResponse(data: data, response: response)
- NetworkRequestLogger.log(request: request, serverResponse: serverResponse)
-
- if let decodedValue = resource.decode(serverResponse) {
- return decodedValue
- }
-
- throw ServerError(serverResponse: serverResponse)
- }
- .mapError { error -> ServerError in
- let serverResponse = ServerResponse(error: error)
- NetworkRequestLogger.log(request: request, serverResponse: serverResponse)
- return ServerError(serverResponse: serverResponse)
- }
- .eraseToAnyPublisher()
- }
}
extension URLRequest {
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift
index 3743c1875..e144ea65a 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateAccountResource.swift
@@ -1,8 +1,7 @@
-import Combine
import Foundation
extension Networker {
- func createAccount(params: Data) -> AnyPublisher {
+ func createAccount(params: Data) async throws -> AuthPayload {
let urlRequest = URLRequest.create(
baseURL: appEnvironment.serverBaseURL,
urlPath: "/api/mobile-auth/create-account",
@@ -14,9 +13,10 @@ extension Networker {
decode: AuthPayload.decode
)
- return urlSession
- .performRequest(resource: resource)
- .receive(on: DispatchQueue.main)
- .eraseToAnyPublisher()
+ do {
+ return try await urlSession.performRequest(resource: resource)
+ } catch {
+ throw (error as? ServerError) ?? .unknown
+ }
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift
index 069b87de4..e5f03e97b 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreatePendingUserResource.swift
@@ -1,9 +1,8 @@
-import Combine
import Foundation
import Models
extension Networker {
- func createPendingUser(params: Data) -> AnyPublisher {
+ func createPendingUser(params: Data) async throws -> PendingUserAuthPayload {
let urlRequest = URLRequest.create(
baseURL: appEnvironment.serverBaseURL,
urlPath: "/api/mobile-auth/sign-up",
@@ -15,9 +14,10 @@ extension Networker {
decode: PendingUserAuthPayload.decode
)
- return urlSession
- .performRequest(resource: resource)
- .receive(on: DispatchQueue.main)
- .eraseToAnyPublisher()
+ do {
+ return try await urlSession.performRequest(resource: resource)
+ } catch {
+ throw (error as? ServerError) ?? .unknown
+ }
}
}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAppleTokenResource.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAppleTokenResource.swift
deleted file mode 100644
index e8a681d52..000000000
--- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAppleTokenResource.swift
+++ /dev/null
@@ -1,26 +0,0 @@
-import Combine
-import Foundation
-import Models
-
-extension Networker {
- func submitAppleToken(token: String) -> AnyPublisher {
- let params = SignInParams(token: token, provider: .apple)
- let encodedParams = (try? JSONEncoder().encode(params)) ?? Data()
-
- let urlRequest = URLRequest.create(
- baseURL: appEnvironment.serverBaseURL,
- urlPath: "/api/mobile-auth/sign-in",
- requestMethod: .post(params: encodedParams)
- )
-
- let resource = ServerResource(
- urlRequest: urlRequest,
- decode: AuthPayload.decode
- )
-
- return urlSession
- .performRequest(resource: resource)
- .receive(on: DispatchQueue.main)
- .eraseToAnyPublisher()
- }
-}
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateGoogleToken.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift
similarity index 65%
rename from apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateGoogleToken.swift
rename to apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift
index df241e3a7..febc2f0cf 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/CreateGoogleToken.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift
@@ -2,8 +2,17 @@ import Foundation
import Models
extension Networker {
+ func submitAppleToken(token: String) async throws -> AuthPayload {
+ let params = SignInParams(token: token, provider: .apple)
+ return try await submitSignInParams(params: params)
+ }
+
func submitGoogleToken(idToken: String) async throws -> AuthPayload {
let params = SignInParams(token: idToken, provider: .google)
+ return try await submitSignInParams(params: params)
+ }
+
+ func submitSignInParams(params: SignInParams) async throws -> AuthPayload {
let encodedParams = (try? JSONEncoder().encode(params)) ?? Data()
let urlRequest = URLRequest.create(
@@ -18,7 +27,7 @@ extension Networker {
)
do {
- return try await urlSession.performReq(resource: resource)
+ return try await urlSession.performRequest(resource: resource)
} catch {
if let error = error as? ServerError {
throw LoginError.make(serverError: error)
diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ValidateUsernameQuery.swift b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ValidateUsernameQuery.swift
index 07137b3ac..4d427950f 100644
--- a/apple/OmnivoreKit/Sources/Services/DataService/Queries/ValidateUsernameQuery.swift
+++ b/apple/OmnivoreKit/Sources/Services/DataService/Queries/ValidateUsernameQuery.swift
@@ -1,30 +1,29 @@
-import Combine
import Foundation
import Models
import SwiftGraphQL
public extension DataService {
- func validateUsernamePublisher(username: String) -> AnyPublisher {
+ func validateUsernamePublisher(username: String) async throws {
let query = Selection.Query {
try $0.validateUsername(username: username)
}
let path = appEnvironment.graphqlPath
- return Deferred {
- Future { promise in
- send(query, to: path) { result in
- switch result {
- case let .success(payload):
- promise(payload.data ? .success(()) : .failure(.nameUnavailable))
- case let .failure(error):
- promise(.failure(UsernameAvailabilityError.make(from: error)))
+ return try await withCheckedThrowingContinuation { continuation in
+ send(query, to: path) { result in
+ switch result {
+ case let .success(payload):
+ if payload.data {
+ continuation.resume()
+ } else {
+ continuation.resume(throwing: UsernameAvailabilityError.nameUnavailable)
}
+ case let .failure(error):
+ continuation.resume(throwing: UsernameAvailabilityError.make(from: error))
}
}
}
- .receive(on: DispatchQueue.main)
- .eraseToAnyPublisher()
}
}
diff --git a/apple/OmnivoreKit/Sources/Views/KeyboardManagement.swift b/apple/OmnivoreKit/Sources/Views/KeyboardManagement.swift
deleted file mode 100644
index ca0e42a3a..000000000
--- a/apple/OmnivoreKit/Sources/Views/KeyboardManagement.swift
+++ /dev/null
@@ -1,63 +0,0 @@
-import Combine
-import SwiftUI
-
-#if !os(macOS)
- import UIKit
-#endif
-
-public extension Publishers {
- static var keyboardHeight: AnyPublisher {
- #if os(iOS)
- let willShow = NotificationCenter.default
- .publisher(for: UIApplication.keyboardWillShowNotification)
- .map(\.keyboardHeight)
- .eraseToAnyPublisher()
-
- let willHide = NotificationCenter.default
- .publisher(for: UIApplication.keyboardWillHideNotification)
- .map { _ in CGFloat(0) }
- .eraseToAnyPublisher()
-
- return Merge(willShow, willHide)
- .eraseToAnyPublisher()
- #elseif os(macOS)
- Future { $0(.success(CGFloat.zero)) }
- .eraseToAnyPublisher()
- #endif
- }
-}
-
-extension Notification {
- var keyboardHeight: CGFloat {
- #if os(iOS)
- (userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect)?.height ?? 0
- #elseif os(macOS)
- 0
- #endif
- }
-}
-
-struct KeyboardAdaptive: ViewModifier {
- @State private var keyboardHeight: CGFloat = 0
-
- func body(content: Content) -> some View {
- content
- .padding(.bottom, keyboardHeight)
- .onReceive(Publishers.keyboardHeight) { self.keyboardHeight = $0 }
- }
-}
-
-extension View {
- func keyboardAdaptive() -> some View {
- ModifiedContent(content: self, modifier: KeyboardAdaptive())
- }
-}
-
-extension View {
- func hideKeyboard() {
- #if os(iOS)
- UIApplication.shared
- .sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
- #endif
- }
-}