// // LibraryImport.swift // Zockerhoehle // // Created by Julian-Steffen Müller on 20.11.19. // Copyright © 2019 Julian-Steffen Müller. All rights reserved. // import Foundation import UIKit import CoreData // MARK: - Library Import class LibraryImport { let CDContext : NSManagedObjectContext init(context CDContext: NSManagedObjectContext = CDManager.shared.viewContext) { self.CDContext = CDContext } static func backupName(from filename : String) -> String? { if filename.hasPrefix("libExp_") && filename.hasSuffix(".json") { var backupName = filename backupName.removeFirst("libExp_".count) backupName.removeLast(".json".count) return backupName }else{ return .none } } func backupFiles() -> [String] { do { guard let documentDirectory = try ICloudManager.backup_folder else { return [] } let filesInDocumentsDir = try FileManager.default.contentsOfDirectory(at: documentDirectory, includingPropertiesForKeys: .none, options: []) let backupFiles = filesInDocumentsDir.filter{ $0.isFileURL }.map { $0.lastPathComponent }.filter { $0.hasPrefix("libExp_") && $0.hasSuffix(".json") }.sorted(by: { $0 > $1 }) return backupFiles }catch let error { print(error) print("LibraryImport::backupFiles - Error reading files from Document directory!") } return [] } private func resetDatabase() { self.CDContext.reset() self.CDContext.performAndWait { let deleteRequests = [NSBatchDeleteRequest(fetchRequest: Game.fetchRequest()), NSBatchDeleteRequest(fetchRequest: Accessory.fetchRequest()), NSBatchDeleteRequest(fetchRequest: Console.fetchRequest()), NSBatchDeleteRequest(fetchRequest: GameSeries.fetchRequest())] let storeCoordinator = CDManager.shared.persistentContainer.persistentStoreCoordinator do { for deleteRequest in deleteRequests { try storeCoordinator.execute(deleteRequest, with: CDContext) } }catch let error { print(error) print("LibraryImport::resetDatabase - Reset of Database failes!") } } } private func makeCDGame(from game: BHLGame, _ gameDict: inout [UUID : Game], _ cdConsole: Console) { let cdGame = Game(context: self.CDContext) gameDict[game.uuid] = cdGame cdGame.name = game.name ?? "n/a" cdGame.uuid = game.uuid cdGame.inWishlist = game.inWishlist cdGame.isFinished = game.isFinished cdGame.finishedDate = game.finishedDate cdGame.lentTo = game.lentTo cdGame.cover_icloud_path = game.cover_icloud_path cdGame.pickupDescription = game.pickupDescription cdGame.isDigital = game.isDigital if let date = Date.from(string: game.createdAt) { cdGame.createdAt = date }else{ print("Could not decode date '\(game.createdAt)' for game '\(cdGame.name)'") } cdConsole.addToGames(cdGame) cdGame.console = cdConsole print("Imported: \(cdGame.name) for \(cdConsole.name)") } private func makeCDAccessory(from accessory: BHLAccessory, _ accessoryDict: inout [UUID : Accessory], _ cdConsole: Console) { let cdAccessory = Accessory(context: self.CDContext) accessoryDict[accessory.uuid] = cdAccessory cdAccessory.name = accessory.name cdAccessory.uuid = accessory.uuid cdAccessory.lentTo = accessory.lentTo cdAccessory.color = accessory.color cdAccessory.manufacturer = accessory.manufacturer cdAccessory.inWishlist = accessory.inWishlist cdConsole.addToAccessories(cdAccessory) cdAccessory.console = cdConsole } private func makeCDConsole(from console : BHLConsole) -> Console { let cdConsole = Console(context: self.CDContext) cdConsole.name = console.name ?? "n/a" cdConsole.uuid = console.uuid cdConsole.manufacturer = console.manufacturer cdConsole.shortName = console.shortName cdConsole.logo_icloud_path = console.logo_icloud_path cdConsole.releaseDate = console.releaseDate return cdConsole } private func makeCDGameSeries(from gameSeries: BHLGameSeries, _ gameDict: inout [UUID : Game]) { let cdGameSeries = GameSeries(context: self.CDContext) cdGameSeries.name = gameSeries.name cdGameSeries.cover_icloud_path = gameSeries.cover_icloud_path for uuid in gameSeries.games { if let game = gameDict[uuid] { cdGameSeries.addToGames(game) game.series = cdGameSeries } } } func importLIB(from filename: String) { guard let file = ICloudManager.backup_folder?.appendingPathComponent(filename) else { print("LibraryImport:importLIB Could not get icloud backup folder") return } if file.isFileURL { do { let data = try Data(contentsOf: file) importLIB(data: data) }catch let error { print("LibraryImport::importLIB::String - Cannot create data from file '\(error)'") } } } func importLIB(data : Data) { do { let library = try JSONDecoder().decode(BHLibrary.self, from: data) resetDatabase() var gameDict = [UUID:Game]() var accessoryDict = [UUID:Accessory]() for console in library.consoles { let cdConsole = makeCDConsole(from: console) 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}) { makeCDGame(from: game, &gameDict, cdConsole) }else{ print("LibraryImport::importLIB - Game with UUID '\(uuid)' not found") } } for uuid in console.accessories { if let accessory = library.accessories.first(where: {$0.uuid == uuid}) { makeCDAccessory(from: accessory, &accessoryDict, cdConsole) }else{ print("LibraryImport::importLIB - Accessory with UUID '\(uuid)' not found") } } } for gameSeries in library.gameSeries { makeCDGameSeries(from: gameSeries, &gameDict) } }catch let error { print("LibraryImport::importLIB::DATA - Error while importing! '\(error)'") } } } // MARK: - BHLLibrary Classes struct BHLibrary : Decodable { let games : [BHLGame] let consoles : [BHLConsole] let gameSeries : [BHLGameSeries] let accessories : [BHLAccessory] } struct BHLGame : Decodable { let uuid : UUID let name : String? let lentTo : String? let isDigital : Bool let inWishlist : Bool let isFinished : Bool let finishedDate : Date? let notes : String? let createdAt : String let pickupDescription : String? let publisher : String? let console : UUID let cover_icloud_path : String? } struct BHLAccessory : Decodable { let uuid : UUID let name : String let color : String? let manufacturer : String? let inWishlist : Bool let lentTo : String? let console : UUID? } struct BHLGameSeries : Decodable { let uuid : UUID let name : String let games : [UUID] let cover_icloud_path : String? } struct BHLConsole : Decodable { let uuid : UUID let name : String? let logo_icloud_path : String? let manufacturer : String? let releaseDate : Date let shortName : String? let accessories : [UUID] let games : [UUID] }