From 0c4018740912303eef17e8321a9f1324a9c45d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dr=2E=20Julian-Steffen=20M=C3=BCller?= Date: Tue, 18 May 2021 11:46:37 +0200 Subject: [PATCH] Partially added icloud support for console logo. Added iCloud utilities and tests --- Zockerhoehle.xcodeproj/project.pbxproj | 144 +++++++++++++++++- Zockerhoehle/AppDelegate.swift | 16 ++ .../CDModel/Console+CoreDataClass.swift | 5 +- .../CDModel/Console+CoreDataProperties.swift | 1 + Zockerhoehle/ICloudManager.swift | 48 ++++++ Zockerhoehle/Info.plist | 14 +- Zockerhoehle/SceneDelegate.swift | 7 +- Zockerhoehle/Utils/URLExtension.swift | 35 +++++ Zockerhoehle/ViewModel/ConsoleViewModel.swift | 4 +- Zockerhoehle/Views/ConsoleAllView.swift | 111 ++++++++------ Zockerhoehle/Views/GamePickupsView.swift | 2 +- Zockerhoehle/Views/Overview.swift | 29 +++- Zockerhoehle/Zockerhoehle.entitlements | 5 + .../Zockerhoehle.xcdatamodel/contents | 3 +- ZockerhoehleTests/Info.plist | 22 +++ ZockerhoehleTests/ZockerhoehleTests.swift | 45 ++++++ 16 files changed, 430 insertions(+), 61 deletions(-) create mode 100644 Zockerhoehle/ICloudManager.swift create mode 100644 Zockerhoehle/Utils/URLExtension.swift create mode 100644 ZockerhoehleTests/Info.plist create mode 100644 ZockerhoehleTests/ZockerhoehleTests.swift diff --git a/Zockerhoehle.xcodeproj/project.pbxproj b/Zockerhoehle.xcodeproj/project.pbxproj index 5e15b4d..48a4526 100644 --- a/Zockerhoehle.xcodeproj/project.pbxproj +++ b/Zockerhoehle.xcodeproj/project.pbxproj @@ -39,9 +39,12 @@ B98CBBDB264E98DE00B1B7AC /* Console+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CBBD8264E98DD00B1B7AC /* Console+CoreDataProperties.swift */; }; B98CBBDC264E98DE00B1B7AC /* GameSeries+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CBBD9264E98DD00B1B7AC /* GameSeries+CoreDataClass.swift */; }; B98CBBDD264E98F300B1B7AC /* Console+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F922D1352F0029BFAD /* Console+CoreDataClass.swift */; }; + B98CBBE3265045AC00B1B7AC /* URLExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CBBE2265045AC00B1B7AC /* URLExtension.swift */; }; + B98CBBEB2652B75F00B1B7AC /* ZockerhoehleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CBBEA2652B75F00B1B7AC /* ZockerhoehleTests.swift */; }; B9A0550122F8C22D0054D9A0 /* GameSeriesAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9A0550022F8C22D0054D9A0 /* GameSeriesAllView.swift */; }; B9A0550322F8C2740054D9A0 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9A0550222F8C2740054D9A0 /* MainView.swift */; }; B9A0550522F8CB400054D9A0 /* GameSeriesLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9A0550422F8CB400054D9A0 /* GameSeriesLibraryView.swift */; }; + B9BCCEB92653BDEA005F46D6 /* ICloudManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9BCCEB82653BDEA005F46D6 /* ICloudManager.swift */; }; B9BCF4CA2168ACB600ECBAAC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B9BCF4C92168ACB600ECBAAC /* LaunchScreen.storyboard */; }; B9D2C6F722E98ED800797F67 /* AccessoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9D2C6F622E98ED800797F67 /* AccessoryViewModel.swift */; }; B9E2A079233B69D400EAEB14 /* GameSeriesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9E2A078233B69D400EAEB14 /* GameSeriesViewModel.swift */; }; @@ -58,6 +61,16 @@ B9F44AE922F4655600FC6B29 /* AccessoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44AE822F4655600FC6B29 /* AccessoryStore.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + B98CBBED2652B75F00B1B7AC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B93C1B9121496BFD0014FD6E /* Project object */; + proxyType = 1; + remoteGlobalIDString = B93C1B9821496BFD0014FD6E; + remoteInfo = Zockerhoehle; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ B983997A233A0295002F9946 /* Embed Watch Content */ = { isa = PBXCopyFilesBuildPhase; @@ -108,9 +121,14 @@ B98CBBD7264E98DD00B1B7AC /* GameSeries+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GameSeries+CoreDataProperties.swift"; sourceTree = ""; }; B98CBBD8264E98DD00B1B7AC /* Console+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Console+CoreDataProperties.swift"; sourceTree = ""; }; B98CBBD9264E98DD00B1B7AC /* GameSeries+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GameSeries+CoreDataClass.swift"; sourceTree = ""; }; + B98CBBE2265045AC00B1B7AC /* URLExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLExtension.swift; sourceTree = ""; }; + B98CBBE82652B75F00B1B7AC /* ZockerhoehleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ZockerhoehleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B98CBBEA2652B75F00B1B7AC /* ZockerhoehleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZockerhoehleTests.swift; sourceTree = ""; }; + B98CBBEC2652B75F00B1B7AC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B9A0550022F8C22D0054D9A0 /* GameSeriesAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesAllView.swift; sourceTree = ""; }; B9A0550222F8C2740054D9A0 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; B9A0550422F8CB400054D9A0 /* GameSeriesLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesLibraryView.swift; sourceTree = ""; }; + B9BCCEB82653BDEA005F46D6 /* ICloudManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ICloudManager.swift; sourceTree = ""; }; B9BCF4C92168ACB600ECBAAC /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; B9D2C6F622E98ED800797F67 /* AccessoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessoryViewModel.swift; sourceTree = ""; }; B9E2A078233B69D400EAEB14 /* GameSeriesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesViewModel.swift; sourceTree = ""; }; @@ -137,6 +155,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + B98CBBE52652B75F00B1B7AC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -175,6 +200,7 @@ B98B2FAB232C0F8C00606DC4 /* ImagePicker.swift */, B9EC09812383F94B004BC9AB /* LibraryExport.swift */, B90E03EA238557D900E79643 /* LibraryImport.swift */, + B98CBBE2265045AC00B1B7AC /* URLExtension.swift */, ); path = Utils; sourceTree = ""; @@ -183,6 +209,7 @@ isa = PBXGroup; children = ( B93C1B9B21496BFD0014FD6E /* Zockerhoehle */, + B98CBBE92652B75F00B1B7AC /* ZockerhoehleTests */, B93C1B9A21496BFD0014FD6E /* Products */, B90B64A12235909900E54BA3 /* Frameworks */, B94CB53522D3708F0029BFAD /* Zockerhoehle copy-Info.plist */, @@ -193,6 +220,7 @@ isa = PBXGroup; children = ( B93C1B9921496BFD0014FD6E /* Zockerhoehle.app */, + B98CBBE82652B75F00B1B7AC /* ZockerhoehleTests.xctest */, ); name = Products; sourceTree = ""; @@ -209,6 +237,7 @@ B9F44ABD22F31DEF00FC6B29 /* SceneDelegate.swift */, B9BCF4C92168ACB600ECBAAC /* LaunchScreen.storyboard */, B98A735F22C1738800FB3410 /* CDManager.swift */, + B9BCCEB82653BDEA005F46D6 /* ICloudManager.swift */, B93C1BA321496BFE0014FD6E /* Assets.xcassets */, B93C1BA821496BFE0014FD6E /* Info.plist */, B98A731722BA9E4600FB3410 /* Zockerhoehle.xcdatamodeld */, @@ -254,6 +283,15 @@ path = CDModel; sourceTree = ""; }; + B98CBBE92652B75F00B1B7AC /* ZockerhoehleTests */ = { + isa = PBXGroup; + children = ( + B98CBBEA2652B75F00B1B7AC /* ZockerhoehleTests.swift */, + B98CBBEC2652B75F00B1B7AC /* Info.plist */, + ); + path = ZockerhoehleTests; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -280,13 +318,31 @@ productReference = B93C1B9921496BFD0014FD6E /* Zockerhoehle.app */; productType = "com.apple.product-type.application"; }; + B98CBBE72652B75F00B1B7AC /* ZockerhoehleTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B98CBBF12652B75F00B1B7AC /* Build configuration list for PBXNativeTarget "ZockerhoehleTests" */; + buildPhases = ( + B98CBBE42652B75F00B1B7AC /* Sources */, + B98CBBE52652B75F00B1B7AC /* Frameworks */, + B98CBBE62652B75F00B1B7AC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B98CBBEE2652B75F00B1B7AC /* PBXTargetDependency */, + ); + name = ZockerhoehleTests; + productName = ZockerhoehleTests; + productReference = B98CBBE82652B75F00B1B7AC /* ZockerhoehleTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ B93C1B9121496BFD0014FD6E /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1100; + LastSwiftUpdateCheck = 1250; LastUpgradeCheck = 1250; ORGANIZATIONNAME = "Julian-Steffen Müller"; TargetAttributes = { @@ -294,6 +350,10 @@ CreatedOnToolsVersion = 9.4.1; LastSwiftMigration = 1020; }; + B98CBBE72652B75F00B1B7AC = { + CreatedOnToolsVersion = 12.5; + TestTargetID = B93C1B9821496BFD0014FD6E; + }; }; }; buildConfigurationList = B93C1B9421496BFD0014FD6E /* Build configuration list for PBXProject "Zockerhoehle" */; @@ -314,6 +374,7 @@ projectRoot = ""; targets = ( B93C1B9821496BFD0014FD6E /* Zockerhoehle */, + B98CBBE72652B75F00B1B7AC /* ZockerhoehleTests */, ); }; /* End PBXProject section */ @@ -328,6 +389,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + B98CBBE62652B75F00B1B7AC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -361,6 +429,7 @@ B98B2FAA2328DF3400606DC4 /* GameSeriesStore.swift in Sources */, B9839991233A0E16002F9946 /* GameSeriesCover+CoreDataProperties.swift in Sources */, B983998C233A0BC9002F9946 /* Cover+CoreDataClass.swift in Sources */, + B98CBBE3265045AC00B1B7AC /* URLExtension.swift in Sources */, B9839992233A0E19002F9946 /* GameSeriesCover+CoreDataClass.swift in Sources */, B94112E0233A4EF800159AE4 /* GamePickupsView.swift in Sources */, B94112E2233B55B100159AE4 /* ConsoleViewModel.swift in Sources */, @@ -397,14 +466,31 @@ B98A736022C1738800FB3410 /* CDManager.swift in Sources */, B94CB50222D1352F0029BFAD /* Cover+CoreDataProperties.swift in Sources */, B9E2A07E233B6E4F00EAEB14 /* ConsoleAllView.swift in Sources */, + B9BCCEB92653BDEA005F46D6 /* ICloudManager.swift in Sources */, B9EC0987238555BF004BC9AB /* SettingsView.swift in Sources */, B9F44AE522F418F600FC6B29 /* ConsoleStore.swift in Sources */, B9E2A079233B69D400EAEB14 /* GameSeriesViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + B98CBBE42652B75F00B1B7AC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B98CBBEB2652B75F00B1B7AC /* ZockerhoehleTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + B98CBBEE2652B75F00B1B7AC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B93C1B9821496BFD0014FD6E /* Zockerhoehle */; + targetProxy = B98CBBED2652B75F00B1B7AC /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ B93C1BA921496BFE0014FD6E /* Debug */ = { isa = XCBuildConfiguration; @@ -537,6 +623,7 @@ "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = Zockerhoehle/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -562,6 +649,7 @@ "$(PROJECT_DIR)/Carthage/Build/iOS", ); INFOPLIST_FILE = Zockerhoehle/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -574,6 +662,51 @@ }; name = Release; }; + B98CBBEF2652B75F00B1B7AC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 85J8CBD673; + INFOPLIST_FILE = ZockerhoehleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = haus.mueller.ZockerhoehleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Zockerhoehle.app/Zockerhoehle"; + }; + name = Debug; + }; + B98CBBF02652B75F00B1B7AC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 85J8CBD673; + INFOPLIST_FILE = ZockerhoehleTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = haus.mueller.ZockerhoehleTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Zockerhoehle.app/Zockerhoehle"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -595,6 +728,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + B98CBBF12652B75F00B1B7AC /* Build configuration list for PBXNativeTarget "ZockerhoehleTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B98CBBEF2652B75F00B1B7AC /* Debug */, + B98CBBF02652B75F00B1B7AC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/Zockerhoehle/AppDelegate.swift b/Zockerhoehle/AppDelegate.swift index f429f64..8ad94ba 100644 --- a/Zockerhoehle/AppDelegate.swift +++ b/Zockerhoehle/AppDelegate.swift @@ -23,6 +23,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate { #endif print("disFinishLaunchung") + /*let url = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")//.appendingPathComponent("Zockerhoehle").appendingPathComponent("consoles") + + if !FileManager.default.fileExists(atPath: url!.path, isDirectory: nil) { + do { + print("------------ \(url)") + try FileManager.default.createDirectory(at: url!, withIntermediateDirectories: true, attributes: nil) + } + catch { + print(error.localizedDescription) + } + }else { + let url2 = url?.appendingPathComponent("test23.txt") + FileManager.default.createFile(atPath: url2!.path, contents: nil, attributes: nil) + print("------------- \(url2)") + }*/ + //FlockeWS.fetchEntries(for: GameCollection.consoleID) diff --git a/Zockerhoehle/CDModel/Console+CoreDataClass.swift b/Zockerhoehle/CDModel/Console+CoreDataClass.swift index c3f7422..3f24ea1 100644 --- a/Zockerhoehle/CDModel/Console+CoreDataClass.swift +++ b/Zockerhoehle/CDModel/Console+CoreDataClass.swift @@ -40,7 +40,7 @@ public class Console: NSManagedObject, Identifiable { } } - public static func sortConsoleByNewestGame(consoleA : Console, consoleB : Console) -> Bool { + public static func compareConsolesByNewestGame(consoleA : Console, consoleB : Console) -> Bool { guard let newestGameConsoleA = (consoleA.games!.allObjects as! [Game]).max(by:{ Game.compareByCreationDate(gameA: $0, gameB: $1) }) else { return false } @@ -72,6 +72,7 @@ extension Console : Encodable { case accessories case games case logo + case logo_icloud_path } public func encode(to encoder: Encoder) throws { @@ -81,7 +82,7 @@ extension Console : Encodable { try container.encode(shortName ?? "", forKey: .shortName) try container.encode(generation, forKey: .generation) try container.encode(manufacturer ?? "", forKey: .manufacturer) - + try container.encode(logo_icloud_path ?? "", forKey: .logo_icloud_path) var accessoryList : [String] = [] for accessory in accessories! { if let accessory = accessory as? Accessory { diff --git a/Zockerhoehle/CDModel/Console+CoreDataProperties.swift b/Zockerhoehle/CDModel/Console+CoreDataProperties.swift index 30b9851..27317bb 100644 --- a/Zockerhoehle/CDModel/Console+CoreDataProperties.swift +++ b/Zockerhoehle/CDModel/Console+CoreDataProperties.swift @@ -26,6 +26,7 @@ extension Console { @NSManaged public var accessories: NSSet? @NSManaged public var games: NSSet? @NSManaged public var logo: Logo? + @NSManaged public var logo_icloud_path : String? } diff --git a/Zockerhoehle/ICloudManager.swift b/Zockerhoehle/ICloudManager.swift new file mode 100644 index 0000000..4f5fbf7 --- /dev/null +++ b/Zockerhoehle/ICloudManager.swift @@ -0,0 +1,48 @@ +// +// ICloudDriveManager.swift +// Zockerhoehle +// +// Created by Julian-Steffen Müller on 18.05.21. +// Copyright © 2021 Julian-Steffen Müller. All rights reserved. +// + +import Foundation +import UIKit + +class ICloudManager { + static let shared : ICloudManager = ICloudManager(); + + static let relative_seperator : [String] = ["iCloud~Zockerhoehle", "Documents"] + + static public func relativePathFrom(url : URL) -> String? { + return url.relative_path_after(pathComponent: relative_seperator) ?? .none + } + + static public func fileExists(at path : String?) -> Bool { + guard let path = path else { return false } + + if let url = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").appendingPathComponent(path) { + return FileManager.default.fileExists(atPath: url.path) + } + + return false; + } + + static public func imageFrom(path : String?) -> UIImage? { + guard let path = path else { return .none } + + do { + let url = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents").appendingPathComponent(path) + let imageData = try Data(contentsOf: url!) + + return UIImage(data: imageData)! + }catch { + return .none + } + + } + + private init() { + + } +} diff --git a/Zockerhoehle/Info.plist b/Zockerhoehle/Info.plist index fe4f05d..31da79b 100644 --- a/Zockerhoehle/Info.plist +++ b/Zockerhoehle/Info.plist @@ -2,6 +2,18 @@ + NSUbiquitousContainers + + iCloud.Zockerhoehle + + NSUbiquitousContainerIsDocumentScopePublic + + NSUbiquitousContainerName + Zockerhoehle + NSUbiquitousContainerSupportedFolderLevels + One + + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName @@ -19,7 +31,7 @@ CFBundleShortVersionString 1.0 CFBundleVersion - 1 + 7 LSApplicationCategoryType LSRequiresIPhoneOS diff --git a/Zockerhoehle/SceneDelegate.swift b/Zockerhoehle/SceneDelegate.swift index 0ea0984..4822716 100644 --- a/Zockerhoehle/SceneDelegate.swift +++ b/Zockerhoehle/SceneDelegate.swift @@ -25,9 +25,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UIApplicationDelegate { if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) - window.rootViewController = UIHostingController(rootView: MainView() + let contentView = MainView() .environmentObject(self.gameSeriesStore) - .environmentObject(self.consolesStore)) + .environmentObject(self.consolesStore) + .environment(\.managedObjectContext, CDManager.shared.viewContext) + + window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() } diff --git a/Zockerhoehle/Utils/URLExtension.swift b/Zockerhoehle/Utils/URLExtension.swift new file mode 100644 index 0000000..db99382 --- /dev/null +++ b/Zockerhoehle/Utils/URLExtension.swift @@ -0,0 +1,35 @@ +// +// URLExtension.swift +// Zockerhoehle +// +// Created by Julian-Steffen Müller on 15.05.21. +// Copyright © 2021 Julian-Steffen Müller. All rights reserved. +// + +import Foundation + +extension URL { + /* + This function created + */ + func relative_path_after(pathComponent seperator_components : [String]) -> String? { + var seperators = seperator_components + + var url = URL(string: "") + for path_component in pathComponents { + if seperators.count == 0 { + if url == .none { + url = URL(string: path_component) + }else { + url = url?.appendingPathComponent(path_component) + } + }else{ + if seperators.first! == path_component { + seperators.removeFirst() + } + } + } + + return url?.absoluteString ?? .none + } +} diff --git a/Zockerhoehle/ViewModel/ConsoleViewModel.swift b/Zockerhoehle/ViewModel/ConsoleViewModel.swift index 526a49a..7bf17e5 100644 --- a/Zockerhoehle/ViewModel/ConsoleViewModel.swift +++ b/Zockerhoehle/ViewModel/ConsoleViewModel.swift @@ -10,7 +10,7 @@ import SwiftUI import Combine class ConsoleViewModel : ObservableObject { - var objectWillChange = ObservableObjectPublisher() + //var objectWillChange = ObservableObjectPublisher() init(_ console: Console) { self.console = console @@ -25,7 +25,7 @@ class ConsoleViewModel : ObservableObject { } } - var name : String { + @Published var name : String { didSet { guard let console = console else { return } console.name = name diff --git a/Zockerhoehle/Views/ConsoleAllView.swift b/Zockerhoehle/Views/ConsoleAllView.swift index 7611bde..4895478 100644 --- a/Zockerhoehle/Views/ConsoleAllView.swift +++ b/Zockerhoehle/Views/ConsoleAllView.swift @@ -12,18 +12,23 @@ import QGrid struct ModalAddConsoleToLibrary : View { @Environment(\.presentationMode) var presentationMode: Binding + @Environment(\.managedObjectContext) private var viewContext @State var modalConsoleName : String = "" + @State var modalConsoleLogoICloudPath : String? = .none - private func addConsoleToLibrary() { - //TODO - } + @State var isImporting : Bool = false + + let defaultImage = UIImage() var addButton : some View { return Button(action: { - addConsoleToLibrary() + let console = Console(context: viewContext) + console.name = self.modalConsoleName + console.logo_icloud_path = self.modalConsoleLogoICloudPath + self.presentationMode.wrappedValue.dismiss()}, - label: { Text("Add") }) + label: { Text("Fertig") }) .disabled(self.modalConsoleName.trimmingCharacters(in: .whitespacesAndNewlines) == "" ) } @@ -31,40 +36,55 @@ struct ModalAddConsoleToLibrary : View { NavigationView { Form { TextField("Name", text: $modalConsoleName) + Image(uiImage: ICloudManager.imageFrom(path: self.modalConsoleLogoICloudPath) ?? defaultImage) + .resizable() + .scaledToFit() + Button(action: { + isImporting = true; + }, label: { Text("Bla")}) } .font(.caption) - .navigationBarTitle(Text("New Console")) + .navigationBarTitle(Text("Neue Konsole anlegen")) .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()) + .fileImporter( + isPresented: $isImporting, + 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)) { + self.modalConsoleLogoICloudPath = ICloudManager.relativePathFrom(url: selectedFile) + + print("Selected Image in iCloud Path \(self.modalConsoleLogoICloudPath ?? "n/a")") + }else{ + Alert(title: Text("Falscher Ordner")) + print("Außerhalb \(selectedFile.relativeString)") + } + }catch{ + print("ConsoleAllView::ModalAddConsoleToLibrary Error getting result '\(result)'") + } + + } } } } struct ConsolesListView : View { - @EnvironmentObject var consoleStore : ConsoleStore + @Environment(\.managedObjectContext) private var viewContext + @FetchRequest(entity: Console.entity(), sortDescriptors: [NSSortDescriptor(key: "manufacturer", ascending: true), NSSortDescriptor(key: "generation", ascending: true), NSSortDescriptor(key: "name", ascending: true)]) + var consoles: FetchedResults + @State var showAddConsoleToLibraryModal: Bool = false var body: some View { - /*List(consoleStore.consoles) {console in - NavigationLink(destination: ConsoleLibraryView(console: console)) { - HStack { - if console.logo?.image != nil { - Image(uiImage: console.logo!.image!).resizable().frame(width: 50.0, height: 50.0) - }else { - Image(systemName: "stop").resizable().frame(width: 50.0, height: 50.0) - } - - VStack(alignment: .leading) { - Text("\(console.name)") - Text("Spiele: \(console.games.filter({($0 as! Game).inWishlist == false}).count), Zubehör: \(console.accessories.filter({($0 as! Accessory).inWishlist == false}).count)") - } - } - } - }*/ - QGrid(consoleStore.consoles, columns: 4, columnsInLandscape: 6) { + QGrid(consoles, columns: 4, columnsInLandscape: 6) { GridCell(console: $0) } .padding() @@ -82,27 +102,28 @@ struct ConsolesListView : View { } struct GridCell: View { - var console: Console - - var body: some View { - VStack() { - if console.logo?.image != nil { - NavigationLink(destination: ConsoleLibraryView(console: console)) { - VStack { - Image(uiImage: (console.logo?.image)!) - .resizable() - .scaledToFit() - //.shadow(color: .primary, radius: 5) - .padding([.horizontal, .top], 7) - Text(console.shortName ?? console.name!).lineLimit(1) + var console: Console + let defaultImage = UIImage() + + var body: some View { + VStack() { + if ICloudManager.fileExists(at: console.logo_icloud_path) { + NavigationLink(destination: ConsoleLibraryView(console: console)) { + VStack { + Image(uiImage: ICloudManager.imageFrom(path: console.logo_icloud_path) ?? defaultImage) + .resizable() + .scaledToFit() + //.shadow(color: .primary, radius: 5) + .padding([.horizontal, .top], 7) + Text(console.shortName ?? console.name!).lineLimit(1) + } + }.buttonStyle(PlainButtonStyle()) + + }else{ + NavigationLink(destination: ConsoleLibraryView(console: console)) { + Text(console.shortName ?? console.name!) } - }.buttonStyle(PlainButtonStyle()) - - }else{ - Text("mu") + } } - // - //Text(person.lastName).lineLimit(1) } - } } diff --git a/Zockerhoehle/Views/GamePickupsView.swift b/Zockerhoehle/Views/GamePickupsView.swift index f104e27..33802cf 100644 --- a/Zockerhoehle/Views/GamePickupsView.swift +++ b/Zockerhoehle/Views/GamePickupsView.swift @@ -18,6 +18,6 @@ struct GamePickupsView: View { } } .padding(.top) // Workaround, damit die Liste beim scrollen nicht unter der NavigationView angezeigt wird - .navigationBarTitle(Text("Latest Pickups")) + .navigationBarTitle(Text("Zuletzt gehortete Spiele")) } } diff --git a/Zockerhoehle/Views/Overview.swift b/Zockerhoehle/Views/Overview.swift index ffa9794..03b967a 100644 --- a/Zockerhoehle/Views/Overview.swift +++ b/Zockerhoehle/Views/Overview.swift @@ -34,22 +34,39 @@ struct OverviewHeader : View { } } +struct EmptyCategory : View { + private var emptyMessage : String + + init(_ emptyMessage: String) { + self.emptyMessage = emptyMessage + } + + var body : some View { + HStack() { + Spacer() + Text("Keine Spiele in der Zockerhöhle!").frame(height: 75) + Spacer() + } + } +} + struct Overview: View { @EnvironmentObject var consoleStore : ConsoleStore @ObservedObject var gameStore : GameStore = GameStore(sortDescriptors: [NSSortDescriptor(key: "createdAt", ascending: false), NSSortDescriptor(key: "name", ascending: true)], fetchLimit: 10) @EnvironmentObject var gameSeriesStore : GameSeriesStore @ObservedObject var featuredStore = FeaturedStore() + var ConsolesOverview : some View { VStack(alignment: .leading) { OverviewHeader(title: "Aktivste Konsolen", destination: ConsolesListView()) if consoleStore.consoles.count == 0 { - Text("No consoles") + EmptyCategory("Keine Konsolen in der Zockerhöhle!") }else{ ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: 0) { - ForEach(consoleStore.consoles.sorted(by: { Console.sortConsoleByNewestGame(consoleA: $0, consoleB: $1)})) { console in + ForEach(consoleStore.consoles.sorted(by: { Console.compareConsolesByNewestGame(consoleA: $0, consoleB: $1)})) { console in NavigationLink(destination: ConsoleLibraryView(console: console)) { VStack(alignment: .leading) { Group { @@ -80,10 +97,10 @@ struct Overview: View { var GamesOverview : some View { VStack(alignment: .leading) { //TODO - OverviewHeader(title: "Latest Game Pickups", destination: GamePickupsView()) + OverviewHeader(title: "Zuletzt gehortete Spiele", destination: GamePickupsView()) if gameStore.games.count == 0 { - Text("No Games") + EmptyCategory("Keine Spiele in der Zockerhöhle") }else { ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: 0) { @@ -121,7 +138,7 @@ struct Overview: View { OverviewHeader(title: "Spielserien", destination: GameSeriesAllView()) if gameSeriesStore.gameSeries.count == 0 { - Text("No game series") + EmptyCategory("Keine Spieleserie angelegt!") }else{ ScrollView(.horizontal, showsIndicators: false) { HStack(alignment: .top, spacing: 0) { @@ -177,7 +194,7 @@ struct Overview: View { } }.buttonStyle(PlainButtonStyle()) }else { - Text("No featured console") + EmptyCategory("Nichts aktuelles :(") } Spacer() } diff --git a/Zockerhoehle/Zockerhoehle.entitlements b/Zockerhoehle/Zockerhoehle.entitlements index 8497878..40c4a24 100644 --- a/Zockerhoehle/Zockerhoehle.entitlements +++ b/Zockerhoehle/Zockerhoehle.entitlements @@ -11,6 +11,11 @@ com.apple.developer.icloud-services CloudKit + CloudDocuments + + com.apple.developer.ubiquity-container-identifiers + + iCloud.Zockerhoehle diff --git a/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents b/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents index 937d382..2f0c822 100644 --- a/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents +++ b/Zockerhoehle/Zockerhoehle.xcdatamodeld/Zockerhoehle.xcdatamodel/contents @@ -13,6 +13,7 @@ + @@ -59,7 +60,7 @@ - + diff --git a/ZockerhoehleTests/Info.plist b/ZockerhoehleTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/ZockerhoehleTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/ZockerhoehleTests/ZockerhoehleTests.swift b/ZockerhoehleTests/ZockerhoehleTests.swift new file mode 100644 index 0000000..bd119de --- /dev/null +++ b/ZockerhoehleTests/ZockerhoehleTests.swift @@ -0,0 +1,45 @@ +// +// ZockerhoehleTests.swift +// ZockerhoehleTests +// +// Created by Julian-Steffen Müller on 17.05.21. +// Copyright © 2021 Julian-Steffen Müller. All rights reserved. +// + +import XCTest +@testable import Zockerhoehle + +class ZockerhoehleTests: XCTestCase { + + let icloud_drive_seperators : [String] = ["iCloud~Zockerhoehle", "Documents"] + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func test_url_icloud_split() throws { + // VALID FILES + let url_valid_file : URL = URL(string: "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~Zockerhoehle/Documents/backup.json")! + let url_valid_file_in_subfolder : URL = URL(string: "file:///private/var/mobile/Library/Mobile%20Documents/iCloud~Zockerhoehle/Documents/consoles/ps4.png")! + + XCTAssertEqual(url_valid_file.relative_path_after(pathComponent: icloud_drive_seperators), "backup.json") + XCTAssertEqual(url_valid_file_in_subfolder.relative_path_after(pathComponent: icloud_drive_seperators), "consoles/ps4.png") + + //INVALID URL + let url_unvalid_url_2 : URL = URL(string: "http://mueller.haus")! + + XCTAssertEqual(url_unvalid_url_2.relative_path_after(pathComponent: icloud_drive_seperators), .none) + + + // INVALID FILE + let url_unvalid_file : URL = URL(string: "file:///private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/photo.jpg")! + + XCTAssertEqual(url_unvalid_file.relative_path_after(pathComponent: icloud_drive_seperators), .none) + + } + +}