mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Tries to refresh apps that are about to expire first, and then always refreshes AltStore itself last, since refreshing AltStore means that the app will quit.
203 lines
7.6 KiB
Swift
203 lines
7.6 KiB
Swift
//
|
|
// UpdatesViewController.swift
|
|
// AltStore
|
|
//
|
|
// Created by Riley Testut on 5/20/19.
|
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
import Roxas
|
|
|
|
class UpdatesViewController: UITableViewController
|
|
{
|
|
private lazy var dataSource = self.makeDataSource()
|
|
|
|
private lazy var dateFormatter: DateFormatter = {
|
|
let dateFormatter = DateFormatter()
|
|
dateFormatter.dateStyle = .short
|
|
dateFormatter.timeStyle = .none
|
|
return dateFormatter
|
|
}()
|
|
|
|
@IBOutlet private var progressView: UIProgressView!
|
|
|
|
required init?(coder aDecoder: NSCoder)
|
|
{
|
|
super.init(coder: aDecoder)
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(UpdatesViewController.didFetchApps(_:)), name: AppManager.didFetchAppsNotification, object: nil)
|
|
}
|
|
|
|
override func viewDidLoad()
|
|
{
|
|
super.viewDidLoad()
|
|
|
|
self.tableView.dataSource = self.dataSource
|
|
|
|
if let navigationBar = self.navigationController?.navigationBar
|
|
{
|
|
self.progressView.translatesAutoresizingMaskIntoConstraints = false
|
|
navigationBar.addSubview(self.progressView)
|
|
|
|
NSLayoutConstraint.activate([self.progressView.widthAnchor.constraint(equalTo: navigationBar.widthAnchor),
|
|
self.progressView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)])
|
|
}
|
|
}
|
|
|
|
override func viewDidAppear(_ animated: Bool)
|
|
{
|
|
super.viewDidAppear(animated)
|
|
|
|
self.update()
|
|
}
|
|
|
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
|
{
|
|
guard segue.identifier == "showAppDetail" else { return }
|
|
|
|
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
|
|
|
let installedApp = self.dataSource.item(at: indexPath)
|
|
guard let app = installedApp.app else { return }
|
|
|
|
let appDetailViewController = segue.destination as! AppDetailViewController
|
|
appDetailViewController.app = app
|
|
}
|
|
}
|
|
|
|
private extension UpdatesViewController
|
|
{
|
|
func makeDataSource() -> RSTFetchedResultsTableViewDataSource<InstalledApp>
|
|
{
|
|
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
|
fetchRequest.predicate = NSPredicate(format: "%K != %K", #keyPath(InstalledApp.version), #keyPath(InstalledApp.app.version))
|
|
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.app?.versionDate, ascending: false)]
|
|
fetchRequest.returnsObjectsAsFaults = false
|
|
|
|
let dataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
|
dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in
|
|
guard let app = installedApp.app else { return }
|
|
|
|
cell.textLabel?.text = app.name + " (\(app.version))"
|
|
|
|
let detailText = self.dateFormatter.string(from: app.versionDate) + "\n\n" + (app.versionDescription ?? "No Update Description")
|
|
|
|
cell.detailTextLabel?.numberOfLines = 0
|
|
cell.detailTextLabel?.text = detailText
|
|
}
|
|
|
|
let placeholderView = RSTPlaceholderView()
|
|
placeholderView.textLabel.text = NSLocalizedString("No Updates", comment: "")
|
|
placeholderView.detailTextLabel.text = NSLocalizedString("There are no app updates at this time.", comment: "")
|
|
dataSource.placeholderView = placeholderView
|
|
|
|
return dataSource
|
|
}
|
|
|
|
func update()
|
|
{
|
|
if let count = self.dataSource.fetchedResultsController.fetchedObjects?.count, count > 0
|
|
{
|
|
self.navigationController?.tabBarItem.badgeValue = String(describing: count)
|
|
}
|
|
else
|
|
{
|
|
self.navigationController?.tabBarItem.badgeValue = nil
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension UpdatesViewController
|
|
{
|
|
func update(_ installedApp: InstalledApp)
|
|
{
|
|
func updateApp()
|
|
{
|
|
let toastView = RSTToastView(text: "Updating...", detailText: nil)
|
|
toastView.tintColor = .altPurple
|
|
toastView.activityIndicatorView.startAnimating()
|
|
toastView.show(in: self.navigationController?.view ?? self.view)
|
|
|
|
let progress = AppManager.shared.install(installedApp.app, presentingViewController: self) { (result) in
|
|
do
|
|
{
|
|
_ = try result.get()
|
|
|
|
DispatchQueue.main.async {
|
|
let installedApp = DatabaseManager.shared.persistentContainer.viewContext.object(with: installedApp.objectID) as! InstalledApp
|
|
|
|
let toastView = RSTToastView(text: "Updated \(installedApp.app.name) to version \(installedApp.version)!", detailText: nil)
|
|
toastView.tintColor = .altPurple
|
|
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2)
|
|
|
|
self.update()
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
DispatchQueue.main.async {
|
|
let toastView = RSTToastView(text: "Failed to update \(installedApp.app.name)", detailText: error.localizedDescription)
|
|
toastView.tintColor = .altPurple
|
|
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2)
|
|
}
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
self.progressView.observedProgress = nil
|
|
self.progressView.progress = 0.0
|
|
}
|
|
}
|
|
|
|
self.progressView.observedProgress = progress
|
|
}
|
|
|
|
if installedApp.app.identifier == App.altstoreAppID
|
|
{
|
|
let alertController = UIAlertController(title: NSLocalizedString("Update AltStore?", comment: ""), message: NSLocalizedString("AltStore will quit upon completion.", comment: ""), preferredStyle: .alert)
|
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Update and Quit", comment: ""), style: .default, handler: { (action) in
|
|
updateApp()
|
|
}))
|
|
alertController.addAction(.cancel)
|
|
|
|
self.present(alertController, animated: true, completion: nil)
|
|
}
|
|
else
|
|
{
|
|
updateApp()
|
|
}
|
|
}
|
|
|
|
@objc func didFetchApps(_ notification: Notification)
|
|
{
|
|
DispatchQueue.main.async {
|
|
if self.dataSource.fetchedResultsController.fetchedObjects == nil
|
|
{
|
|
do { try self.dataSource.fetchedResultsController.performFetch() }
|
|
catch { print("Error fetching:", error) }
|
|
}
|
|
|
|
self.update()
|
|
}
|
|
}
|
|
}
|
|
|
|
extension UpdatesViewController
|
|
{
|
|
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
|
|
{
|
|
let updateAction = UITableViewRowAction(style: .normal, title: "Update") { [weak self] (action, indexPath) in
|
|
guard let installedApp = self?.dataSource.item(at: indexPath) else { return }
|
|
self?.update(installedApp)
|
|
}
|
|
updateAction.backgroundColor = .altPurple
|
|
|
|
return [updateAction]
|
|
}
|
|
|
|
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
|
|
{
|
|
}
|
|
}
|