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

View File

@@ -2,6 +2,6 @@
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "self:Zockerhoehle.xcodeproj"> location = "self:">
</FileRef> </FileRef>
</Workspace> </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> <key>orderHint</key>
<integer>0</integer> <integer>0</integer>
</dict> </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> </dict>
<key>SuppressBuildableAutocreation</key> <key>SuppressBuildableAutocreation</key>
<dict> <dict>

View File

@@ -23,8 +23,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
#endif #endif
print("disFinishLaunchung") 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 return true
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -2,7 +2,7 @@
"images" : [ "images" : [
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "star.png", "filename" : "Image.png",
"scale" : "1x" "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" : [ "images" : [
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "basket-supermarket.png",
"scale" : "1x" "scale" : "1x"
}, },
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "Image.png",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"idiom" : "universal", "idiom" : "universal",
"filename" : "Image-1.png",
"scale" : "3x" "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 application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail. 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 container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? { if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately. // Replace this implementation with code to handle the error appropriately.
@@ -43,6 +44,7 @@ class CDManager {
fatalError("Unresolved error \(error), \(error.userInfo)") fatalError("Unresolved error \(error), \(error.userInfo)")
} }
}) })
container.viewContext.automaticallyMergesChangesFromParent = true
return container return container
}() }()
@@ -53,10 +55,13 @@ class CDManager {
// MARK: - Core Data Saving support // MARK: - Core Data Saving support
func saveContext () { func saveContext () {
persistentContainer.performBackgroundTask({(context) in print("CHANGES: \(self.persistentContainer.viewContext.hasChanges)")
self.persistentContainer.performBackgroundTask({(context) in
print("Context \(context.hasChanges)")
if context.hasChanges { if context.hasChanges {
do { do {
try context.save() try context.save()
print("--------------------saved-------")
} catch { } catch {
// Replace this implementation with code to handle the error appropriately. // 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. // 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 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 name: String
@NSManaged public var inWishlist: Bool @NSManaged public var inWishlist: Bool
@NSManaged public var console: Console? @NSManaged public var console: Console?
@NSManaged public var lentTo : String?
@NSManaged public var uuid : UUID
} }

View File

@@ -9,14 +9,96 @@
import Foundation import Foundation
import CoreData import CoreData
import SwiftUI
import UIKit
@objc(Console) @objc(Console)
public class Console: NSManagedObject, Identifiable { 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 { public var id : NSManagedObjectID {
get { get {
return self.objectID 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 // Console+CoreDataProperties.swift
// Zockerhoehle // Zockerhoehle
// //
// Created by Julian-Steffen Müller on 06.07.19. // Created by Julian-Steffen Müller on 14.05.21.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved. // Copyright © 2021 Julian-Steffen Müller. All rights reserved.
// //
// //
@@ -17,11 +17,14 @@ extension Console {
return NSFetchRequest<Console>(entityName: "Console") return NSFetchRequest<Console>(entityName: "Console")
} }
@NSManaged public var circumstances: String?
@NSManaged public var generation: Int64 @NSManaged public var generation: Int64
@NSManaged public var manufacturer: String? @NSManaged public var manufacturer: String?
@NSManaged public var name: String @NSManaged public var name: String?
@NSManaged public var accessories: NSSet @NSManaged public var shortName: String?
@NSManaged public var games: NSSet @NSManaged public var uuid: UUID?
@NSManaged public var accessories: NSSet?
@NSManaged public var games: NSSet?
@NSManaged public var logo: Logo? @NSManaged public var logo: Logo?
} }

View File

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

View File

@@ -18,6 +18,6 @@ extension Cover {
} }
@NSManaged public var image: UIImage? @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 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 // Game+CoreDataProperties.swift
// Zockerhoehle // Zockerhoehle
// //
// Created by Julian-Steffen Müller on 06.07.19. // Created by Julian-Steffen Müller on 14.05.21.
// Copyright © 2019 Julian-Steffen Müller. All rights reserved. // Copyright © 2021 Julian-Steffen Müller. All rights reserved.
// //
// //
@@ -11,20 +11,24 @@ import Foundation
import CoreData import CoreData
extension Game{ extension Game {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Game> { @nonobjc public class func fetchRequest() -> NSFetchRequest<Game> {
return NSFetchRequest<Game>(entityName: "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 inWishlist: Bool
@NSManaged public var isDigital: 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 notes: String?
@NSManaged public var publisher: String? @NSManaged public var publisher: String?
@NSManaged public var uuid: UUID?
@NSManaged public var console: Console? @NSManaged public var console: Console?
@NSManaged public var series: GameSeries?
@NSManaged public var cover: Cover? @NSManaged public var cover: Cover?
@NSManaged public var series: GameSeries?
} }

View File

@@ -2,15 +2,65 @@
// GameSeries+CoreDataClass.swift // GameSeries+CoreDataClass.swift
// Zockerhoehle // 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. // Copyright © 2019 Julian-Steffen Müller. All rights reserved.
// //
// //
import Foundation import Foundation
import CoreData import CoreData
import UIKit
@objc(GameSeries) @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 // GameSeries+CoreDataProperties.swift
// Zockerhoehle // 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. // Copyright © 2019 Julian-Steffen Müller. All rights reserved.
// //
// //
@@ -17,9 +17,10 @@ extension GameSeries {
return NSFetchRequest<GameSeries>(entityName: "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 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> <string>1.0</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>
@@ -46,6 +48,12 @@
</array> </array>
</dict> </dict>
</dict> </dict>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key> <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 { class SceneDelegate: UIResponder, UIWindowSceneDelegate, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
let gameStore = GameStore(console: .none) let consolesStore = ConsoleStore()
let consoleStore = ConsoleStore() let gameSeriesStore = GameSeriesStore()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 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`. // 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 // Use a UIHostingController as window root view controller
if let windowScene = scene as? UIWindowScene { if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene) 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 self.window = window
window.makeKeyAndVisible() 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 self.consoleFilter = console
} }
override init() {
super.init()
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) { func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("GameStore::controllerDidChangeContent") print("GameStore::controllerDidChangeContent")
self.objectWillChange.send() self.objectWillChange.send()

View File

@@ -10,52 +10,62 @@ import Foundation
import UIKit import UIKit
import Combine import Combine
import SwiftUI import SwiftUI
import CoreData
class AccessoryViewModel : ObservableObject { class AccessoryViewModel : ObservableObject {
var objectWillChange = ObservableObjectPublisher() var objectWillChange = ObservableObjectPublisher()
var inWishlist : Bool { var inWishlist : Bool {
didSet { didSet {
if let accessory = self.accessory { guard let accessory = self.accessory else { return }
accessory.inWishlist = inWishlist
} accessory.inWishlist = inWishlist
} }
} }
var manufacturer : String? { var manufacturer : String? {
didSet { didSet {
if let accessory = self.accessory { guard let accessory = self.accessory else { return }
accessory.manufacturer = manufacturer
} accessory.manufacturer = manufacturer
} }
} }
var color : String? { var color : String? {
didSet { didSet {
if let accessory = self.accessory { guard let accessory = self.accessory else { return }
accessory.color = color
} accessory.color = color
} }
} }
var name : String { var name : String {
didSet { didSet {
if let accessory = self.accessory { guard let accessory = self.accessory else { return }
accessory.name = name
} accessory.name = name
} }
} }
private var accessory : Accessory? { var lentTo : String? {
didSet { didSet {
if accessory != nil { guard let accessory = self.accessory else { return }
self.name = accessory!.name
self.inWishlist = accessory!.inWishlist accessory.lentTo = lentTo;
self.color = accessory!.color
self.manufacturer = accessory!.manufacturer
}
} }
} }
private var accessory : Accessory? {
didSet {
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) { init(accessory : Accessory) {
self.accessory = accessory self.accessory = accessory
@@ -65,11 +75,24 @@ class AccessoryViewModel : ObservableObject {
self.color = accessory.color self.color = accessory.color
self.manufacturer = accessory.manufacturer self.manufacturer = accessory.manufacturer
_ = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).sink { self.NSManagedObjectChangedObserver = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).first().sink { notification in
//TODO guard let accessory = self.accessory else { return }
self.objectWillChange.send() guard let userInfo = notification.userInfo else { return }
if let accessory = self.accessory {
print("Accessory: \(accessory.name) Notification: \($0.description)") 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()
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 { class GameStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
var objectWillChange = PassthroughSubject<Void , Never>() var objectWillChange = PassthroughSubject<Void , Never>()
var consoleFilter : Console? var consoleFilter : Console?
var gameSeriesFilter : GameSeries?
var sortDescriptors : [NSSortDescriptor] = [NSSortDescriptor(key: "name", ascending: true)]
var fetchLimit : Int = 0
var games : [Game] { var games : [Game] {
get { get {
if self.fetchResultsController.fetchedObjects == nil { if self.fetchResultsController.fetchedObjects == nil {
do { do {
try fetchResultsController.performFetch() try fetchResultsController.performFetch()
}catch { }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() var gamesFetch : NSFetchRequest<Game> = Game.fetchRequest()
if let console = consoleFilter { if let console = consoleFilter {
gamesFetch = Game.fetchRequest(console: console) 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) let gamesFetchRC = NSFetchedResultsController(fetchRequest: gamesFetch, managedObjectContext: CDManager.shared.viewContext, sectionNameKeyPath: nil, cacheName: nil)
@@ -44,13 +67,30 @@ class GameStore : NSObject, ObservableObject, NSFetchedResultsControllerDelegate
return gamesFetchRC return gamesFetchRC
}() }()
init(console : Console?) { init(console : Console?, fetchLimit : Int = 0) {
super.init() super.init()
self.consoleFilter = console 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>) { func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
print("GameStore::controllerDidChangeContent")
self.objectWillChange.send() self.objectWillChange.send()
} }
} }

View File

@@ -13,64 +13,133 @@ import CoreData
import UIKit import UIKit
class GameViewModel : ObservableObject { class GameViewModel : ObservableObject {
var objectWillChange = ObservableObjectPublisher() var objectWillChange = ObservableObjectPublisher()
private var game : Game? { private var game : Game? {
didSet { didSet {
if game != nil { guard let game = game else { return }
self.name = game!.name
self.inWishlist = game!.inWishlist self.name = game.name!
self.isDigital = game!.isDigital self.inWishlist = game.inWishlist
self.isFinished = game!.isFinished 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 { var name : String {
didSet { didSet {
if let game = self.game { guard let game = self.game else { return }
game.name = name
} game.name = name
} }
} }
var inWishlist : Bool { var inWishlist : Bool {
didSet { didSet {
if let game = self.game { guard let game = self.game else { return }
game.inWishlist = inWishlist
} game.inWishlist = inWishlist
} }
} }
var isDigital : Bool { var isDigital : Bool {
didSet { didSet {
if let game = self.game { guard let game = self.game else { return }
game.isDigital = isDigital
} game.isDigital = isDigital
} }
} }
var isFinished : Bool { var isFinished : Bool {
didSet { didSet {
if let game = self.game { guard let game = self.game else { return }
game.isFinished = isFinished
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) { init(game : Game) {
self.game = game self.game = game
self.name = game.name self.name = game.name!
self.inWishlist = game.inWishlist self.inWishlist = game.inWishlist
self.isDigital = game.isDigital self.isDigital = game.isDigital
self.isFinished = game.isFinished self.isFinished = game.isFinished
self.gameSeries = game.series?.id ?? ""
self.lentTo = game.lentTo ?? ""
_ = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).sink { self.NSManagedObjectChangedObserver = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: CDManager.shared.viewContext).first().sink { notification in
//TODO
self.objectWillChange.send() guard let game = self.game else { return }
if let game = self.game { guard let userInfo = notification.userInfo else { return }
print("Game: \(game.name) Notification: \($0.description)")
var managedObjectIsMatching = false
if let inserts = userInfo[NSInsertedObjectsKey] as? Set<NSManagedObject> {
if inserts.contains(game) { managedObjectIsMatching = true }
}
if let updates = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject> {
if updates.contains(game) { managedObjectIsMatching = true }
}
if let deletes = userInfo[NSDeletedObjectsKey] as? Set<NSManagedObject> {
if deletes.contains(game) { managedObjectIsMatching = true }
}
if managedObjectIsMatching {
self.objectWillChange.send()
print("GameViewModel::NSMangedObjectChanged Update of \(game.name)")
} }
} }
} }

View File

@@ -28,11 +28,3 @@ struct AccessoryDetailView : View {
self.accessoryVM = accessoryVM! 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 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 { struct ConsoleLibraryView : View {
@State var isVideogamesSelected = true @State var isVideogamesSelected = true
@@ -16,20 +78,9 @@ struct ConsoleLibraryView : View {
@ObservedObject var accessoryStore : AccessoryStore @ObservedObject var accessoryStore : AccessoryStore
@State var showWishlist = false @State var showWishlist = false
@State var showAddToConsoleLibraryModal: Bool = false
@State var showLogoImagePicker = 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") })
}
}
var body: some View { var body: some View {
VStack { VStack {
HStack { HStack {
@@ -44,14 +95,29 @@ struct ConsoleLibraryView : View {
if self.isVideogamesSelected { if self.isVideogamesSelected {
List { List {
ForEach(gameStore.games.filter({$0.inWishlist == self.showWishlist})) { game in ForEach(gameStore.games.filter({$0.inWishlist == self.showWishlist})) { game in
NavigationLink(destination: GameDetailView(gameVM: GameViewModel(game: game))) { NavigationLink(destination: GameDetailView(game: game)) {
Text("\(game.name)") 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 { }else {
List { List {
ForEach(accessoryStore.accessories.filter({$0.inWishlist == self.showWishlist})) { accessory in ForEach(accessoryStore.accessories.filter({$0.inWishlist == self.showWishlist})) { accessory in
NavigationLink(destination: AccessoryDetailView(accessoryVM: AccessoryViewModel(accessory: accessory))) { NavigationLink(destination: AccessoryDetailView(accessoryVM: AccessoryViewModel(accessory: accessory))) {
Text("\(accessory.name)") 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: .navigationBarItems(trailing:
HStack { HStack {
Button(action: { NavigationLink(destination: ConsoleEditView(self.console!)) {
self.isModal = true Image(systemName: "pencil.and.ellipsis.rectangle")
}) { }
Image(systemName: "plus")
}.padding(5) Button(action: { self.showAddToConsoleLibraryModal = true },
Button(action: { label: { Image(systemName: "plus")})
self.showWishlist.toggle() .padding(5)
}) {
Image(systemName: "star") Button(action: { self.showWishlist.toggle()},
}.accentColor(self.showWishlist ? Color.red : Color.blue) label: { Image("wishlist")
.resizable()
.aspectRatio(contentMode: .fit)
})
.accentColor(self.showWishlist ? Color.red : Color.blue)
}) })
.sheet(isPresented: $isModal, onDismiss: {}, content: { .sheet(isPresented: $showAddToConsoleLibraryModal) {
self.modal if self.console != nil {
}) ModalAddToConsoleLibrary(modalAddToWishlist: self.showWishlist, isVideogamesSelected: true, console: self.console!)
}else{
Text("Fehler: Keine Konsole übergeben")
}
}
} }
init(console : Console?) { init(console : Console?) {
@@ -84,11 +160,3 @@ struct ConsoleLibraryView : View {
self.accessoryStore = AccessoryStore(console: console) 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,54 +9,146 @@
import SwiftUI import SwiftUI
struct GameDetailView : View { struct GameDetailView : View {
//@Binding var g : Game
@ObservedObject var gameVM : GameViewModel @ObservedObject var gameVM : GameViewModel
@State private var showDeleteAlert : Bool = false
@State var hasFinishedDate : Bool = false @State var hasFinishedDate : Bool = false
@State var playthroughDate : Date = Date() @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 { var body: some View {
Form { Form {
TextField("Videogame name", text: $gameVM.name) Section {
TextField("Videogame name", text: $gameVM.name)
Toggle(isOn: $gameVM.isDigital, label: { //Gray color should indicate immutable data
Text("Nur Digital") Text("\(gameVM.console?.name ?? "No console")").foregroundColor(.gray)
})
Toggle(isOn: $gameVM.isDigital, label: {
Toggle(isOn: $gameVM.inWishlist, label: { Text("Nur Digital")
Text("In Wunschliste")
})
Toggle(isOn: $gameVM.isFinished , label: {
Text("Durchgezockt")
})
if gameVM.isFinished {
Toggle(isOn: $hasFinishedDate, label: {
Text("Gibts ein Datum")
}) })
Toggle(isOn: $gameVM.inWishlist, label: {
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")
})
if gameVM.isFinished {
Toggle(isOn: $hasFinishedDate, label: {
Text("Gibts ein Datum")
})
}
if hasFinishedDate && gameVM.isFinished {
DatePicker("Durchgezockt am",
selection: $playthroughDate,
in: ...Date(),
displayedComponents: [.date])
}
} }
if hasFinishedDate && gameVM.isFinished { imageCoverSection
DatePicker("Durchgezockt am",
selection: $playthroughDate, Section{
in: ...Date(), gameDeleteButton
displayedComponents: [.date])
} }
}.navigationBarTitle(Text("\(gameVM.name)"), displayMode: .automatic) }
.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?) { init(gameVM : GameViewModel?) {
self.gameVM = gameVM! self.gameVM = gameVM!
} }
}
init(game : Game) {
#if DEBUG self.gameVM = GameViewModel(game: game)
struct GameDetailView_Previews : PreviewProvider {
static var previews: some View {
GameDetailView(gameVM: nil)
} }
} }
#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"?> <?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"> <entity name="Accessory" representedClassName="Accessory" syncable="YES">
<attribute name="circumstances" optional="YES" attributeType="String"/> <attribute name="circumstances" optional="YES" attributeType="String"/>
<attribute name="color" optional="YES" attributeType="String"/> <attribute name="color" optional="YES" attributeType="String"/>
<attribute name="inWishlist" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <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="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"/> <relationship name="console" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Console" inverseName="accessories" inverseEntity="Console"/>
</entity> </entity>
<entity name="Console" representedClassName="Console" syncable="YES"> <entity name="Console" representedClassName="Console" syncable="YES">
<attribute name="circumstances" optional="YES" attributeType="String"/> <attribute name="circumstances" optional="YES" attributeType="String"/>
<attribute name="generation" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="generation" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="manufacturer" optional="YES" attributeType="String"/> <attribute name="manufacturer" optional="YES" attributeType="String"/>
<attribute name="name" attributeType="String"/> <attribute name="name" attributeType="String" defaultValueString=""/>
<relationship name="accessories" toMany="YES" deletionRule="Nullify" destinationEntity="Accessory" inverseName="console" inverseEntity="Accessory"/> <attribute name="shortName" optional="YES" attributeType="String"/>
<relationship name="games" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="console" inverseEntity="Game"/> <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"/> <relationship name="logo" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="Logo" inverseName="console" inverseEntity="Logo"/>
</entity> </entity>
<entity name="Cover" representedClassName="Cover" syncable="YES"> <entity name="Cover" representedClassName="Cover" syncable="YES">
@@ -23,12 +27,15 @@
</entity> </entity>
<entity name="Game" representedClassName="Game" syncable="YES"> <entity name="Game" representedClassName="Game" syncable="YES">
<attribute name="circumstances" optional="YES" attributeType="String"/> <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="inWishlist" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="isDigital" 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="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="notes" optional="YES" attributeType="String"/>
<attribute name="publisher" 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="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="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"/> <relationship name="series" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GameSeries" inverseName="games" inverseEntity="GameSeries"/>
@@ -37,20 +44,26 @@
</fetchedProperty> </fetchedProperty>
</entity> </entity>
<entity name="GameSeries" representedClassName="GameSeries" syncable="YES"> <entity name="GameSeries" representedClassName="GameSeries" syncable="YES">
<attribute name="cover" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData" allowsExternalBinaryDataStorage="YES"/> <attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="name" optional="YES" attributeType="String"/> <attribute name="uuid" optional="YES" attributeType="UUID" defaultValueString="00000000-0000-0000-0000-000000000000" usesScalarValueType="NO"/>
<relationship name="games" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="series" inverseEntity="Game"/> <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>
<entity name="Logo" representedClassName="Logo" syncable="YES"> <entity name="Logo" representedClassName="Logo" syncable="YES">
<attribute name="image" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData"/> <attribute name="image" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromData"/>
<relationship name="console" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Console" inverseName="logo" inverseEntity="Console"/> <relationship name="console" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Console" inverseName="logo" inverseEntity="Console"/>
</entity> </entity>
<elements> <elements>
<element name="Accessory" positionX="-265.9140625" positionY="29.15625" width="128" height="133"/> <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="148"/> <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="73"/> <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="221"/> <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="88"/> <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"/> <element name="Logo" positionX="-66.7109375" positionY="110.9765625" width="128" height="73"/>
</elements> </elements>
</model> </model>