New Baseline -- Added basic Cloudkit support and changed to julian.steffen@me.com Apple Developer Account

This commit is contained in:
2021-05-14 17:42:35 +02:00
parent 6e548640ff
commit bcf3098bfe
63 changed files with 2378 additions and 1086 deletions

View File

@@ -1 +0,0 @@
github "saoudrizwan/Disk"

View File

@@ -3,61 +3,87 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
B926F12D2149B264004D36B7 /* FlockeEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = B926F12C2149B264004D36B7 /* FlockeEntry.swift */; };
B926F131214AD9E4004D36B7 /* GameCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B926F130214AD9E4004D36B7 /* GameCollection.swift */; };
B926F139214AE884004D36B7 /* FlockeWS.swift in Sources */ = {isa = PBXBuildFile; fileRef = B926F138214AE884004D36B7 /* FlockeWS.swift */; };
B926F13C214C44FE004D36B7 /* Attachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = B926F13B214C44FE004D36B7 /* Attachment.swift */; };
B90E03EB238557D900E79643 /* LibraryImport.swift in Sources */ = {isa = PBXBuildFile; fileRef = B90E03EA238557D900E79643 /* LibraryImport.swift */; };
B926F14721502D53004D36B7 /* CodableExtensionAny.swift in Sources */ = {isa = PBXBuildFile; fileRef = B926F14621502D53004D36B7 /* CodableExtensionAny.swift */; };
B926F14A21502DE1004D36B7 /* ConsoleEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = B926F14921502DE1004D36B7 /* ConsoleEntry.swift */; };
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 */; };
B94112E2233B55B100159AE4 /* ConsoleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94112E1233B55B100159AE4 /* ConsoleViewModel.swift */; };
B94112E4233B597D00159AE4 /* ConsoleEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94112E3233B597D00159AE4 /* ConsoleEditView.swift */; };
B94CB4FF22D1352F0029BFAD /* Accessory+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F322D1352F0029BFAD /* Accessory+CoreDataClass.swift */; };
B94CB50022D1352F0029BFAD /* Accessory+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F422D1352F0029BFAD /* Accessory+CoreDataProperties.swift */; };
B94CB50122D1352F0029BFAD /* Cover+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F522D1352F0029BFAD /* Cover+CoreDataClass.swift */; };
B94CB50222D1352F0029BFAD /* Cover+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F622D1352F0029BFAD /* Cover+CoreDataProperties.swift */; };
B94CB50322D1352F0029BFAD /* Game+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F722D1352F0029BFAD /* Game+CoreDataClass.swift */; };
B94CB50422D1352F0029BFAD /* Game+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F822D1352F0029BFAD /* Game+CoreDataProperties.swift */; };
B94CB50522D1352F0029BFAD /* Console+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F922D1352F0029BFAD /* Console+CoreDataClass.swift */; };
B94CB50622D1352F0029BFAD /* Console+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4FA22D1352F0029BFAD /* Console+CoreDataProperties.swift */; };
B94CB50722D1352F0029BFAD /* GameSeries+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4FB22D1352F0029BFAD /* GameSeries+CoreDataClass.swift */; };
B94CB50822D1352F0029BFAD /* GameSeries+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4FC22D1352F0029BFAD /* GameSeries+CoreDataProperties.swift */; };
B94CB50922D1352F0029BFAD /* Logo+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4FD22D1352F0029BFAD /* Logo+CoreDataClass.swift */; };
B94CB50A22D1352F0029BFAD /* Logo+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4FE22D1352F0029BFAD /* Logo+CoreDataProperties.swift */; };
B94CB53722D3B3CC0029BFAD /* GameDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */; };
B97AD2E0244CE4B4004AF00D /* Disk in Frameworks */ = {isa = PBXBuildFile; productRef = B97AD2DF244CE4B4004AF00D /* Disk */; };
B9839983233A086A002F9946 /* Overview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9839982233A086A002F9946 /* Overview.swift */; };
B983998C233A0BC9002F9946 /* Cover+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94CB4F522D1352F0029BFAD /* Cover+CoreDataClass.swift */; };
B9839991233A0E16002F9946 /* GameSeriesCover+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9839985233A0ADB002F9946 /* GameSeriesCover+CoreDataProperties.swift */; };
B9839992233A0E19002F9946 /* GameSeriesCover+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9839984233A0ADB002F9946 /* GameSeriesCover+CoreDataClass.swift */; };
B98A734D22BAD27D00FB3410 /* Zockerhoehle.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B98A731722BA9E4600FB3410 /* Zockerhoehle.xcdatamodeld */; };
B98A735E22BFAA4B00FB3410 /* ConsoleLibraryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B926F12E2149B6F5004D36B7 /* ConsoleLibraryViewController.swift */; };
B98A736022C1738800FB3410 /* CDManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98A735F22C1738800FB3410 /* CDManager.swift */; };
B98B2FAA2328DF3400606DC4 /* GameSeriesStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98B2FA92328DF3400606DC4 /* GameSeriesStore.swift */; };
B98B2FAC232C0F8C00606DC4 /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98B2FAB232C0F8C00606DC4 /* ImagePicker.swift */; };
B98CBBD2264E933A00B1B7AC /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B98CBBD1264E933A00B1B7AC /* CloudKit.framework */; };
B98CBBDA264E98DD00B1B7AC /* GameSeries+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = B98CBBD7264E98DD00B1B7AC /* GameSeries+CoreDataProperties.swift */; };
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 */; };
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 */; };
B9BCF4CA2168ACB600ECBAAC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B9BCF4C92168ACB600ECBAAC /* LaunchScreen.storyboard */; };
B9D2C6F722E98ED800797F67 /* AccessoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9D2C6F622E98ED800797F67 /* AccessoryViewModel.swift */; };
B9F002E52187AA3200E12B0A /* FlockeConnector.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F002E42187AA3200E12B0A /* FlockeConnector.swift */; };
B9E2A079233B69D400EAEB14 /* GameSeriesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9E2A078233B69D400EAEB14 /* GameSeriesViewModel.swift */; };
B9E2A07B233B6A8F00EAEB14 /* GameSeriesEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9E2A07A233B6A8F00EAEB14 /* GameSeriesEditView.swift */; };
B9E2A07E233B6E4F00EAEB14 /* ConsoleAllView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9E2A07C233B6E4F00EAEB14 /* ConsoleAllView.swift */; };
B9E2A081233BA62100EAEB14 /* FeaturedStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9E2A080233BA62100EAEB14 /* FeaturedStore.swift */; };
B9EC09822383F94B004BC9AB /* LibraryExport.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EC09812383F94B004BC9AB /* LibraryExport.swift */; };
B9EC098523854C24004BC9AB /* QGrid in Frameworks */ = {isa = PBXBuildFile; productRef = B9EC098423854C24004BC9AB /* QGrid */; };
B9EC0987238555BF004BC9AB /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9EC0986238555BF004BC9AB /* SettingsView.swift */; };
B9F44ABA22F312E600FC6B29 /* ConsoleLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44AB922F312E600FC6B29 /* ConsoleLibraryView.swift */; };
B9F44ABE22F31DEF00FC6B29 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44ABD22F31DEF00FC6B29 /* SceneDelegate.swift */; };
B9F44AE322F3216F00FC6B29 /* ConsolesListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44AE222F3216F00FC6B29 /* ConsolesListView.swift */; };
B9F44AE522F418F600FC6B29 /* ConsoleStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44AE422F418F600FC6B29 /* ConsoleStore.swift */; };
B9F44AE722F429D300FC6B29 /* GameStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44AE622F429D300FC6B29 /* GameStore.swift */; };
B9F44AE922F4655600FC6B29 /* AccessoryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9F44AE822F4655600FC6B29 /* AccessoryStore.swift */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
B983997A233A0295002F9946 /* Embed Watch Content */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
dstSubfolderSpec = 16;
files = (
);
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
B926F12C2149B264004D36B7 /* FlockeEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlockeEntry.swift; sourceTree = "<group>"; };
B926F12E2149B6F5004D36B7 /* ConsoleLibraryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLibraryViewController.swift; sourceTree = "<group>"; };
B926F130214AD9E4004D36B7 /* GameCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameCollection.swift; sourceTree = "<group>"; };
B926F138214AE884004D36B7 /* FlockeWS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlockeWS.swift; sourceTree = "<group>"; };
B926F13B214C44FE004D36B7 /* Attachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Attachment.swift; sourceTree = "<group>"; };
B90E03EA238557D900E79643 /* LibraryImport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryImport.swift; sourceTree = "<group>"; };
B926F14621502D53004D36B7 /* CodableExtensionAny.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableExtensionAny.swift; sourceTree = "<group>"; };
B926F14921502DE1004D36B7 /* ConsoleEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleEntry.swift; sourceTree = "<group>"; };
B93C1B9921496BFD0014FD6E /* Zockerhoehle.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Zockerhoehle.app; sourceTree = BUILT_PRODUCTS_DIR; };
B93C1B9C21496BFD0014FD6E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
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>"; };
B94112E1233B55B100159AE4 /* ConsoleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleViewModel.swift; sourceTree = "<group>"; };
B94112E3233B597D00159AE4 /* ConsoleEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleEditView.swift; sourceTree = "<group>"; };
B94CB4F322D1352F0029BFAD /* Accessory+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Accessory+CoreDataClass.swift"; sourceTree = "<group>"; };
B94CB4F422D1352F0029BFAD /* Accessory+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Accessory+CoreDataProperties.swift"; sourceTree = "<group>"; };
B94CB4F522D1352F0029BFAD /* Cover+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Cover+CoreDataClass.swift"; sourceTree = "<group>"; };
@@ -66,20 +92,35 @@
B94CB4F822D1352F0029BFAD /* Game+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Game+CoreDataProperties.swift"; sourceTree = "<group>"; };
B94CB4F922D1352F0029BFAD /* Console+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Console+CoreDataClass.swift"; sourceTree = "<group>"; };
B94CB4FA22D1352F0029BFAD /* Console+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Console+CoreDataProperties.swift"; sourceTree = "<group>"; };
B94CB4FB22D1352F0029BFAD /* GameSeries+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GameSeries+CoreDataClass.swift"; sourceTree = "<group>"; };
B94CB4FC22D1352F0029BFAD /* GameSeries+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GameSeries+CoreDataProperties.swift"; sourceTree = "<group>"; };
B94CB4FD22D1352F0029BFAD /* Logo+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logo+CoreDataClass.swift"; sourceTree = "<group>"; };
B94CB4FE22D1352F0029BFAD /* Logo+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Logo+CoreDataProperties.swift"; sourceTree = "<group>"; };
B94CB53522D3708F0029BFAD /* Zockerhoehle copy-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Zockerhoehle copy-Info.plist"; path = "/Users/julian/Entwicklung/Zockerhoehle/Zockerhoehle copy-Info.plist"; sourceTree = "<absolute>"; };
B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameDetailView.swift; sourceTree = "<group>"; };
B9839982233A086A002F9946 /* Overview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Overview.swift; sourceTree = "<group>"; };
B9839984233A0ADB002F9946 /* GameSeriesCover+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GameSeriesCover+CoreDataClass.swift"; sourceTree = "<group>"; };
B9839985233A0ADB002F9946 /* GameSeriesCover+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GameSeriesCover+CoreDataProperties.swift"; sourceTree = "<group>"; };
B98A731822BA9E4600FB3410 /* Zockerhoehle.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Zockerhoehle.xcdatamodel; sourceTree = "<group>"; };
B98A735F22C1738800FB3410 /* CDManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CDManager.swift; sourceTree = "<group>"; };
B98B2FA92328DF3400606DC4 /* GameSeriesStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesStore.swift; sourceTree = "<group>"; };
B98B2FAB232C0F8C00606DC4 /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = "<group>"; };
B98CBBD0264E933400B1B7AC /* Zockerhoehle.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Zockerhoehle.entitlements; sourceTree = "<group>"; };
B98CBBD1264E933A00B1B7AC /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; };
B98CBBD7264E98DD00B1B7AC /* GameSeries+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GameSeries+CoreDataProperties.swift"; sourceTree = "<group>"; };
B98CBBD8264E98DD00B1B7AC /* Console+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Console+CoreDataProperties.swift"; sourceTree = "<group>"; };
B98CBBD9264E98DD00B1B7AC /* GameSeries+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GameSeries+CoreDataClass.swift"; sourceTree = "<group>"; };
B9A0550022F8C22D0054D9A0 /* GameSeriesAllView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesAllView.swift; sourceTree = "<group>"; };
B9A0550222F8C2740054D9A0 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
B9A0550422F8CB400054D9A0 /* GameSeriesLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesLibraryView.swift; sourceTree = "<group>"; };
B9BCF4C92168ACB600ECBAAC /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
B9D2C6F622E98ED800797F67 /* AccessoryViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessoryViewModel.swift; sourceTree = "<group>"; };
B9F002E42187AA3200E12B0A /* FlockeConnector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlockeConnector.swift; sourceTree = "<group>"; };
B9E2A078233B69D400EAEB14 /* GameSeriesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesViewModel.swift; sourceTree = "<group>"; };
B9E2A07A233B6A8F00EAEB14 /* GameSeriesEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSeriesEditView.swift; sourceTree = "<group>"; };
B9E2A07C233B6E4F00EAEB14 /* ConsoleAllView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConsoleAllView.swift; sourceTree = "<group>"; };
B9E2A080233BA62100EAEB14 /* FeaturedStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeaturedStore.swift; sourceTree = "<group>"; };
B9EC09812383F94B004BC9AB /* LibraryExport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryExport.swift; sourceTree = "<group>"; };
B9EC0986238555BF004BC9AB /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
B9F44AB922F312E600FC6B29 /* ConsoleLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleLibraryView.swift; sourceTree = "<group>"; };
B9F44ABD22F31DEF00FC6B29 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
B9F44AE222F3216F00FC6B29 /* ConsolesListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsolesListView.swift; sourceTree = "<group>"; };
B9F44AE422F418F600FC6B29 /* ConsoleStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleStore.swift; sourceTree = "<group>"; };
B9F44AE622F429D300FC6B29 /* GameStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameStore.swift; sourceTree = "<group>"; };
B9F44AE822F4655600FC6B29 /* AccessoryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessoryStore.swift; sourceTree = "<group>"; };
@@ -90,6 +131,9 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B98CBBD2264E933A00B1B7AC /* CloudKit.framework in Frameworks */,
B9EC098523854C24004BC9AB /* QGrid in Frameworks */,
B97AD2E0244CE4B4004AF00D /* Disk in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -99,36 +143,26 @@
B90B64A12235909900E54BA3 /* Frameworks */ = {
isa = PBXGroup;
children = (
B98CBBD1264E933A00B1B7AC /* CloudKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
B926F134214AE2C0004D36B7 /* Model */ = {
isa = PBXGroup;
children = (
B926F130214AD9E4004D36B7 /* GameCollection.swift */,
B926F13B214C44FE004D36B7 /* Attachment.swift */,
B926F12C2149B264004D36B7 /* FlockeEntry.swift */,
B926F14921502DE1004D36B7 /* ConsoleEntry.swift */,
);
path = Model;
sourceTree = "<group>";
};
B926F135214AE2D4004D36B7 /* ViewController */ = {
isa = PBXGroup;
children = (
B926F12E2149B6F5004D36B7 /* ConsoleLibraryViewController.swift */,
);
path = ViewController;
sourceTree = "<group>";
};
B926F136214AE2E3004D36B7 /* Views */ = {
isa = PBXGroup;
children = (
B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */,
B9E2A07A233B6A8F00EAEB14 /* GameSeriesEditView.swift */,
B9A0550422F8CB400054D9A0 /* GameSeriesLibraryView.swift */,
B9A0550022F8C22D0054D9A0 /* GameSeriesAllView.swift */,
B93D60CD22D88F5700DD390F /* AccessoryDetailView.swift */,
B94CB53622D3B3CC0029BFAD /* GameDetailView.swift */,
B94112E3233B597D00159AE4 /* ConsoleEditView.swift */,
B9F44AB922F312E600FC6B29 /* ConsoleLibraryView.swift */,
B9F44AE222F3216F00FC6B29 /* ConsolesListView.swift */,
B9E2A07C233B6E4F00EAEB14 /* ConsoleAllView.swift */,
B94112DF233A4EF800159AE4 /* GamePickupsView.swift */,
B9A0550222F8C2740054D9A0 /* MainView.swift */,
B9EC0986238555BF004BC9AB /* SettingsView.swift */,
B9839982233A086A002F9946 /* Overview.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -136,20 +170,15 @@
B926F13A214AF21B004D36B7 /* Utils */ = {
isa = PBXGroup;
children = (
B926F138214AE884004D36B7 /* FlockeWS.swift */,
B9F002E42187AA3200E12B0A /* FlockeConnector.swift */,
B926F14621502D53004D36B7 /* CodableExtensionAny.swift */,
B94112DD233A37DD00159AE4 /* DateConversion.swift */,
B98B2FAB232C0F8C00606DC4 /* ImagePicker.swift */,
B9EC09812383F94B004BC9AB /* LibraryExport.swift */,
B90E03EA238557D900E79643 /* LibraryImport.swift */,
);
path = Utils;
sourceTree = "<group>";
};
B926F14821502D7F004D36B7 /* Lib */ = {
isa = PBXGroup;
children = (
B926F14621502D53004D36B7 /* CodableExtensionAny.swift */,
);
path = Lib;
sourceTree = "<group>";
};
B93C1B9021496BFD0014FD6E = {
isa = PBXGroup;
children = (
@@ -171,13 +200,11 @@
B93C1B9B21496BFD0014FD6E /* Zockerhoehle */ = {
isa = PBXGroup;
children = (
B98CBBD0264E933400B1B7AC /* Zockerhoehle.entitlements */,
B93D60CF22E5006F00DD390F /* ViewModel */,
B98A734622BACA9C00FB3410 /* CDModel */,
B926F14821502D7F004D36B7 /* Lib */,
B926F13A214AF21B004D36B7 /* Utils */,
B926F136214AE2E3004D36B7 /* Views */,
B926F135214AE2D4004D36B7 /* ViewController */,
B926F134214AE2C0004D36B7 /* Model */,
B93C1B9C21496BFD0014FD6E /* AppDelegate.swift */,
B9F44ABD22F31DEF00FC6B29 /* SceneDelegate.swift */,
B9BCF4C92168ACB600ECBAAC /* LaunchScreen.storyboard */,
@@ -192,11 +219,15 @@
B93D60CF22E5006F00DD390F /* ViewModel */ = {
isa = PBXGroup;
children = (
B94112E1233B55B100159AE4 /* ConsoleViewModel.swift */,
B93D60D022E5009700DD390F /* GameViewModel.swift */,
B9E2A078233B69D400EAEB14 /* GameSeriesViewModel.swift */,
B9D2C6F622E98ED800797F67 /* AccessoryViewModel.swift */,
B9F44AE422F418F600FC6B29 /* ConsoleStore.swift */,
B9F44AE622F429D300FC6B29 /* GameStore.swift */,
B9F44AE822F4655600FC6B29 /* AccessoryStore.swift */,
B98B2FA92328DF3400606DC4 /* GameSeriesStore.swift */,
B9E2A080233BA62100EAEB14 /* FeaturedStore.swift */,
);
path = ViewModel;
sourceTree = "<group>";
@@ -204,6 +235,11 @@
B98A734622BACA9C00FB3410 /* CDModel */ = {
isa = PBXGroup;
children = (
B98CBBD8264E98DD00B1B7AC /* Console+CoreDataProperties.swift */,
B98CBBD9264E98DD00B1B7AC /* GameSeries+CoreDataClass.swift */,
B98CBBD7264E98DD00B1B7AC /* GameSeries+CoreDataProperties.swift */,
B9839984233A0ADB002F9946 /* GameSeriesCover+CoreDataClass.swift */,
B9839985233A0ADB002F9946 /* GameSeriesCover+CoreDataProperties.swift */,
B94CB4F322D1352F0029BFAD /* Accessory+CoreDataClass.swift */,
B94CB4F422D1352F0029BFAD /* Accessory+CoreDataProperties.swift */,
B94CB4F522D1352F0029BFAD /* Cover+CoreDataClass.swift */,
@@ -212,8 +248,6 @@
B94CB4F822D1352F0029BFAD /* Game+CoreDataProperties.swift */,
B94CB4F922D1352F0029BFAD /* Console+CoreDataClass.swift */,
B94CB4FA22D1352F0029BFAD /* Console+CoreDataProperties.swift */,
B94CB4FB22D1352F0029BFAD /* GameSeries+CoreDataClass.swift */,
B94CB4FC22D1352F0029BFAD /* GameSeries+CoreDataProperties.swift */,
B94CB4FD22D1352F0029BFAD /* Logo+CoreDataClass.swift */,
B94CB4FE22D1352F0029BFAD /* Logo+CoreDataProperties.swift */,
);
@@ -231,12 +265,17 @@
B93C1B9621496BFD0014FD6E /* Frameworks */,
B93C1B9721496BFD0014FD6E /* Resources */,
B90B64A4223590DA00E54BA3 /* ShellScript */,
B983997A233A0295002F9946 /* Embed Watch Content */,
);
buildRules = (
);
dependencies = (
);
name = Zockerhoehle;
packageProductDependencies = (
B9EC098423854C24004BC9AB /* QGrid */,
B97AD2DF244CE4B4004AF00D /* Disk */,
);
productName = Zockerhoehle;
productReference = B93C1B9921496BFD0014FD6E /* Zockerhoehle.app */;
productType = "com.apple.product-type.application";
@@ -247,8 +286,8 @@
B93C1B9121496BFD0014FD6E /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0940;
LastUpgradeCheck = 0940;
LastSwiftUpdateCheck = 1100;
LastUpgradeCheck = 1250;
ORGANIZATIONNAME = "Julian-Steffen Müller";
TargetAttributes = {
B93C1B9821496BFD0014FD6E = {
@@ -266,6 +305,10 @@
Base,
);
mainGroup = B93C1B9021496BFD0014FD6E;
packageReferences = (
B9EC098323854C24004BC9AB /* XCRemoteSwiftPackageReference "QGrid" */,
B97AD2DE244CE4B4004AF00D /* XCRemoteSwiftPackageReference "Disk" */,
);
productRefGroup = B93C1B9A21496BFD0014FD6E /* Products */;
projectDirPath = "";
projectRoot = "";
@@ -296,7 +339,6 @@
inputFileListPaths = (
);
inputPaths = (
"$(SRCROOT)/Carthage/Build/iOS/Disk.framework",
);
outputFileListPaths = (
);
@@ -304,7 +346,8 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/usr/local/bin/carthage copy-frameworks\n";
shellScript = "
";
};
/* End PBXShellScriptBuildPhase section */
@@ -313,39 +356,50 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B926F139214AE884004D36B7 /* FlockeWS.swift in Sources */,
B926F13C214C44FE004D36B7 /* Attachment.swift in Sources */,
B98CBBDD264E98F300B1B7AC /* Console+CoreDataClass.swift in Sources */,
B98CBBDB264E98DE00B1B7AC /* Console+CoreDataProperties.swift in Sources */,
B98B2FAA2328DF3400606DC4 /* GameSeriesStore.swift in Sources */,
B9839991233A0E16002F9946 /* GameSeriesCover+CoreDataProperties.swift in Sources */,
B983998C233A0BC9002F9946 /* Cover+CoreDataClass.swift in Sources */,
B9839992233A0E19002F9946 /* GameSeriesCover+CoreDataClass.swift in Sources */,
B94112E0233A4EF800159AE4 /* GamePickupsView.swift in Sources */,
B94112E2233B55B100159AE4 /* ConsoleViewModel.swift in Sources */,
B9A0550522F8CB400054D9A0 /* GameSeriesLibraryView.swift in Sources */,
B9839983233A086A002F9946 /* Overview.swift in Sources */,
B93C1B9D21496BFD0014FD6E /* AppDelegate.swift in Sources */,
B94CB53722D3B3CC0029BFAD /* GameDetailView.swift in Sources */,
B94CB50322D1352F0029BFAD /* Game+CoreDataClass.swift in Sources */,
B94CB50822D1352F0029BFAD /* GameSeries+CoreDataProperties.swift in Sources */,
B98B2FAC232C0F8C00606DC4 /* ImagePicker.swift in Sources */,
B9F44ABA22F312E600FC6B29 /* ConsoleLibraryView.swift in Sources */,
B9F44ABE22F31DEF00FC6B29 /* SceneDelegate.swift in Sources */,
B9A0550322F8C2740054D9A0 /* MainView.swift in Sources */,
B9D2C6F722E98ED800797F67 /* AccessoryViewModel.swift in Sources */,
B9F002E52187AA3200E12B0A /* FlockeConnector.swift in Sources */,
B98CBBDA264E98DD00B1B7AC /* GameSeries+CoreDataProperties.swift in Sources */,
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 */,
B926F12D2149B264004D36B7 /* FlockeEntry.swift in Sources */,
B98A735E22BFAA4B00FB3410 /* ConsoleLibraryViewController.swift in Sources */,
B94CB50522D1352F0029BFAD /* Console+CoreDataClass.swift in Sources */,
B90E03EB238557D900E79643 /* LibraryImport.swift in Sources */,
B94CB50922D1352F0029BFAD /* Logo+CoreDataClass.swift in Sources */,
B94CB50622D1352F0029BFAD /* Console+CoreDataProperties.swift in Sources */,
B926F131214AD9E4004D36B7 /* GameCollection.swift in Sources */,
B98CBBDC264E98DE00B1B7AC /* GameSeries+CoreDataClass.swift in Sources */,
B9A0550122F8C22D0054D9A0 /* GameSeriesAllView.swift in Sources */,
B94112E4233B597D00159AE4 /* ConsoleEditView.swift in Sources */,
B94112DE233A37DD00159AE4 /* DateConversion.swift in Sources */,
B9E2A081233BA62100EAEB14 /* FeaturedStore.swift in Sources */,
B93D60CE22D88F5700DD390F /* AccessoryDetailView.swift in Sources */,
B926F14A21502DE1004D36B7 /* ConsoleEntry.swift in Sources */,
B94CB50722D1352F0029BFAD /* GameSeries+CoreDataClass.swift in Sources */,
B9EC09822383F94B004BC9AB /* LibraryExport.swift in Sources */,
B94CB50022D1352F0029BFAD /* Accessory+CoreDataProperties.swift in Sources */,
B9E2A07B233B6A8F00EAEB14 /* GameSeriesEditView.swift in Sources */,
B926F14721502D53004D36B7 /* CodableExtensionAny.swift in Sources */,
B94CB50422D1352F0029BFAD /* Game+CoreDataProperties.swift in Sources */,
B9F44AE922F4655600FC6B29 /* AccessoryStore.swift in Sources */,
B98A736022C1738800FB3410 /* CDManager.swift in Sources */,
B94CB50222D1352F0029BFAD /* Cover+CoreDataProperties.swift in Sources */,
B9F44AE322F3216F00FC6B29 /* ConsolesListView.swift in Sources */,
B94CB50122D1352F0029BFAD /* Cover+CoreDataClass.swift in Sources */,
B9E2A07E233B6E4F00EAEB14 /* ConsoleAllView.swift in Sources */,
B9EC0987238555BF004BC9AB /* SettingsView.swift in Sources */,
B9F44AE522F418F600FC6B29 /* ConsoleStore.swift in Sources */,
B9E2A079233B69D400EAEB14 /* GameSeriesViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -379,6 +433,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -440,6 +495,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@@ -472,10 +528,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "";
CODE_SIGN_ENTITLEMENTS = Zockerhoehle/Zockerhoehle.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = M9N7K3KZX9;
DEVELOPMENT_TEAM = 85J8CBD673;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Carthage/Build/iOS",
@@ -485,7 +541,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "de.mm-neuemedien.Zockerhoehle";
PRODUCT_BUNDLE_IDENTIFIER = haus.mueller.zockerhoehle;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
@@ -497,7 +553,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = "";
CODE_SIGN_ENTITLEMENTS = Zockerhoehle/Zockerhoehle.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = M9N7K3KZX9;
@@ -510,7 +566,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = "de.mm-neuemedien.Zockerhoehle";
PRODUCT_BUNDLE_IDENTIFIER = haus.mueller.zockerhoehle;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
@@ -541,6 +597,38 @@
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
B97AD2DE244CE4B4004AF00D /* XCRemoteSwiftPackageReference "Disk" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/saoudrizwan/Disk";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.6.4;
};
};
B9EC098323854C24004BC9AB /* XCRemoteSwiftPackageReference "QGrid" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Q-Mobile/QGrid";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.1.3;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
B97AD2DF244CE4B4004AF00D /* Disk */ = {
isa = XCSwiftPackageProductDependency;
package = B97AD2DE244CE4B4004AF00D /* XCRemoteSwiftPackageReference "Disk" */;
productName = Disk;
};
B9EC098423854C24004BC9AB /* QGrid */ = {
isa = XCSwiftPackageProductDependency;
package = B9EC098323854C24004BC9AB /* XCRemoteSwiftPackageReference "QGrid" */;
productName = QGrid;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */
B98A731722BA9E4600FB3410 /* Zockerhoehle.xcdatamodeld */ = {
isa = XCVersionGroup;

View File

@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:Zockerhoehle.xcodeproj">
location = "self:">
</FileRef>
</Workspace>

View File

@@ -0,0 +1,25 @@
{
"object": {
"pins": [
{
"package": "Disk",
"repositoryURL": "https://github.com/saoudrizwan/Disk",
"state": {
"branch": null,
"revision": "b0cb4fdf23e51849cc2460bdc6de795c3bcca99d",
"version": "0.6.4"
}
},
{
"package": "QGrid",
"repositoryURL": "https://github.com/Q-Mobile/QGrid",
"state": {
"branch": null,
"revision": "40607aec336a8097c94bcadb81762592d89073ac",
"version": "0.1.3"
}
}
]
},
"version": 1
}

View File

@@ -14,6 +14,16 @@
<key>orderHint</key>
<integer>0</integer>
</dict>
<key>ZockerhoehleWatch (Notification).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>ZockerhoehleWatch.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>

View File

@@ -23,8 +23,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
#endif
print("disFinishLaunchung")
FlockeWS.fetchEntries(for: GameCollection.consoleID)
//FlockeWS.fetchEntries(for: GameCollection.consoleID)
//TODO Game Serien entfernen
/*let gameSeriesEntities = ["Assasins Creed", "Mario", "Zelda", "Pikmin", "Heroes of Might and Magic", "Gears of War"]
for series in gameSeriesEntities {
let cdSeries = GameSeries(context: CDManager.shared.viewContext)@
cdSeries.uuid = UUID()
cdSeries.name = series
}*/
return true
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -2,7 +2,7 @@
"images" : [
{
"idiom" : "universal",
"filename" : "star.png",
"filename" : "Image.png",
"scale" : "1x"
},
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -2,15 +2,16 @@
"images" : [
{
"idiom" : "universal",
"filename" : "basket-supermarket.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "Image.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "Image-1.png",
"scale" : "3x"
}
],

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -26,7 +26,8 @@ class CDManager {
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Zockerhoehle")
let container = NSPersistentCloudKitContainer(name: "Zockerhoehle")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
@@ -43,6 +44,7 @@ class CDManager {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
@@ -53,10 +55,13 @@ class CDManager {
// MARK: - Core Data Saving support
func saveContext () {
persistentContainer.performBackgroundTask({(context) in
print("CHANGES: \(self.persistentContainer.viewContext.hasChanges)")
self.persistentContainer.performBackgroundTask({(context) in
print("Context \(context.hasChanges)")
if context.hasChanges {
do {
try context.save()
print("--------------------saved-------")
} catch {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

View File

@@ -18,3 +18,37 @@ public class Accessory: NSManagedObject, Identifiable {
return fetchRequest
}
}
/*
@NSManaged public var color: String?
@NSManaged public var manufacturer: String?
@NSManaged public var name: String
@NSManaged public var inWishlist: Bool
@NSManaged public var console: Console?
@NSManaged public var lentTo : String?
@NSManaged public var uuid : UUID
*/
extension Accessory : Encodable {
enum CodingKeys: String, CodingKey {
case uuid
case color
case manufacturer
case name
case inWishlist
case console
case lentTo
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(uuid, forKey: .uuid)
try container.encode(color, forKey: .color)
try container.encode(manufacturer, forKey: .manufacturer)
try container.encode(name, forKey: .name)
try container.encode(inWishlist, forKey: .inWishlist)
try container.encode(console?.uuid!.uuidString ?? "", forKey: .console)
try container.encode(lentTo, forKey: .lentTo)
}
}

View File

@@ -22,5 +22,7 @@ extension Accessory {
@NSManaged public var name: String
@NSManaged public var inWishlist: Bool
@NSManaged public var console: Console?
@NSManaged public var lentTo : String?
@NSManaged public var uuid : UUID
}

View File

@@ -9,14 +9,96 @@
import Foundation
import CoreData
import SwiftUI
import UIKit
@objc(Console)
public class Console: NSManagedObject, Identifiable {
var logoAsUIImage : UIImage? {
get {
if let logoImage = self.logo?.image {
return logoImage
}
return .none
}
set {
if let logoImage = newValue {
let newLogo = Logo(context: CDManager.shared.viewContext)
newLogo.console = self
newLogo.image = logoImage
self.logo = newLogo
}/*else{
//This deletes the cover?
if let logo = self.logo {
print("Console::logoImage::set DELETE Logo")
CDManager.shared.viewContext.delete(logo)
}
}*/
}
}
public static func sortConsoleByNewestGame(consoleA : Console, consoleB : Console) -> Bool {
guard let newestGameConsoleA = (consoleA.games!.allObjects as! [Game]).max(by:{
Game.compareByCreationDate(gameA: $0, gameB: $1)
}) else { return false }
guard let newestGameConsoleB = (consoleB.games!.allObjects as! [Game]).max(by:{
Game.compareByCreationDate(gameA: $0, gameB: $1)
}) else { return false }
guard let gameACreated = newestGameConsoleA.createdAt else { return true }
guard let gameBCreated = newestGameConsoleB.createdAt else { return false }
return gameACreated > gameBCreated
}
public var id : NSManagedObjectID {
get {
return self.objectID
}
}
}
extension Console : Encodable {
enum CodingKeys: String, CodingKey {
case uuid
case name
case shortName
case generation
case manufacturer
case accessories
case games
case logo
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(uuid, forKey: .uuid)
try container.encode(name, forKey: .name)
try container.encode(shortName ?? "", forKey: .shortName)
try container.encode(generation, forKey: .generation)
try container.encode(manufacturer ?? "", forKey: .manufacturer)
var accessoryList : [String] = []
for accessory in accessories! {
if let accessory = accessory as? Accessory {
accessoryList.append(accessory.uuid.uuidString)
}
}
try container.encode(accessoryList, forKey: .accessories)
var gamesList : [String] = []
for game in games! {
if let game = game as? Game {
gamesList.append(game.uuid!.uuidString)
}
}
try container.encode(gamesList, forKey: .games)
let logoBase64 = logo?.image?.pngData()?.base64EncodedString() ?? ""
try container.encode(logoBase64, forKey: .logo)
}
}

View File

@@ -2,8 +2,8 @@
// Console+CoreDataProperties.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 06.07.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
// Created by Julian-Steffen Müller on 14.05.21.
// Copyright © 2021 Julian-Steffen Müller. All rights reserved.
//
//
@@ -17,11 +17,14 @@ extension Console {
return NSFetchRequest<Console>(entityName: "Console")
}
@NSManaged public var circumstances: String?
@NSManaged public var generation: Int64
@NSManaged public var manufacturer: String?
@NSManaged public var name: String
@NSManaged public var accessories: NSSet
@NSManaged public var games: NSSet
@NSManaged public var name: String?
@NSManaged public var shortName: String?
@NSManaged public var uuid: UUID?
@NSManaged public var accessories: NSSet?
@NSManaged public var games: NSSet?
@NSManaged public var logo: Logo?
}

View File

@@ -12,5 +12,4 @@ import CoreData
@objc(Cover)
public class Cover: NSManagedObject {
}

View File

@@ -18,6 +18,6 @@ extension Cover {
}
@NSManaged public var image: UIImage?
@NSManaged public var game: Game?
@NSManaged public var game: Game
}

View File

@@ -19,4 +19,78 @@ public class Game: NSManagedObject, Identifiable {
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) {
if let url = URL(string: objectIDStringified) {
let persistentStoreCoordinator = CDManager.shared.persistentContainer.persistentStoreCoordinator
if let objectID = persistentStoreCoordinator.managedObjectID(forURIRepresentation: url) {
if let gameSeries = CDManager.shared.viewContext.object(with: objectID) as? GameSeries {
self.series = gameSeries
}
}
}
}
public static func compareByCreationDate(gameA : Game, gameB : Game) -> Bool {
guard let gameACreated = gameA.createdAt else { return true }
guard let gameBCreated = gameB.createdAt else { return false }
return gameACreated < gameBCreated
}
init(context: NSManagedObjectContext) {
super.init(entity: Game.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)
}
}
extension Game : Encodable {
enum CodingKeys: String, CodingKey {
case uuid
case name
case notes
case isDigital
case lentTo
case createdAt
case publisher
case isFinished
case inWishlist
case console
case gameSeries
case cover
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(uuid, forKey: .uuid)
try container.encode(name, forKey: .name)
try container.encode(notes ?? "", forKey: .notes)
try container.encode(isDigital, forKey: .isDigital)
try container.encode(lentTo ?? "", forKey: .lentTo)
try container.encode(createdAt?.formattedInTimeZone(), forKey: .createdAt)
try container.encode(publisher ?? "", forKey: .publisher)
try container.encode(isFinished, forKey: .isFinished)
try container.encode(inWishlist, forKey: .inWishlist)
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

@@ -2,8 +2,8 @@
// Game+CoreDataProperties.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 06.07.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
// Created by Julian-Steffen Müller on 14.05.21.
// Copyright © 2021 Julian-Steffen Müller. All rights reserved.
//
//
@@ -11,20 +11,24 @@ import Foundation
import CoreData
extension Game{
extension Game {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Game> {
return NSFetchRequest<Game>(entityName: "Game")
}
@NSManaged public var isFinished: Bool
@NSManaged public var circumstances: String?
@NSManaged public var createdAt: Date?
@NSManaged public var inWishlist: Bool
@NSManaged public var isDigital: Bool
@NSManaged public var name: String
@NSManaged public var isFinished: Bool
@NSManaged public var lentTo: String?
@NSManaged public var name: String?
@NSManaged public var notes: String?
@NSManaged public var publisher: String?
@NSManaged public var uuid: UUID?
@NSManaged public var console: Console?
@NSManaged public var series: GameSeries?
@NSManaged public var cover: Cover?
@NSManaged public var series: GameSeries?
}

View File

@@ -2,15 +2,65 @@
// GameSeries+CoreDataClass.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 06.07.19.
// Created by Julian-Steffen Müller on 24.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
//
import Foundation
import CoreData
import UIKit
@objc(GameSeries)
public class GameSeries: NSManagedObject {
public class GameSeries: NSManagedObject, Identifiable {
public var id : String {
return objectID.uriRepresentation().absoluteString
}
var coverAsUIImage : UIImage? {
get {
if let coverImage = self.cover?.image {
return coverImage
}
return .none
}
set {
if let coverImage = newValue {
let newCover = GameSeriesCover(context: CDManager.shared.viewContext)
newCover.gameSeries = self
newCover.image = coverImage
self.cover = newCover
}
}
}
}
extension GameSeries : Encodable {
enum CodingKeys: String, CodingKey {
case uuid
case name
case games
case cover
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(uuid, forKey: .uuid)
try container.encode(name, forKey: .name)
var gamesList : [String] = []
for game in games {
if let game = game as? Game {
gamesList.append(game.objectID.uriRepresentation().absoluteString)
}
}
try container.encode(gamesList, forKey: .games)
let coverBase64 = cover?.image?.pngData()?.base64EncodedString() ?? ""
try container.encode(coverBase64, forKey: .cover)
}
}

View File

@@ -2,7 +2,7 @@
// GameSeries+CoreDataProperties.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 06.07.19.
// Created by Julian-Steffen Müller on 24.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
//
@@ -17,9 +17,10 @@ extension GameSeries {
return NSFetchRequest<GameSeries>(entityName: "GameSeries")
}
@NSManaged public var cover: Data?
@NSManaged public var name: String?
@NSManaged public var name: String
@NSManaged public var uuid : UUID
@NSManaged public var games: NSSet
@NSManaged public var cover: GameSeriesCover?
}

View File

@@ -0,0 +1,16 @@
//
// GameSeriesCover+CoreDataClass.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 24.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
//
import Foundation
import CoreData
@objc(GameSeriesCover)
public class GameSeriesCover: NSManagedObject {
}

View File

@@ -0,0 +1,23 @@
//
// GameSeriesCover+CoreDataProperties.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 24.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
//
import Foundation
import CoreData
import UIKit
extension GameSeriesCover {
@nonobjc public class func fetchRequest() -> NSFetchRequest<GameSeriesCover> {
return NSFetchRequest<GameSeriesCover>(entityName: "GameSeriesCover")
}
@NSManaged public var image: UIImage?
@NSManaged public var gameSeries: GameSeries?
}

View File

@@ -20,6 +20,8 @@
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
@@ -46,6 +48,12 @@
</array>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>

View File

@@ -1,31 +0,0 @@
//
// Attachment.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 14.09.18.
// Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//
import Foundation
typealias AttachmentID = String
struct Attachment : Codable {
var id : AttachmentID
var type : String
var file : Data?
var isDeleted : Bool
var description : String
var createdOn : String
var changedOn : String
enum CodingKeys : String, CodingKey {
case id = "_id"
case type
case file
case isDeleted
case description
case createdOn = "created"
case changedOn = "changed"
}
}

View File

@@ -1,44 +0,0 @@
//
// ConsoleEntry.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 17.09.18.
// Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//
import Foundation
class ConsoleEntry : FlockeEntry {
var generation : Int? {
get {
return self.content["Generation"] as? Int
}
}
var manufacturer : String? {
get {
return self.content["Manufacturer"] as? String
}
}
override init(entry: FlockeEntry) {
super.init(entry: entry)
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
}
override func encodeContent(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
var contentContainer = container.nestedContainer(keyedBy: CodingKeysContentConsoleEntry.self, forKey: CodingKeys.content)
try contentContainer.encode(self.generation, forKey: CodingKeysContentConsoleEntry.Generation)
try contentContainer.encode(self.manufacturer, forKey: CodingKeysContentConsoleEntry.Manufacturer)
}
enum CodingKeysContentConsoleEntry : String, CodingKey {
case Generation = "Generation"
case Manufacturer
}
}

View File

@@ -1,103 +0,0 @@
//
// Console.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 12.09.18.
// Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//
import Foundation
typealias FlockeEntryID = String
class FlockeEntry : Codable {
let id : FlockeEntryID
let isDeleted : Bool
let name : String
let createdOn : String
let changedOn : String
let content : [String : Any]
var iconAttachment : Attachment?
var parents : [FlockeEntryID]
init (entry : FlockeEntry) {
self.id = entry.id
self.isDeleted = entry.isDeleted
self.name = entry.name
self.createdOn = entry.createdOn
self.changedOn = entry.changedOn
self.content = entry.content
self.iconAttachment = entry.iconAttachment
self.parents = entry.parents
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
isDeleted = try container.decode(Bool.self, forKey: .isDeleted)
name = try container.decode(String.self, forKey: .name)
createdOn = try container.decode(String.self, forKey: .createdOn)
changedOn = try container.decode(String.self, forKey: .changedOn)
parents = try container.decode([FlockeEntryID].self, forKey: .parents)
do {
content = try container.decode([String : Any].self, forKey: .content)
}catch{
content = [:]
}
do {
let attachmentContainer = try container.nestedContainer(keyedBy: CodingKeys.self, forKey: .attachments)
iconAttachment = try? attachmentContainer.decode(Attachment.self, forKey: .icon)
}catch {
iconAttachment = nil
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.id, forKey: .id)
try container.encode(self.isDeleted, forKey: .isDeleted)
try container.encode(self.name, forKey: .name)
try container.encode(self.createdOn, forKey: .createdOn)
try container.encode(self.changedOn, forKey: .changedOn)
try container.encode(self.parents, forKey: .parents)
var attachmentContainer = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .attachments)
try attachmentContainer.encode(self.iconAttachment, forKey: .icon)
if (!self.content.isEmpty) {
try encodeContent(to: encoder);
}
}
func encodeContent(to encoder: Encoder) throws {}
enum CodingKeys : String, CodingKey {
case id = "_id"
case isDeleted
case name = "name"
case createdOn = "created"
case changedOn = "changed"
case content
case manufacturer = "Manufacturer"
case generation = "Generation"
case attachments
case icon
case parents
}
}
extension FlockeEntry: Equatable {
static func == (lhs: FlockeEntry, rhs: FlockeEntry) -> Bool {
return lhs.id == rhs.id
}
}

View File

@@ -1,206 +0,0 @@
//
// Consoles.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 13.09.18.
// Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import Disk
import CoreData
protocol ConsoleItemObserver {
func consoleItemDataUpdate(for parent : FlockeEntryID)
}
protocol AttachmentObserver {
func attachmentUpdate(attachmentID : AttachmentID)
}
class GameCollection {
static let shared : GameCollection = GameCollection()
//TODO: Zusammenfuehren von consoles und consoleItemsByParent
var consoles : [ConsoleEntry] = []
var consoleItemsByParent : [FlockeEntryID : [FlockeEntry]] = [:]
var attachments : [AttachmentID : Attachment] = [:]
private var consoleItemObservers : [ConsoleItemObserver] = []
private var attachmentObservers : [AttachmentObserver] = []
init() {
do {
self.consoles = try Disk.retrieve("consoles.json", from: .caches, as: [ConsoleEntry].self)
self.consoleItemsByParent = try Disk.retrieve("consoleItems.json", from: .caches, as: [FlockeEntryID : [FlockeEntry]].self)
self.attachments = try Disk.retrieve("attachments.json", from: .caches, as: [AttachmentID : Attachment].self)
//Fill core data on Startup
print("Game Collection import begin");
let cdm = CDManager.shared
var tmpConsole : Console? = .none
for console in self.consoles {
let cdConsole = Console(entity: Console.entity(), insertInto: cdm.viewContext)
cdConsole.name = console.name
if cdConsole.name == "Nintendo Entertainment System" {
tmpConsole = cdConsole
}
cdConsole.generation = Int64(console.generation ?? 0)
cdConsole.manufacturer = console.manufacturer
guard let consoleItems = self.consoleItemsByParent[console.id] else {
print("No games: \(console.name)")
continue
}
if let imgID = console.iconAttachment?.id , let img = self.attachments[imgID]?.file {
let cdLogo = Logo(entity: Logo.entity(), insertInto: cdm.viewContext)
cdLogo.image = UIImage(data: img)
cdConsole.logo = cdLogo
}
for item in consoleItems {
if (item.parents.contains(GameCollection.accessoryID)) {
let cdAccessory = Accessory(entity: Accessory.entity(), insertInto: cdm.viewContext)
cdAccessory.name = item.name
cdAccessory.inWishlist = item.parents.contains(GameCollection.wishlistID)
cdConsole.addToAccessories(cdAccessory)
}else if (item.parents.contains(GameCollection.videogameID)) {
let cdGame = Game(entity: Game.entity(), insertInto: cdm.viewContext)
cdGame.name = item.name
cdGame.inWishlist = item.parents.contains(GameCollection.wishlistID)
cdConsole.addToGames(cdGame)
}
}
}
guard let con = tmpConsole else {
print("No Console")
return
}
let fetchReq = NSFetchRequest<Game>(entityName: "Game")
fetchReq.predicate = NSPredicate(format: "console=%@", con)
do {
let fetchRes = try CDManager.shared.viewContext.fetch(fetchReq)
for g in fetchRes {
print("\(g.name)")
}
}catch {
print(error)
}
}catch let error as NSError {
print("Load of Chached not possible \(error.localizedDescription)");
}
}
func addFlockeEntryObserver(observer: ConsoleItemObserver) {
self.consoleItemObservers.append(observer)
}
func addAttachmentObserver(observer: AttachmentObserver) {
self.attachmentObservers.append(observer)
}
fileprivate func notifyConSoleItemObservers(_ parent: FlockeEntryID) {
for observer in self.consoleItemObservers {
observer.consoleItemDataUpdate(for: parent)
}
}
func updateGameItems(items : [FlockeEntry], with parent : FlockeEntryID) {
if parent == GameCollection.consoleID {
updateConsoles(entrys: items)
}else {
self.consoleItemsByParent[parent] = items
do {
try Disk.save(self.consoleItemsByParent, to: .caches, as: "consoleItems.json")
}catch let error as NSError {
print("Write of GameItems not possible \(error.localizedDescription)");
}
}
notifyConSoleItemObservers(parent)
}
func remove(from: FlockeEntry, parentID: FlockeEntryID) {
from.parents.removeAll(where: {$0 == parentID})
for parent in from.parents {
notifyConSoleItemObservers(parent)
}
}
func removeGameItem(from console : FlockeEntry, item: FlockeEntry) {
guard let consoleItems : [FlockeEntry] = self.consoleItemsByParent[console.id] else {
return
}
if let index = consoleItems.firstIndex(of: item) {
consoleItemsByParent[console.id]!.remove(at: index);
notifyConSoleItemObservers(console.id)
}
}
func updateAttachment(attachment : Attachment) {
self.attachments[attachment.id] = attachment
do {
try Disk.save(self.attachments, to: .caches, as: "attachments.json")
}catch let error as NSError {
print("Write of Attachments not possible \(error.localizedDescription)");
}
for observer in self.attachmentObservers {
observer.attachmentUpdate(attachmentID: attachment.id)
}
}
func updateConsoles(entrys: [FlockeEntry]) {
self.consoles = []
for entry in entrys {
self.consoles.append(ConsoleEntry(entry: entry))
}
//IN-Place Sort
self.consoles.sort {
//1. Sort by manufacturer
if ($0.manufacturer == $1.manufacturer) {
// //2. Sort by console generation
if ($0.generation == $1.generation) {
// //3. Sort by console name
return $0.name < $1.name
}
return $0.generation! < $1.generation!
}
return $0.manufacturer! < $1.manufacturer!
}
do {
try Disk.save(self.consoles, to: .caches, as: "consoles.json")
}catch let error as NSError {
print("Write of ConsoleItem not possible \(error.localizedDescription)");
}
}
}
extension GameCollection {
static let consoleID : FlockeEntryID = "5b0c2ff8ba0c0b4ae7559911"
static let videogameID : FlockeEntryID = "5b116282ba0c0b4ae75599be"
static let wishlistID : FlockeEntryID = "5b11628dba0c0b4ae75599bf"
static let accessoryID : FlockeEntryID = "5b1162ccba0c0b4ae75599c1"
}

View File

@@ -13,8 +13,8 @@ import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate, UIApplicationDelegate {
var window: UIWindow?
let gameStore = GameStore(console: .none)
let consoleStore = ConsoleStore()
let consolesStore = ConsoleStore()
let gameSeriesStore = GameSeriesStore()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
@@ -24,8 +24,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate, UIApplicationDelegate {
// Use a UIHostingController as window root view controller
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
let view = ConsolesListView(gameStore: self.gameStore)
window.rootViewController = UIHostingController(rootView: view)
window.rootViewController = UIHostingController(rootView: MainView()
.environmentObject(self.gameSeriesStore)
.environmentObject(self.consolesStore))
self.window = window
window.makeKeyAndVisible()
}

View File

@@ -0,0 +1,27 @@
//
// DateConversion.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 24.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import Foundation
extension Date {
func formattedInTimeZone(timezone : TimeZone = .current) -> String {
// 1) Create a DateFormatter() object.
let format = DateFormatter()
// 2) Set the current timezone to .current, or America/Chicago.
format.timeZone = timezone
// 3) Set the format of the altered date.
format.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
// 4) Set the current date, altered by timezone.
let dateString = format.string(from: self)
return dateString
}
}

View File

@@ -1,45 +0,0 @@
//
// FlockeConnector.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 29.10.18.
// Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//
import Foundation
class FlockeConnector {
let flockeURL : URL?
let bodyDict : NSDictionary?
let requestType : String
let completionHandler : (Data?, URLResponse?, Error?) -> Void
init(url : String, requestType : String = "GET", bodyDict : NSDictionary?, _ completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) {
self.flockeURL = URL.init(string: url)
self.bodyDict = bodyDict
self.requestType = requestType
self.completionHandler = completionHandler
}
func doRequest() {
guard let url = self.flockeURL else {
print("DO Request: Creating URL fails")
return
}
do {
let requestBodyJSON = try JSONSerialization.data(withJSONObject: self.bodyDict!)
var req = URLRequest.init(url: url)
req.httpMethod = self.requestType
req.httpBody = requestBodyJSON
req.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: req, completionHandler: self.completionHandler).resume()
}catch {
print("Cannot convert to JSON")
return
}
}
}

View File

@@ -1,152 +0,0 @@
//
// FlockeLoader.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 13.09.18.
// Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import Disk
extension Notification.Name {
static let FlockeWSDeleteFinished = Notification.Name("flockeWSDeleteFinished")
static let FlockeWSAddFinished = Notification.Name("flockeWSAddFinished")
}
class FlockeWS {
@objc enum ERROR_STATE : Int {
case NO_ERROR
case REQUEST_FAILED
}
static func fetchEntries(for parent: FlockeEntryID) {
guard let fetchEntriesURL = URL.init(string: "http://flocke.mueller.haus:8081/entry/parent/\(parent)") else {
print("FETCH ENTRIES: Creating URL fails")
return
}
let task = URLSession.shared.dataTask(with: fetchEntriesURL) { (data, resp, err) in
if (err == nil) {
let consoles : [FlockeEntry] = try! JSONDecoder().decode([FlockeEntry].self, from: data!)
DispatchQueue.main.async {
GameCollection.shared.updateGameItems(items: consoles, with: parent)
for console in consoles {
FlockeWS.fetchEntries(for: console.id)
if let att = console.iconAttachment {
FlockeWS.fetchAttachment(attachment: att.id)
}
}
}
}else{
print("FLOCKEWS::fetchEntries; \(err?.localizedDescription ?? "N/A")")
}
}
task.resume()
}
static func fetchAttachment(attachment attachmentId : AttachmentID) {
guard let fetchAttachmentURL = URL.init(string: "http://flocke.mueller.haus:8081/attachment/\(attachmentId)") else {
print("FETCH ATTACHMENT: Creating URL fails")
return
}
URLSession.shared.dataTask(with: fetchAttachmentURL) { (data, resp, err) in
if (err == nil) {
let attachment : Attachment = try! JSONDecoder().decode(Attachment.self, from: data!)
DispatchQueue.main.async {
GameCollection.shared.updateAttachment(attachment: attachment)
}
}
}.resume()
}
static func deleteEntry(by entry: FlockeEntryID) {
guard let flockeDeleteEntryURL = URL.init(string: "http://flocke.mueller.haus:8081/entry/\(entry)") else {
print("DELETE ENTRY: Creating URL fails")
return
}
var req = URLRequest.init(url: flockeDeleteEntryURL)
req.httpMethod = "DELETE"
URLSession.shared.dataTask(with: req) { (data, resp, err) in
if (err == nil) {
DispatchQueue.main.async {
//TODO GameCollection Update
NotificationCenter.default.post(name: .FlockeWSDeleteFinished, object: nil)
}
}
}.resume()
}
static func deleteParents(entry entryID : FlockeEntryID, parents : [FlockeEntryID]) {
print("DELETE PARENTS WS \(entryID) - \(parents)")
let addJSON : NSMutableDictionary = NSMutableDictionary()
addJSON.setValue(entryID, forKey: "id")
addJSON.setValue(parents, forKey: "parents")
do {
let jsonBodyData = try JSONSerialization.data(withJSONObject: addJSON, options: [])
let jsonBodyString = String(data: jsonBodyData, encoding: .utf8) ?? "{}"
print(jsonBodyString)
let connector : FlockeConnector = FlockeConnector(url: "http://flocke.mueller.haus:8081/entry/deleteParents",
requestType: "PUT",
bodyDict: addJSON) { (data, resp, err) in
// DispatchQueue.main.async {
// //NOP
// }
}
connector.doRequest()
}catch {
return;
}
return;
}
static func addEntry(name : String, consoleID : FlockeEntryID, isVideogame : Bool, isOnWishlist : Bool) {
print("ADD WS \(name) \(isVideogame) \(isOnWishlist)")
var parents : [String] = [consoleID]
if (isVideogame) {
parents.append(GameCollection.videogameID)
}else{
parents.append(GameCollection.accessoryID)
}
if isOnWishlist {
parents.append(GameCollection.wishlistID)
}
let addJSON : NSMutableDictionary = NSMutableDictionary()
addJSON.setValue(name, forKey: "name")
addJSON.setValue(parents, forKey: "parents")
let connector : FlockeConnector = FlockeConnector(url: "http://flocke.mueller.haus:8081/entry/",
requestType: "POST",
bodyDict: addJSON) { (data, resp, err) in
var requestError = FlockeWS.ERROR_STATE.NO_ERROR
if (err != nil) { requestError = FlockeWS.ERROR_STATE.REQUEST_FAILED }
DispatchQueue.main.async {
let userInfo:[String: FlockeWS.ERROR_STATE] = ["error_state": requestError]
NotificationCenter.default.post(name: .FlockeWSAddFinished, object: nil, userInfo: userInfo)
}
}
connector.doRequest()
}
}

View File

@@ -0,0 +1,63 @@
import SwiftUI
import Combine
import UIKit
final class ImagePicker : ObservableObject {
static let shared : ImagePicker = ImagePicker()
private init() {} //force using the singleton: ImagePicker.shared
let view = ImagePicker.View()
let coordinator = ImagePicker.Coordinator()
// Bindable Object part
let objectWillChange = PassthroughSubject<UIImage?, Never>()
var image: UIImage? = .none {
didSet {
objectWillChange.send(image)
print("ImagePicker::image Image Changed")
}
}
}
extension ImagePicker {
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
// UIImagePickerControllerDelegate
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
ImagePicker.shared.image = uiImage
picker.dismiss(animated:true)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated:true)
}
}
struct View: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
ImagePicker.shared.coordinator
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker.View>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController,
context: UIViewControllerRepresentableContext<ImagePicker.View>) {
}
}
}

View File

@@ -0,0 +1,100 @@
//
// LibraryExport.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 19.11.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import Compression
struct LibraryExporter : Encodable {
private var games : [Game]
private var consoles : [Console]
private var gameSeries : [GameSeries]
private var accessories : [Accessory]
init (games: [Game], consoles: [Console], gameSeries: [GameSeries], accessories : [Accessory]) {
self.games = games
self.consoles = consoles
self.gameSeries = gameSeries
self.accessories = accessories
}
enum CodingKeys: String, CodingKey {
case games
case consoles
case gameSeries
case accessories
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(games, forKey: .games)
try container.encode(consoles, forKey: .consoles)
try container.encode(gameSeries, forKey: .gameSeries)
try container.encode(accessories, forKey: .accessories)
}
func export() -> Data? {
let jsonEncoder = JSONEncoder()
do {
let exportJSON = try jsonEncoder.encode(self)
return exportJSON
}catch {
return Data?.none
}
}
func export(name : String) {
if let data = export() {
//let exportFileName = "libExp_\(Date().formattedInTimeZone()).json"
let exportFileName = "libExp_\(name).json"
let byteCount = data.count // replace with data.count
let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useMB] // optional: restricts the units to MB only
bcf.countStyle = .file
let fileSizeAsString = bcf.string(fromByteCount: Int64(byteCount))
print("Exported Library to '\(exportFileName)' (Size: \(fileSizeAsString))")
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let encodedFileURL = documentDirectory.appendingPathComponent(exportFileName)
try data.write(to: encodedFileURL)
}catch {
}
}
}
//Currently python decoder missing 15 characters
func exportCompressedZLIB() -> Data? {
if let data = self.export() {
let exportString = String(data: data, encoding: .utf8)!
var sourceBuffer = Array(exportString.utf8)
let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: exportString.count)
let algorithm = COMPRESSION_ZLIB
let compressedSize = compression_encode_buffer(destinationBuffer, exportString.count,
&sourceBuffer, exportString.count,
nil,
algorithm)
if compressedSize > 0 {
let exportData = NSData(bytesNoCopy: destinationBuffer, length: compressedSize) as Data
return exportData
}else {
print("LibraryExport::exportCompressedZLIB - Compression failed 'compressedSite == 0'")
return Data?.none
}
}
return Data?.none
}
}

View File

@@ -0,0 +1,328 @@
//
// 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 {
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 {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
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() {
print(GameSeriesStore().gameSeries)
CDManager.shared.viewContext.reset()
CDManager.shared.viewContext.performAndWait {
let deleteRequests =
[NSBatchDeleteRequest(fetchRequest: Game.fetchRequest()),
NSBatchDeleteRequest(fetchRequest: Accessory.fetchRequest()),
NSBatchDeleteRequest(fetchRequest: Console.fetchRequest()),
NSBatchDeleteRequest(fetchRequest: GameSeries.fetchRequest()),
NSBatchDeleteRequest(fetchRequest: Logo.fetchRequest()),
NSBatchDeleteRequest(fetchRequest: Cover.fetchRequest())]
let storeCoordinator = CDManager.shared.persistentContainer.persistentStoreCoordinator
do {
for deleteRequest in deleteRequests {
try storeCoordinator.execute(deleteRequest, with: CDManager.shared.viewContext)
}
}catch let error {
print(error)
print("LibraryImport::resetDatabase - Reset of Database failes!")
}
}
print(GameSeriesStore().gameSeries)
}
private func makeCDGame(from game: BHLGame, _ gameDict: inout [UUID : Game], _ cdConsole: Console) {
let cdGame = Game(context: CDManager.shared.viewContext)
gameDict[game.uuid] = cdGame
cdGame.name = game.name
cdGame.uuid = game.uuid
cdGame.inWishlist = game.inWishlist
cdGame.isFinished = game.isFinished
cdGame.lentTo = game.lentTo
//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)")
}
private func makeCDAccessory(from accessory: BHLAccessory, _ accessoryDict: inout [UUID : Accessory], _ cdConsole: Console) {
let cdAccessory = Accessory(context: CDManager.shared.viewContext)
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: CDManager.shared.viewContext)
cdConsole.name = console.name
cdConsole.uuid = console.uuid
cdConsole.manufacturer = console.manufacturer
cdConsole.shortName = console.shortName
cdConsole.generation = Int64(console.generation)
let cdLogo = Logo(context: CDManager.shared.viewContext)
cdLogo.image = console.logo
cdLogo.console = cdConsole
cdConsole.logo = cdLogo
return cdConsole
}
private func makeCDGameSeries(from gameSeries: BHLGameSeries, _ gameDict: inout [UUID : Game]) {
let cdGameSeries = GameSeries(context: CDManager.shared.viewContext)
cdGameSeries.name = gameSeries.name
let cdCover = GameSeriesCover(context: CDManager.shared.viewContext)
cdCover.image = gameSeries.cover
cdCover.gameSeries = cdGameSeries
cdGameSeries.cover = cdCover
for uuid in gameSeries.games {
if let game = gameDict[uuid] {
cdGameSeries.addToGames(game)
game.series = cdGameSeries
}
}
}
func importLIB(from filename: String) {
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let file = documentDirectory.appendingPathComponent(filename)
if file.isFileURL {
let library = try JSONDecoder().decode(BHLibrary.self, from: Data(contentsOf: file))
resetDatabase()
var gameDict = [UUID:Game]()
var accessoryDict = [UUID:Accessory]()
for console in library.consoles {
let cdConsole = makeCDConsole(from: console)
print("CONSOLE: \(cdConsole.name) 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(error)
print("LibraryImport::importLIB - Error while importing!")
}
}
}
// 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 notes : String?
let createdAt : String?
let publisher : String?
let console : UUID?
let series : UUID?
let cover : UIImage?
enum CodingKeys: String, CodingKey {
case uuid
case name
case lentTo
case isDigital
case inWishlist
case isFinished
case notes
case createdAt
case publisher
case console
case series
case cover
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
uuid = try container.decode(UUID.self, forKey: .uuid)
name = try container.decode(String.self, forKey: .name)
lentTo = try container.decode(String?.self, forKey: .lentTo)
isDigital = try container.decode(Bool.self, forKey: .isDigital)
inWishlist = try container.decode(Bool.self, forKey: .inWishlist)
isFinished = try container.decode(Bool.self, forKey: .isFinished)
notes = try container.decode(String?.self, forKey: .notes)
createdAt = try container.decode(String?.self, forKey: .notes)
publisher = try container.decode(String?.self, forKey: .publisher)
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
}
}
}
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 cover : UIImage?
let games : [UUID]
enum CodingKeys: String, CodingKey {
case uuid
case name
case cover
case games
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
uuid = try container.decode(UUID.self, forKey: .uuid)
name = try container.decode(String.self, forKey: .name)
games = try container.decode([UUID].self, forKey: .games)
if let coverBase64 = try container.decode(String?.self, forKey: .cover),
let coverData = Data(base64Encoded: coverBase64) {
cover = UIImage(data: coverData)
}else {
cover = .none
}
}
}
struct BHLConsole : Decodable {
let uuid : UUID
let name : String
let logo : UIImage?
let generation : Int
let manufacturer : String?
let shortName : String?
let accessories : [UUID]
let games : [UUID]
enum CodingKeys: String, CodingKey {
case uuid
case name
case logo
case generation
case manufacturer
case shortName
case accessories
case games
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
uuid = try container.decode(UUID.self, forKey: .uuid)
name = try container.decode(String.self, forKey: .name)
generation = try container.decode(Int.self, forKey: .generation)
manufacturer = try container.decode(String?.self, forKey: .manufacturer)
shortName = try container.decode(String?.self, forKey: .shortName)
accessories = try container.decode([UUID].self, forKey: .accessories)
games = try container.decode([UUID].self, forKey: .games)
if let coverBase64 = try container.decode(String?.self, forKey: .logo),
let coverData = Data(base64Encoded: coverBase64) {
logo = UIImage(data: coverData)
}else {
logo = .none
}
}
}

View File

@@ -1,213 +0,0 @@
//
// ConsoleViewController.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 12.09.18.
// Copyright © 2018 Julian-Steffen Müller. All rights reserved.
//
/*
import UIKit
import CoreData
import Combine
class ConsoleLibraryViewController: UIViewController {
@IBOutlet weak var consoleItemTable: UITableView!
@IBOutlet weak var category: UISegmentedControl!
@IBOutlet weak var toggleWishList: UIBarButtonItem!
var subsriber : Subscribers.Sink<NSSet, Never>?
var videoGamesInLibrary : [Bool : [Game]] = [:]
var accessoriesinLibrary : [Bool : [Accessory]] = [:]
var showWishlist : Bool = false
var console : Console? {
didSet {
self.title = console?.name ?? "N/A"
//Remove subscription to old console object
subsriber?.cancel()
//subsriber = console?.publisher(for: \.games, options: .new).sink(receiveValue: {_ in
// self.refreshConsoleLibrary()
//})
refreshConsoleLibrary()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detail" {
guard let indexpath = self.consoleItemTable.indexPathForSelectedRow else {
print("ConsoleLibraryViewController::prepare::detail IndexPath for selectedRow could not be retreived")
return
}
if let dest = segue.destination as? GameDetailController {
dest.game = self.videoGamesInLibrary[self.showWishlist]?[indexpath.row]
}else if let dest = segue.destination as? AccessoryDetailController {
dest.accessory = self.accessoriesinLibrary[self.showWishlist]?[indexpath.row]
}
}else if (segue.identifier == "consoleEntryAdd") {
guard let addPopup = segue.destination as? AddEntryPopUpViewController else {
print("ConsoleLibraryViewController::prepare::consoleEntryAdd Destination is no AddEntryPopUpViewController")
return
}
addPopup.console = self.console
addPopup.isWishlist = self.showWishlist
addPopup.isVideogame = self.category.selectedSegmentIndex == 0
}
}
@IBAction func categoryChanged(_ sender: Any) {
self.consoleItemTable.reloadData()
}
override func viewWillAppear(_ animated: Bool) {
if self.showWishlist {
self.toggleWishList.tintColor = UIColor.red
}else{
self.toggleWishList.tintColor = UIColor.black
}
self.toggleWishList.tintColor = UIColor.black
}
func refreshConsoleLibrary() {
let videogames = self.console?.games.map({$0 as! Game}) ?? []
self.videoGamesInLibrary[true] = videogames.filter({$0.inWishlist})
self.videoGamesInLibrary[false] = videogames.filter({!$0.inWishlist})
let accessories = self.console?.accessories.map({$0 as! Accessory}) ?? []
self.accessoriesinLibrary[true] = accessories.filter({$0.inWishlist})
self.accessoriesinLibrary[false] = accessories.filter({!$0.inWishlist})
consoleItemTable?.reloadData()
}
@IBAction func toggleWishlist(_ sender: Any) {
self.showWishlist = !self.showWishlist
if self.showWishlist {
self.toggleWishList.tintColor = UIColor.red
}else{
self.toggleWishList.tintColor = UIColor.black
}
consoleItemTable.reloadData()
}
}
extension ConsoleLibraryViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.category.selectedSegmentIndex == 0 {
return self.videoGamesInLibrary[self.showWishlist]?.count ?? 0
}else{
return self.accessoriesinLibrary[self.showWishlist]?.count ?? 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if self.category.selectedSegmentIndex == 0 {
let gameCell = tableView.dequeueReusableCell(withIdentifier: "gameCell") as! GameCell
gameCell.game = self.videoGamesInLibrary[self.showWishlist]?[indexPath.row]
return gameCell
}else{
let accessoryCell = tableView.dequeueReusableCell(withIdentifier: "accessoryCell") as! AccessoryCell
accessoryCell.accessory = self.accessoriesinLibrary[self.showWishlist]?[indexPath.row]
return accessoryCell
}
}
}
// Swipe Action - Delete / move console items from / into the library
extension ConsoleLibraryViewController: UITableViewDelegate {
// Move console item to library
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let bought = UIContextualAction(style: .destructive, title: "") { (action, view, actionDone) in
actionDone(true)
if self.category.selectedSegmentIndex == 0 {
let game = self.videoGamesInLibrary[self.showWishlist]?[indexPath.row]
game?.inWishlist = false
}else{
let accessory = self.accessoriesinLibrary[self.showWishlist]?[indexPath.row]
accessory?.inWishlist = false
}
self.refreshConsoleLibrary()
}
bought.image = #imageLiteral(resourceName: "cave")
bought.backgroundColor = #colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1)
var actions : [UIContextualAction] = []
if (self.showWishlist) {
actions.append(bought)
}
let swipeConf = UISwipeActionsConfiguration(actions: actions)
swipeConf.performsFirstActionWithFullSwipe = false
return swipeConf
}
// Delete console item from library
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let delete = UIContextualAction(style: .destructive, title: "Delete") { (action, view, actionDone) in
var nso : NSManagedObject? = .none
var name : String?
if self.category.selectedSegmentIndex == 0 {
nso = self.videoGamesInLibrary[self.showWishlist]?[indexPath.row]
name = self.videoGamesInLibrary[self.showWishlist]?[indexPath.row].name
}else{
nso = self.accessoriesinLibrary[self.showWishlist]?[indexPath.row]
name = self.accessoriesinLibrary[self.showWishlist]?[indexPath.row].name
}
let deleteWarning = UIAlertController( title: "Aus Zockerhöhle entfernen",
message: "Willst du '\(name ?? "N/A")' wirklich aus der Zockerhöhle tragen?",
preferredStyle: .alert)
deleteWarning.addAction(UIAlertAction(title: "Ja", style: .destructive, handler: { (action) in
actionDone(true)
if nso != nil {
CDManager.shared.viewContext.delete(nso!)
}
}))
deleteWarning.addAction(UIAlertAction(title: "Nein", style: .cancel, handler: { (action) in
actionDone(false)
}))
self.present(deleteWarning, animated: true, completion: nil)
}
delete.image = #imageLiteral(resourceName: "delete")
delete.backgroundColor = #colorLiteral(red: 0.7450980544, green: 0.1568627506, blue: 0.07450980693, alpha: 1)
let swipeConf = UISwipeActionsConfiguration(actions: [delete])
swipeConf.performsFirstActionWithFullSwipe = false
return swipeConf
}
}
*/

View File

@@ -49,6 +49,10 @@ class AccessoryStore : NSObject, ObservableObject, NSFetchedResultsControllerDel
self.consoleFilter = console
}
override init() {
super.init()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("GameStore::controllerDidChangeContent")
self.objectWillChange.send()

View File

@@ -10,53 +10,63 @@ import Foundation
import UIKit
import Combine
import SwiftUI
import CoreData
class AccessoryViewModel : ObservableObject {
var objectWillChange = ObservableObjectPublisher()
var inWishlist : Bool {
didSet {
if let accessory = self.accessory {
guard let accessory = self.accessory else { return }
accessory.inWishlist = inWishlist
}
}
}
var manufacturer : String? {
didSet {
if let accessory = self.accessory {
guard let accessory = self.accessory else { return }
accessory.manufacturer = manufacturer
}
}
}
var color : String? {
didSet {
if let accessory = self.accessory {
guard let accessory = self.accessory else { return }
accessory.color = color
}
}
}
var name : String {
didSet {
if let accessory = self.accessory {
guard let accessory = self.accessory else { return }
accessory.name = name
}
}
}
var lentTo : String? {
didSet {
guard let accessory = self.accessory else { return }
accessory.lentTo = lentTo;
}
}
private var accessory : Accessory? {
didSet {
if accessory != nil {
self.name = accessory!.name
self.inWishlist = accessory!.inWishlist
self.color = accessory!.color
self.manufacturer = accessory!.manufacturer
}
guard let accessory = accessory else { return }
self.name = accessory.name
self.inWishlist = accessory.inWishlist
self.color = accessory.color
self.manufacturer = accessory.manufacturer
}
}
var NSManagedObjectChangedObserver : AnyCancellable? = .none
init(accessory : Accessory) {
self.accessory = accessory
@@ -65,11 +75,24 @@ class AccessoryViewModel : ObservableObject {
self.color = accessory.color
self.manufacturer = accessory.manufacturer
_ = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).sink {
//TODO
self.NSManagedObjectChangedObserver = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).first().sink { notification in
guard let accessory = self.accessory else { return }
guard let userInfo = notification.userInfo else { return }
var managedObjectIsMatching = false
if let inserts = userInfo[NSInsertedObjectsKey] as? Set<NSManagedObject> {
if inserts.contains(accessory) { managedObjectIsMatching = true }
}
if let updates = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject> {
if updates.contains(accessory) { managedObjectIsMatching = true }
}
if let deletes = userInfo[NSDeletedObjectsKey] as? Set<NSManagedObject> {
if deletes.contains(accessory) { managedObjectIsMatching = true }
}
if managedObjectIsMatching {
self.objectWillChange.send()
if let accessory = self.accessory {
print("Accessory: \(accessory.name) Notification: \($0.description)")
print("AccessoryViewModel::NSMangedObjectChanged MY Accessory")
}
}
}

View File

@@ -0,0 +1,55 @@
//
// ConsoleViewModel.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 25.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
import Combine
class ConsoleViewModel : ObservableObject {
var objectWillChange = ObservableObjectPublisher()
init(_ console: Console) {
self.console = console
self.name = console.name!
}
private var console : Console? {
didSet {
guard let console = console else { return }
self.name = console.name!
}
}
var name : String {
didSet {
guard let console = console else { return }
console.name = name
}
}
var logo : UIImage? {
get {
if let logoImage = self.console?.logo?.image {
return logoImage
}
return .none
}
set {
if let console = self.console {
if let logoImage = newValue {
let newLogo = Logo(context: CDManager.shared.viewContext)
newLogo.console = console
newLogo.image = logoImage
console.logo = newLogo
}
}
}
}
}

View File

@@ -0,0 +1,54 @@
//
// FeaturedStore.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 25.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import Combine
class FeaturedStore : ObservableObject {
var objectWillChange = PassthroughSubject<Void, Never>()
var gameStore = GameStore(sortDescriptors: [NSSortDescriptor(key: "createdAt", ascending: false), NSSortDescriptor(key: "name", ascending: true)], fetchLimit: 1)
var consoleStore = ConsoleStore()
var consoleStoreSubscriber : AnyCancellable?
var gameStoreSubscriber : AnyCancellable?
init() {
_ = self.consoleStore.consoles
if (self.gameStore.games.count > 0) {
self.featuredGame = self.gameStore.games.first
}
self.featuredConsole = self.featuredGame?.console
gameStoreSubscriber = self.gameStore.objectWillChange.sink { _ in
self.featuredGame = self.gameStore.games.first
self.featuredConsole = self.featuredGame?.console
}
}
var featuredGame : Game? {
willSet {
objectWillChange.send()
}
}
var featuredAccessory : Accessory? {
willSet {
objectWillChange.send()
}
}
var featuredConsole : Console? {
willSet {
objectWillChange.send()
}
}
}

View File

@@ -0,0 +1,48 @@
//
// ConsoleStore.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 02.08.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import Combine
import SwiftUI
import CoreData
class GameSeriesStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
var objectWillChange = ObservableObjectPublisher()
var gameSeries : [GameSeries] {
get {
if self.fetchResultsController.fetchedObjects == nil {
do {
try fetchResultsController.performFetch()
}catch {
print("ConsoleStore::consoles Perform Fetch not possible '\(error)'")
}
}
return self.fetchResultsController.fetchedObjects ?? []
}
}
lazy var fetchResultsController : NSFetchedResultsController<GameSeries> = {
let fetch : NSFetchRequest<GameSeries> = GameSeries.fetchRequest()
fetch.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
var fetchRC = NSFetchedResultsController(fetchRequest: fetch, managedObjectContext: CDManager.shared.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchRC.delegate = self
return fetchRC
}()
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("AllConsolesViewController::controllerDidChangeContent")
self.objectWillChange.send()
}
}

View File

@@ -0,0 +1,55 @@
//
// ConsoleViewModel.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 25.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
import Combine
class GameSeriesViewModel : ObservableObject {
var objectWillChange = ObservableObjectPublisher()
init(_ gameSeries: GameSeries) {
self.gameSeries = gameSeries
self.name = gameSeries.name
}
private var gameSeries : GameSeries? {
didSet {
guard let gameSeries = gameSeries else { return }
self.name = gameSeries.name
}
}
var name : String {
didSet {
guard let gameSeries = gameSeries else { return }
gameSeries.name = name
}
}
var cover : UIImage? {
get {
if let logoImage = self.gameSeries?.cover?.image {
return logoImage
}
return .none
}
set {
if let gameSeries = self.gameSeries {
if let image = newValue {
let newCover = GameSeriesCover(context: CDManager.shared.viewContext)
newCover.gameSeries = gameSeries
newCover.image = image
gameSeries.cover = newCover
}
}
}
}
}

View File

@@ -14,10 +14,14 @@ import Combine
class GameStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
var objectWillChange = PassthroughSubject<Void , Never>()
var consoleFilter : Console?
var gameSeriesFilter : GameSeries?
var sortDescriptors : [NSSortDescriptor] = [NSSortDescriptor(key: "name", ascending: true)]
var fetchLimit : Int = 0
var games : [Game] {
get {
if self.fetchResultsController.fetchedObjects == nil {
do {
try fetchResultsController.performFetch()
}catch {
@@ -25,7 +29,21 @@ class GameStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate
}
}
return self.fetchResultsController.fetchedObjects ?? []
let result = self.fetchResultsController.fetchedObjects ?? []
//Anscheinend ein Bug im FetchResultController das er das fetchLimit manchmal ignoriert
if self.fetchLimit > 0 && result.count > self.fetchLimit {
print("GameStore::games Fetch limit of ignored. Expected: \(self.fetchLimit) Delivered: \(result.count)")
var clampedResult : [Game] = []
for index in 0..<self.fetchLimit {
clampedResult.append(result[index])
}
return clampedResult
}
return result
}
}
@@ -33,9 +51,14 @@ class GameStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate
var gamesFetch : NSFetchRequest<Game> = Game.fetchRequest()
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.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
gamesFetch.fetchLimit = self.fetchLimit
gamesFetch.sortDescriptors = self.sortDescriptors
let gamesFetchRC = NSFetchedResultsController(fetchRequest: gamesFetch, managedObjectContext: CDManager.shared.viewContext, sectionNameKeyPath: nil, cacheName: nil)
@@ -44,13 +67,30 @@ class GameStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate
return gamesFetchRC
}()
init(console : Console?) {
init(console : Console?, fetchLimit : Int = 0) {
super.init()
self.consoleFilter = console
self.fetchLimit = fetchLimit
}
init(gameSeries: GameSeries?, fetchLimit : Int = 0) {
super.init()
self.gameSeriesFilter = gameSeries
self.fetchLimit = fetchLimit
}
init(sortDescriptors : [NSSortDescriptor], fetchLimit : Int = 0) {
super.init()
self.sortDescriptors = sortDescriptors
self.fetchLimit = fetchLimit
}
init(fetchLimit : Int = 0) {
super.init()
self.fetchLimit = fetchLimit
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("GameStore::controllerDidChangeContent")
self.objectWillChange.send()
}
}

View File

@@ -17,60 +17,129 @@ class GameViewModel : ObservableObject {
private var game : Game? {
didSet {
if game != nil {
self.name = game!.name
self.inWishlist = game!.inWishlist
self.isDigital = game!.isDigital
self.isFinished = game!.isFinished
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 {
if let game = self.game {
guard let game = self.game else { return }
game.name = name
}
}
}
var inWishlist : Bool {
didSet {
if let game = self.game {
guard let game = self.game else { return }
game.inWishlist = inWishlist
}
}
}
var isDigital : Bool {
didSet {
if let game = self.game {
guard let game = self.game else { return }
game.isDigital = isDigital
}
}
}
var isFinished : Bool {
didSet {
if let game = self.game {
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.name = game.name!
self.inWishlist = game.inWishlist
self.isDigital = game.isDigital
self.isFinished = game.isFinished
self.gameSeries = game.series?.id ?? ""
self.lentTo = game.lentTo ?? ""
_ = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).sink {
//TODO
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()
if let game = self.game {
print("Game: \(game.name) Notification: \($0.description)")
print("GameViewModel::NSMangedObjectChanged Update of \(game.name)")
}
}
}

View File

@@ -28,11 +28,3 @@ struct AccessoryDetailView : View {
self.accessoryVM = accessoryVM!
}
}
#if DEBUG
struct AccessoryDetailView_Previews : PreviewProvider {
static var previews: some View {
AccessoryDetailView(accessoryVM: nil)
}
}
#endif

View File

@@ -0,0 +1,108 @@
//
// ConsolesListView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 01.08.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import SwiftUI
import QGrid
struct ModalAddConsoleToLibrary : View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State var modalConsoleName : String = ""
private func addConsoleToLibrary() {
//TODO
}
var addButton : some View {
return Button(action: {
addConsoleToLibrary()
self.presentationMode.wrappedValue.dismiss()},
label: { Text("Add") })
.disabled(self.modalConsoleName.trimmingCharacters(in: .whitespacesAndNewlines) == "" )
}
var body: some View {
NavigationView {
Form {
TextField("Name", text: $modalConsoleName)
}
.font(.caption)
.navigationBarTitle(Text("New Console"))
.navigationBarItems(leading: Button(action: { self.presentationMode.wrappedValue.dismiss() },
label: {Text("Cancel")}),
//Add Button is disabled if no name is entered
trailing: addButton)
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
struct ConsolesListView : View {
@EnvironmentObject var consoleStore : ConsoleStore
@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) {
GridCell(console: $0)
}
.padding()
.navigationBarTitle(Text("Zockerhöhle"))
.navigationBarItems(trailing:
Button(action: {
self.showAddConsoleToLibraryModal = true
}) {
Image(systemName: "plus")
})
.sheet(isPresented: $showAddConsoleToLibraryModal) {
ModalAddConsoleToLibrary()
}
}
}
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)
}
}.buttonStyle(PlainButtonStyle())
}else{
Text("mu")
}
//
//Text(person.lastName).lineLimit(1)
}
}
}

View File

@@ -0,0 +1,42 @@
//
// ConsoleDetailView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 25.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
struct ConsoleEditView: View {
@ObservedObject var consoleViewModel : ConsoleViewModel
@State var showLogoImagePicker = false
var body: some View {
Form {
TextField("Name der Konsole", text: $consoleViewModel.name)
consoleViewModel.logo.map {
Image (uiImage: $0)
.resizable()
.frame(width:100, height: 100)
}
Button(action: { self.showLogoImagePicker = true }, label: { Text("Pick Image") })
}
.navigationBarTitle("Editiere Konsole")
.sheet(isPresented: $showLogoImagePicker) {
ImagePicker.shared.view
}
.onReceive(ImagePicker.shared.objectWillChange, perform: { image in
if let image = image {
self.consoleViewModel.logo = image
}
})
}
init(_ console : Console) {
self.consoleViewModel = ConsoleViewModel(console)
}
}

View File

@@ -8,6 +8,68 @@
import SwiftUI
struct ModalAddToConsoleLibrary : View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State var modalAddName : String = ""
@State var modalAddToWishlist : Bool
@State var modalIsDigital : Bool = false
@State var isVideogamesSelected : Bool
var console : Console;
private func addGameToLibrary() {
let game = Game(context: CDManager.shared.viewContext)
game.name = self.modalAddName
game.console = self.console;
game.inWishlist = self.modalAddToWishlist
game.isDigital = self.modalIsDigital
}
private func addAccessoryToLibrary() {
let accessory = Accessory(context: CDManager.shared.viewContext)
accessory.name = self.modalAddName
accessory.console = self.console
accessory.inWishlist = self.modalAddToWishlist
}
var addButton : some View {
return Button(action: {
if (self.isVideogamesSelected) {
self.addGameToLibrary()
}else{
self.addAccessoryToLibrary()
}
self.presentationMode.wrappedValue.dismiss()},
label: { Text("Add") })
.disabled(self.modalAddName.trimmingCharacters(in: .whitespacesAndNewlines) == "" )
}
var body: some View {
NavigationView {
Form {
TextField("Name", text: $modalAddName)
if self.isVideogamesSelected {
Toggle(isOn: $modalIsDigital, label: {
Text("Ist Digital")
})
}
Toggle(isOn: $modalAddToWishlist, label: {
Text("Auf die Wunschliste")
})
}
.font(.caption)
.navigationBarTitle(self.isVideogamesSelected ? Text("New Game") : Text("New Accessory"))
.navigationBarItems(leading: Button(action: { self.presentationMode.wrappedValue.dismiss() },
label: {Text("Cancel")}),
//Add Button is disabled if no name is entered
trailing: addButton)
.navigationViewStyle(StackNavigationViewStyle())
}
}
}
struct ConsoleLibraryView : View {
@State var isVideogamesSelected = true
@@ -16,19 +78,8 @@ struct ConsoleLibraryView : View {
@ObservedObject var accessoryStore : AccessoryStore
@State var showWishlist = false
@State var isModal: Bool = false
var modal: some View {
NavigationView {
Form {
Text("bl")
}
.font(.caption)
.navigationBarTitle(self.isVideogamesSelected ? Text("New Game") : Text("New Accessory"))
.navigationBarItems(trailing: Button(action: { self.isModal = false } ) { Text("Done") })
}
}
@State var showAddToConsoleLibraryModal: Bool = false
@State var showLogoImagePicker = false
var body: some View {
VStack {
@@ -44,14 +95,29 @@ struct ConsoleLibraryView : View {
if self.isVideogamesSelected {
List {
ForEach(gameStore.games.filter({$0.inWishlist == self.showWishlist})) { game in
NavigationLink(destination: GameDetailView(gameVM: GameViewModel(game: game))) {
Text("\(game.name)")
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)
}
}
}
}
}
}else {
List {
ForEach(accessoryStore.accessories.filter({$0.inWishlist == self.showWishlist})) { accessory in
NavigationLink(destination: AccessoryDetailView(accessoryVM: AccessoryViewModel(accessory: accessory))) {
Text("\(accessory.name)")
@@ -59,23 +125,33 @@ struct ConsoleLibraryView : View {
}
}
}
}.navigationBarTitle(Text("\(self.console?.name ?? "N/A")"), displayMode: .automatic)
}
.navigationBarTitle(Text("\(self.console?.name ?? "N/A")"), displayMode: .automatic)
.navigationBarItems(trailing:
HStack {
Button(action: {
self.isModal = true
}) {
Image(systemName: "plus")
}.padding(5)
Button(action: {
self.showWishlist.toggle()
}) {
Image(systemName: "star")
}.accentColor(self.showWishlist ? Color.red : Color.blue)
NavigationLink(destination: ConsoleEditView(self.console!)) {
Image(systemName: "pencil.and.ellipsis.rectangle")
}
Button(action: { self.showAddToConsoleLibraryModal = true },
label: { Image(systemName: "plus")})
.padding(5)
Button(action: { self.showWishlist.toggle()},
label: { Image("wishlist")
.resizable()
.aspectRatio(contentMode: .fit)
})
.sheet(isPresented: $isModal, onDismiss: {}, content: {
self.modal
.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")
}
}
}
init(console : Console?) {
@@ -84,11 +160,3 @@ struct ConsoleLibraryView : View {
self.accessoryStore = AccessoryStore(console: console)
}
}
#if DEBUG
struct ConsoleLibraryView_Previews : PreviewProvider {
static var previews: some View {
ConsoleLibraryView(console: .none)
}
}
#endif

View File

@@ -1,37 +0,0 @@
//
// ConsolesListView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 01.08.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import Foundation
import SwiftUI
struct ConsolesListView : View {
@ObservedObject var consoleStore : ConsoleStore = ConsoleStore()
@ObservedObject var gameStore : GameStore
var body: some View {
NavigationView {
List {
ForEach(consoleStore.consoles) {console in
//TODO environmentObject should not be passed through the hierarchy
//This is only for fixin a swiftui bug
NavigationLink(destination: ConsoleLibraryView(console: console)) {
Text("\(console.name)")
}
}
}.navigationBarTitle(Text("Zockerhöhle"))
}
}
}
#if DEBUG
struct ConsolesListView_Previews : PreviewProvider {
static var previews: some View {
ConsolesListView(gameStore: GameStore(console: .none))
}
}
#endif

View File

@@ -9,16 +9,59 @@
import SwiftUI
struct GameDetailView : View {
//@Binding var g : Game
@ObservedObject var gameVM : GameViewModel
@State private var showDeleteAlert : Bool = false
@State var hasFinishedDate : Bool = false
@State var playthroughDate : Date = Date()
@EnvironmentObject var gameSeriesStore : GameSeriesStore
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State var showingPicker = false
@State var image : Image? = nil
@State var isLent : Bool = false
var gameSeriesPicker : some View {
Picker(selection: $gameVM.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)
//Gray color should indicate immutable data
Text("\(gameVM.console?.name ?? "No console")").foregroundColor(.gray)
Toggle(isOn: $gameVM.isDigital, label: {
Text("Nur Digital")
})
@@ -27,6 +70,20 @@ struct GameDetailView : View {
Text("In Wunschliste")
})
Toggle(isOn: $isLent, label: {
Text("Verliehen?")
}).onReceive(gameVM.objectWillChange) { _ in
self.isLent = self.gameVM.lentTo != "";
}.onAppear() {
self.isLent = self.gameVM.lentTo != "";
}
if isLent {
TextField("Verliehen an", text: $gameVM.lentTo)
}
gameSeriesPicker
Toggle(isOn: $gameVM.isFinished , label: {
Text("Durchgezockt")
})
@@ -43,20 +100,55 @@ struct GameDetailView : View {
in: ...Date(),
displayedComponents: [.date])
}
}
}.navigationBarTitle(Text("\(gameVM.name)"), displayMode: .automatic)
imageCoverSection
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
}
}
}
//Irgendwie gibt es einen Bug der den Alert im navigationBarItem zwei mal aufruft.
//Deswegen muss der delete Button is auf weiteres in die Form
var gameDeleteButton : some View {
Button(action: {
self.showDeleteAlert = true
}, label: {
Spacer()
Text("Spiel löschen")
Spacer()
//Image(systemName: "trash").foregroundColor(.red)
})
.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()
}), secondaryButton: Alert.Button.cancel(Text("Lieber doch nicht")))
})
}
init(gameVM : GameViewModel?) {
self.gameVM = gameVM!
}
}
#if DEBUG
struct GameDetailView_Previews : PreviewProvider {
static var previews: some View {
GameDetailView(gameVM: nil)
init(game : Game) {
self.gameVM = GameViewModel(game: game)
}
}
#endif

View File

@@ -0,0 +1,23 @@
//
// LatestGamePickupsView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 24.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
struct GamePickupsView: View {
@ObservedObject var gameStore = GameStore(sortDescriptors: [NSSortDescriptor(key: "createdAt", ascending: false), NSSortDescriptor(key: "name", ascending: true)], fetchLimit: 50)
var body: some View {
List(gameStore.games) { game in
NavigationLink(destination: GameDetailView(game: game)) {
Text("\(game.name!)")
}
}
.padding(.top) // Workaround, damit die Liste beim scrollen nicht unter der NavigationView angezeigt wird
.navigationBarTitle(Text("Latest Pickups"))
}
}

View File

@@ -0,0 +1,89 @@
//
// GameSeriesListView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 05.08.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
struct GameSeriesAllView: View {
@EnvironmentObject var gameSeriesStore : GameSeriesStore
@State var modalVisible : Bool = false
@State var modalAddName : String = ""
@State var showingPicker : Bool = false
@State var image : UIImage?
var modalAddGameSeries: some View {
NavigationView {
Form {
TextField("Name", text: $modalAddName)
self.image.map { Image(uiImage: $0).resizable().frame(width: 100, height: 100)}
Button(action: { self.showingPicker = true}, label: { Text("Wähle Bild") })
}
.navigationBarTitle(Text("New Game Series"))
.navigationBarItems(leading: Button(action: {self.modalVisible = false }, label: {Text("Cancel")})
//Add Button is disabled if no name is entered
, trailing: Button(action: {
let gameSeries = GameSeries(context: CDManager.shared.viewContext)
gameSeries.name = self.modalAddName
if let image = self.image {
let cover = GameSeriesCover(context: CDManager.shared.viewContext)
cover.image = image
cover.gameSeries = gameSeries
gameSeries.cover = cover
}
self.modalVisible = false
}) { Text("Add") }.disabled(self.modalAddName.trimmingCharacters(in: .whitespacesAndNewlines) == "" ))
.sheet(isPresented: $showingPicker,
onDismiss: {
}, content: {
ImagePicker.shared.view
})
}
}
func resetModalVariables() {
self.modalAddName = ""
}
var body: some View {
List(gameSeriesStore.gameSeries) { gameSeries in
NavigationLink(destination: GameSeriesLibraryView(gameSeries: gameSeries)) {
HStack {
gameSeries.cover?.image.map {
Image(uiImage: $0)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 50)
}
VStack(alignment: .leading) {
Text(gameSeries.name)
//Sonst wird der TExt kurioser Weise abgeschnitten wenn ein Bild davor ist. Hier nochmal später rein
//schauen
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
Text("Spiele: \(gameSeries.games.filter({!($0 as! Game).inWishlist}).count ?? 0)/\(gameSeries.games.count ?? 0)").font(.caption)
}
}
}
}
.padding()
.navigationBarTitle(Text("Videospielserien"))
.navigationBarItems(trailing:
Button(action: {
self.modalVisible = true
}) {
Image(systemName: "plus")
})
.sheet(isPresented: $modalVisible, onDismiss: {}, content: {
self.modalAddGameSeries
})
}
}

