mirror of
https://github.com/SideStore/SideStore.git
synced 2026-04-09 04:05:39 +02: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 */; };
|
BFD52C2022A1A9EC000B7ED1 /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1D22A1A9EC000B7ED1 /* node.c */; };
|
||||||
BFD52C2122A1A9EC000B7ED1 /* node_list.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1E22A1A9EC000B7ED1 /* node_list.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 */; };
|
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 */; };
|
BFE6325A22A83BEB00F30809 /* Authentication.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFE6325922A83BEB00F30809 /* Authentication.storyboard */; };
|
||||||
BFE6325C22A83C0100F30809 /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6325B22A83C0100F30809 /* AuthenticationViewController.swift */; };
|
BFE6325C22A83C0100F30809 /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6325B22A83C0100F30809 /* AuthenticationViewController.swift */; };
|
||||||
BFE6325E22A8497000F30809 /* SelectTeamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE6325D22A8497000F30809 /* SelectTeamViewController.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; };
|
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; };
|
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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
BFE6325D22A8497000F30809 /* SelectTeamViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectTeamViewController.swift; sourceTree = "<group>"; };
|
||||||
@@ -675,9 +679,11 @@
|
|||||||
BFD2478A2284C49000981D42 /* Apps */,
|
BFD2478A2284C49000981D42 /* Apps */,
|
||||||
BFBBE2E2229320A2002097FA /* My Apps */,
|
BFBBE2E2229320A2002097FA /* My Apps */,
|
||||||
BFB1169E22933DDC00BB457C /* Updates */,
|
BFB1169E22933DDC00BB457C /* Updates */,
|
||||||
|
BFDB69FB22A9A7A6007EA6D6 /* Account */,
|
||||||
BFC51D7922972F1F00388324 /* Server */,
|
BFC51D7922972F1F00388324 /* Server */,
|
||||||
BFD247982284D7FC00981D42 /* Model */,
|
BFD247982284D7FC00981D42 /* Model */,
|
||||||
BFD2478D2284C4C700981D42 /* Components */,
|
BFD2478D2284C4C700981D42 /* Components */,
|
||||||
|
BFDB6A0622A9B114007EA6D6 /* Protocols */,
|
||||||
BFD2479D2284FBBD00981D42 /* Extensions */,
|
BFD2479D2284FBBD00981D42 /* Extensions */,
|
||||||
BFD247962284D7C100981D42 /* Resources */,
|
BFD247962284D7C100981D42 /* Resources */,
|
||||||
BFD247972284D7D800981D42 /* Supporting Files */,
|
BFD247972284D7D800981D42 /* Supporting Files */,
|
||||||
@@ -771,6 +777,22 @@
|
|||||||
path = Connections;
|
path = Connections;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
BFE6325822A83BA800F30809 /* Authentication */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -1128,6 +1150,7 @@
|
|||||||
files = (
|
files = (
|
||||||
BFE6325E22A8497000F30809 /* SelectTeamViewController.swift in Sources */,
|
BFE6325E22A8497000F30809 /* SelectTeamViewController.swift in Sources */,
|
||||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
||||||
|
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */,
|
||||||
BFE6326A22A85DAF00F30809 /* ReplaceCertificateViewController.swift in Sources */,
|
BFE6326A22A85DAF00F30809 /* ReplaceCertificateViewController.swift in Sources */,
|
||||||
BFD247722284B9A500981D42 /* MyAppsViewController.swift in Sources */,
|
BFD247722284B9A500981D42 /* MyAppsViewController.swift in Sources */,
|
||||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */,
|
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */,
|
||||||
@@ -1149,6 +1172,7 @@
|
|||||||
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
||||||
BFD52BD622A08A85000B7ED1 /* Server.swift in Sources */,
|
BFD52BD622A08A85000B7ED1 /* Server.swift in Sources */,
|
||||||
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
||||||
|
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */,
|
||||||
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */,
|
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */,
|
||||||
BF43003022A71C960051E2BC /* UserDefaults+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
|
if UserDefaults.standard.firstLaunch == nil
|
||||||
{
|
{
|
||||||
Keychain.shared.appleIDEmailAddress = nil
|
Keychain.shared.reset()
|
||||||
Keychain.shared.appleIDPassword = nil
|
|
||||||
Keychain.shared.signingCertificatePrivateKey = nil
|
|
||||||
Keychain.shared.signingCertificateIdentifier = nil
|
|
||||||
|
|
||||||
UserDefaults.standard.firstLaunch = Date()
|
UserDefaults.standard.firstLaunch = Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,21 +77,29 @@ class AuthenticationOperation: RSTOperation
|
|||||||
|
|
||||||
// Account
|
// Account
|
||||||
let account = Account(altAccount, context: context)
|
let account = Account(altAccount, context: context)
|
||||||
|
account.isActiveAccount = true
|
||||||
|
|
||||||
let otherAccountsFetchRequest = Account.fetchRequest() as NSFetchRequest<Account>
|
let otherAccountsFetchRequest = Account.fetchRequest() as NSFetchRequest<Account>
|
||||||
otherAccountsFetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(Account.identifier), account.identifier)
|
otherAccountsFetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(Account.identifier), account.identifier)
|
||||||
|
|
||||||
let otherAccounts = try context.fetch(otherAccountsFetchRequest)
|
let otherAccounts = try context.fetch(otherAccountsFetchRequest)
|
||||||
otherAccounts.forEach(context.delete(_:))
|
for account in otherAccounts
|
||||||
|
{
|
||||||
|
account.isActiveAccount = false
|
||||||
|
}
|
||||||
|
|
||||||
// Team
|
// Team
|
||||||
let team = Team(altTeam, account: account, context: context)
|
let team = Team(altTeam, account: account, context: context)
|
||||||
|
team.isActiveTeam = true
|
||||||
|
|
||||||
let otherTeamsFetchRequest = Team.fetchRequest() as NSFetchRequest<Team>
|
let otherTeamsFetchRequest = Team.fetchRequest() as NSFetchRequest<Team>
|
||||||
otherTeamsFetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(Team.identifier), team.identifier)
|
otherTeamsFetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(Team.identifier), team.identifier)
|
||||||
|
|
||||||
let otherTeams = try context.fetch(otherTeamsFetchRequest)
|
let otherTeams = try context.fetch(otherTeamsFetchRequest)
|
||||||
otherTeams.forEach(context.delete(_:))
|
for team in otherTeams
|
||||||
|
{
|
||||||
|
team.isActiveTeam = false
|
||||||
|
}
|
||||||
|
|
||||||
// Save
|
// Save
|
||||||
try context.save()
|
try context.save()
|
||||||
@@ -264,27 +272,12 @@ private extension AuthenticationOperation
|
|||||||
case .success(let teams):
|
case .success(let teams):
|
||||||
|
|
||||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
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>
|
completionHandler(.success(altTeam))
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch
|
else
|
||||||
{
|
{
|
||||||
print("Error fetching Teams.", error)
|
|
||||||
|
|
||||||
selectTeam(from: teams)
|
selectTeam(from: teams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,16 +57,7 @@ private extension SelectTeamViewController
|
|||||||
dataSource.proxy = self
|
dataSource.proxy = self
|
||||||
dataSource.cellConfigurationHandler = { [weak self] (cell, team, indexPath) in
|
dataSource.cellConfigurationHandler = { [weak self] (cell, team, indexPath) in
|
||||||
cell.textLabel?.text = team.name
|
cell.textLabel?.text = team.name
|
||||||
|
cell.detailTextLabel?.text = team.type.localizedDescription
|
||||||
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.accessoryType = (self?.selectedTeam == team) ? .checkmark : .none
|
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="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="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="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>
|
</connections>
|
||||||
</tabBarController>
|
</tabBarController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
||||||
@@ -468,11 +469,145 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1518" y="1124"/>
|
<point key="canvasLocation" x="1518" y="1124"/>
|
||||||
</scene>
|
</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">
|
<scene sceneID="H4l-3g-QrV">
|
||||||
<objects>
|
<objects>
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="gJ2-NJ-8DO" sceneMemberID="viewController">
|
<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/>
|
<toolbarItems/>
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="mb8-TA-qKp">
|
<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"/>
|
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ class Keychain
|
|||||||
private init()
|
private init()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reset()
|
||||||
|
{
|
||||||
|
self.appleIDEmailAddress = nil
|
||||||
|
self.appleIDPassword = nil
|
||||||
|
self.signingCertificatePrivateKey = nil
|
||||||
|
self.signingCertificateIdentifier = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Keychain
|
extension Keychain
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import CoreData
|
|||||||
import AltSign
|
import AltSign
|
||||||
|
|
||||||
@objc(Account)
|
@objc(Account)
|
||||||
class Account: NSManagedObject
|
class Account: NSManagedObject, Fetchable
|
||||||
{
|
{
|
||||||
var localizedName: String {
|
var localizedName: String {
|
||||||
var components = PersonNameComponents()
|
var components = PersonNameComponents()
|
||||||
@@ -30,6 +30,8 @@ class Account: NSManagedObject
|
|||||||
@NSManaged var firstName: String
|
@NSManaged var firstName: String
|
||||||
@NSManaged var lastName: String
|
@NSManaged var lastName: String
|
||||||
|
|
||||||
|
@NSManaged var isActiveAccount: Bool
|
||||||
|
|
||||||
/* Relationships */
|
/* Relationships */
|
||||||
@NSManaged var teams: Set<Team>
|
@NSManaged var teams: Set<Team>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<attribute name="appleID" attributeType="String" syncable="YES"/>
|
<attribute name="appleID" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="firstName" attributeType="String" syncable="YES"/>
|
<attribute name="firstName" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="identifier" 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"/>
|
<attribute name="lastName" attributeType="String" syncable="YES"/>
|
||||||
<relationship name="teams" toMany="YES" deletionRule="Cascade" destinationEntity="Team" inverseName="account" inverseEntity="Team" syncable="YES"/>
|
<relationship name="teams" toMany="YES" deletionRule="Cascade" destinationEntity="Team" inverseName="account" inverseEntity="Team" syncable="YES"/>
|
||||||
<uniquenessConstraints>
|
<uniquenessConstraints>
|
||||||
@@ -39,6 +40,7 @@
|
|||||||
</entity>
|
</entity>
|
||||||
<entity name="Team" representedClassName="Team" syncable="YES">
|
<entity name="Team" representedClassName="Team" syncable="YES">
|
||||||
<attribute name="identifier" attributeType="String" 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="name" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="type" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" 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"/>
|
<relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="teams" inverseEntity="Account" syncable="YES"/>
|
||||||
@@ -49,9 +51,9 @@
|
|||||||
</uniquenessConstraints>
|
</uniquenessConstraints>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<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="App" positionX="-63" positionY="-18" width="128" height="210"/>
|
||||||
<element name="InstalledApp" positionX="-63" positionY="0" width="128" height="120"/>
|
<element name="InstalledApp" positionX="-63" positionY="0" width="128" height="120"/>
|
||||||
<element name="Team" positionX="-45" positionY="81" width="128" height="105"/>
|
<element name="Team" positionX="-45" positionY="81" width="128" height="120"/>
|
||||||
<element name="Account" positionX="-36" positionY="90" width="128" height="120"/>
|
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
||||||
@@ -38,6 +38,35 @@ public extension DatabaseManager
|
|||||||
completionHandler(error)
|
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
|
public extension DatabaseManager
|
||||||
@@ -46,3 +75,22 @@ public extension DatabaseManager
|
|||||||
return self.persistentContainer.viewContext
|
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
|
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)
|
@objc(Team)
|
||||||
class Team: NSManagedObject
|
class Team: NSManagedObject, Fetchable
|
||||||
{
|
{
|
||||||
/* Properties */
|
/* Properties */
|
||||||
@NSManaged var name: String
|
@NSManaged var name: String
|
||||||
@NSManaged var identifier: String
|
@NSManaged var identifier: String
|
||||||
@NSManaged var type: ALTTeamType
|
@NSManaged var type: ALTTeamType
|
||||||
|
|
||||||
|
@NSManaged var isActiveTeam: Bool
|
||||||
|
|
||||||
/* Relationships */
|
/* Relationships */
|
||||||
@NSManaged private(set) var account: Account!
|
@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