Changed GameLibraryView and GameDetailView to new swiftui core data best practise. Fixed Import/Export of Library

This commit is contained in:
2021-05-18 17:29:23 +02:00
parent 73ced9a452
commit 29c8101d22
11 changed files with 138 additions and 313 deletions

View File

@@ -12,7 +12,6 @@
B93C1B9D21496BFD0014FD6E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B93C1B9C21496BFD0014FD6E /* AppDelegate.swift */; };
B93C1BA421496BFE0014FD6E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B93C1BA321496BFE0014FD6E /* Assets.xcassets */; };
B93D60CE22D88F5700DD390F /* AccessoryDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B93D60CD22D88F5700DD390F /* AccessoryDetailView.swift */; };
B93D60D122E5009700DD390F /* GameViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B93D60D022E5009700DD390F /* GameViewModel.swift */; };
B94112DE233A37DD00159AE4 /* DateConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94112DD233A37DD00159AE4 /* DateConversion.swift */; };
B94112E0233A4EF800159AE4 /* GamePickupsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94112DF233A4EF800159AE4 /* GamePickupsView.swift */; };
B94112E4233B597D00159AE4 /* ConsoleEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94112E3233B597D00159AE4 /* ConsoleEditView.swift */; };
@@ -92,7 +91,6 @@
B93C1BA321496BFE0014FD6E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
B93C1BA821496BFE0014FD6E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
B93D60CD22D88F5700DD390F /* AccessoryDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessoryDetailView.swift; sourceTree = "<group>"; };
B93D60D022E5009700DD390F /* GameViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameViewModel.swift; sourceTree = "<group>"; };
B94112DD233A37DD00159AE4 /* DateConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateConversion.swift; sourceTree = "<group>"; };
B94112DF233A4EF800159AE4 /* GamePickupsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GamePickupsView.swift; sourceTree = "<group>"; };
B94112E3233B597D00159AE4 /* ConsoleEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleEditView.swift; sourceTree = "<group>"; };
@@ -249,7 +247,6 @@
B93D60CF22E5006F00DD390F /* ViewModel */ = {
isa = PBXGroup;
children = (
B93D60D022E5009700DD390F /* GameViewModel.swift */,
B9E2A078233B69D400EAEB14 /* GameSeriesViewModel.swift */,
B9D2C6F622E98ED800797F67 /* AccessoryViewModel.swift */,
B9F44AE422F418F600FC6B29 /* ConsoleStore.swift */,
@@ -446,7 +443,6 @@
B94CB50A22D1352F0029BFAD /* Logo+CoreDataProperties.swift in Sources */,
B94CB4FF22D1352F0029BFAD /* Accessory+CoreDataClass.swift in Sources */,
B98A734D22BAD27D00FB3410 /* Zockerhoehle.xcdatamodeld in Sources */,
B93D60D122E5009700DD390F /* GameViewModel.swift in Sources */,
B9F44AE722F429D300FC6B29 /* GameStore.swift in Sources */,
B90E03EB238557D900E79643 /* LibraryImport.swift in Sources */,
B94CB50922D1352F0029BFAD /* Logo+CoreDataClass.swift in Sources */,

View File

@@ -75,7 +75,7 @@ extension Console : Encodable {
var gamesList : [String] = []
for game in games! {
if let game = game as? Game {
gamesList.append(game.uuid!.uuidString)
gamesList.append(game.uuid.uuidString)
}
}
try container.encode(gamesList, forKey: .games)

View File

@@ -13,19 +13,7 @@ import SwiftUI
@objc(Game)
public class Game: NSManagedObject, Identifiable {
@nonobjc public class func fetchRequest(console : Console) -> NSFetchRequest<Game> {
let fetchRequest = NSFetchRequest<Game>(entityName: "Game")
fetchRequest.predicate = NSPredicate(format: "console == %@", console)
return fetchRequest
}
@nonobjc public class func fetchRequest(gameSeries : GameSeries) -> NSFetchRequest<Game> {
let fetchRequest = NSFetchRequest<Game>(entityName: "Game")
fetchRequest.predicate = NSPredicate(format: "series == %@", gameSeries)
return fetchRequest
}
public func addGameSeries(by objectIDStringified : String) {
public func addGameSeries(by objectIDStringified : String) {
if let url = URL(string: objectIDStringified) {
let persistentStoreCoordinator = CDManager.shared.persistentContainer.persistentStoreCoordinator
@@ -44,16 +32,21 @@ public class Game: NSManagedObject, Identifiable {
return gameACreated < gameBCreated
}
init(context: NSManagedObjectContext) {
super.init(entity: Game.entity(), insertInto: context)
public var id : NSManagedObjectID {
get {
return self.objectID
}
}
@objc
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
if self.createdAt == .none {
self.createdAt = Date()
}
}
@objc
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
self.uuid = UUID()
print("Set UUID to \(self.uuid)")
}
}
@@ -70,11 +63,12 @@ extension Game : Encodable {
case inWishlist
case console
case gameSeries
case cover
case cover_icloud_path
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
print("UUID: \(self.uuid)")
try container.encode(uuid, forKey: .uuid)
try container.encode(name, forKey: .name)
try container.encode(notes ?? "", forKey: .notes)
@@ -84,13 +78,11 @@ extension Game : Encodable {
try container.encode(publisher ?? "", forKey: .publisher)
try container.encode(isFinished, forKey: .isFinished)
try container.encode(inWishlist, forKey: .inWishlist)
try container.encode(cover_icloud_path ?? "", forKey: .cover_icloud_path)
let consoleUUID : String = console?.uuid.uuidString ?? ""
let gameSeriesUUID : String = series?.uuid.uuidString ?? ""
try container.encode(consoleUUID, forKey: .console)
try container.encode(gameSeriesUUID, forKey: .gameSeries)
let imgBase64 = cover?.image?.pngData()?.base64EncodedString() ?? ""
try container.encode(imgBase64, forKey: .cover)
}
}

View File

@@ -26,9 +26,9 @@ extension Game {
@NSManaged public var name: String?
@NSManaged public var notes: String?
@NSManaged public var publisher: String?
@NSManaged public var uuid: UUID?
@NSManaged public var uuid: UUID
@NSManaged public var cover_icloud_path : String?
@NSManaged public var console: Console?
@NSManaged public var cover: Cover?
@NSManaged public var series: GameSeries?
}

View File

@@ -80,13 +80,9 @@ class LibraryImport {
cdGame.inWishlist = game.inWishlist
cdGame.isFinished = game.isFinished
cdGame.lentTo = game.lentTo
cdGame.cover_icloud_path = game.cover_icloud_path
//TODO: cdGame.createdAt = game.createdAt.
let cdCover = Cover(context: CDManager.shared.viewContext)
cdCover.image = game.cover
cdCover.game = cdGame
cdGame.cover = cdCover
cdConsole.addToGames(cdGame)
cdGame.console = cdConsole
print("Imported: \(cdGame.name) for \(cdConsole.name)")
@@ -152,7 +148,7 @@ class LibraryImport {
for console in library.consoles {
let cdConsole = makeCDConsole(from: console)
print("CONSOLE: \(cdConsole.name) with \(console.games.count) games")
print("CONSOLE: \(cdConsole.name ?? "n/a") with \(console.games.count) games")
for uuid in console.games {
if let game = library.games.first(where: {$0.uuid == uuid}) {
@@ -204,7 +200,7 @@ struct BHLGame : Decodable {
let publisher : String?
let console : UUID?
let series : UUID?
let cover : UIImage?
let cover_icloud_path : String?
enum CodingKeys: String, CodingKey {
case uuid
@@ -218,7 +214,7 @@ struct BHLGame : Decodable {
case publisher
case console
case series
case cover
case cover_icloud_path
}
init(from decoder: Decoder) throws {
@@ -232,16 +228,10 @@ struct BHLGame : Decodable {
notes = try container.decode(String?.self, forKey: .notes)
createdAt = try container.decode(String?.self, forKey: .notes)
publisher = try container.decode(String?.self, forKey: .publisher)
cover_icloud_path = try container.decode(String?.self, forKey: .cover_icloud_path)
console = try container.decode(UUID?.self, forKey: .console)
series = try container.decode(UUID?.self, forKey: .console)
if let coverBase64 = try container.decode(String?.self, forKey: .cover),
let coverData = Data(base64Encoded: coverBase64) {
cover = UIImage(data: coverData)
}else {
cover = .none
}
}
}

View File

@@ -49,13 +49,14 @@ class GameStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate
lazy var fetchResultsController : NSFetchedResultsController<Game> = {
var gamesFetch : NSFetchRequest<Game> = Game.fetchRequest()
if let console = consoleFilter {
/*if let console = consoleFilter {
gamesFetch = Game.fetchRequest(console: console)
}else if let gameSeries = gameSeriesFilter {
gamesFetch = Game.fetchRequest(gameSeries: gameSeries)
}else {
print("No filter: fetch Limit: \(self.fetchLimit)")
}
}*/
gamesFetch.fetchLimit = self.fetchLimit
gamesFetch.sortDescriptors = self.sortDescriptors

View File

@@ -1,146 +0,0 @@
//
// GameViewModel.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 21.07.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import SwiftUI
import Combine
import CoreData
import UIKit
class GameViewModel : ObservableObject {
var objectWillChange = ObservableObjectPublisher()
private var game : Game? {
didSet {
guard let game = game else { return }
self.name = game.name!
self.inWishlist = game.inWishlist
self.isDigital = game.isDigital
self.isFinished = game.isFinished
}
}
var lentTo : String {
didSet {
guard let game = self.game else { return }
if lentTo != "" {
game.lentTo = lentTo;
}
}
}
var name : String {
didSet {
guard let game = self.game else { return }
game.name = name
}
}
var inWishlist : Bool {
didSet {
guard let game = self.game else { return }
game.inWishlist = inWishlist
}
}
var isDigital : Bool {
didSet {
guard let game = self.game else { return }
game.isDigital = isDigital
}
}
var isFinished : Bool {
didSet {
guard let game = self.game else { return }
game.isFinished = isFinished
}
}
var gameSeries : String {
willSet {
if newValue != "" , let game = self.game {
game.addGameSeries(by: newValue)
}
}
}
var console : Console? {
get {
return self.game?.console
}
}
var cover : UIImage? {
get {
if let coverImage = self.game?.cover?.image {
return coverImage
}
return .none
}
set {
if let game = self.game {
if let coverImage = newValue {
let newCover = Cover(context: CDManager.shared.viewContext)
newCover.game = game
newCover.image = coverImage
game.cover = newCover
}
}
}
}
func removeGame() {
if let game = self.game {
CDManager.shared.viewContext.delete(game)
}
}
var NSManagedObjectChangedObserver : AnyCancellable? = .none
init(game : Game) {
self.game = game
self.name = game.name!
self.inWishlist = game.inWishlist
self.isDigital = game.isDigital
self.isFinished = game.isFinished
self.gameSeries = game.series?.id ?? ""
self.lentTo = game.lentTo ?? ""
self.NSManagedObjectChangedObserver = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).first().sink { notification in
guard let game = self.game else { return }
guard let userInfo = notification.userInfo else { return }
var managedObjectIsMatching = false
if let inserts = userInfo[NSInsertedObjectsKey] as? Set<NSManagedObject> {
if inserts.contains(game) { managedObjectIsMatching = true }
}
if let updates = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject> {
if updates.contains(game) { managedObjectIsMatching = true }
}
if let deletes = userInfo[NSDeletedObjectsKey] as? Set<NSManagedObject> {
if deletes.contains(game) { managedObjectIsMatching = true }
}
if managedObjectIsMatching {
self.objectWillChange.send()
print("GameViewModel::NSMangedObjectChanged Update of \(game.name)")
}
}
}
}

View File

@@ -43,7 +43,7 @@ struct ModalAddToConsoleLibrary : View {
self.addAccessoryToLibrary()
}
self.presentationMode.wrappedValue.dismiss()},
label: { Text("Add") })
label: { Text("Fertig") })
.disabled(self.modalAddName.trimmingCharacters(in: .whitespacesAndNewlines) == "" )
}
@@ -61,9 +61,9 @@ struct ModalAddToConsoleLibrary : View {
})
}
.font(.caption)
.navigationBarTitle(self.isVideogamesSelected ? Text("New Game") : Text("New Accessory"))
.navigationBarTitle(self.isVideogamesSelected ? Text("Spiel hinzufügen") : Text("Zubehör hinzufügen"))
.navigationBarItems(leading: Button(action: { self.presentationMode.wrappedValue.dismiss() },
label: {Text("Cancel")}),
label: {Text("Abbrechen")}),
//Add Button is disabled if no name is entered
trailing: addButton)
.navigationViewStyle(StackNavigationViewStyle())
@@ -72,14 +72,27 @@ struct ModalAddToConsoleLibrary : View {
}
struct ConsoleLibraryView : View {
@State var isVideogamesSelected = true
var console : Console?
@ObservedObject var gameStore : GameStore
@ObservedObject var accessoryStore : AccessoryStore
@ObservedObject var console : Console
@Environment(\.managedObjectContext) private var viewContext
var gamesFetchRequest: FetchRequest<Game>
var games: FetchedResults<Game> { gamesFetchRequest.wrappedValue }
var accessoryFetchRequest: FetchRequest<Accessory>
var accessories: FetchedResults<Accessory> { accessoryFetchRequest.wrappedValue }
@State var isVideogamesSelected = true
@State var showWishlist = false
@State var showAddToConsoleLibraryModal: Bool = false
@State var showLogoImagePicker = false
init (console: Console) {
self.console = console
self.gamesFetchRequest = FetchRequest<Game>(entity: Game.entity(), sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(format: "console == %@", console))
self.accessoryFetchRequest = FetchRequest<Accessory>(entity: Accessory.entity(), sortDescriptors: [NSSortDescriptor(key: "name", ascending: true)], predicate: NSPredicate(format: "console == %@", console))
}
var body: some View {
VStack {
@@ -93,43 +106,32 @@ struct ConsoleLibraryView : View {
Spacer()
}
if self.isVideogamesSelected {
List {
ForEach(gameStore.games.filter({$0.inWishlist == self.showWishlist})) { game in
NavigationLink(destination: GameDetailView(game: game)) {
HStack {
/*game.cover.map {
Image(uiImage: $0.image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 75)
}*/
Text("\(game.name!)")
if game.isDigital {
Image("digitalGame")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 15)
}
List(games.filter({$0.inWishlist == self.showWishlist})) { game in
NavigationLink(destination: GameDetailView(game: game)) {
HStack {
Text("\(game.name ?? "n/a")")
if game.isDigital {
Image("digitalGame")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 15)
}
}
}
}
}else {
List {
ForEach(accessoryStore.accessories.filter({$0.inWishlist == self.showWishlist})) { accessory in
NavigationLink(destination: AccessoryDetailView(accessoryVM: AccessoryViewModel(accessory: accessory))) {
Text("\(accessory.name)")
}
List(accessories.filter({$0.inWishlist == self.showWishlist})) {accessory in
NavigationLink(destination: AccessoryDetailView(accessoryVM: AccessoryViewModel(accessory: accessory))) {
Text("\(accessory.name)")
}
}
}
}
.navigationBarTitle(Text("\(self.console?.name ?? "N/A")"), displayMode: .automatic)
.navigationBarTitle(Text("\(self.console.name ?? "n/a")"), displayMode: .automatic)
.navigationBarItems(trailing:
HStack {
NavigationLink(destination: ConsoleEditView(console: self.console!)) {
NavigationLink(destination: ConsoleEditView(console: self.console)) {
Image(systemName: "pencil.and.ellipsis.rectangle")
}
@@ -145,18 +147,7 @@ struct ConsoleLibraryView : View {
.accentColor(self.showWishlist ? Color.red : Color.blue)
})
.sheet(isPresented: $showAddToConsoleLibraryModal) {
if self.console != nil {
ModalAddToConsoleLibrary(modalAddToWishlist: self.showWishlist, isVideogamesSelected: true, console: self.console!)
}else{
Text("Fehler: Keine Konsole übergeben")
}
ModalAddToConsoleLibrary(modalAddToWishlist: self.showWishlist, isVideogamesSelected: self.isVideogamesSelected, console: self.console)
}
}
init(console : Console?) {
self.console = console
self.gameStore = GameStore(console: console)
self.accessoryStore = AccessoryStore(console: console)
}
}

View File

@@ -9,7 +9,7 @@
import SwiftUI
struct GameDetailView : View {
@ObservedObject var gameVM : GameViewModel
@ObservedObject var game : Game
@State private var showDeleteAlert : Bool = false
@State var hasFinishedDate : Bool = false
@@ -18,83 +18,64 @@ struct GameDetailView : View {
@EnvironmentObject var gameSeriesStore : GameSeriesStore
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State var showingPicker = false
@State var image : Image? = nil
@State var isImportingCover : Bool = false
@State var isLent : Bool = false
let defaultImage = UIImage()
var gameSeriesPicker : some View {
Picker(selection: $gameVM.gameSeries, label:
Text("TODO GameSeries Picker")
/*Picker(selection: $game.gameSeries, label:
Text("Spieleserie")
, content: {
Text("Keine").tag("")
ForEach(gameSeriesStore.gameSeries) { gameSeries in
Text("\(gameSeries.name)").tag(gameSeries.id)
}
})
})*/
}
var imageCoverSection : some View {
Section {
VStack{
Button("Show image picker") {
self.showingPicker = true
}
if self.gameVM.cover != nil {
Image(uiImage: self.gameVM.cover!)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 100)
}
}
}
}
var body: some View {
Form {
Section {
TextField("Videogame name", text: $gameVM.name)
TextField("Videogame name", text: $game.name ?? "")
//Gray color should indicate immutable data
Text("\(gameVM.console?.name ?? "No console")").foregroundColor(.gray)
Text("\(game.console?.name ?? "No console")").foregroundColor(.gray)
Toggle(isOn: $gameVM.isDigital, label: {
Toggle(isOn: $game.isDigital, label: {
Text("Nur Digital")
})
Toggle(isOn: $gameVM.inWishlist, label: {
Toggle(isOn: $game.inWishlist, label: {
Text("In Wunschliste")
})
Toggle(isOn: $isLent, label: {
Text("Verliehen?")
}).onReceive(gameVM.objectWillChange) { _ in
self.isLent = self.gameVM.lentTo != "";
}).onReceive(game.objectWillChange) { _ in
self.isLent = self.game.lentTo != "";
}.onAppear() {
self.isLent = self.gameVM.lentTo != "";
self.isLent = self.game.lentTo != "";
}
if isLent {
TextField("Verliehen an", text: $gameVM.lentTo)
TextField("Verliehen an", text: $game.lentTo ?? "")
}
gameSeriesPicker
Toggle(isOn: $gameVM.isFinished , label: {
Toggle(isOn: $game.isFinished , label: {
Text("Durchgezockt")
})
if gameVM.isFinished {
if game.isFinished {
Toggle(isOn: $hasFinishedDate, label: {
Text("Gibts ein Datum")
})
}
if hasFinishedDate && gameVM.isFinished {
if hasFinishedDate && game.isFinished {
DatePicker("Durchgezockt am",
selection: $playthroughDate,
in: ...Date(),
@@ -102,23 +83,43 @@ struct GameDetailView : View {
}
}
imageCoverSection
Section {
VStack{
Button("Neues Cover auswählen") {
self.isImportingCover = true
}
Image(uiImage: ICloudManager.imageFrom(path: game.cover_icloud_path) ?? defaultImage)
.resizable()
.frame(width:100, height: 100)
}
}
Section{
gameDeleteButton
}
}
.navigationBarTitle(Text("\(gameVM.name)"), displayMode: .automatic)
.sheet(isPresented: $showingPicker,
onDismiss: {
// do whatever you need here
}, content: {
ImagePicker.shared.view
})
.onReceive(ImagePicker.shared.objectWillChange) { image in
if let image = image {
self.gameVM.cover = image
.navigationBarTitle(Text("\(game.name ?? "N/A")"), displayMode: .automatic)
.fileImporter(
isPresented: $isImportingCover,
allowedContentTypes: [.jpeg, .png],
allowsMultipleSelection: false
) { result in
do {
let selectedFile : URL = try result.get().first!
//It seems that isUbiquitousItem checks if the file is contained in the Apps iCloud folder
if (FileManager.default.isUbiquitousItem(at: selectedFile)) {
game.cover_icloud_path = ICloudManager.relativePathFrom(url: selectedFile)
print("Selected Image in iCloud Path \(game.cover_icloud_path ?? "n/a")")
}else{
Alert(title: Text("Falscher Ordner"))
print("Außerhalb \(selectedFile.relativeString)")
}
}catch{
print("ConsoleAllView::ModalAddConsoleToLibrary Error getting result '\(result)'")
}
}
}
@@ -136,19 +137,12 @@ struct GameDetailView : View {
})
.accentColor(.red)
.alert(isPresented: $showDeleteAlert, content: {
Alert(title: Text("Aus Zockerhöhle entfernen"), message: Text("Willst du '\(self.gameVM.name)' wirklich aus der Zockerhöhle werfen?"), primaryButton: Alert.Button.destructive(Text("Ja!"), action: {
//self.presentationMode.value.dismiss()
self.gameVM.removeGame()
Alert(title: Text("Aus Zockerhöhle entfernen"), message: Text("Willst du '\(self.game.name ?? "n/a")' wirklich aus der Zockerhöhle werfen?"), primaryButton: Alert.Button.destructive(Text("Ja!"), action: {
//TZOOOODOOOOOO
//self.gameVM.removeGame()
}), secondaryButton: Alert.Button.cancel(Text("Lieber doch nicht")))
})
}
init(gameVM : GameViewModel?) {
self.gameVM = gameVM!
}
init(game : Game) {
self.gameVM = GameViewModel(game: game)
}
}

View File

@@ -7,7 +7,7 @@
//
import SwiftUI
import CoreData
//extension String: Identifiable {
// public var id: String {
// return self
@@ -20,15 +20,24 @@ struct SettingsView: View {
@State var importFiles : [String] = LibraryImport().backupFiles()
func exportLibrary() {
let games = GameStore().games
let consoles = ConsoleStore().consoles
let gameSeries = GameSeriesStore().gameSeries
let accessories = AccessoryStore().accessories
let libExport = LibraryExporter(games: games, consoles: consoles, gameSeries: gameSeries, accessories: accessories)
libExport.export(name: Date().formattedInTimeZone())
importFiles = LibraryImport().backupFiles()
do {
let gamesFR = NSFetchRequest<Game>(entityName: "Game")
let consolesFR = NSFetchRequest<Console>(entityName: "Console")
let gameSeriesFR = NSFetchRequest<GameSeries>(entityName: "GameSeries")
let accessoriesFR = NSFetchRequest<Accessory>(entityName: "Accessory")
let games = try CDManager.shared.viewContext.fetch(gamesFR)
let consoles = try CDManager.shared.viewContext.fetch(consolesFR)
let gameSeries = try CDManager.shared.viewContext.fetch(gameSeriesFR)
let accessories = try CDManager.shared.viewContext.fetch(accessoriesFR)
let libExport = LibraryExporter(games: games, consoles: consoles, gameSeries: gameSeries, accessories: accessories)
libExport.export(name: Date().formattedInTimeZone())
importFiles = LibraryImport().backupFiles()
}catch {
print("ERROR during export")
}
}
var body: some View {

View File

@@ -23,10 +23,9 @@
</entity>
<entity name="Cover" representedClassName="Cover" syncable="YES">
<attribute name="image" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData"/>
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="cover" inverseEntity="Game"/>
</entity>
<entity name="Game" representedClassName="Game" syncable="YES">
<attribute name="circumstances" optional="YES" attributeType="String"/>
<attribute name="cover_icloud_path" optional="YES" attributeType="String"/>
<attribute name="createdAt" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="inWishlist" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="isDigital" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
@@ -37,7 +36,6 @@
<attribute name="publisher" optional="YES" attributeType="String"/>
<attribute name="uuid" optional="YES" attributeType="UUID" defaultValueString="00000000-0000-0000-0000-000000000000" usesScalarValueType="NO"/>
<relationship name="console" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Console" inverseName="games" inverseEntity="Console"/>
<relationship name="cover" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Cover" inverseName="game" inverseEntity="Cover"/>
<relationship name="series" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GameSeries" inverseName="games" inverseEntity="GameSeries"/>
<fetchedProperty name="fetchedProperty" optional="YES">
<fetchRequest name="fetchedPropertyFetchRequest" entity="Game"/>
@@ -59,8 +57,8 @@
<elements>
<element name="Accessory" positionX="-265.9140625" positionY="29.15625" width="128" height="149"/>
<element name="Console" positionX="-535.7890625" positionY="56.03515625" width="128" height="164"/>
<element name="Cover" positionX="-66.69921875" positionY="223.48046875" width="128" height="59"/>
<element name="Game" positionX="-288.828125" positionY="276.7421875" width="128" height="245"/>
<element name="Cover" positionX="-66.69921875" positionY="223.48046875" width="128" height="44"/>
<element name="Game" positionX="-288.828125" positionY="276.7421875" width="128" height="230"/>
<element name="GameSeries" positionX="-686.828125" positionY="359.20703125" width="128" height="89"/>
<element name="GameSeriesCover" positionX="-477" positionY="180" width="128" height="59"/>
<element name="Logo" positionX="-66.7109375" positionY="110.9765625" width="128" height="44"/>