Skip to content

Instantly share code, notes, and snippets.

@austenstrine
Last active June 25, 2019 01:56
Show Gist options
  • Save austenstrine/c559f37686596f0a0af6700b33f1c661 to your computer and use it in GitHub Desktop.
Save austenstrine/c559f37686596f0a0af6700b33f1c661 to your computer and use it in GitHub Desktop.
An API for CoreData that allows safe access and mutation of "global" variables.
// AppManagedDataController.swift
// Yah-El
//
// Created by Austen Strine on 5/20/19.
// Copyright © 2019 GoGreen. All rights reserved.
//
import CoreData
// *****************************
// * *
// * properties & init *
// * *
// *****************************
/**
Used to safely access and mutate "global" CoreData variables from any context in the app.
*/
class AppManagedDataController:Hashable, UsesAppData, UsesAppHelper, UsesAppNetwork, UsesAppDelegate, UsesPrintTrace, UsesPersistenceManager
{
//hashable
static func == (lhs: AppManagedDataController, rhs: AppManagedDataController) -> Bool
{
return lhs.hashValue == rhs.hashValue
}
var hashValue:Int
{
return uniqueContext.hashValue
}
//class name access
let classForCoder = AppManagedDataController.self
//managed object properties
private typealias SELF = AppManagedDataController
private var uniqueContext:NSManagedObjectContext
var context:NSManagedObjectContext
{
return uniqueContext
}
//new order in progress properties
private static var newOrder: NewOrder? = nil
var newOrder: NewOrder? {
get
{
return objectGet(staticObject: &SELF.newOrder)
}
set(newValue)
{
objectSet(staticObject: &SELF.newOrder, newValue: newValue)
}
}
private static var newOrderClient: Client? = nil
var newOrderClient: Client? {
get
{
return objectGet(staticObject: &SELF.newOrderClient)
}
set(newValue)
{
objectSet(staticObject: &SELF.newOrderClient, newValue: newValue)
}
}
private static var selectedBillingAddress: Address? = nil
var selectedBillingAddress: Address? {
get
{
return objectGet(staticObject: &SELF.selectedBillingAddress)
}
set(newValue)
{
objectSet(staticObject: &SELF.selectedBillingAddress, newValue: newValue)
}
}
private static var selectedShippingAddress: Address? = nil
var selectedShippingAddress: Address? {
get
{
return objectGet(staticObject: &SELF.selectedShippingAddress)
}
set(newValue)
{
objectSet(staticObject: &SELF.selectedShippingAddress, newValue: newValue)
}
}
private static var shippingAddresses: [Address] = []
var shippingAddresses:[Address]{
get
{
return collectionGet(
staticCollection: &SELF.shippingAddresses)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.shippingAddresses,
newValue: newValue)
}
}
private static var billingAddresses: [Address] = []
var billingAddresses:[Address]{
get
{
return collectionGet(
staticCollection: &SELF.billingAddresses)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.billingAddresses,
newValue: newValue)
}
}
private static var newOrderProducts: [OrderedProduct] = []
var newOrderProducts: [OrderedProduct] {
get
{
return collectionGet(
staticCollection: &SELF.newOrderProducts)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.newOrderProducts,
newValue: newValue)
}
}
//load/edit new order properties
var savedNewOrders: [NewOrder]
{
get
{
return collectionDynamicFetch(type: [NewOrder].self)
}
}
private static var queuedNewOrders: [NewOrder] = []
var queuedNewOrders: [NewOrder] {
get
{
return collectionGet(
staticCollection: &SELF.queuedNewOrders)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.queuedNewOrders,
newValue: newValue)
}
}
//order search and view properties
private static var orderSearchResults: [Order] = []
var orderSearchResults: [Order] {
get
{
return collectionGet(
staticCollection: &SELF.orderSearchResults)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.orderSearchResults,
newValue: newValue)
}
}
private static var recentlySubmittedOrders: [Order] = []
var recentlySubmittedOrders: [Order] {
get
{
return collectionGet(
staticCollection: &SELF.recentlySubmittedOrders)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.recentlySubmittedOrders,
newValue: newValue)
}
}
private static var viewOrder: Order? = nil
var viewOrder: Order? {
get
{
return objectGet(staticObject: &SELF.viewOrder)
}
set(newValue)
{
objectSet(staticObject: &SELF.viewOrder, newValue: newValue)
}
}
private static var viewChildOrders: [Order] = []
var viewChildOrders: [Order] {
get
{
return collectionGet(
staticCollection: &SELF.viewChildOrders)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.viewChildOrders,
newValue: newValue)
}
}
//client create, search, and view properties
private static var queuedNewClients: [CreateClient] = []
var queuedNewClients: [CreateClient] {
get
{
return collectionGet(
staticCollection: &SELF.queuedNewClients)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.queuedNewClients,
newValue: newValue)
}
}
private static var editCreateClient: CreateClient? = nil
var editCreateClient: CreateClient? {
get
{
return objectGet(staticObject: &SELF.editCreateClient)
}
set(newValue)
{
objectSet(staticObject: &SELF.editCreateClient, newValue: newValue)
}
}
private static var clientSearchResults: [Client] = []
var clientSearchResults: [Client] {
get
{
return collectionGet(
staticCollection: &SELF.clientSearchResults)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.clientSearchResults,
newValue: newValue)
}
}
private static var viewClient: Client? = nil
var viewClient: Client? {
get
{
return objectGet(staticObject: &SELF.viewClient)
}
set(newValue)
{
objectSet(staticObject: &SELF.viewClient, newValue: newValue)
}
}
//product search and view properties
private static var storedProductSearchResults: [StoredProduct] = []
var storedProductSearchResults: [StoredProduct] {
get
{
return collectionGet(
staticCollection: &SELF.storedProductSearchResults)
}
set(newValue)
{
collectionSet(
staticCollection: &SELF.storedProductSearchResults,
newValue: newValue)
}
}
private static var viewStoredProduct: StoredProduct? = nil
var viewStoredProduct: StoredProduct? {
get
{
return objectGet(staticObject: &SELF.viewStoredProduct)
}
set(newValue)
{
objectSet(staticObject: &SELF.viewStoredProduct, newValue: newValue)
}
}
//initializer
init(context cntxt: NSManagedObjectContext)
{
uniqueContext = cntxt
}
}
// *****************************
// * *
// * private funcs *
// * *
// *****************************
extension AppManagedDataController
{
private func collectionGet<T:NSManagedObject>(staticCollection:inout [T]) -> [T]
{
persistenceManager.saveLock.lock()
defer{persistenceManager.saveLock.unlock()}
for (index, item) in staticCollection.enumerated().reversed()
{
if item.isDeleted
{
staticCollection.remove(at: index)
}
}
var freshCollection = [T]()
for object in staticCollection
{
let id = object.objectID
let freshObject = try! context.existingObject(with: id) as! T
context.refresh(freshObject, mergeChanges: false)
freshCollection.append(freshObject)
}
staticCollection = freshCollection
return staticCollection
}
private func collectionSet<T:NSManagedObject>(staticCollection:inout [T], newValue newCollection:[T])
{
//persistenceManager.saveLock.lock()
//defer{persistenceManager.saveLock.unlock()}
ensureWriteContext("\(type(of:staticCollection))")
staticCollection = newCollection
persistenceManager.save(context)
}
private func collectionDynamicFetch<T:NSManagedObject>(type: [T].Type) -> [T]
{
persistenceManager.saveLock.lock()
defer{persistenceManager.saveLock.unlock()}
let placeholder:[T] = try! PersistenceManager.shared.fetch(T.self, context:context)
return placeholder
}
private func objectGet<T:NSManagedObject>(staticObject:inout T?) -> T?
{
persistenceManager.saveLock.lock()
defer{persistenceManager.saveLock.unlock()}
if staticObject.exists
{
if staticObject!.isDeleted
{
staticObject = nil
return nil
}
let id = staticObject!.objectID
staticObject = try! context.existingObject(with: id) as! T
context.refresh(staticObject!, mergeChanges: false)
}
return staticObject
}
private func objectSet<T:NSManagedObject>(staticObject:inout T?, newValue newObject:T?)
{
//persistenceManager.saveLock.lock()
//defer{persistenceManager.saveLock.unlock()}
ensureWriteContext("\(T.self)")
if newObject == nil
{
staticObject = nil
return
}
staticObject = newObject
persistenceManager.save(context)
}
fileprivate func ensureWriteContext(_ type:String)
{
guard context != persistenceManager.readContext else
{
fatalError("Illegal write on the read context for \(type)!")
}
}
fileprivate func ensureWriteContext()
{
guard context != persistenceManager.readContext else
{
fatalError("Illegal write on the read context!")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment