Supports both iPhone + iPad screenshots

Prefers showing screenshots for current device, but falls back to all screenshots if there are no relevant ones.
This commit is contained in:
Riley Testut
2023-10-13 13:40:08 -05:00
committed by Magesh K
parent 57059967c6
commit a49e16f591
13 changed files with 258 additions and 76 deletions

View File

@@ -8,6 +8,8 @@
import CoreData
import AltSign
public extension AppScreenshot
{
static let defaultAspectRatio = CGSize(width: 9, height: 19.5)
@@ -40,6 +42,13 @@ public class AppScreenshot: NSManagedObject, Fetchable, Decodable
@NSManaged private var width: NSNumber?
@NSManaged private var height: NSNumber?
// Defaults to .iphone
@nonobjc public var deviceType: ALTDeviceType {
get { ALTDeviceType(rawValue: Int(_deviceType)) }
set { _deviceType = Int16(newValue.rawValue) }
}
@NSManaged @objc(deviceType) private var _deviceType: Int16
@NSManaged public internal(set) var appBundleID: String
@NSManaged public internal(set) var sourceID: String
@@ -58,12 +67,13 @@ public class AppScreenshot: NSManagedObject, Fetchable, Decodable
super.init(entity: entity, insertInto: context)
}
internal init(imageURL: URL, size: CGSize?, context: NSManagedObjectContext)
internal init(imageURL: URL, size: CGSize?, deviceType: ALTDeviceType, context: NSManagedObjectContext)
{
super.init(entity: AppScreenshot.entity(), insertInto: context)
self.imageURL = imageURL
self.size = size
self.deviceType = deviceType
}
public required init(from decoder: Decoder) throws
@@ -79,6 +89,21 @@ public class AppScreenshot: NSManagedObject, Fetchable, Decodable
self.width = try container.decodeIfPresent(Int16.self, forKey: .width).map { NSNumber(value: $0) }
self.height = try container.decodeIfPresent(Int16.self, forKey: .height).map { NSNumber(value: $0) }
}
public override func awakeFromInsert()
{
super.awakeFromInsert()
self.deviceType = .iphone
}
}
extension AppScreenshot
{
var screenshotID: String {
let screenshotID = "\(self.imageURL.absoluteString)|\(self.deviceType)"
return screenshotID
}
}
public extension AppScreenshot
@@ -88,3 +113,89 @@ public extension AppScreenshot
return NSFetchRequest<AppScreenshot>(entityName: "AppScreenshot")
}
}
internal struct AppScreenshots: Decodable
{
var screenshots: [AppScreenshot] = []
enum CodingKeys: String, CodingKey
{
case iphone
case ipad
}
init(from decoder: Decoder) throws
{
let container: KeyedDecodingContainer<CodingKeys>
do
{
container = try decoder.container(keyedBy: CodingKeys.self)
}
catch DecodingError.typeMismatch
{
// ONLY catch the container's DecodingError.typeMismatch, not the below decodeIfPresent()'s
// Fallback to single array.
var collection = try Collection(from: decoder)
collection.deviceType = .iphone
self.screenshots = collection.screenshots
return
}
if var collection = try container.decodeIfPresent(Collection.self, forKey: .iphone)
{
collection.deviceType = .iphone
self.screenshots += collection.screenshots
}
if var collection = try container.decodeIfPresent(Collection.self, forKey: .ipad)
{
collection.deviceType = .ipad
self.screenshots += collection.screenshots
}
}
}
extension AppScreenshots
{
struct Collection: Decodable
{
var screenshots: [AppScreenshot] = []
var deviceType: ALTDeviceType = .iphone {
didSet {
self.screenshots.forEach { $0.deviceType = self.deviceType }
}
}
init(from decoder: Decoder) throws
{
guard let context = decoder.managedObjectContext else { preconditionFailure("Decoder must have non-nil NSManagedObjectContext.") }
var container = try decoder.unkeyedContainer()
while !container.isAtEnd
{
do
{
// Attempt parsing as URL first.
let imageURL = try container.decode(URL.self)
let screenshot = AppScreenshot(imageURL: imageURL, size: nil, deviceType: self.deviceType, context: context)
self.screenshots.append(screenshot)
}
catch DecodingError.typeMismatch
{
// Fall back to parsing full AppScreenshot (preferred).
let screenshot = try container.decode(AppScreenshot.self)
self.screenshots.append(screenshot)
}
}
}
}
}