mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-08 22:33:26 +01:00
[AltStoreCore] Supports additional source JSON values for detailed “About” page
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21512" systemVersion="21G83" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21513" systemVersion="22D68" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Account" representedClassName="Account" syncable="YES">
|
||||
<attribute name="appleID" attributeType="String"/>
|
||||
<attribute name="firstName" attributeType="String"/>
|
||||
@@ -150,10 +150,18 @@
|
||||
</entity>
|
||||
<entity name="Source" representedClassName="Source" syncable="YES">
|
||||
<attribute name="error" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
||||
<attribute name="hasFeaturedApps" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||
<attribute name="headerImageURL" optional="YES" attributeType="URI"/>
|
||||
<attribute name="iconURL" optional="YES" attributeType="URI"/>
|
||||
<attribute name="identifier" attributeType="String"/>
|
||||
<attribute name="localizedDescription" optional="YES" attributeType="String"/>
|
||||
<attribute name="name" attributeType="String"/>
|
||||
<attribute name="sourceURL" attributeType="URI"/>
|
||||
<attribute name="subtitle" optional="YES" attributeType="String"/>
|
||||
<attribute name="tintColor" optional="YES" attributeType="Transformable" valueTransformerName="ALTSecureValueTransformer"/>
|
||||
<attribute name="websiteURL" optional="YES" attributeType="URI"/>
|
||||
<relationship name="apps" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="StoreApp" inverseName="source" inverseEntity="StoreApp"/>
|
||||
<relationship name="featuredApps" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="StoreApp" inverseName="featuringSource" inverseEntity="StoreApp"/>
|
||||
<relationship name="newsItems" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="NewsItem" inverseName="source" inverseEntity="NewsItem"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
@@ -178,6 +186,7 @@
|
||||
<attribute name="version" attributeType="String"/>
|
||||
<attribute name="versionDate" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="versionDescription" optional="YES" attributeType="String"/>
|
||||
<relationship name="featuringSource" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Source" inverseName="featuredApps" inverseEntity="Source"/>
|
||||
<relationship name="installedApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="storeApp" inverseEntity="InstalledApp"/>
|
||||
<relationship name="latestVersion" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="AppVersion" inverseName="latestVersionApp" inverseEntity="AppVersion"/>
|
||||
<relationship name="loggedErrors" toMany="YES" deletionRule="Nullify" destinationEntity="LoggedError" inverseName="storeApp" inverseEntity="LoggedError"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -144,6 +144,8 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
||||
@NSManaged public var newsItems: Set<NewsItem>
|
||||
|
||||
@NSManaged @objc(source) public var _source: Source?
|
||||
@NSManaged public internal(set) var featuringSource: Source?
|
||||
|
||||
@NSManaged @objc(permissions) public var _permissions: NSOrderedSet
|
||||
|
||||
@NSManaged @objc(latestVersion) public private(set) var latestSupportedVersion: AppVersion?
|
||||
|
||||
Reference in New Issue
Block a user