mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
[AltStoreCore] Supports additional source JSON values for detailed “About” page
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user