[AltStoreCore] Supports additional source JSON values for detailed “About” page

This commit is contained in:
Riley Testut
2023-04-04 13:46:04 -05:00
committed by Magesh K
parent 404bd1450b
commit bdb1d68b6b
3 changed files with 104 additions and 2 deletions

View File

@@ -176,6 +176,27 @@ public struct SourceJSON: Codable {
}
public extension Source
{
// Fallbacks for optional JSON values.
var effectiveIconURL: URL? {
return self.iconURL ?? self.apps.first?.iconURL
}
var effectiveHeaderImageURL: URL? {
return self.headerImageURL ?? self.effectiveIconURL
}
var effectiveTintColor: UIColor? {
return self.tintColor ?? self.apps.first?.tintColor
}
var effectiveFeaturedApps: [StoreApp] {
return self.featuredApps ?? self.apps
}
}
@objc(Source)
public class Source: NSManagedObject, Fetchable, Decodable
{
@@ -184,6 +205,17 @@ public class Source: NSManagedObject, Fetchable, Decodable
@NSManaged public var identifier: String
@NSManaged public var sourceURL: URL
/* Source Detail */
@NSManaged public var subtitle: String?
@NSManaged public var websiteURL: URL?
@NSManaged public var localizedDescription: String?
// Optional properties with fallbacks.
// `private` to prevent accidentally using instead of `effective[PropertyName]`
@NSManaged private var iconURL: URL?
@NSManaged private var headerImageURL: URL?
@NSManaged private var tintColor: UIColor?
@NSManaged public var error: NSError?
/* Non-Core Data Properties */
@@ -193,6 +225,9 @@ public class Source: NSManagedObject, Fetchable, Decodable
@objc(apps) @NSManaged public private(set) var _apps: NSOrderedSet
@objc(newsItems) @NSManaged public private(set) var _newsItems: NSOrderedSet
@objc(featuredApps) @NSManaged public private(set) var _featuredApps: NSOrderedSet
@objc(hasFeaturedApps) @NSManaged private var _hasFeaturedApps: Bool
@nonobjc public var apps: [StoreApp] {
get {
return self._apps.array as! [StoreApp]
@@ -211,14 +246,27 @@ public class Source: NSManagedObject, Fetchable, Decodable
}
}
// `internal` to prevent accidentally using instead of `effectiveFeaturedApps`
@nonobjc internal var featuredApps: [StoreApp]? {
return self._hasFeaturedApps ? self._featuredApps.array as? [StoreApp] : nil
}
private enum CodingKeys: String, CodingKey
{
case name
case identifier
case sourceURL
case userInfo
case subtitle
case localizedDescription = "description"
case iconURL
case headerImageURL = "headerURL"
case websiteURL = "website"
case tintColor
case apps
case news
case featuredApps
case userInfo
}
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
@@ -241,6 +289,22 @@ public class Source: NSManagedObject, Fetchable, Decodable
self.name = try container.decode(String.self, forKey: .name)
self.identifier = try container.decode(String.self, forKey: .identifier)
// Optional Values
self.subtitle = try container.decodeIfPresent(String.self, forKey: .subtitle)
self.websiteURL = try container.decodeIfPresent(URL.self, forKey: .websiteURL)
self.localizedDescription = try container.decodeIfPresent(String.self, forKey: .localizedDescription)
self.iconURL = try container.decodeIfPresent(URL.self, forKey: .iconURL)
self.headerImageURL = try container.decodeIfPresent(URL.self, forKey: .headerImageURL)
if let tintColorHex = try container.decodeIfPresent(String.self, forKey: .tintColor)
{
guard let tintColor = UIColor(hexString: tintColorHex) else {
throw DecodingError.dataCorruptedError(forKey: .tintColor, in: container, debugDescription: "Hex code is invalid.")
}
self.tintColor = tintColor
}
let userInfo = try container.decodeIfPresent([String: String].self, forKey: .userInfo)
self.userInfo = userInfo?.reduce(into: [:]) { $0[ALTSourceUserInfoKey($1.key)] = $1.value }
@@ -275,6 +339,10 @@ public class Source: NSManagedObject, Fetchable, Decodable
}
}
self._newsItems = NSMutableOrderedSet(array: newsItems)
let featuredAppBundleIDs = try container.decodeIfPresent([String].self, forKey: .featuredApps)
let featuredApps = featuredAppBundleIDs?.compactMap { appsByID[$0] }
self.setFeaturedApps(featuredApps)
}
catch
{
@@ -288,6 +356,29 @@ public class Source: NSManagedObject, Fetchable, Decodable
}
}
internal extension Source
{
func setFeaturedApps(_ featuredApps: [StoreApp]?)
{
// Explicitly update relationships for all apps to ensure featuredApps merges correctly.
for case let storeApp as StoreApp in self._apps
{
if let featuredApps, featuredApps.contains(where: { $0.bundleIdentifier == storeApp.bundleIdentifier })
{
storeApp.featuringSource = self
}
else
{
storeApp.featuringSource = nil
}
}
self._featuredApps = NSOrderedSet(array: featuredApps ?? [])
self._hasFeaturedApps = (featuredApps != nil)
}
}
public extension Source
{
@nonobjc class func fetchRequest() -> NSFetchRequest<Source>