mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-08 22:33:26 +01:00
[AltStore] Basic Account tab
This commit is contained in:
@@ -144,6 +144,8 @@
|
||||
BFD52C2022A1A9EC000B7ED1 /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1D22A1A9EC000B7ED1 /* node.c */; };
|
||||
BFD52C2122A1A9EC000B7ED1 /* node_list.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1E22A1A9EC000B7ED1 /* node_list.c */; };
|
||||
BFD52C2222A1A9EC000B7ED1 /* cnary.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1F22A1A9EC000B7ED1 /* cnary.c */; };
|
||||
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */; };
|
||||
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */; };
|
||||
BFE6325A22A83BEB00F30809 /* Authentication.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFE6325922A83BEB00F30809 /* Authentication.storyboard */; };
|
||||
BFE6325C22A83C0100F30809 /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6325B22A83C0100F30809 /* AuthenticationViewController.swift */; };
|
||||
BFE6325E22A8497000F30809 /* SelectTeamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6325D22A8497000F30809 /* SelectTeamViewController.swift */; };
|
||||
@@ -361,6 +363,8 @@
|
||||
BFD52C1D22A1A9EC000B7ED1 /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node.c; path = Dependencies/libplist/libcnary/node.c; sourceTree = SOURCE_ROOT; };
|
||||
BFD52C1E22A1A9EC000B7ED1 /* node_list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node_list.c; path = Dependencies/libplist/libcnary/node_list.c; sourceTree = SOURCE_ROOT; };
|
||||
BFD52C1F22A1A9EC000B7ED1 /* cnary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cnary.c; path = Dependencies/libplist/libcnary/cnary.c; sourceTree = SOURCE_ROOT; };
|
||||
BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
|
||||
BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = "<group>"; };
|
||||
BFE6325922A83BEB00F30809 /* Authentication.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Authentication.storyboard; sourceTree = "<group>"; };
|
||||
BFE6325B22A83C0100F30809 /* AuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = "<group>"; };
|
||||
BFE6325D22A8497000F30809 /* SelectTeamViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTeamViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -675,9 +679,11 @@
|
||||
BFD2478A2284C49000981D42 /* Apps */,
|
||||
BFBBE2E2229320A2002097FA /* My Apps */,
|
||||
BFB1169E22933DDC00BB457C /* Updates */,
|
||||
BFDB69FB22A9A7A6007EA6D6 /* Account */,
|
||||
BFC51D7922972F1F00388324 /* Server */,
|
||||
BFD247982284D7FC00981D42 /* Model */,
|
||||
BFD2478D2284C4C700981D42 /* Components */,
|
||||
BFDB6A0622A9B114007EA6D6 /* Protocols */,
|
||||
BFD2479D2284FBBD00981D42 /* Extensions */,
|
||||
BFD247962284D7C100981D42 /* Resources */,
|
||||
BFD247972284D7D800981D42 /* Supporting Files */,
|
||||
@@ -771,6 +777,22 @@
|
||||
path = Connections;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFDB69FB22A9A7A6007EA6D6 /* Account */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */,
|
||||
);
|
||||
path = Account;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFDB6A0622A9B114007EA6D6 /* Protocols */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */,
|
||||
);
|
||||
path = Protocols;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFE6325822A83BA800F30809 /* Authentication */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1128,6 +1150,7 @@
|
||||
files = (
|
||||
BFE6325E22A8497000F30809 /* SelectTeamViewController.swift in Sources */,
|
||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
||||
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */,
|
||||
BFE6326A22A85DAF00F30809 /* ReplaceCertificateViewController.swift in Sources */,
|
||||
BFD247722284B9A500981D42 /* MyAppsViewController.swift in Sources */,
|
||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */,
|
||||
@@ -1149,6 +1172,7 @@
|
||||
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
||||
BFD52BD622A08A85000B7ED1 /* Server.swift in Sources */,
|
||||
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
||||
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */,
|
||||
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */,
|
||||
BF43003022A71C960051E2BC /* UserDefaults+AltStore.swift in Sources */,
|
||||
);
|
||||
|
||||
148
AltStore/Account/AccountViewController.swift
Normal file
148
AltStore/Account/AccountViewController.swift
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// AccountViewController.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 6/6/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Roxas
|
||||
|
||||
class AccountViewController: UITableViewController
|
||||
{
|
||||
private var team: Team?
|
||||
|
||||
private lazy var placeholderView = self.makePlaceholderView()
|
||||
|
||||
@IBOutlet var accountNameLabel: UILabel!
|
||||
@IBOutlet var accountEmailLabel: UILabel!
|
||||
|
||||
@IBOutlet var teamNameLabel: UILabel!
|
||||
@IBOutlet var teamTypeLabel: UILabel!
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.update()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
private extension AccountViewController
|
||||
{
|
||||
func makePlaceholderView() -> RSTPlaceholderView
|
||||
{
|
||||
let placeholderView = RSTPlaceholderView()
|
||||
placeholderView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
placeholderView.textLabel.text = NSLocalizedString("Not Signed In", comment: "")
|
||||
placeholderView.detailTextLabel.text = NSLocalizedString("Please sign in with your Apple ID to download and refresh apps.", comment: "")
|
||||
|
||||
let signInButton = UIButton(type: .system)
|
||||
signInButton.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
|
||||
signInButton.setTitle(NSLocalizedString("Sign In", comment: ""), for: .normal)
|
||||
signInButton.addTarget(self, action: #selector(AccountViewController.signIn(_:)), for: .primaryActionTriggered)
|
||||
placeholderView.stackView.addArrangedSubview(signInButton)
|
||||
|
||||
return placeholderView
|
||||
}
|
||||
|
||||
func update()
|
||||
{
|
||||
if let team = DatabaseManager.shared.activeTeam()
|
||||
{
|
||||
self.tableView.separatorStyle = .singleLine
|
||||
self.tableView.isScrollEnabled = true
|
||||
self.tableView.backgroundView = nil
|
||||
|
||||
self.navigationItem.rightBarButtonItem?.isEnabled = true
|
||||
|
||||
self.accountNameLabel.text = team.account.localizedName
|
||||
self.accountEmailLabel.text = team.account.appleID
|
||||
|
||||
self.teamNameLabel.text = team.name
|
||||
self.teamTypeLabel.text = team.type.localizedDescription
|
||||
|
||||
self.team = team
|
||||
}
|
||||
else
|
||||
{
|
||||
self.tableView.separatorStyle = .none
|
||||
self.tableView.isScrollEnabled = false
|
||||
self.tableView.backgroundView = self.placeholderView
|
||||
|
||||
self.navigationItem.rightBarButtonItem?.isEnabled = false
|
||||
|
||||
self.team = nil
|
||||
}
|
||||
|
||||
if self.isViewLoaded
|
||||
{
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension AccountViewController
|
||||
{
|
||||
@objc func signIn(_ sender: UIButton)
|
||||
{
|
||||
sender.isIndicatingActivity = true
|
||||
|
||||
AppManager.shared.authenticate(presentingViewController: self) { (result) in
|
||||
DispatchQueue.main.async {
|
||||
sender.isIndicatingActivity = false
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func signOut(_ sender: UIBarButtonItem)
|
||||
{
|
||||
func signOut()
|
||||
{
|
||||
DatabaseManager.shared.signOut { (error) in
|
||||
DispatchQueue.main.async {
|
||||
if let error = error
|
||||
{
|
||||
let toastView = RSTToastView(text: error.localizedDescription, detailText: nil)
|
||||
toastView.tintColor = .red
|
||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
||||
}
|
||||
else
|
||||
{
|
||||
let toastView = RSTToastView(text: NSLocalizedString("Successfully Signed Out!", comment: ""), detailText: nil)
|
||||
toastView.tintColor = .altPurple
|
||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
||||
}
|
||||
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Are you sure you want to sign out?", comment: ""), message: NSLocalizedString("You will no longer be able to install or refresh apps once you sign out.", comment: ""), preferredStyle: .actionSheet)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Sign Out", comment: ""), style: .destructive) { _ in signOut() })
|
||||
alertController.addAction(.cancel)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension AccountViewController
|
||||
{
|
||||
override func numberOfSections(in tableView: UITableView) -> Int
|
||||
{
|
||||
let count = (self.team == nil) ? 0 : super.numberOfSections(in: tableView)
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,11 +36,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
if UserDefaults.standard.firstLaunch == nil
|
||||
{
|
||||
Keychain.shared.appleIDEmailAddress = nil
|
||||
Keychain.shared.appleIDPassword = nil
|
||||
Keychain.shared.signingCertificatePrivateKey = nil
|
||||
Keychain.shared.signingCertificateIdentifier = nil
|
||||
|
||||
Keychain.shared.reset()
|
||||
UserDefaults.standard.firstLaunch = Date()
|
||||
}
|
||||
|
||||
|
||||
@@ -77,21 +77,29 @@ class AuthenticationOperation: RSTOperation
|
||||
|
||||
// Account
|
||||
let account = Account(altAccount, context: context)
|
||||
account.isActiveAccount = true
|
||||
|
||||
let otherAccountsFetchRequest = Account.fetchRequest() as NSFetchRequest<Account>
|
||||
otherAccountsFetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(Account.identifier), account.identifier)
|
||||
|
||||
let otherAccounts = try context.fetch(otherAccountsFetchRequest)
|
||||
otherAccounts.forEach(context.delete(_:))
|
||||
for account in otherAccounts
|
||||
{
|
||||
account.isActiveAccount = false
|
||||
}
|
||||
|
||||
// Team
|
||||
let team = Team(altTeam, account: account, context: context)
|
||||
team.isActiveTeam = true
|
||||
|
||||
let otherTeamsFetchRequest = Team.fetchRequest() as NSFetchRequest<Team>
|
||||
otherTeamsFetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(Team.identifier), team.identifier)
|
||||
|
||||
let otherTeams = try context.fetch(otherTeamsFetchRequest)
|
||||
otherTeams.forEach(context.delete(_:))
|
||||
for team in otherTeams
|
||||
{
|
||||
team.isActiveTeam = false
|
||||
}
|
||||
|
||||
// Save
|
||||
try context.save()
|
||||
@@ -264,27 +272,12 @@ private extension AuthenticationOperation
|
||||
case .success(let teams):
|
||||
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
do
|
||||
if let activeTeam = DatabaseManager.shared.activeTeam(in: context), let altTeam = teams.first(where: { $0.identifier == activeTeam.identifier })
|
||||
{
|
||||
let fetchRequest = Team.fetchRequest() as NSFetchRequest<Team>
|
||||
fetchRequest.fetchLimit = 1
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
|
||||
let fetchedTeams = try context.fetch(fetchRequest)
|
||||
|
||||
if let fetchedTeam = fetchedTeams.first, let altTeam = teams.first(where: { $0.identifier == fetchedTeam.identifier })
|
||||
{
|
||||
completionHandler(.success(altTeam))
|
||||
}
|
||||
else
|
||||
{
|
||||
selectTeam(from: teams)
|
||||
}
|
||||
completionHandler(.success(altTeam))
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
print("Error fetching Teams.", error)
|
||||
|
||||
selectTeam(from: teams)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,16 +57,7 @@ private extension SelectTeamViewController
|
||||
dataSource.proxy = self
|
||||
dataSource.cellConfigurationHandler = { [weak self] (cell, team, indexPath) in
|
||||
cell.textLabel?.text = team.name
|
||||
|
||||
switch team.type
|
||||
{
|
||||
case .unknown: cell.detailTextLabel?.text = NSLocalizedString("Unknown", comment: "")
|
||||
case .free: cell.detailTextLabel?.text = NSLocalizedString("Free Developer Account", comment: "")
|
||||
case .individual: cell.detailTextLabel?.text = NSLocalizedString("Individual", comment: "")
|
||||
case .organization: cell.detailTextLabel?.text = NSLocalizedString("Organization", comment: "")
|
||||
@unknown default: cell.detailTextLabel?.text = nil
|
||||
}
|
||||
|
||||
cell.detailTextLabel?.text = team.type.localizedDescription
|
||||
cell.accessoryType = (self?.selectedTeam == team) ? .checkmark : .none
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<segue destination="XF0-gk-CxQ" kind="relationship" relationship="viewControllers" id="uf8-vX-M3B"/>
|
||||
<segue destination="v8c-d9-T9x" kind="relationship" relationship="viewControllers" id="fqx-5p-YCD"/>
|
||||
<segue destination="gJ2-NJ-8DO" kind="relationship" relationship="viewControllers" id="J4s-tm-3YV"/>
|
||||
<segue destination="MGm-Zy-ffn" kind="relationship" relationship="viewControllers" id="quv-RY-0rM"/>
|
||||
</connections>
|
||||
</tabBarController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
||||
@@ -468,11 +469,145 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1518" y="1124"/>
|
||||
</scene>
|
||||
<!--My Apps-->
|
||||
<!--Account-->
|
||||
<scene sceneID="GaO-Ug-BdZ">
|
||||
<objects>
|
||||
<navigationController id="MGm-Zy-ffn" sceneMemberID="viewController">
|
||||
<tabBarItem key="tabBarItem" title="Account" image="second" id="8Ic-ki-txH"/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="rzJ-pZ-611">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="VBC-qD-V1a" kind="relationship" relationship="rootViewController" id="tgI-RK-57z"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="cM4-hZ-uHG" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="750" y="1846"/>
|
||||
</scene>
|
||||
<!--Account-->
|
||||
<scene sceneID="Xdi-2V-rwM">
|
||||
<objects>
|
||||
<tableViewController id="VBC-qD-V1a" customClass="AccountViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="dOC-Gz-Ieu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Account" id="nOs-a4-lBS">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="4Qf-b3-Kd1" detailTextLabel="zvb-TJ-uGW" style="IBUITableViewCellStyleValue1" id="HgQ-vv-9nH">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="HgQ-vv-9nH" id="SSv-nz-f4V">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4Qf-b3-Kd1">
|
||||
<rect key="frame" x="16" y="12" width="45" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Riley Testut (iOS Developer)" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="zvb-TJ-uGW">
|
||||
<rect key="frame" x="144.5" y="12" width="214.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="1UL-2f-ayi" detailTextLabel="2YH-D5-AnU" style="IBUITableViewCellStyleValue1" id="7cR-Qb-5GU">
|
||||
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="7cR-Qb-5GU" id="iPP-TB-jnD">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Email" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="1UL-2f-ayi">
|
||||
<rect key="frame" x="16" y="12" width="41" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="riley@rileytestut.com" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2YH-D5-AnU">
|
||||
<rect key="frame" x="198" y="12" width="161" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Team" id="xqO-qN-967">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" textLabel="ktC-F0-e9P" detailTextLabel="GtD-Jo-ONK" style="IBUITableViewCellStyleSubtitle" id="itp-Ya-UBR">
|
||||
<rect key="frame" x="0.0" y="199.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="itp-Ya-UBR" id="w1A-z5-P4W">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ktC-F0-e9P">
|
||||
<rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="GtD-Jo-ONK">
|
||||
<rect key="frame" x="16" y="25.5" width="44" height="14.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection footerTitle="" id="Yg2-vc-vLQ">
|
||||
<cells/>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="VBC-qD-V1a" id="1Xd-SN-tww"/>
|
||||
<outlet property="delegate" destination="VBC-qD-V1a" id="KEk-wr-hab"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Account" id="Mtw-26-mVI">
|
||||
<barButtonItem key="rightBarButtonItem" title="Sign Out" id="0wM-zj-gVA">
|
||||
<color key="tintColor" red="1" green="0.14901960780000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<action selector="signOut:" destination="VBC-qD-V1a" id="X7d-Qp-VWw"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="accountEmailLabel" destination="2YH-D5-AnU" id="fyD-e7-ygs"/>
|
||||
<outlet property="accountNameLabel" destination="zvb-TJ-uGW" id="mCh-p8-qCs"/>
|
||||
<outlet property="teamNameLabel" destination="ktC-F0-e9P" id="2Zg-sh-2mY"/>
|
||||
<outlet property="teamTypeLabel" destination="GtD-Jo-ONK" id="Jzp-2K-Bjk"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="BE4-68-0PU" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1518" y="1847"/>
|
||||
</scene>
|
||||
<!--Updates-->
|
||||
<scene sceneID="H4l-3g-QrV">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="gJ2-NJ-8DO" sceneMemberID="viewController">
|
||||
<tabBarItem key="tabBarItem" title="My Apps" image="second" id="n2m-w3-Ltw"/>
|
||||
<tabBarItem key="tabBarItem" title="Updates" image="first" id="n2m-w3-Ltw"/>
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="mb8-TA-qKp">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
||||
|
||||
@@ -20,6 +20,14 @@ class Keychain
|
||||
private init()
|
||||
{
|
||||
}
|
||||
|
||||
func reset()
|
||||
{
|
||||
self.appleIDEmailAddress = nil
|
||||
self.appleIDPassword = nil
|
||||
self.signingCertificatePrivateKey = nil
|
||||
self.signingCertificateIdentifier = nil
|
||||
}
|
||||
}
|
||||
|
||||
extension Keychain
|
||||
|
||||
@@ -12,7 +12,7 @@ import CoreData
|
||||
import AltSign
|
||||
|
||||
@objc(Account)
|
||||
class Account: NSManagedObject
|
||||
class Account: NSManagedObject, Fetchable
|
||||
{
|
||||
var localizedName: String {
|
||||
var components = PersonNameComponents()
|
||||
@@ -30,6 +30,8 @@ class Account: NSManagedObject
|
||||
@NSManaged var firstName: String
|
||||
@NSManaged var lastName: String
|
||||
|
||||
@NSManaged var isActiveAccount: Bool
|
||||
|
||||
/* Relationships */
|
||||
@NSManaged var teams: Set<Team>
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<attribute name="appleID" attributeType="String" syncable="YES"/>
|
||||
<attribute name="firstName" attributeType="String" syncable="YES"/>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isActiveAccount" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="lastName" attributeType="String" syncable="YES"/>
|
||||
<relationship name="teams" toMany="YES" deletionRule="Cascade" destinationEntity="Team" inverseName="account" inverseEntity="Team" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
@@ -39,6 +40,7 @@
|
||||
</entity>
|
||||
<entity name="Team" representedClassName="Team" syncable="YES">
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isActiveTeam" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
|
||||
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="teams" inverseEntity="Account" syncable="YES"/>
|
||||
@@ -49,9 +51,9 @@
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Account" positionX="-36" positionY="90" width="128" height="135"/>
|
||||
<element name="App" positionX="-63" positionY="-18" width="128" height="210"/>
|
||||
<element name="InstalledApp" positionX="-63" positionY="0" width="128" height="120"/>
|
||||
<element name="Team" positionX="-45" positionY="81" width="128" height="105"/>
|
||||
<element name="Account" positionX="-36" positionY="90" width="128" height="120"/>
|
||||
<element name="Team" positionX="-45" positionY="81" width="128" height="120"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -38,6 +38,35 @@ public extension DatabaseManager
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
|
||||
func signOut(completionHandler: @escaping (Error?) -> Void)
|
||||
{
|
||||
self.persistentContainer.performBackgroundTask { (context) in
|
||||
if let account = self.activeAccount(in: context)
|
||||
{
|
||||
account.isActiveAccount = false
|
||||
}
|
||||
|
||||
if let team = self.activeTeam(in: context)
|
||||
{
|
||||
team.isActiveTeam = false
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
try context.save()
|
||||
|
||||
Keychain.shared.reset()
|
||||
|
||||
completionHandler(nil)
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to save when signing out.", error)
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension DatabaseManager
|
||||
@@ -46,3 +75,22 @@ public extension DatabaseManager
|
||||
return self.persistentContainer.viewContext
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseManager
|
||||
{
|
||||
func activeAccount(in context: NSManagedObjectContext = DatabaseManager.shared.viewContext) -> Account?
|
||||
{
|
||||
let predicate = NSPredicate(format: "%K == YES", #keyPath(Account.isActiveAccount))
|
||||
|
||||
let activeAccount = Account.first(satisfying: predicate, in: context)
|
||||
return activeAccount
|
||||
}
|
||||
|
||||
func activeTeam(in context: NSManagedObjectContext = DatabaseManager.shared.viewContext) -> Team?
|
||||
{
|
||||
let predicate = NSPredicate(format: "%K == YES", #keyPath(Team.isActiveTeam))
|
||||
|
||||
let activeTeam = Team.first(satisfying: predicate, in: context)
|
||||
return activeTeam
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,30 @@ import CoreData
|
||||
|
||||
import AltSign
|
||||
|
||||
extension ALTTeamType
|
||||
{
|
||||
var localizedDescription: String {
|
||||
switch self
|
||||
{
|
||||
case .free: return NSLocalizedString("Free Developer Account", comment: "")
|
||||
case .individual: return NSLocalizedString("Individual", comment: "")
|
||||
case .organization: return NSLocalizedString("Organization", comment: "")
|
||||
case .unknown: fallthrough
|
||||
@unknown default: return NSLocalizedString("Unknown", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc(Team)
|
||||
class Team: NSManagedObject
|
||||
class Team: NSManagedObject, Fetchable
|
||||
{
|
||||
/* Properties */
|
||||
@NSManaged var name: String
|
||||
@NSManaged var identifier: String
|
||||
@NSManaged var type: ALTTeamType
|
||||
|
||||
@NSManaged var isActiveTeam: Bool
|
||||
|
||||
/* Relationships */
|
||||
@NSManaged private(set) var account: Account!
|
||||
|
||||
|
||||
61
AltStore/Protocols/Fetchable.swift
Normal file
61
AltStore/Protocols/Fetchable.swift
Normal file
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// NSManagedObject+Conveniences.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 6/6/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
|
||||
protocol Fetchable: NSManagedObject
|
||||
{
|
||||
}
|
||||
|
||||
extension Fetchable
|
||||
{
|
||||
static func first(satisfying predicate: NSPredicate? = nil, sortedBy sortDescriptors: [NSSortDescriptor]? = nil, in context: NSManagedObjectContext) -> Self?
|
||||
{
|
||||
let managedObjects = Self.all(satisfying: predicate, sortedBy: sortDescriptors, in: context, returnFirstResult: true)
|
||||
return managedObjects.first
|
||||
}
|
||||
|
||||
static func all(satisfying predicate: NSPredicate? = nil, sortedBy sortDescriptors: [NSSortDescriptor]? = nil, in context: NSManagedObjectContext) -> [Self]
|
||||
{
|
||||
let managedObjects = Self.all(satisfying: predicate, sortedBy: sortDescriptors, in: context, returnFirstResult: false)
|
||||
return managedObjects
|
||||
}
|
||||
|
||||
private static func all(satisfying predicate: NSPredicate? = nil, sortedBy sortDescriptors: [NSSortDescriptor]? = nil, in context: NSManagedObjectContext, returnFirstResult: Bool) -> [Self]
|
||||
{
|
||||
let registeredObjects = context.registeredObjects.lazy.compactMap({ $0 as? Self }).filter({ predicate?.evaluate(with: $0) != false })
|
||||
|
||||
if let managedObject = registeredObjects.first, returnFirstResult
|
||||
{
|
||||
return [managedObject]
|
||||
}
|
||||
|
||||
let fetchRequest = self.fetchRequest() as! NSFetchRequest<Self>
|
||||
fetchRequest.predicate = predicate
|
||||
fetchRequest.sortDescriptors = sortDescriptors
|
||||
|
||||
do
|
||||
{
|
||||
let managedObjects = try context.fetch(fetchRequest)
|
||||
|
||||
if let managedObject = managedObjects.first, returnFirstResult
|
||||
{
|
||||
return [managedObject]
|
||||
}
|
||||
else
|
||||
{
|
||||
return managedObjects
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to fetch managed objects.", error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user