mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Revises appPermissions JSON format
• Split into `entitlements` and `privacy` sections • `entitlements` is an array of entitlement keys • `privacy` is a dictionary mapping UsageDescription keys to their descriptions
This commit is contained in:
@@ -73,7 +73,7 @@ public extension ALTAppPermissionType
|
||||
}
|
||||
|
||||
@objc(AppPermission) @dynamicMemberLookup
|
||||
public class AppPermission: NSManagedObject, Decodable, Fetchable
|
||||
public class AppPermission: NSManagedObject, Fetchable
|
||||
{
|
||||
/* Properties */
|
||||
@NSManaged public var type: ALTAppPermissionType
|
||||
@@ -108,37 +108,13 @@ public class AppPermission: NSManagedObject, Decodable, Fetchable
|
||||
super.init(entity: entity, insertInto: context)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey
|
||||
convenience init(permission: String, usageDescription: String?, type: ALTAppPermissionType, context: NSManagedObjectContext)
|
||||
{
|
||||
case name
|
||||
case usageDescription
|
||||
}
|
||||
|
||||
public required init(from decoder: Decoder) throws
|
||||
{
|
||||
guard let context = decoder.managedObjectContext else { preconditionFailure("Decoder must have non-nil NSManagedObjectContext.") }
|
||||
self.init(entity: AppPermission.entity(), insertInto: context)
|
||||
|
||||
super.init(entity: AppPermission.entity(), insertInto: context)
|
||||
|
||||
do
|
||||
{
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
self._permission = try container.decode(String.self, forKey: .name)
|
||||
self.usageDescription = try container.decodeIfPresent(String.self, forKey: .usageDescription)
|
||||
|
||||
// Will be updated from StoreApp.
|
||||
self.type = .unknown
|
||||
}
|
||||
catch
|
||||
{
|
||||
if let context = self.managedObjectContext
|
||||
{
|
||||
context.delete(self)
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
self._permission = permission
|
||||
self.usageDescription = usageDescription
|
||||
self.type = type
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,3 +136,113 @@ public extension AppPermission
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AnyDecodable: Decodable
|
||||
{
|
||||
init(from decoder: Decoder) throws
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal struct AppPermissions: Decodable
|
||||
{
|
||||
var entitlements: [AppPermission] = []
|
||||
var privacy: [AppPermission] = []
|
||||
|
||||
private enum CodingKeys: String, CodingKey, Decodable
|
||||
{
|
||||
case entitlements
|
||||
case privacy
|
||||
|
||||
// Legacy
|
||||
case name
|
||||
case usageDescription
|
||||
}
|
||||
|
||||
init(from decoder: Decoder) throws
|
||||
{
|
||||
guard let context = decoder.managedObjectContext else { preconditionFailure("Decoder must have non-nil NSManagedObjectContext.") }
|
||||
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.entitlements = try self.parseEntitlements(from: container, into: context)
|
||||
self.privacy = try self.parsePrivacyPermissions(from: container, into: context)
|
||||
}
|
||||
|
||||
private func parseEntitlements(from container: KeyedDecodingContainer<CodingKeys>, into context: NSManagedObjectContext) throws -> [AppPermission]
|
||||
{
|
||||
guard container.contains(.entitlements) else { return [] }
|
||||
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
// Legacy
|
||||
// Must parse as [String: String], NOT [CodingKeys: String], to avoid incorrect DecodingError.typeMismatch error.
|
||||
let rawEntitlements = try container.decode([[String: String]].self, forKey: .entitlements)
|
||||
|
||||
let entitlements = try rawEntitlements.compactMap { (dictionary) -> AppPermission? in
|
||||
guard let name = dictionary[CodingKeys.name.rawValue] else {
|
||||
let context = DecodingError.Context(codingPath: container.codingPath, debugDescription: "Legacy entitlements must have `name` key.")
|
||||
throw DecodingError.keyNotFound(CodingKeys.name, context)
|
||||
}
|
||||
|
||||
let entitlement = AppPermission(permission: name, usageDescription: nil, type: .entitlement, context: context)
|
||||
return entitlement
|
||||
}
|
||||
|
||||
return entitlements
|
||||
}
|
||||
catch DecodingError.typeMismatch
|
||||
{
|
||||
// Detailed
|
||||
// AnyDecodable ensures we're forward-compatible with any values we may later require for entitlement permissions.
|
||||
let rawEntitlements = try container.decode([String: AnyDecodable?].self, forKey: .entitlements)
|
||||
|
||||
let entitlements = rawEntitlements.map { AppPermission(permission: $0.key, usageDescription: nil, type: .entitlement, context: context) }
|
||||
return entitlements
|
||||
}
|
||||
}
|
||||
catch DecodingError.typeMismatch
|
||||
{
|
||||
// Default
|
||||
let rawEntitlements = try container.decode([String].self, forKey: .entitlements)
|
||||
|
||||
let entitlements = rawEntitlements.map { AppPermission(permission: $0, usageDescription: nil, type: .entitlement, context: context) }
|
||||
return entitlements
|
||||
}
|
||||
}
|
||||
|
||||
private func parsePrivacyPermissions(from container: KeyedDecodingContainer<CodingKeys>, into context: NSManagedObjectContext) throws -> [AppPermission]
|
||||
{
|
||||
guard container.contains(.privacy) else { return [] }
|
||||
|
||||
do
|
||||
{
|
||||
// Legacy
|
||||
// Must parse as [String: String], NOT [CodingKeys: String], to avoid incorrect DecodingError.typeMismatch error.
|
||||
let rawPermissions = try container.decode([[String: String]].self, forKey: .privacy)
|
||||
|
||||
let permissions = try rawPermissions.compactMap { (dictionary) -> AppPermission? in
|
||||
guard let name = dictionary[CodingKeys.name.rawValue] else {
|
||||
let context = DecodingError.Context(codingPath: container.codingPath, debugDescription: "Legacy privacy permissions must have `name` key.")
|
||||
throw DecodingError.keyNotFound(CodingKeys.name, context)
|
||||
}
|
||||
|
||||
let usageDescription = dictionary[CodingKeys.usageDescription.rawValue]
|
||||
|
||||
let permission = AppPermission(permission: name, usageDescription: usageDescription, type: .privacy, context: context)
|
||||
return permission
|
||||
}
|
||||
|
||||
return permissions
|
||||
}
|
||||
catch DecodingError.typeMismatch
|
||||
{
|
||||
// Default
|
||||
let rawPermissions = try container.decode([String: String?].self, forKey: .privacy)
|
||||
|
||||
let permissions = rawPermissions.map { AppPermission(permission: $0, usageDescription: $1, type: .privacy, context: context) }
|
||||
return permissions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,6 @@ public extension StoreApp
|
||||
#endif
|
||||
|
||||
static let dolphinAppID = "me.oatmealdome.dolphinios-njb"
|
||||
|
||||
private struct AppPermissions: Decodable
|
||||
{
|
||||
var entitlements: [AppPermission]?
|
||||
var privacy: [AppPermission]?
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
@@ -299,10 +293,7 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
|
||||
|
||||
if let appPermissions = try container.decodeIfPresent(AppPermissions.self, forKey: .permissions)
|
||||
{
|
||||
appPermissions.entitlements?.forEach { $0.type = .entitlement }
|
||||
appPermissions.privacy?.forEach { $0.type = .privacy }
|
||||
|
||||
let allPermissions = (appPermissions.entitlements ?? []) + (appPermissions.privacy ?? [])
|
||||
let allPermissions = appPermissions.entitlements + appPermissions.privacy
|
||||
for permission in allPermissions
|
||||
{
|
||||
permission.appBundleID = self.bundleIdentifier
|
||||
|
||||
Reference in New Issue
Block a user