mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-19 11:43:24 +01:00
Replaces StoreApp.latestVersion with latestSupportedVersion + latestAvailableVersion
We now store the latest supported version as a relationship on StoreApp, rather than the latest available version. This allows us to reference the latest supported version in predicates and sort descriptors. However, we kept the underlying Core Data property name the same to avoid extra migration.
This commit is contained in:
@@ -81,7 +81,7 @@ class AppContentViewController: UITableViewController
|
|||||||
self.subtitleLabel.text = self.app.subtitle
|
self.subtitleLabel.text = self.app.subtitle
|
||||||
self.descriptionTextView.text = self.app.localizedDescription
|
self.descriptionTextView.text = self.app.localizedDescription
|
||||||
|
|
||||||
if let version = self.app.latestVersion
|
if let version = self.app.latestAvailableVersion
|
||||||
{
|
{
|
||||||
self.versionDescriptionTextView.text = version.localizedDescription
|
self.versionDescriptionTextView.text = version.localizedDescription
|
||||||
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.version)
|
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.version)
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ private extension AppViewController
|
|||||||
button.progress = progress
|
button.progress = progress
|
||||||
}
|
}
|
||||||
|
|
||||||
if let versionDate = self.app.latestVersion?.date, versionDate > Date()
|
if let versionDate = self.app.latestAvailableVersion?.date, versionDate > Date()
|
||||||
{
|
{
|
||||||
self.bannerView.button.countdownDate = versionDate
|
self.bannerView.button.countdownDate = versionDate
|
||||||
self.navigationBarDownloadButton.countdownDate = versionDate
|
self.navigationBarDownloadButton.countdownDate = versionDate
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ private extension AppDelegate
|
|||||||
for update in updates
|
for update in updates
|
||||||
{
|
{
|
||||||
guard !previousUpdates.contains(where: { $0[#keyPath(InstalledApp.bundleIdentifier)] == update.bundleIdentifier }) else { continue }
|
guard !previousUpdates.contains(where: { $0[#keyPath(InstalledApp.bundleIdentifier)] == update.bundleIdentifier }) else { continue }
|
||||||
guard let storeApp = update.storeApp, let version = storeApp.version else { continue }
|
guard let storeApp = update.storeApp, let version = storeApp.latestSupportedVersion else { continue }
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = NSLocalizedString("New Update Available", comment: "")
|
content.title = NSLocalizedString("New Update Available", comment: "")
|
||||||
|
|||||||
@@ -113,9 +113,9 @@ private extension BrowseViewController
|
|||||||
let progress = AppManager.shared.installationProgress(for: app)
|
let progress = AppManager.shared.installationProgress(for: app)
|
||||||
cell.bannerView.button.progress = progress
|
cell.bannerView.button.progress = progress
|
||||||
|
|
||||||
if let versionDate = app.latestVersion?.date, versionDate > Date()
|
if let versionDate = app.latestSupportedVersion?.date, versionDate > Date()
|
||||||
{
|
{
|
||||||
cell.bannerView.button.countdownDate = app.versionDate
|
cell.bannerView.button.countdownDate = versionDate
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ private extension MyAppsViewController
|
|||||||
func makeUpdatesDataSource() -> RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>
|
func makeUpdatesDataSource() -> RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>
|
||||||
{
|
{
|
||||||
let fetchRequest = InstalledApp.updatesFetchRequest()
|
let fetchRequest = InstalledApp.updatesFetchRequest()
|
||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.storeApp?.latestVersion?.date, ascending: true),
|
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.storeApp?.latestSupportedVersion?.date, ascending: false),
|
||||||
NSSortDescriptor(keyPath: \InstalledApp.name, ascending: true)]
|
NSSortDescriptor(keyPath: \InstalledApp.name, ascending: true)]
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
|
||||||
@@ -195,21 +195,21 @@ private extension MyAppsViewController
|
|||||||
dataSource.cellIdentifierHandler = { _ in "UpdateCell" }
|
dataSource.cellIdentifierHandler = { _ in "UpdateCell" }
|
||||||
dataSource.cellConfigurationHandler = { [weak self] (cell, installedApp, indexPath) in
|
dataSource.cellConfigurationHandler = { [weak self] (cell, installedApp, indexPath) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
guard let app = installedApp.storeApp, let latestVersion = app.latestVersion else { return }
|
guard let app = installedApp.storeApp, let latestSupportedVersion = app.latestSupportedVersion else { return }
|
||||||
|
|
||||||
let cell = cell as! UpdateCollectionViewCell
|
let cell = cell as! UpdateCollectionViewCell
|
||||||
cell.layoutMargins.left = self.view.layoutMargins.left
|
cell.layoutMargins.left = self.view.layoutMargins.left
|
||||||
cell.layoutMargins.right = self.view.layoutMargins.right
|
cell.layoutMargins.right = self.view.layoutMargins.right
|
||||||
|
|
||||||
cell.tintColor = app.tintColor ?? .altPrimary
|
cell.tintColor = app.tintColor ?? .altPrimary
|
||||||
cell.versionDescriptionTextView.text = app.versionDescription
|
cell.versionDescriptionTextView.text = latestSupportedVersion.localizedDescription
|
||||||
|
|
||||||
cell.bannerView.iconImageView.image = nil
|
cell.bannerView.iconImageView.image = nil
|
||||||
cell.bannerView.iconImageView.isIndicatingActivity = true
|
cell.bannerView.iconImageView.isIndicatingActivity = true
|
||||||
|
|
||||||
cell.bannerView.configure(for: app)
|
cell.bannerView.configure(for: app)
|
||||||
|
|
||||||
let versionDate = Date().relativeDateString(since: latestVersion.date, dateFormatter: self.dateFormatter)
|
let versionDate = Date().relativeDateString(since: latestSupportedVersion.date, dateFormatter: self.dateFormatter)
|
||||||
cell.bannerView.subtitleLabel.text = versionDate
|
cell.bannerView.subtitleLabel.text = versionDate
|
||||||
|
|
||||||
let appName: String
|
let appName: String
|
||||||
@@ -223,7 +223,7 @@ private extension MyAppsViewController
|
|||||||
appName = app.name
|
appName = app.name
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.bannerView.accessibilityLabel = String(format: NSLocalizedString("%@ %@ update. Released on %@.", comment: ""), appName, latestVersion.version, versionDate)
|
cell.bannerView.accessibilityLabel = String(format: NSLocalizedString("%@ %@ update. Released on %@.", comment: ""), appName, latestSupportedVersion.version, versionDate)
|
||||||
|
|
||||||
cell.bannerView.button.isIndicatingActivity = false
|
cell.bannerView.button.isIndicatingActivity = false
|
||||||
cell.bannerView.button.addTarget(self, action: #selector(MyAppsViewController.updateApp(_:)), for: .primaryActionTriggered)
|
cell.bannerView.button.addTarget(self, action: #selector(MyAppsViewController.updateApp(_:)), for: .primaryActionTriggered)
|
||||||
|
|||||||
@@ -392,9 +392,9 @@ extension NewsViewController
|
|||||||
let progress = AppManager.shared.installationProgress(for: storeApp)
|
let progress = AppManager.shared.installationProgress(for: storeApp)
|
||||||
footerView.bannerView.button.progress = progress
|
footerView.bannerView.button.progress = progress
|
||||||
|
|
||||||
if let versionDate = storeApp.latestVersion?.date, versionDate > Date()
|
if let versionDate = storeApp.latestSupportedVersion?.date, versionDate > Date()
|
||||||
{
|
{
|
||||||
footerView.bannerView.button.countdownDate = storeApp.versionDate
|
footerView.bannerView.button.countdownDate = versionDate
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class AppVersion: NSManagedObject, Decodable, Fetchable
|
|||||||
|
|
||||||
/* Relationships */
|
/* Relationships */
|
||||||
@NSManaged public private(set) var app: StoreApp?
|
@NSManaged public private(set) var app: StoreApp?
|
||||||
@NSManaged public private(set) var latestVersionApp: StoreApp?
|
@NSManaged @objc(latestVersionApp) public internal(set) var latestSupportedVersionApp: StoreApp?
|
||||||
|
|
||||||
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
||||||
{
|
{
|
||||||
@@ -113,4 +113,8 @@ public extension AppVersion
|
|||||||
|
|
||||||
return appVersion
|
return appVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSupported: Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ private extension DatabaseManager
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
storeApp = StoreApp.makeAltStoreApp(in: context)
|
storeApp = StoreApp.makeAltStoreApp(in: context)
|
||||||
storeApp.latestVersion?.version = localApp.version
|
storeApp.latestSupportedVersion?.version = localApp.version
|
||||||
storeApp.source = altStoreSource
|
storeApp.source = altStoreSource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ public extension InstalledApp
|
|||||||
{
|
{
|
||||||
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
||||||
fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K != nil AND %K != %K",
|
fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K != nil AND %K != %K",
|
||||||
#keyPath(InstalledApp.isActive), #keyPath(InstalledApp.storeApp), #keyPath(InstalledApp.version), #keyPath(InstalledApp.storeApp.latestVersion.version))
|
#keyPath(InstalledApp.isActive), #keyPath(InstalledApp.storeApp), #keyPath(InstalledApp.version), #keyPath(InstalledApp.storeApp.latestSupportedVersion.version))
|
||||||
return fetchRequest
|
return fetchRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ open class MergePolicy: RSTRelationshipPreservingMergePolicy
|
|||||||
let conflictingAppVersions = conflict.conflictingObjects.lazy.compactMap { $0 as? AppVersion }
|
let conflictingAppVersions = conflict.conflictingObjects.lazy.compactMap { $0 as? AppVersion }
|
||||||
|
|
||||||
// Primary AppVersion == AppVersion whose latestVersionApp.latestVersion points back to itself.
|
// Primary AppVersion == AppVersion whose latestVersionApp.latestVersion points back to itself.
|
||||||
if let primaryAppVersion = conflictingAppVersions.first(where: { $0.latestVersionApp?.latestVersion == $0 }),
|
if let primaryAppVersion = conflictingAppVersions.first(where: { $0.latestSupportedVersionApp?.latestSupportedVersion == $0 }),
|
||||||
let secondaryAppVersion = conflictingAppVersions.first(where: { $0 != primaryAppVersion })
|
let secondaryAppVersion = conflictingAppVersions.first(where: { $0 != primaryAppVersion })
|
||||||
{
|
{
|
||||||
secondaryAppVersion.managedObjectContext?.delete(secondaryAppVersion)
|
secondaryAppVersion.managedObjectContext?.delete(secondaryAppVersion)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ fileprivate extension NSManagedObject
|
|||||||
|
|
||||||
func setStoreAppLatestVersion(_ appVersion: NSManagedObject)
|
func setStoreAppLatestVersion(_ appVersion: NSManagedObject)
|
||||||
{
|
{
|
||||||
self.setValue(appVersion, forKey: #keyPath(StoreApp.latestVersion))
|
self.setValue(appVersion, forKey: #keyPath(StoreApp.latestSupportedVersion))
|
||||||
|
|
||||||
let versions = NSOrderedSet(array: [appVersion])
|
let versions = NSOrderedSet(array: [appVersion])
|
||||||
self.setValue(versions, forKey: #keyPath(StoreApp._versions))
|
self.setValue(versions, forKey: #keyPath(StoreApp._versions))
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
@NSManaged @objc(source) public var _source: Source?
|
@NSManaged @objc(source) public var _source: Source?
|
||||||
@NSManaged @objc(permissions) public var _permissions: NSOrderedSet
|
@NSManaged @objc(permissions) public var _permissions: NSOrderedSet
|
||||||
|
|
||||||
@NSManaged public private(set) var latestVersion: AppVersion?
|
@NSManaged @objc(latestVersion) public private(set) var latestSupportedVersion: AppVersion?
|
||||||
@NSManaged @objc(versions) public private(set) var _versions: NSOrderedSet
|
@NSManaged @objc(versions) public private(set) var _versions: NSOrderedSet
|
||||||
|
|
||||||
@NSManaged public private(set) var loggedErrors: NSSet /* Set<LoggedError> */ // Use NSSet to avoid eagerly fetching values.
|
@NSManaged public private(set) var loggedErrors: NSSet /* Set<LoggedError> */ // Use NSSet to avoid eagerly fetching values.
|
||||||
@@ -227,16 +227,30 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension StoreApp
|
internal extension StoreApp
|
||||||
{
|
{
|
||||||
func setVersions(_ versions: [AppVersion])
|
func setVersions(_ versions: [AppVersion])
|
||||||
{
|
{
|
||||||
guard let latestVersion = versions.first else { preconditionFailure("StoreApp must have at least one AppVersion.") }
|
|
||||||
|
|
||||||
self.latestVersion = latestVersion
|
|
||||||
self._versions = NSOrderedSet(array: versions)
|
self._versions = NSOrderedSet(array: versions)
|
||||||
|
|
||||||
|
let latestSupportedVersion = versions.first(where: { $0.isSupported })
|
||||||
|
self.latestSupportedVersion = latestSupportedVersion
|
||||||
|
|
||||||
|
for case let version as AppVersion in self._versions
|
||||||
|
{
|
||||||
|
if version == latestSupportedVersion
|
||||||
|
{
|
||||||
|
version.latestSupportedVersionApp = self
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ensure we replace any previous relationship when merging.
|
||||||
|
version.latestSupportedVersionApp = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Preserve backwards compatibility by assigning legacy property values.
|
// Preserve backwards compatibility by assigning legacy property values.
|
||||||
|
guard let latestVersion = versions.first else { preconditionFailure("StoreApp must have at least one AppVersion.") }
|
||||||
self._version = latestVersion.version
|
self._version = latestVersion.version
|
||||||
self._versionDate = latestVersion.date
|
self._versionDate = latestVersion.date
|
||||||
self._versionDescription = latestVersion.localizedDescription
|
self._versionDescription = latestVersion.localizedDescription
|
||||||
@@ -247,6 +261,10 @@ private extension StoreApp
|
|||||||
|
|
||||||
public extension StoreApp
|
public extension StoreApp
|
||||||
{
|
{
|
||||||
|
var latestAvailableVersion: AppVersion? {
|
||||||
|
return self._versions.firstObject as? AppVersion
|
||||||
|
}
|
||||||
|
|
||||||
@nonobjc class func fetchRequest() -> NSFetchRequest<StoreApp>
|
@nonobjc class func fetchRequest() -> NSFetchRequest<StoreApp>
|
||||||
{
|
{
|
||||||
return NSFetchRequest<StoreApp>(entityName: "StoreApp")
|
return NSFetchRequest<StoreApp>(entityName: "StoreApp")
|
||||||
|
|||||||
Reference in New Issue
Block a user