Add LazilyDecoding for CoreData embedded objects

This commit is contained in:
Shadowfacts 2020-04-11 15:33:39 -04:00
parent 2a419eb87c
commit 7deb4fc5b4
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
8 changed files with 96 additions and 10 deletions

View File

@ -8,7 +8,7 @@
import Foundation
public class Attachment: Decodable {
public class Attachment: Codable {
public let id: String
public let kind: Kind
public let url: URL
@ -58,7 +58,7 @@ public class Attachment: Decodable {
}
extension Attachment {
public enum Kind: String, Decodable {
public enum Kind: String, Codable {
case image
case video
case gifv
@ -68,7 +68,7 @@ extension Attachment {
}
extension Attachment {
public class Metadata: Decodable {
public class Metadata: Codable {
public let length: String?
public let duration: Float?
public let audioEncoding: String?
@ -99,7 +99,7 @@ extension Attachment {
}
}
public class ImageMetadata: Decodable {
public class ImageMetadata: Codable {
public let width: Int?
public let height: Int?
public let size: String?

View File

@ -8,7 +8,7 @@
import Foundation
public class Emoji: Decodable {
public class Emoji: Codable {
public let shortcode: String
public let url: URL
public let staticURL: URL

View File

@ -8,7 +8,7 @@
import Foundation
public class Mention: Decodable {
public class Mention: Codable {
public let url: URL
public let username: String
public let acct: String

View File

@ -25,6 +25,7 @@
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60D2B8123844C71001B87A3 /* BaseStatusTableViewCell.swift */; };
D60E2F272442372B005F8713 /* StatusMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60E2F232442372B005F8713 /* StatusMO.swift */; };
D60E2F292442372B005F8713 /* AccountMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60E2F252442372B005F8713 /* AccountMO.swift */; };
D60E2F2C24423EAD005F8713 /* LazilyDecoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */; };
D60E2F2E244248BF005F8713 /* MastodonCachePersistentStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60E2F2D244248BF005F8713 /* MastodonCachePersistentStore.swift */; };
D61099B42144B0CC00432DC2 /* Pachyderm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */; };
D61099BB2144B0CC00432DC2 /* PachydermTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61099BA2144B0CC00432DC2 /* PachydermTests.swift */; };
@ -1236,6 +1237,7 @@
D6028B9A2150811100F223B9 /* MastodonCache.swift */,
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */,
D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */,
D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */,
D6F1F84E2193B9BE00F5FE67 /* Caching */,
D6757A7A2157E00100721E32 /* XCallbackURL */,
D62D241E217AA46B005076CC /* Shortcuts */,
@ -1664,6 +1666,7 @@
D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */,
D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */,
D60E2F272442372B005F8713 /* StatusMO.swift in Sources */,
D60E2F2C24423EAD005F8713 /* LazilyDecoding.swift in Sources */,
D62D2424217ABF3F005076CC /* NSUserActivity+Extensions.swift in Sources */,
D646C958213B367000269FB5 /* LargeImageShrinkAnimationController.swift in Sources */,
D6A3BC852321F6C100FD64D5 /* AccountListTableViewController.swift in Sources */,

View File

@ -9,6 +9,7 @@
import Foundation
import CoreData
import Pachyderm
@objc(AccountMO)
public class AccountMO: NSManagedObject {
@ -22,8 +23,8 @@ public class AccountMO: NSManagedObject {
@NSManaged public var bot: Bool
@NSManaged public var createdAt: Date?
@NSManaged public var displayName: String?
@NSManaged public var emojis: Data?
@NSManaged public var fields: Data?
@NSManaged public var emojisData: Data?
@NSManaged public var fieldsData: Data?
@NSManaged public var followersCount: Int64
@NSManaged public var followingCount: Int64
@NSManaged public var header: URL?
@ -36,4 +37,10 @@ public class AccountMO: NSManagedObject {
@NSManaged public var username: String?
@NSManaged public var movedTo: AccountMO?
@LazilyDecoding(arrayFrom: \AccountMO.emojisData)
var emojis: [Emoji]
@LazilyDecoding(arrayFrom: \AccountMO.fieldsData)
var fields: [Account.Field]
}

View File

@ -9,6 +9,7 @@
import Foundation
import CoreData
import Pachyderm
@objc(StatusMO)
public final class StatusMO: NSManagedObject {
@ -41,4 +42,16 @@ public final class StatusMO: NSManagedObject {
@NSManaged public var inReplyToAccount: AccountMO?
@NSManaged public var reblog: StatusMO?
@LazilyDecoding(arrayFrom: \StatusMO.attachmentsData)
var attachments: [Attachment]
@LazilyDecoding(arrayFrom: \StatusMO.emojisData)
var emoji: [Emoji]
@LazilyDecoding(arrayFrom: \StatusMO.hashtagsData)
var hashtags: [Hashtag]
@LazilyDecoding(arrayFrom: \StatusMO.mentionsData)
var mentions: [Mention]
}

View File

@ -6,8 +6,8 @@
<attribute name="bot" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="displayName" attributeType="String"/>
<attribute name="emojis" attributeType="Binary"/>
<attribute name="fields" optional="YES" attributeType="Binary"/>
<attribute name="emojisData" attributeType="Binary"/>
<attribute name="fieldsData" optional="YES" attributeType="Binary"/>
<attribute name="followersCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="followingCount" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="header" attributeType="URI"/>

View File

@ -0,0 +1,63 @@
//
// LazyDecoding.swift
// Tusker
//
// Created by Shadowfacts on 4/11/20.
// Copyright © 2020 Shadowfacts. All rights reserved.
//
import Foundation
private let decoder = PropertyListDecoder()
private let encoder = PropertyListEncoder()
@propertyWrapper
struct LazilyDecoding<Enclosing, Value: Codable> {
private let keyPath: ReferenceWritableKeyPath<Enclosing, Data?>
private let fallback: Value
private var value: Value?
init(from keyPath: ReferenceWritableKeyPath<Enclosing, Data?>, fallback: Value) {
self.keyPath = keyPath
self.fallback = fallback
}
var wrappedValue: Value {
get { fatalError("called LazilyDecoding wrappedValue getter") }
set { fatalError("called LazilyDecoding wrappedValue setter") }
}
static subscript(_enclosingInstance instance: Enclosing, wrapped wrappedKeyPath: ReferenceWritableKeyPath<Enclosing, Value>, storage storageKeyPath: ReferenceWritableKeyPath<Enclosing, Self>) -> Value {
get {
var wrapper = instance[keyPath: storageKeyPath]
if let value = wrapper.value {
return value
} else {
guard let data = instance[keyPath: wrapper.keyPath] else { return wrapper.fallback }
do {
let value = try decoder.decode(Value.self, from: data)
wrapper.value = value
instance[keyPath: storageKeyPath] = wrapper
return value
} catch {
return wrapper.fallback
}
}
}
set {
var wrapper = instance[keyPath: storageKeyPath]
wrapper.value = newValue
instance[keyPath: storageKeyPath] = wrapper
let newData = try? encoder.encode(newValue)
instance[keyPath: wrapper.keyPath] = newData
}
}
}
extension LazilyDecoding {
init(arrayFrom keyPath: ReferenceWritableKeyPath<Enclosing, Data?>) {
self.init(from: keyPath, fallback: [] as! Value)
}
}