View File

@@ -0,0 +1,42 @@
//
// ConsoleDetailView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 25.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
struct GameSeriesEditView: View {
@ObservedObject var gameSeriesViewModel : GameSeriesViewModel
@State var showCoverImagePicker = false
var body: some View {
Form {
TextField("Name der Konsole", text: $gameSeriesViewModel.name)
gameSeriesViewModel.cover.map {
Image (uiImage: $0)
.resizable()
.frame(width:100, height: 100)
}
Button(action: { self.showCoverImagePicker = true }, label: { Text("Pick Image") })
}
.navigationBarTitle("Editiere Spieleserie")
.sheet(isPresented: $showCoverImagePicker) {
ImagePicker.shared.view
}
.onReceive(ImagePicker.shared.objectWillChange, perform: { image in
if let image = image {
self.gameSeriesViewModel.cover = image
}
})
}
init(_ gameSeries : GameSeries) {
self.gameSeriesViewModel = GameSeriesViewModel(gameSeries)
}
}

View File

@@ -0,0 +1,50 @@
//
// GameSeriesDetailView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 05.08.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
struct GameSeriesLibraryView: View {
@ObservedObject var gameStore : GameStore
@State var isDetailActivated : Bool = false
var gameSeries : GameSeries?
var body: some View {
List(gameStore.games) { game in
NavigationLink(destination: GameDetailView(game: game)) {
Text("\(game.name!)")
if game.isDigital {
Image("digitalGame")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 15)
}
if game.inWishlist {
Image("wishlist")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 15)
}
}
}
.navigationBarTitle(Text(self.gameSeries?.name ?? "n/a"))
.navigationBarItems(trailing:
NavigationLink(destination: GameSeriesEditView(self.gameSeries!)) {
Image(systemName: "pencil.and.ellipsis.rectangle")
}
)
.padding()
}
init(gameSeries: GameSeries?) {
self.gameSeries = gameSeries
gameStore = GameStore(gameSeries: gameSeries)
}
}

