mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-11 15:53:30 +01:00
[ADD] Authentication view for connecting SideStore to an Apple ID
This commit is contained in:
109
AltStore/Views/Settings/ConnectAppleIDView.swift
Normal file
109
AltStore/Views/Settings/ConnectAppleIDView.swift
Normal file
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// ConnectAppleIDView.swift
|
||||
// SideStore
|
||||
//
|
||||
// Created by Fabian Thies on 29.11.22.
|
||||
// Copyright © 2022 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AltSign
|
||||
|
||||
struct ConnectAppleIDView: View {
|
||||
typealias AuthenticationHandler = (String, String, @escaping (Result<(ALTAccount, ALTAppleAPISession), Error>) -> Void) -> Void
|
||||
typealias CompletionHandler = ((ALTAccount, ALTAppleAPISession, String)?) -> Void
|
||||
|
||||
@Environment(\.dismiss)
|
||||
private var dismiss
|
||||
|
||||
var authenticationHandler: AuthenticationHandler?
|
||||
var completionHandler: CompletionHandler?
|
||||
|
||||
@State var email: String = ""
|
||||
@State var password: String = ""
|
||||
@State var isLoading: Bool = false
|
||||
|
||||
var isFormValid: Bool {
|
||||
!email.isEmpty && !password.isEmpty
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 32) {
|
||||
Text("Sign in with your Apple ID to get started.")
|
||||
|
||||
VStack(spacing: 16) {
|
||||
RoundedTextField(title: "Apple ID", placeholder: "user@sidestore.io", text: $email)
|
||||
|
||||
RoundedTextField(title: "Password", placeholder: "••••••", text: $password, isSecure: true)
|
||||
}
|
||||
|
||||
SwiftUI.Button(action: signIn) {
|
||||
Text("Sign in")
|
||||
.bold()
|
||||
}
|
||||
.buttonStyle(FilledButtonStyle(isLoading: isLoading))
|
||||
.disabled(!isFormValid)
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text("Why do we need this?")
|
||||
.bold()
|
||||
|
||||
Text("Your Apple ID is used to configure apps so they can be installed on this device. Your credentials will be stored securely in this device's Keychain and sent only to Apple for authentication.")
|
||||
}
|
||||
.padding()
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.foregroundColor(Color(.secondarySystemBackground))
|
||||
)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.navigationTitle("Connect your Apple ID")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
SwiftUI.Button(action: self.cancel) {
|
||||
Text("Cancel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func signIn() {
|
||||
self.isLoading = true
|
||||
self.authenticationHandler?(email, password) { (result) in
|
||||
defer {
|
||||
self.isLoading = false
|
||||
}
|
||||
|
||||
switch result
|
||||
{
|
||||
case .failure(ALTAppleAPIError.requiresTwoFactorAuthentication):
|
||||
// Ignore
|
||||
break
|
||||
|
||||
case .failure(let error as NSError):
|
||||
let error = error.withLocalizedFailure(NSLocalizedString("Failed to Log In", comment: ""))
|
||||
print(error)
|
||||
|
||||
case .success((let account, let session)):
|
||||
self.completionHandler?((account, session, password))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancel() {
|
||||
self.completionHandler?(nil)
|
||||
// self.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectAppleIDView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ConnectAppleIDView()
|
||||
}
|
||||
}
|
||||
@@ -13,49 +13,74 @@ import Intents
|
||||
|
||||
struct SettingsView: View {
|
||||
|
||||
var connectedAppleID: Team? {
|
||||
DatabaseManager.shared.activeTeam()
|
||||
}
|
||||
|
||||
@SwiftUI.FetchRequest(sortDescriptors: [], predicate: NSPredicate(format: "%K == YES", #keyPath(Team.isActiveTeam)))
|
||||
var connectedTeams: FetchedResults<Team>
|
||||
|
||||
|
||||
@AppStorage("isBackgroundRefreshEnabled")
|
||||
var isBackgroundRefreshEnabled: Bool = true
|
||||
|
||||
@State var isShowingConnectAppleIDView = false
|
||||
@State var isShowingAddShortcutView = false
|
||||
|
||||
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section {
|
||||
|
||||
if let team = DatabaseManager.shared.activeTeam() {
|
||||
if let connectedAppleID = connectedTeams.first {
|
||||
HStack {
|
||||
Text("Name")
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
Text(team.name)
|
||||
Text(connectedAppleID.name)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("E-Mail")
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
Text(team.account.appleID)
|
||||
Text(connectedAppleID.account.appleID)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Type")
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
Text(team.type.localizedDescription)
|
||||
Text(connectedAppleID.type.localizedDescription)
|
||||
}
|
||||
} else {
|
||||
SwiftUI.Button {
|
||||
self.connectAppleID()
|
||||
} label: {
|
||||
Text("Connect your Apple ID")
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
HStack {
|
||||
Text("Connected Apple ID")
|
||||
Spacer()
|
||||
SwiftUI.Button {
|
||||
|
||||
} label: {
|
||||
Text("Sign Out")
|
||||
.font(.callout)
|
||||
.bold()
|
||||
if !connectedTeams.isEmpty {
|
||||
HStack {
|
||||
Text("Connected Apple ID")
|
||||
Spacer()
|
||||
SwiftUI.Button {
|
||||
self.disconnectAppleID()
|
||||
} label: {
|
||||
Text("Sign Out")
|
||||
.font(.callout)
|
||||
.bold()
|
||||
}
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
VStack(spacing: 4) {
|
||||
Text("Your Apple ID is required to sign the apps you install with SideStore.")
|
||||
|
||||
Text("Your credentials are only sent to Apple's servers and are not accessible by the SideStore Team. Once successfully logged in, the login details are stored securely on your device.")
|
||||
}
|
||||
}
|
||||
|
||||
Section {
|
||||
@@ -63,16 +88,14 @@ struct SettingsView: View {
|
||||
Text("Background Refresh")
|
||||
})
|
||||
|
||||
if #available(iOS 14.0, *) {
|
||||
SwiftUI.Button {
|
||||
self.isShowingAddShortcutView = true
|
||||
} label: {
|
||||
Text("Add to Siri...")
|
||||
}
|
||||
.sheet(isPresented: self.$isShowingAddShortcutView) {
|
||||
if let shortcut = INShortcut(intent: INInteraction.refreshAllApps().intent) {
|
||||
SiriShortcutSetupView(shortcut: shortcut)
|
||||
}
|
||||
SwiftUI.Button {
|
||||
self.isShowingAddShortcutView = true
|
||||
} label: {
|
||||
Text("Add to Siri...")
|
||||
}
|
||||
.sheet(isPresented: self.$isShowingAddShortcutView) {
|
||||
if let shortcut = INShortcut(intent: INInteraction.refreshAllApps().intent) {
|
||||
SiriShortcutSetupView(shortcut: shortcut)
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
@@ -114,7 +137,7 @@ struct SettingsView: View {
|
||||
Section {
|
||||
|
||||
} footer: {
|
||||
Text("SideStore 1.0.0")
|
||||
Text("SideStore \(appVersion)")
|
||||
.multilineTextAlignment(.center)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
@@ -135,6 +158,42 @@ struct SettingsView: View {
|
||||
}
|
||||
|
||||
|
||||
// var appleIDSection: some View {
|
||||
//
|
||||
// }
|
||||
|
||||
|
||||
|
||||
func connectAppleID() {
|
||||
AppManager.shared.authenticate(presentingViewController: nil) { (result) in
|
||||
DispatchQueue.main.async {
|
||||
switch result
|
||||
{
|
||||
case .failure(OperationError.cancelled):
|
||||
// Ignore
|
||||
break
|
||||
|
||||
case .failure(let error):
|
||||
NotificationManager.shared.reportError(error: error)
|
||||
|
||||
case .success: break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func disconnectAppleID() {
|
||||
DatabaseManager.shared.signOut { (error) in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error
|
||||
{
|
||||
NotificationManager.shared.reportError(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func switchToUIKit() {
|
||||
let storyboard = UIStoryboard(name: "Main", bundle: .main)
|
||||
let rootVC = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController
|
||||
|
||||
Reference in New Issue
Block a user