mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Supports new “versions” key in source JSON
Allows sources to list multiple versions of an app. Preserves backwards compatibility by assigning legacy version values when assigning AppVersions.
This commit is contained in:
committed by
Joseph Mattello
parent
5765cb8330
commit
fa160124d2
@@ -80,10 +80,21 @@ 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
|
||||||
self.versionDescriptionTextView.text = self.app.versionDescription
|
|
||||||
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), self.app.version)
|
if let version = self.app.latestVersion
|
||||||
self.versionDateLabel.text = Date().relativeDateString(since: self.app.versionDate, dateFormatter: self.dateFormatter)
|
{
|
||||||
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: Int64(self.app.size))
|
self.versionDescriptionTextView.text = version.localizedDescription
|
||||||
|
self.versionLabel.text = String(format: NSLocalizedString("Version %@", comment: ""), version.version)
|
||||||
|
self.versionDateLabel.text = Date().relativeDateString(since: version.date, dateFormatter: self.dateFormatter)
|
||||||
|
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: version.size)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.versionDescriptionTextView.text = nil
|
||||||
|
self.versionLabel.text = nil
|
||||||
|
self.versionDateLabel.text = nil
|
||||||
|
self.sizeLabel.text = self.byteCountFormatter.string(fromByteCount: 0)
|
||||||
|
}
|
||||||
|
|
||||||
self.descriptionTextView.maximumNumberOfLines = 5
|
self.descriptionTextView.maximumNumberOfLines = 5
|
||||||
self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
|
self.descriptionTextView.moreButton.addTarget(self, action: #selector(AppContentViewController.toggleCollapsingSection(_:)), for: .primaryActionTriggered)
|
||||||
|
|||||||
@@ -384,10 +384,10 @@ private extension AppViewController
|
|||||||
button.progress = progress
|
button.progress = progress
|
||||||
}
|
}
|
||||||
|
|
||||||
if Date() < self.app.versionDate
|
if let versionDate = self.app.latestVersion?.date, versionDate > Date()
|
||||||
{
|
{
|
||||||
self.bannerView.button.countdownDate = self.app.versionDate
|
self.bannerView.button.countdownDate = versionDate
|
||||||
self.navigationBarDownloadButton.countdownDate = self.app.versionDate
|
self.navigationBarDownloadButton.countdownDate = versionDate
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -380,11 +380,11 @@ 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 else { continue }
|
guard let storeApp = update.storeApp, let version = storeApp.version else { continue }
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = NSLocalizedString("New Update Available", comment: "")
|
content.title = NSLocalizedString("New Update Available", comment: "")
|
||||||
content.body = String(format: NSLocalizedString("%@ %@ is now available for download.", comment: ""), update.name, storeApp.version)
|
content.body = String(format: NSLocalizedString("%@ %@ is now available for download.", comment: ""), update.name, version)
|
||||||
content.sound = .default
|
content.sound = .default
|
||||||
|
|
||||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ 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 Date() < app.versionDate
|
if let versionDate = app.latestVersion?.date, versionDate > Date()
|
||||||
{
|
{
|
||||||
cell.bannerView.button.countdownDate = app.versionDate
|
cell.bannerView.button.countdownDate = app.versionDate
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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?.versionDate, ascending: true),
|
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.storeApp?.latestVersion?.date, ascending: true),
|
||||||
NSSortDescriptor(keyPath: \InstalledApp.name, ascending: true)]
|
NSSortDescriptor(keyPath: \InstalledApp.name, ascending: true)]
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
|
||||||
@@ -195,7 +195,7 @@ 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 else { return }
|
guard let app = installedApp.storeApp, let latestVersion = app.latestVersion 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
|
||||||
@@ -209,7 +209,7 @@ private extension MyAppsViewController
|
|||||||
|
|
||||||
cell.bannerView.configure(for: app)
|
cell.bannerView.configure(for: app)
|
||||||
|
|
||||||
let versionDate = Date().relativeDateString(since: app.versionDate, dateFormatter: self.dateFormatter)
|
let versionDate = Date().relativeDateString(since: latestVersion.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, app.version, versionDate)
|
cell.bannerView.accessibilityLabel = String(format: NSLocalizedString("%@ %@ update. Released on %@.", comment: ""), appName, latestVersion.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)
|
||||||
|
|||||||
@@ -391,7 +391,7 @@ 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 Date() < storeApp.versionDate
|
if let versionDate = storeApp.latestVersion?.date, versionDate > Date()
|
||||||
{
|
{
|
||||||
footerView.bannerView.button.countdownDate = storeApp.versionDate
|
footerView.bannerView.button.countdownDate = storeApp.versionDate
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ public class AppVersion: NSManagedObject, Decodable, Fetchable
|
|||||||
super.init(entity: entity, insertInto: context)
|
super.init(entity: entity, insertInto: context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey
|
private enum CodingKeys: String, CodingKey
|
||||||
{
|
{
|
||||||
case version
|
case version
|
||||||
@@ -55,31 +54,34 @@ public class AppVersion: NSManagedObject, Decodable, Fetchable
|
|||||||
case localizedDescription
|
case localizedDescription
|
||||||
case downloadURL
|
case downloadURL
|
||||||
case size
|
case size
|
||||||
case _minOSVersion
|
|
||||||
case _maxOSVersion
|
|
||||||
case appBundleID
|
|
||||||
case sourceID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(from decoder: Decoder) throws
|
public required init(from decoder: Decoder) throws
|
||||||
{
|
{
|
||||||
guard let context = decoder.managedObjectContext else { preconditionFailure("Decoder must have non-nil NSManagedObjectContext.") }
|
guard let context = decoder.managedObjectContext else { preconditionFailure("Decoder must have non-nil NSManagedObjectContext.") }
|
||||||
|
|
||||||
super.init(entity: NewsItem.entity(), insertInto: context)
|
super.init(entity: AppVersion.entity(), insertInto: context)
|
||||||
|
|
||||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
do
|
||||||
self.version = try container.decode(String.self, forKey: .version)
|
{
|
||||||
self.date = try container.decode(Date.self, forKey: .date)
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
self.localizedDescription = try container.decodeIfPresent(String.self, forKey: .localizedDescription)
|
self.version = try container.decode(String.self, forKey: .version)
|
||||||
self.downloadURL = try container.decode(URL.self, forKey: .downloadURL)
|
self.date = try container.decode(Date.self, forKey: .date)
|
||||||
self.size = try container.decode(Int64.self, forKey: .size)
|
self.localizedDescription = try container.decodeIfPresent(String.self, forKey: .localizedDescription)
|
||||||
|
|
||||||
self._minOSVersion = try container.decodeIfPresent(String.self, forKey: ._minOSVersion)
|
self.downloadURL = try container.decode(URL.self, forKey: .downloadURL)
|
||||||
self._maxOSVersion = try container.decodeIfPresent(String.self, forKey: ._maxOSVersion)
|
self.size = try container.decode(Int64.self, forKey: .size)
|
||||||
|
}
|
||||||
self.appBundleID = try container.decode(String.self, forKey: .appBundleID)
|
catch
|
||||||
self.sourceID = try container.decodeIfPresent(String.self, forKey: .sourceID)
|
{
|
||||||
|
if let context = self.managedObjectContext
|
||||||
|
{
|
||||||
|
context.delete(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,4 +91,26 @@ public extension AppVersion
|
|||||||
{
|
{
|
||||||
return NSFetchRequest<AppVersion>(entityName: "AppVersion")
|
return NSFetchRequest<AppVersion>(entityName: "AppVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class func makeAppVersion(
|
||||||
|
version: String,
|
||||||
|
date: Date,
|
||||||
|
localizedDescription: String? = nil,
|
||||||
|
downloadURL: URL,
|
||||||
|
size: Int64,
|
||||||
|
appBundleID: String,
|
||||||
|
sourceID: String? = nil,
|
||||||
|
in context: NSManagedObjectContext) -> AppVersion
|
||||||
|
{
|
||||||
|
let appVersion = AppVersion(context: context)
|
||||||
|
appVersion.version = version
|
||||||
|
appVersion.date = date
|
||||||
|
appVersion.localizedDescription = localizedDescription
|
||||||
|
appVersion.downloadURL = downloadURL
|
||||||
|
appVersion.size = size
|
||||||
|
appVersion.appBundleID = appBundleID
|
||||||
|
appVersion.sourceID = sourceID
|
||||||
|
|
||||||
|
return appVersion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ private extension DatabaseManager
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
storeApp = StoreApp.makeAltStoreApp(in: context)
|
storeApp = StoreApp.makeAltStoreApp(in: context)
|
||||||
storeApp.version = localApp.version
|
storeApp.latestVersion?.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.version))
|
#keyPath(InstalledApp.isActive), #keyPath(InstalledApp.storeApp), #keyPath(InstalledApp.version), #keyPath(InstalledApp.storeApp.latestVersion.version))
|
||||||
return fetchRequest
|
return fetchRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ open class MergePolicy: RSTRelationshipPreservingMergePolicy
|
|||||||
{
|
{
|
||||||
permission.managedObjectContext?.delete(permission)
|
permission.managedObjectContext?.delete(permission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete previous versions (different than below).
|
||||||
|
for case let appVersion as AppVersion in previousApp._versions where appVersion.app == nil
|
||||||
|
{
|
||||||
|
appVersion.managedObjectContext?.delete(appVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -55,6 +61,16 @@ open class MergePolicy: RSTRelationshipPreservingMergePolicy
|
|||||||
permission.managedObjectContext?.delete(permission)
|
permission.managedObjectContext?.delete(permission)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let contextApp = conflict.conflictingObjects.first as? StoreApp
|
||||||
|
{
|
||||||
|
let contextVersions = Set(contextApp._versions.lazy.compactMap { $0 as? AppVersion }.map { $0.version })
|
||||||
|
for case let appVersion as AppVersion in databaseObject._versions where !contextVersions.contains(appVersion.version)
|
||||||
|
{
|
||||||
|
print("[ALTLog] Deleting cached app version: \(appVersion.appBundleID + "_" + appVersion.version), not in:", contextApp.versions.map { $0.appBundleID + "_" + $0.version })
|
||||||
|
appVersion.managedObjectContext?.delete(appVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case let databaseObject as Source:
|
case let databaseObject as Source:
|
||||||
guard let conflictedObject = conflict.conflictingObjects.first as? Source else { break }
|
guard let conflictedObject = conflict.conflictingObjects.first as? Source else { break }
|
||||||
|
|
||||||
|
|||||||
@@ -103,22 +103,41 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
|
|
||||||
@NSManaged public private(set) var developerName: String
|
@NSManaged public private(set) var developerName: String
|
||||||
@NSManaged public private(set) var localizedDescription: String
|
@NSManaged public private(set) var localizedDescription: String
|
||||||
@NSManaged public private(set) var size: Int32
|
@NSManaged @objc(size) private var _size: Int32
|
||||||
|
|
||||||
@NSManaged public private(set) var iconURL: URL
|
@NSManaged public private(set) var iconURL: URL
|
||||||
@NSManaged public private(set) var screenshotURLs: [URL]
|
@NSManaged public private(set) var screenshotURLs: [URL]
|
||||||
|
|
||||||
@NSManaged public var version: String
|
@NSManaged @objc(version) private var _version: String
|
||||||
@NSManaged public private(set) var versionDate: Date
|
@NSManaged @objc(versionDate) private var _versionDate: Date
|
||||||
@NSManaged public private(set) var versionDescription: String?
|
@NSManaged @objc(versionDescription) private var _versionDescription: String?
|
||||||
|
|
||||||
@NSManaged public private(set) var downloadURL: URL
|
@NSManaged @objc(downloadURL) private var _downloadURL: URL
|
||||||
@NSManaged public private(set) var platformURLs: PlatformURLs?
|
@NSManaged public private(set) var platformURLs: PlatformURLs?
|
||||||
|
|
||||||
@NSManaged public private(set) var tintColor: UIColor?
|
@NSManaged public private(set) var tintColor: UIColor?
|
||||||
@NSManaged public private(set) var isBeta: Bool
|
@NSManaged public private(set) var isBeta: Bool
|
||||||
|
|
||||||
@NSManaged public var sourceIdentifier: String?
|
@objc public internal(set) var sourceIdentifier: String? {
|
||||||
|
get {
|
||||||
|
self.willAccessValue(forKey: #keyPath(sourceIdentifier))
|
||||||
|
defer { self.didAccessValue(forKey: #keyPath(sourceIdentifier)) }
|
||||||
|
|
||||||
|
let sourceIdentifier = self.primitiveSourceIdentifier
|
||||||
|
return sourceIdentifier
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self.willChangeValue(forKey: #keyPath(sourceIdentifier))
|
||||||
|
self.primitiveSourceIdentifier = newValue
|
||||||
|
self.didChangeValue(forKey: #keyPath(sourceIdentifier))
|
||||||
|
|
||||||
|
for version in self.versions
|
||||||
|
{
|
||||||
|
version.sourceID = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@NSManaged private var primitiveSourceIdentifier: String?
|
||||||
|
|
||||||
@NSManaged public var sortIndex: Int32
|
@NSManaged public var sortIndex: Int32
|
||||||
|
|
||||||
@@ -152,6 +171,31 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
return self._versions.array as! [AppVersion]
|
return self._versions.array as! [AppVersion]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@nonobjc public var size: Int64? {
|
||||||
|
guard let version = self.latestVersion else { return nil }
|
||||||
|
return version.size
|
||||||
|
}
|
||||||
|
|
||||||
|
@nonobjc public var version: String? {
|
||||||
|
guard let version = self.latestVersion else { return nil }
|
||||||
|
return version.version
|
||||||
|
}
|
||||||
|
|
||||||
|
@nonobjc public var versionDescription: String? {
|
||||||
|
guard let version = self.latestVersion else { return nil }
|
||||||
|
return version.localizedDescription
|
||||||
|
}
|
||||||
|
|
||||||
|
@nonobjc public var versionDate: Date? {
|
||||||
|
guard let version = self.latestVersion else { return nil }
|
||||||
|
return version.date
|
||||||
|
}
|
||||||
|
|
||||||
|
@nonobjc public var downloadURL: URL? {
|
||||||
|
guard let version = self.latestVersion else { return nil }
|
||||||
|
return version.downloadURL
|
||||||
|
}
|
||||||
|
|
||||||
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
||||||
{
|
{
|
||||||
super.init(entity: entity, insertInto: context)
|
super.init(entity: entity, insertInto: context)
|
||||||
@@ -175,6 +219,7 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
case permissions
|
case permissions
|
||||||
case size
|
case size
|
||||||
case isBeta = "beta"
|
case isBeta = "beta"
|
||||||
|
case versions
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(from decoder: Decoder) throws
|
public required init(from decoder: Decoder) throws
|
||||||
@@ -194,10 +239,6 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
|
|
||||||
self.subtitle = try container.decodeIfPresent(String.self, forKey: .subtitle)
|
self.subtitle = try container.decodeIfPresent(String.self, forKey: .subtitle)
|
||||||
|
|
||||||
self.version = try container.decode(String.self, forKey: .version)
|
|
||||||
self.versionDate = try container.decode(Date.self, forKey: .versionDate)
|
|
||||||
self.versionDescription = try container.decodeIfPresent(String.self, forKey: .versionDescription)
|
|
||||||
|
|
||||||
self.iconURL = try container.decode(URL.self, forKey: .iconURL)
|
self.iconURL = try container.decode(URL.self, forKey: .iconURL)
|
||||||
self.screenshotURLs = try container.decodeIfPresent([URL].self, forKey: .screenshotURLs) ?? []
|
self.screenshotURLs = try container.decodeIfPresent([URL].self, forKey: .screenshotURLs) ?? []
|
||||||
|
|
||||||
@@ -207,14 +248,14 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
self.platformURLs = platformURLs
|
self.platformURLs = platformURLs
|
||||||
// Backwards compatibility, use the fiirst (iOS will be first since sorted that way)
|
// Backwards compatibility, use the fiirst (iOS will be first since sorted that way)
|
||||||
if let first = platformURLs.sorted().first {
|
if let first = platformURLs.sorted().first {
|
||||||
self.downloadURL = first.downloadURL
|
self._downloadURL = first.downloadURL
|
||||||
} else {
|
} else {
|
||||||
throw DecodingError.dataCorruptedError(forKey: .platformURLs, in: container, debugDescription: "platformURLs has no entries")
|
throw DecodingError.dataCorruptedError(forKey: .platformURLs, in: container, debugDescription: "platformURLs has no entries")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if let downloadURL = downloadURL {
|
} else if let downloadURL = downloadURL {
|
||||||
self.downloadURL = downloadURL
|
self._downloadURL = downloadURL
|
||||||
} else {
|
} else {
|
||||||
throw DecodingError.dataCorruptedError(forKey: .downloadURL, in: container, debugDescription: "E downloadURL:String or downloadURLs:[[Platform:URL]] key required.")
|
throw DecodingError.dataCorruptedError(forKey: .downloadURL, in: container, debugDescription: "E downloadURL:String or downloadURLs:[[Platform:URL]] key required.")
|
||||||
}
|
}
|
||||||
@@ -228,11 +269,40 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
self.tintColor = tintColor
|
self.tintColor = tintColor
|
||||||
}
|
}
|
||||||
|
|
||||||
self.size = try container.decode(Int32.self, forKey: .size)
|
|
||||||
self.isBeta = try container.decodeIfPresent(Bool.self, forKey: .isBeta) ?? false
|
self.isBeta = try container.decodeIfPresent(Bool.self, forKey: .isBeta) ?? false
|
||||||
|
|
||||||
let permissions = try container.decodeIfPresent([AppPermission].self, forKey: .permissions) ?? []
|
let permissions = try container.decodeIfPresent([AppPermission].self, forKey: .permissions) ?? []
|
||||||
self._permissions = NSOrderedSet(array: permissions)
|
self._permissions = NSOrderedSet(array: permissions)
|
||||||
|
|
||||||
|
if let versions = try container.decodeIfPresent([AppVersion].self, forKey: .versions)
|
||||||
|
{
|
||||||
|
//TODO: Throw error if there isn't at least one version.
|
||||||
|
|
||||||
|
for version in versions
|
||||||
|
{
|
||||||
|
version.appBundleID = self.bundleIdentifier
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setVersions(versions)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let version = try container.decode(String.self, forKey: .version)
|
||||||
|
let versionDate = try container.decode(Date.self, forKey: .versionDate)
|
||||||
|
let versionDescription = try container.decodeIfPresent(String.self, forKey: .versionDescription)
|
||||||
|
|
||||||
|
let downloadURL = try container.decode(URL.self, forKey: .downloadURL)
|
||||||
|
let size = try container.decode(Int32.self, forKey: .size)
|
||||||
|
|
||||||
|
let appVersion = AppVersion.makeAppVersion(version: version,
|
||||||
|
date: versionDate,
|
||||||
|
localizedDescription: versionDescription,
|
||||||
|
downloadURL: downloadURL,
|
||||||
|
size: Int64(size),
|
||||||
|
appBundleID: self.bundleIdentifier,
|
||||||
|
in: context)
|
||||||
|
self.setVersions([appVersion])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -246,6 +316,24 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extension StoreApp
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Preserve backwards compatibility by assigning legacy property values.
|
||||||
|
self._version = latestVersion.version
|
||||||
|
self._versionDate = latestVersion.date
|
||||||
|
self._versionDescription = latestVersion.localizedDescription
|
||||||
|
self._downloadURL = latestVersion.downloadURL
|
||||||
|
self._size = Int32(latestVersion.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public extension StoreApp
|
public extension StoreApp
|
||||||
{
|
{
|
||||||
@nonobjc class func fetchRequest() -> NSFetchRequest<StoreApp>
|
@nonobjc class func fetchRequest() -> NSFetchRequest<StoreApp>
|
||||||
@@ -262,9 +350,16 @@ public extension StoreApp
|
|||||||
app.localizedDescription = "SideStore is an alternative App Store."
|
app.localizedDescription = "SideStore is an alternative App Store."
|
||||||
app.iconURL = URL(string: "https://user-images.githubusercontent.com/705880/63392210-540c5980-c37b-11e9-968c-8742fc68ab2e.png")!
|
app.iconURL = URL(string: "https://user-images.githubusercontent.com/705880/63392210-540c5980-c37b-11e9-968c-8742fc68ab2e.png")!
|
||||||
app.screenshotURLs = []
|
app.screenshotURLs = []
|
||||||
app.version = "1.0"
|
app.sourceIdentifier = Source.altStoreIdentifier
|
||||||
app.versionDate = Date()
|
|
||||||
app.downloadURL = URL(string: "http://rileytestut.com")!
|
let appVersion = AppVersion.makeAppVersion(version: "1.0",
|
||||||
|
date: Date(),
|
||||||
|
downloadURL: URL(string: "http://rileytestut.com")!,
|
||||||
|
size: 0,
|
||||||
|
appBundleID: app.bundleIdentifier,
|
||||||
|
sourceID: Source.altStoreIdentifier,
|
||||||
|
in: context)
|
||||||
|
app.setVersions([appVersion])
|
||||||
|
|
||||||
#if BETA
|
#if BETA
|
||||||
app.isBeta = true
|
app.isBeta = true
|
||||||
|
|||||||
Reference in New Issue
Block a user