View File

@@ -0,0 +1,27 @@
//
// MainView.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 05.08.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
struct MainView: View {
var body: some View {
Overview()
// TabView {
// ConsolesListView()
// .tabItem({ Text("Konsolen") })
// .tag(1)
// GameSeriesAllView()
// .tabItem({ Text("Serien") })
// .tag(2)
// Overview()
// .tabItem({ Text("Überischt") })
// .tag(0)
// }
}
}

View File

@@ -0,0 +1,206 @@
//
// Overview.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 24.09.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
struct OverviewHeader<Destination : View> : View {
private var destination : Destination
private var title : String
init(title : String, destination : Destination) {
self.title = title
self.destination = destination
}
var body : some View {
HStack {
Text("\(self.title)")
.font(.headline)
.padding(.leading, 15)
.padding(.top, 5)
Spacer()
NavigationLink(destination: self.destination) {
Text("Alle anzeigen")
.font(.caption)
.padding(.top, 10)
.padding(.trailing, 15)
}
}
}
}
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")
}else{
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(consoleStore.consoles.sorted(by: { Console.sortConsoleByNewestGame(consoleA: $0, consoleB: $1)})) { console in
NavigationLink(destination: ConsoleLibraryView(console: console)) {
VStack(alignment: .leading) {
Group {
console.logo?.image.map {
Image(uiImage: $0)
.resizable()
.padding(10)
.cornerRadius(5)
}
}
.frame(width: 100, height: 100)
//.background(Color(UIColor.lightGray))
//.cornerRadius(5)
Text("\(console.name!)")
.font(.caption)
.frame(width: 100)
}.padding(.leading, 15)
}.buttonStyle(PlainButtonStyle())
}
}.padding(.trailing, 15)
}
}
}
}
var GamesOverview : some View {
VStack(alignment: .leading) {
//TODO
OverviewHeader(title: "Latest Game Pickups", destination: GamePickupsView())
if gameStore.games.count == 0 {
Text("No Games")
}else {
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(gameStore.games) { game in
NavigationLink(destination: GameDetailView(game: game)) {
VStack(alignment: .leading) {
Group {
/*game.cover?.image.map {
Image(uiImage: $0)
.resizable()
.padding(10)
.cornerRadius(5)
}*/
Text("\(self.gameStore.fetchLimit)")
}
.frame(width: 100, height: 100)
.background(Color(UIColor.lightGray))
.cornerRadius(5)
Text("\(game.name!)")
.font(.caption)
.frame(width: 100)
}.padding(.leading, 15)
}.buttonStyle(PlainButtonStyle())
}
}.padding(.trailing, 15)
}
}
}
}
var GameSeriesOverview : some View {
VStack(alignment: .leading) {
OverviewHeader(title: "Spielserien", destination: GameSeriesAllView())
if gameSeriesStore.gameSeries.count == 0 {
Text("No game series")
}else{
ScrollView(.horizontal, showsIndicators: false) {
HStack(alignment: .top, spacing: 0) {
ForEach(gameSeriesStore.gameSeries) { gameSeries in
NavigationLink(destination: GameSeriesLibraryView(gameSeries: gameSeries)) {
VStack(alignment: .leading) {
Group {
/*game.cover?.image.map {
Image(uiImage: $0)
.resizable()
.padding(10)
.cornerRadius(5)
}*/
Text("S")
}
.frame(width: 100, height: 100)
.background(Color(UIColor.lightGray))
.cornerRadius(5)
Text("\(gameSeries.name)")
.font(.caption)
.frame(width: 100)
}.padding(.leading, 15)
}.buttonStyle(PlainButtonStyle())
}
}.padding(.trailing, 15)
}
}
}
}
var featured : some View {
VStack(alignment: .leading) {
HStack {
Text("Gerade Aktuell")
.font(.headline)
.padding(.leading, 15)
.padding(.top, 5)
Spacer()
}
HStack {
Spacer()
if featuredStore.featuredConsole != nil {
NavigationLink(destination: ConsoleLibraryView(console: featuredStore.featuredConsole!)) {
if featuredStore.featuredConsole?.logo?.image != nil {
featuredStore.featuredConsole?.logo?.image.map {
Image(uiImage: $0).resizable().frame(width: 200, height: 200)
}
}else{
//TODO: Symbol für fehlendes Bild
Image(systemName: "cave").frame(width: 200, height: 200)
}
}.buttonStyle(PlainButtonStyle())
}else {
Text("No featured console")
}
Spacer()
}
}
}
var body: some View {
NavigationView {
ScrollView(.vertical, showsIndicators: true) {
VStack {
featured
Divider()
ConsolesOverview
Divider()
GamesOverview
Divider()
GameSeriesOverview
}.navigationBarTitle("Zockerhöhle")
}.navigationBarItems(trailing:
NavigationLink(destination: SettingsView(), label: { Image(systemName: "gear") })
)
}.navigationViewStyle(StackNavigationViewStyle())
}
}

View File

@@ -0,0 +1,54 @@
//
// Settings.swift
// Zockerhoehle
//
// Created by Julian-Steffen Müller on 20.11.19.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved.
//
import SwiftUI
//extension String: Identifiable {
// public var id: String {
// return self
// }
//}
struct SettingsView: View {
@State var backupImportFileIndex = -1
@State var backupImportUpdateExistingEntrys = false
@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()
}
var body: some View {
Form {
Section(header: Text("Backup Library")) {
Button(action: { self.exportLibrary() }, label: { Text("Do Backup") })
}
Section(header: Text("Restore Library")) {
Picker(selection: $backupImportFileIndex, label: Text("Choose Backup")) {
Text("None").tag(-1)
ForEach(0 ..< (importFiles.count), id: \.self) {
Text(LibraryImport.backupName(from: self.importFiles[$0]) ?? "n/a").tag($0)
}.navigationBarTitle("Backups")
}
Toggle("Yes, I want to reset my Database", isOn: $backupImportUpdateExistingEntrys)
Button(action: {
LibraryImport().importLIB(from: self.importFiles[self.backupImportFileIndex])
}, label: { Text("Reset & Import") }).disabled(backupImportFileIndex < 0 || !backupImportUpdateExistingEntrys)
}
}.navigationBarTitle("Einstellungen")
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.Zockerhoehle</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
</dict>
</plist>

View File

@@ -1,20 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14865.6" systemVersion="18F132" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="18154" systemVersion="20B29" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="Accessory" representedClassName="Accessory" syncable="YES">
<attribute name="circumstances" optional="YES" attributeType="String"/>
<attribute name="color" optional="YES" attributeType="String"/>
<attribute name="inWishlist" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="lentTo" optional="YES" attributeType="String"/>
<attribute name="manufacturer" optional="YES" attributeType="String"/>
<attribute name="name" attributeType="String"/>
<attribute name="name" attributeType="String" defaultValueString=""/>
<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="accessories" inverseEntity="Console"/>
</entity>
<entity name="Console" representedClassName="Console" syncable="YES">
<attribute name="circumstances" optional="YES" attributeType="String"/>
<attribute name="generation" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="manufacturer" optional="YES" attributeType="String"/>
<attribute name="name" attributeType="String"/>
<relationship name="accessories" toMany="YES" deletionRule="Nullify" destinationEntity="Accessory" inverseName="console" inverseEntity="Accessory"/>
<relationship name="games" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="console" inverseEntity="Game"/>
<attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="shortName" optional="YES" attributeType="String"/>
<attribute name="uuid" optional="YES" attributeType="UUID" defaultValueString="00000000-0000-0000-0000-000000000000" usesScalarValueType="NO"/>
<relationship name="accessories" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Accessory" inverseName="console" inverseEntity="Accessory"/>
<relationship name="games" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="console" inverseEntity="Game"/>
<relationship name="logo" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Logo" inverseName="console" inverseEntity="Logo"/>
</entity>
<entity name="Cover" representedClassName="Cover" syncable="YES">
@@ -23,12 +27,15 @@
</entity>
<entity name="Game" representedClassName="Game" syncable="YES">
<attribute name="circumstances" 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"/>
<attribute name="isFinished" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String"/>
<attribute name="lentTo" optional="YES" attributeType="String"/>
<attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="notes" optional="YES" attributeType="String"/>
<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"/>
@@ -37,20 +44,26 @@
</fetchedProperty>
</entity>
<entity name="GameSeries" representedClassName="GameSeries" syncable="YES">
<attribute name="cover" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" allowsExternalBinaryDataStorage="YES"/>
<attribute name="name" optional="YES" attributeType="String"/>
<relationship name="games" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="series" inverseEntity="Game"/>
<attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="uuid" optional="YES" attributeType="UUID" defaultValueString="00000000-0000-0000-0000-000000000000" usesScalarValueType="NO"/>
<relationship name="cover" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GameSeriesCover" inverseName="gameSeries" inverseEntity="GameSeriesCover"/>
<relationship name="games" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="series" inverseEntity="Game"/>
</entity>
<entity name="GameSeriesCover" representedClassName="GameSeriesCover" syncable="YES">
<attribute name="image" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData"/>
<relationship name="gameSeries" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GameSeries" inverseName="cover" inverseEntity="GameSeries"/>
</entity>
<entity name="Logo" representedClassName="Logo" syncable="YES">
<attribute name="image" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData"/>
<relationship name="console" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Console" inverseName="logo" inverseEntity="Console"/>
</entity>
<elements>
<element name="Accessory" positionX="-265.9140625" positionY="29.15625" width="128" height="133"/>
<element name="Console" positionX="-535.7890625" positionY="56.03515625" width="128" height="148"/>
<element name="Cover" positionX="-66.69921875" positionY="223.48046875" width="128" height="73"/>
<element name="Game" positionX="-288.828125" positionY="276.7421875" width="128" height="221"/>
<element name="GameSeries" positionX="-686.828125" positionY="359.20703125" width="128" height="88"/>
<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="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="73"/>
</elements>
</model>