Merge branch 'feature/harmony' into develop
This commit is contained in:
commit
986b329178
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,3 +13,6 @@
|
||||
[submodule "Cores/GBCDeltaCore"]
|
||||
path = Cores/GBCDeltaCore
|
||||
url = git@github.com:rileytestut/GBCDeltaCore.git
|
||||
[submodule "External/Harmony"]
|
||||
path = External/Harmony
|
||||
url = https://github.com/rileytestut/Harmony.git
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 6da27f9b694e886d6c9805ba54848b0f0a922598
|
||||
Subproject commit 473474019bd2e3be623cd593da4c1e8c4e51fb80
|
||||
@ -36,6 +36,10 @@
|
||||
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF15AF831F54B43B009B6AAB /* ActionInput.swift */; };
|
||||
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */; };
|
||||
BF1DAD5D1D9F576000E752A7 /* SystemControllerSkinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1DAD5C1D9F576000E752A7 /* SystemControllerSkinsViewController.swift */; };
|
||||
BF1F45A421AF274D00EF9895 /* SyncResultViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1F45A321AF274D00EF9895 /* SyncResultViewController.swift */; };
|
||||
BF1F45AB21AF4B5800EF9895 /* SyncResultsViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF1F45AA21AF4B5800EF9895 /* SyncResultsViewController.storyboard */; };
|
||||
BF1F45AD21AF57BA00EF9895 /* HarmonyMetadataKey+Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1F45AC21AF57BA00EF9895 /* HarmonyMetadataKey+Keys.swift */; };
|
||||
BF1F45BF21AF676F00EF9895 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1F45BE21AF676F00EF9895 /* Box.swift */; };
|
||||
BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2451BB73FE800CCF94A /* Assets.xcassets */; };
|
||||
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */; };
|
||||
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF31878A1D489AAA00BD020D /* CheatValidator.swift */; };
|
||||
@ -48,11 +52,19 @@
|
||||
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF353FFE1C5DA3C500C1184C /* PausePresentationControllerContentView.xib */; };
|
||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */; };
|
||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */; };
|
||||
BF3D6C512202865F0083E05A /* Delta2ToDelta3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF3D6C502202865F0083E05A /* Delta2ToDelta3.xcmappingmodel */; };
|
||||
BF3D6C53220286750083E05A /* Delta3ToDelta4.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BF3D6C52220286750083E05A /* Delta3ToDelta4.xcmappingmodel */; };
|
||||
BF4828841F9027B600028B97 /* Delta.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF4828811F9027B600028B97 /* Delta.xcdatamodeld */; };
|
||||
BF4828861F9028F500028B97 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4828851F9028F500028B97 /* System.swift */; };
|
||||
BF4828881F90290F00028B97 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4828871F90290F00028B97 /* Action.swift */; };
|
||||
BF48F74E219A16DA00BC2FC1 /* SyncingServicesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF48F74D219A16DA00BC2FC1 /* SyncingServicesViewController.swift */; };
|
||||
BF48F755219A1EF000BC2FC1 /* Harmony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF48F754219A1EEB00BC2FC1 /* Harmony.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
BF48F756219A1EF000BC2FC1 /* Harmony.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF48F754219A1EEB00BC2FC1 /* Harmony.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BF48F75B219A1F8A00BC2FC1 /* Harmony_Drive.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF48F75A219A1F8300BC2FC1 /* Harmony_Drive.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
BF48F75C219A1F8A00BC2FC1 /* Harmony_Drive.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF48F75A219A1F8300BC2FC1 /* Harmony_Drive.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BF525EE81FF5F370004AA849 /* DeepLinkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF525EE71FF5F370004AA849 /* DeepLinkController.swift */; };
|
||||
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF525EE91FF6CD12004AA849 /* DeepLink.swift */; };
|
||||
BF56450D220239B800A8EA26 /* GameControllerInputMappingMigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF56450C220239B800A8EA26 /* GameControllerInputMappingMigrationPolicy.swift */; };
|
||||
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */; };
|
||||
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */; };
|
||||
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */; };
|
||||
@ -75,6 +87,8 @@
|
||||
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942921E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift */; };
|
||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */; };
|
||||
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF5E7F451B9A652600AE44F8 /* Settings.storyboard */; };
|
||||
BF63A1A321A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63A1A221A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift */; };
|
||||
BF63A1B521A4B76E00EE8F61 /* RecordVersionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63A1B421A4B76E00EE8F61 /* RecordVersionsViewController.swift */; };
|
||||
BF6424831F5B8F3F00D6AB44 /* ListMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6424821F5B8F3F00D6AB44 /* ListMenuViewController.swift */; };
|
||||
BF6424851F5CBDC900D6AB44 /* UIView+ParentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */; };
|
||||
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */; };
|
||||
@ -93,6 +107,8 @@
|
||||
BF7AE8081C2E858400B1B5BC /* GridMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */; };
|
||||
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */; };
|
||||
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */; };
|
||||
BF8A333421A484A000A42FD4 /* BadgedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */; };
|
||||
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */; };
|
||||
BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */; };
|
||||
BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */; };
|
||||
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2761E4977BF0030E7AD /* GameMetadata.swift */; };
|
||||
@ -104,14 +120,20 @@
|
||||
BF9F4FD01AAD7B87004C9500 /* DeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63BDE91D389EEB00FCB040 /* GameViewController.swift */; };
|
||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FEC1B8AA4FA00495943 /* Settings.swift */; };
|
||||
BFAB9F7D219A43380080EC7D /* SyncManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAB9F7C219A43380080EC7D /* SyncManager.swift */; };
|
||||
BFAB9F88219A4B670080EC7D /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = BFAB9F87219A4B670080EC7D /* GoogleService-Info.plist */; };
|
||||
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAB2E21EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift */; };
|
||||
BFC3628021ADE2BA00EF2BE6 /* UIAlertController+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC3627F21ADE2BA00EF2BE6 /* UIAlertController+Error.swift */; };
|
||||
BFC6F7B81F435BC500221B96 /* Input+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6F7B71F435BC500221B96 /* Input+Display.swift */; };
|
||||
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; };
|
||||
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */; };
|
||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */; };
|
||||
BFDB3418219E4B1700595A62 /* SyncStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */; };
|
||||
BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */; };
|
||||
BFE022A01F5B57FF0052D888 /* PopoverMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */; };
|
||||
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */; };
|
||||
BFE593CA21F3F8B7003412A6 /* GameSave.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE593C921F3F8B7003412A6 /* GameSave.swift */; };
|
||||
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE593CB21F3F8C2003412A6 /* _GameSave.swift */; };
|
||||
BFEC732D1AAECC4A00650035 /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFEC732C1AAECC4A00650035 /* Roxas.framework */; };
|
||||
BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFEC732C1AAECC4A00650035 /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFEF24F31F7DD4FD00454C62 /* SaveStateMigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */; };
|
||||
@ -137,6 +159,8 @@
|
||||
dstSubfolderSpec = 10;
|
||||
files = (
|
||||
BF9F4FD01AAD7B87004C9500 /* DeltaCore.framework in Embed Frameworks */,
|
||||
BF48F75C219A1F8A00BC2FC1 /* Harmony_Drive.framework in Embed Frameworks */,
|
||||
BF48F756219A1EF000BC2FC1 /* Harmony.framework in Embed Frameworks */,
|
||||
BFF0742D1E9DC17500ACDF4A /* GBCDeltaCore.framework in Embed Frameworks */,
|
||||
BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */,
|
||||
BF0418151D01E93400E85BCF /* GBADeltaCore.framework in Embed Frameworks */,
|
||||
@ -156,6 +180,7 @@
|
||||
BF0418131D01E93400E85BCF /* GBADeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GBADeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF04E6FE1DB8625C000F35D3 /* ControllerSkinsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllerSkinsViewController.swift; sourceTree = "<group>"; };
|
||||
BF07200E219A3A9500F05DA4 /* ZIPFoundation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = ZIPFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF0758DE2202827C005110F2 /* Delta 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Delta 4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
BF090CF11B490D8300DCAB45 /* Delta-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Delta-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
BF1020E21F95B05B00313182 /* DeltaToDelta2.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = DeltaToDelta2.xcmappingmodel; sourceTree = "<group>"; };
|
||||
BF107EC31BF413F000E0C32C /* GamesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesViewController.swift; sourceTree = "<group>"; };
|
||||
@ -165,6 +190,10 @@
|
||||
BF15AF831F54B43B009B6AAB /* ActionInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionInput.swift; sourceTree = "<group>"; };
|
||||
BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Importing.swift"; sourceTree = "<group>"; };
|
||||
BF1DAD5C1D9F576000E752A7 /* SystemControllerSkinsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemControllerSkinsViewController.swift; sourceTree = "<group>"; };
|
||||
BF1F45A321AF274D00EF9895 /* SyncResultViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncResultViewController.swift; sourceTree = "<group>"; };
|
||||
BF1F45AA21AF4B5800EF9895 /* SyncResultsViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SyncResultsViewController.storyboard; sourceTree = "<group>"; };
|
||||
BF1F45AC21AF57BA00EF9895 /* HarmonyMetadataKey+Keys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HarmonyMetadataKey+Keys.swift"; sourceTree = "<group>"; };
|
||||
BF1F45BE21AF676F00EF9895 /* Box.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Box.swift; sourceTree = "<group>"; };
|
||||
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Delta.entitlements; sourceTree = "<group>"; };
|
||||
BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods.framework; path = "Pods/../build/Debug-appletvos/Pods.framework"; sourceTree = "<group>"; };
|
||||
BF27CC941BCB7B7A00A20D89 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
||||
@ -180,12 +209,19 @@
|
||||
BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PauseStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||
BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveStatesViewController.swift; sourceTree = "<group>"; };
|
||||
BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PauseTransitionCoordinator.swift; sourceTree = "<group>"; };
|
||||
BF3D6C502202865F0083E05A /* Delta2ToDelta3.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = Delta2ToDelta3.xcmappingmodel; sourceTree = "<group>"; };
|
||||
BF3D6C52220286750083E05A /* Delta3ToDelta4.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; path = Delta3ToDelta4.xcmappingmodel; sourceTree = "<group>"; };
|
||||
BF4828821F9027B600028B97 /* Delta 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Delta 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
BF4828831F9027B600028B97 /* Delta.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Delta.xcdatamodel; sourceTree = "<group>"; };
|
||||
BF4828851F9028F500028B97 /* System.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = "<group>"; };
|
||||
BF4828871F90290F00028B97 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
|
||||
BF48F74D219A16DA00BC2FC1 /* SyncingServicesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncingServicesViewController.swift; sourceTree = "<group>"; };
|
||||
BF48F754219A1EEB00BC2FC1 /* Harmony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Harmony.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF48F75A219A1F8300BC2FC1 /* Harmony_Drive.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Harmony_Drive.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BF525EE71FF5F370004AA849 /* DeepLinkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkController.swift; sourceTree = "<group>"; };
|
||||
BF525EE91FF6CD12004AA849 /* DeepLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLink.swift; sourceTree = "<group>"; };
|
||||
BF5645092202381000A8EA26 /* Delta 3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Delta 3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
BF56450C220239B800A8EA26 /* GameControllerInputMappingMigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameControllerInputMappingMigrationPolicy.swift; sourceTree = "<group>"; };
|
||||
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadControllerSkinImageOperation.swift; sourceTree = "<group>"; };
|
||||
BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadImageURLOperation.swift; sourceTree = "<group>"; };
|
||||
BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
@ -211,6 +247,8 @@
|
||||
BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
|
||||
BF5E7F451B9A652600AE44F8 /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Settings.storyboard; path = Delta/Base.lproj/Settings.storyboard; sourceTree = SOURCE_ROOT; };
|
||||
BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControllerInputsViewController.swift; sourceTree = "<group>"; };
|
||||
BF63A1A221A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordSyncStatusViewController.swift; sourceTree = "<group>"; };
|
||||
BF63A1B421A4B76E00EE8F61 /* RecordVersionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordVersionsViewController.swift; sourceTree = "<group>"; };
|
||||
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = "<group>"; };
|
||||
BF6424821F5B8F3F00D6AB44 /* ListMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListMenuViewController.swift; sourceTree = "<group>"; };
|
||||
BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+ParentViewController.swift"; sourceTree = "<group>"; };
|
||||
@ -232,6 +270,8 @@
|
||||
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+FontSize.swift"; sourceTree = "<group>"; };
|
||||
BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridMenuViewController.swift; sourceTree = "<group>"; };
|
||||
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Delta.swift"; sourceTree = "<group>"; };
|
||||
BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgedTableViewCell.swift; sourceTree = "<group>"; };
|
||||
BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSyncStatusViewController.swift; sourceTree = "<group>"; };
|
||||
BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuController.swift; sourceTree = "<group>"; };
|
||||
BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCalloutView.swift; sourceTree = "<group>"; };
|
||||
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameMetadata.swift; sourceTree = "<group>"; };
|
||||
@ -239,14 +279,20 @@
|
||||
BF99A5961DC2F9C400468E9E /* ControllerSkinTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllerSkinTableViewCell.swift; sourceTree = "<group>"; };
|
||||
BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFAA1FEC1B8AA4FA00495943 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
|
||||
BFAB9F7C219A43380080EC7D /* SyncManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncManager.swift; sourceTree = "<group>"; };
|
||||
BFAB9F87219A4B670080EC7D /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
||||
BFBAB2E21EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DeltaCoreProtocol+Delta.swift"; sourceTree = "<group>"; };
|
||||
BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SNESDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFC3627F21ADE2BA00EF2BE6 /* UIAlertController+Error.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Error.swift"; sourceTree = "<group>"; };
|
||||
BFC6F7B71F435BC500221B96 /* Input+Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Input+Display.swift"; sourceTree = "<group>"; };
|
||||
BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheatsViewController.swift; sourceTree = "<group>"; };
|
||||
BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewControllerContextTransitioning+Conveniences.swift"; sourceTree = "<group>"; };
|
||||
BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStatusViewController.swift; sourceTree = "<group>"; };
|
||||
BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollectionViewController.swift; sourceTree = "<group>"; };
|
||||
BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuButton.swift; sourceTree = "<group>"; };
|
||||
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveStatesStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||
BFE593C921F3F8B7003412A6 /* GameSave.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameSave.swift; sourceTree = "<group>"; };
|
||||
BFE593CB21F3F8C2003412A6 /* _GameSave.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _GameSave.swift; sourceTree = "<group>"; };
|
||||
BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveStateMigrationPolicy.swift; sourceTree = "<group>"; };
|
||||
BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GBCDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -271,10 +317,12 @@
|
||||
files = (
|
||||
BF9F4FCF1AAD7B87004C9500 /* DeltaCore.framework in Frameworks */,
|
||||
BFEC732D1AAECC4A00650035 /* Roxas.framework in Frameworks */,
|
||||
BF48F75B219A1F8A00BC2FC1 /* Harmony_Drive.framework in Frameworks */,
|
||||
BF99C6941D0A9AA600BA92BC /* SNESDeltaCore.framework in Frameworks */,
|
||||
BF0418141D01E93400E85BCF /* GBADeltaCore.framework in Frameworks */,
|
||||
BF072010219A3A9D00F05DA4 /* ZIPFoundation.framework in Frameworks */,
|
||||
BFF0742C1E9DC17500ACDF4A /* GBCDeltaCore.framework in Frameworks */,
|
||||
BF48F755219A1EF000BC2FC1 /* Harmony.framework in Frameworks */,
|
||||
4FE8465FD28810191C3E5212 /* Pods_Delta.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -298,6 +346,8 @@
|
||||
BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */,
|
||||
BFC6F7B71F435BC500221B96 /* Input+Display.swift */,
|
||||
BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */,
|
||||
BFC3627F21ADE2BA00EF2BE6 /* UIAlertController+Error.swift */,
|
||||
BF1F45AC21AF57BA00EF9895 /* HarmonyMetadataKey+Keys.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -350,6 +400,18 @@
|
||||
path = "Game Selection";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF48F74C219A16C100BC2FC1 /* Syncing */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF48F74D219A16DA00BC2FC1 /* SyncingServicesViewController.swift */,
|
||||
BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */,
|
||||
BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */,
|
||||
BF63A1A221A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift */,
|
||||
BF63A1B421A4B76E00EE8F61 /* RecordVersionsViewController.swift */,
|
||||
);
|
||||
path = Syncing;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BF525EE61FF5F355004AA849 /* Deep Linking */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -363,6 +425,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF4828871F90290F00028B97 /* Action.swift */,
|
||||
BF1F45BE21AF676F00EF9895 /* Box.swift */,
|
||||
BFE0229C1F5B56840052D888 /* Popover Menu */,
|
||||
BF5942671E09BBB70051894B /* Collection View */,
|
||||
BF71CF881FE90471001F1613 /* Table View */,
|
||||
@ -420,6 +483,7 @@
|
||||
BF5942791E09BC830051894B /* Game.swift */,
|
||||
BF59427A1E09BC830051894B /* GameCollection.swift */,
|
||||
BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */,
|
||||
BFE593C921F3F8B7003412A6 /* GameSave.swift */,
|
||||
BF59427B1E09BC830051894B /* SaveState.swift */,
|
||||
);
|
||||
path = Human;
|
||||
@ -433,6 +497,7 @@
|
||||
BF5942831E09BC8B0051894B /* _Game.swift */,
|
||||
BF5942841E09BC8B0051894B /* _GameCollection.swift */,
|
||||
BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */,
|
||||
BFE593CB21F3F8C2003412A6 /* _GameSave.swift */,
|
||||
BF5942851E09BC8B0051894B /* _SaveState.swift */,
|
||||
);
|
||||
path = Machine;
|
||||
@ -496,6 +561,7 @@
|
||||
children = (
|
||||
BFFA4C081E8A24D600D87934 /* GameTableViewCell.swift */,
|
||||
BF71CF891FE904B1001F1613 /* GameTableViewCell.xib */,
|
||||
BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */,
|
||||
);
|
||||
path = "Table View";
|
||||
sourceTree = "<group>";
|
||||
@ -547,6 +613,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF07200E219A3A9500F05DA4 /* ZIPFoundation.framework */,
|
||||
BF48F754219A1EEB00BC2FC1 /* Harmony.framework */,
|
||||
BF48F75A219A1F8300BC2FC1 /* Harmony_Drive.framework */,
|
||||
BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */,
|
||||
BF0418131D01E93400E85BCF /* GBADeltaCore.framework */,
|
||||
BF27CC941BCB7B7A00A20D89 /* GameController.framework */,
|
||||
@ -570,10 +638,21 @@
|
||||
BF71CF851FE8FFF1001F1613 /* App Icon Shortcuts */,
|
||||
BF11734E1DA32CEC00047DF8 /* Controllers */,
|
||||
BF1DAD5B1D9F574900E752A7 /* Controller Skins */,
|
||||
BF48F74C219A16C100BC2FC1 /* Syncing */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFAB9F7B219A43270080EC7D /* Syncing */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFAB9F7C219A43380080EC7D /* SyncManager.swift */,
|
||||
BF1F45A321AF274D00EF9895 /* SyncResultViewController.swift */,
|
||||
BF1F45AA21AF4B5800EF9895 /* SyncResultsViewController.storyboard */,
|
||||
);
|
||||
path = Syncing;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFC9B7371CEFCD08008629BB /* Cheats */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -609,6 +688,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF1020E21F95B05B00313182 /* DeltaToDelta2.xcmappingmodel */,
|
||||
BF3D6C502202865F0083E05A /* Delta2ToDelta3.xcmappingmodel */,
|
||||
BF3D6C52220286750083E05A /* Delta3ToDelta4.xcmappingmodel */,
|
||||
BFEF24F11F7DD4BE00454C62 /* Policies */,
|
||||
);
|
||||
path = Migrations;
|
||||
@ -618,6 +699,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */,
|
||||
BF56450C220239B800A8EA26 /* GameControllerInputMappingMigrationPolicy.swift */,
|
||||
);
|
||||
path = Policies;
|
||||
sourceTree = "<group>";
|
||||
@ -652,6 +734,7 @@
|
||||
BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */,
|
||||
BFAA1FEB1B8AA4E800495943 /* Settings */,
|
||||
BF59426C1E09BC450051894B /* Database */,
|
||||
BFAB9F7B219A43270080EC7D /* Syncing */,
|
||||
BF59428C1E09BCE50051894B /* Importing */,
|
||||
BF930FFB1EB6D6EC00E8DBA0 /* Systems */,
|
||||
BF525EE61FF5F355004AA849 /* Deep Linking */,
|
||||
@ -669,6 +752,7 @@
|
||||
BF090CF11B490D8300DCAB45 /* Delta-Bridging-Header.h */,
|
||||
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */,
|
||||
BFFA71DB1AAC406100EE9DD1 /* Info.plist */,
|
||||
BFAB9F87219A4B670080EC7D /* GoogleService-Info.plist */,
|
||||
);
|
||||
path = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
@ -725,7 +809,6 @@
|
||||
BFFA71D51AAC406100EE9DD1 /* Resources */,
|
||||
BF9F4FCC1AAD7AEE004C9500 /* Embed Frameworks */,
|
||||
B444B2BB31CBCEE7D86E943D /* [CP] Embed Pods Frameworks */,
|
||||
3521C8FA1BB6004A6E9FE324 /* [CP] Copy Pods Resources */,
|
||||
BF6BF3281EB897F6008E83CD /* Fabric */,
|
||||
);
|
||||
buildRules = (
|
||||
@ -791,11 +874,13 @@
|
||||
files = (
|
||||
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */,
|
||||
BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */,
|
||||
BFAB9F88219A4B670080EC7D /* GoogleService-Info.plist in Resources */,
|
||||
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */,
|
||||
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */,
|
||||
BF02D5DA1DDEBB3000A5E131 /* openvgdb.sqlite in Resources */,
|
||||
BF71CF8A1FE904B1001F1613 /* GameTableViewCell.xib in Resources */,
|
||||
BFFC46461D59861000AF2CC6 /* LaunchScreen.storyboard in Resources */,
|
||||
BF1F45AB21AF4B5800EF9895 /* SyncResultsViewController.storyboard in Resources */,
|
||||
BF353FF61C5D837600C1184C /* PauseMenu.storyboard in Resources */,
|
||||
BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
@ -804,30 +889,22 @@
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
3521C8FA1BB6004A6E9FE324 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Delta/Pods-Delta-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
B444B2BB31CBCEE7D86E943D /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${SRCROOT}/Pods/Target Support Files/Pods-Delta/Pods-Delta-frameworks.sh",
|
||||
"${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SMCalloutView/SMCalloutView.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/SQLite.swift/SQLite.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SDWebImage.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SMCalloutView.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLite.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
@ -868,13 +945,16 @@
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Delta-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
@ -892,13 +972,19 @@
|
||||
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */,
|
||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
||||
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
|
||||
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
|
||||
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
||||
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */,
|
||||
BF63A1A321A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift in Sources */,
|
||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */,
|
||||
BF59428A1E09BC8B0051894B /* _SaveState.swift in Sources */,
|
||||
BF5942801E09BC830051894B /* SaveState.swift in Sources */,
|
||||
BF59428E1E09BCFB0051894B /* ImportController.swift in Sources */,
|
||||
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */,
|
||||
BF48F74E219A16DA00BC2FC1 /* SyncingServicesViewController.swift in Sources */,
|
||||
BFE593CA21F3F8B7003412A6 /* GameSave.swift in Sources */,
|
||||
BF63A1B521A4B76E00EE8F61 /* RecordVersionsViewController.swift in Sources */,
|
||||
BF6424831F5B8F3F00D6AB44 /* ListMenuViewController.swift in Sources */,
|
||||
BF1020E31F95B05B00313182 /* DeltaToDelta2.xcmappingmodel in Sources */,
|
||||
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */,
|
||||
@ -907,7 +993,9 @@
|
||||
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */,
|
||||
BF4828841F9027B600028B97 /* Delta.xcdatamodeld in Sources */,
|
||||
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
||||
BFC3628021ADE2BA00EF2BE6 /* UIAlertController+Error.swift in Sources */,
|
||||
BF353FF91C5D870B00C1184C /* MenuItem.swift in Sources */,
|
||||
BFDB3418219E4B1700595A62 /* SyncStatusViewController.swift in Sources */,
|
||||
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */,
|
||||
BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */,
|
||||
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */,
|
||||
@ -918,6 +1006,7 @@
|
||||
BF04E6FF1DB8625C000F35D3 /* ControllerSkinsViewController.swift in Sources */,
|
||||
BF5942891E09BC8B0051894B /* _GameCollection.swift in Sources */,
|
||||
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */,
|
||||
BF1F45AD21AF57BA00EF9895 /* HarmonyMetadataKey+Keys.swift in Sources */,
|
||||
BF71CF871FE90006001F1613 /* AppIconShortcutsViewController.swift in Sources */,
|
||||
BF1DAD5D1D9F576000E752A7 /* SystemControllerSkinsViewController.swift in Sources */,
|
||||
BFE022A01F5B57FF0052D888 /* PopoverMenuButton.swift in Sources */,
|
||||
@ -927,7 +1016,10 @@
|
||||
BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */,
|
||||
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */,
|
||||
BF34FA071CF0F510006624C7 /* EditCheatViewController.swift in Sources */,
|
||||
BF1F45A421AF274D00EF9895 /* SyncResultViewController.swift in Sources */,
|
||||
BF3D6C53220286750083E05A /* Delta3ToDelta4.xcmappingmodel in Sources */,
|
||||
BF5942881E09BC8B0051894B /* _Game.swift in Sources */,
|
||||
BF56450D220239B800A8EA26 /* GameControllerInputMappingMigrationPolicy.swift in Sources */,
|
||||
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */,
|
||||
BFFC461F1D59823500AF2CC6 /* GamesStoryboardSegue.swift in Sources */,
|
||||
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */,
|
||||
@ -935,6 +1027,7 @@
|
||||
BF353FFF1C5DA3C500C1184C /* PausePresentationController.swift in Sources */,
|
||||
BFFC464C1D5998D600AF2CC6 /* CheatTableViewCell.swift in Sources */,
|
||||
BF5942941E09BD1A0051894B /* NSManagedObject+Conveniences.swift in Sources */,
|
||||
BF1F45BF21AF676F00EF9895 /* Box.swift in Sources */,
|
||||
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */,
|
||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
||||
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */,
|
||||
@ -944,13 +1037,16 @@
|
||||
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,
|
||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
||||
BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */,
|
||||
BF8A333421A484A000A42FD4 /* BadgedTableViewCell.swift in Sources */,
|
||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
||||
BF3D6C512202865F0083E05A /* Delta2ToDelta3.xcmappingmodel in Sources */,
|
||||
BF59426F1E09BC5D0051894B /* DatabaseManager.swift in Sources */,
|
||||
BF4828861F9028F500028B97 /* System.swift in Sources */,
|
||||
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */,
|
||||
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
|
||||
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
|
||||
BFAB9F7D219A43380080EC7D /* SyncManager.swift in Sources */,
|
||||
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */,
|
||||
BF1173501DA32CF600047DF8 /* ControllersSettingsViewController.swift in Sources */,
|
||||
BFFC461E1D59823500AF2CC6 /* GamesPresentationController.swift in Sources */,
|
||||
@ -1079,6 +1175,7 @@
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = "-DGLES_SILENCE_DEPRECATION";
|
||||
OTHER_SWIFT_FLAGS = "-Xfrontend -debug-time-function-bodies";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Delta;
|
||||
PRODUCT_NAME = Delta;
|
||||
@ -1130,6 +1227,7 @@
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_CFLAGS = "-DGLES_SILENCE_DEPRECATION";
|
||||
OTHER_SWIFT_FLAGS = "-Xfrontend -debug-time-function-bodies";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Delta;
|
||||
PRODUCT_NAME = Delta;
|
||||
@ -1146,8 +1244,10 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Delta/Supporting Files/Delta.entitlements";
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
DEVELOPMENT_TEAM = 6XVY5G3U44;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
PROVISIONING_PROFILE = "";
|
||||
@ -1165,8 +1265,10 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Delta/Supporting Files/Delta.entitlements";
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
DEVELOPMENT_TEAM = 6XVY5G3U44;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DIMPACTOR";
|
||||
@ -1214,10 +1316,12 @@
|
||||
BF4828811F9027B600028B97 /* Delta.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
BF0758DE2202827C005110F2 /* Delta 4.xcdatamodel */,
|
||||
BF5645092202381000A8EA26 /* Delta 3.xcdatamodel */,
|
||||
BF4828821F9027B600028B97 /* Delta 2.xcdatamodel */,
|
||||
BF4828831F9027B600028B97 /* Delta.xcdatamodel */,
|
||||
);
|
||||
currentVersion = BF4828821F9027B600028B97 /* Delta 2.xcdatamodel */;
|
||||
currentVersion = BF0758DE2202827C005110F2 /* Delta 4.xcdatamodel */;
|
||||
path = Delta.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
||||
@ -76,6 +76,34 @@
|
||||
ReferencedContainer = "container:Cores/GBCDeltaCore/GBCDeltaCore.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "BFA1C8D31ECD01C100DEA99D"
|
||||
BuildableName = "Harmony.framework"
|
||||
BlueprintName = "Harmony"
|
||||
ReferencedContainer = "container:External/Harmony/Harmony.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "BFECF9F82016982D0012B9FC"
|
||||
BuildableName = "Harmony_Drive.framework"
|
||||
BlueprintName = "Harmony-Drive"
|
||||
ReferencedContainer = "container:External/Harmony/Backends/Drive/Harmony-Drive.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
@ -145,7 +173,7 @@
|
||||
<EnvironmentVariable
|
||||
key = "OS_ACTIVITY_MODE"
|
||||
value = "disable"
|
||||
isEnabled = "YES">
|
||||
isEnabled = "NO">
|
||||
</EnvironmentVariable>
|
||||
</EnvironmentVariables>
|
||||
<AdditionalOptions>
|
||||
|
||||
3
Delta.xcworkspace/contents.xcworkspacedata
generated
3
Delta.xcworkspace/contents.xcworkspacedata
generated
@ -16,6 +16,9 @@
|
||||
<FileRef
|
||||
location = "group:Cores/GBCDeltaCore/GBCDeltaCore.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Harmony/Harmony.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:External/Roxas/Roxas.xcodeproj">
|
||||
</FileRef>
|
||||
|
||||
@ -36,10 +36,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate
|
||||
gestureRecognizer.delaysTouchesBegan = false
|
||||
}
|
||||
|
||||
// Database
|
||||
DatabaseManager.shared.loadPersistentStores { (description, error) in
|
||||
}
|
||||
|
||||
// Controllers
|
||||
ExternalGameControllerManager.shared.startMonitoring()
|
||||
|
||||
|
||||
@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13528" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13526"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -42,11 +41,18 @@
|
||||
<segue destination="xMK-Cs-fAS" kind="presentation" id="uN5-PN-7FK"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" systemItem="add" id="FeA-O5-xd2">
|
||||
<connections>
|
||||
<action selector="importFiles" destination="jeE-WD-wXO" id="A1s-kE-NkM"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<rightBarButtonItems>
|
||||
<barButtonItem systemItem="add" id="FeA-O5-xd2">
|
||||
<connections>
|
||||
<action selector="importFiles" destination="jeE-WD-wXO" id="A1s-kE-NkM"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem title="Sync" id="6Fp-px-Dow">
|
||||
<connections>
|
||||
<action selector="sync" destination="jeE-WD-wXO" id="FFs-LZ-GEr"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</rightBarButtonItems>
|
||||
</navigationItem>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="JYx-xE-nis" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
@ -238,7 +244,7 @@
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="SettingsButton" width="16" height="16"/>
|
||||
<image name="SettingsButton" width="22" height="22"/>
|
||||
</resources>
|
||||
<inferredMetricsTieBreakers>
|
||||
<segue reference="Tey-6Z-UHp"/>
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="ssH-mM-uG6">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="ssH-mM-uG6">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -19,7 +18,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Delta 0.6.0" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Str-BY-agW">
|
||||
<rect key="frame" x="0.0" y="673" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="817" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
@ -222,17 +221,68 @@
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Syncing" id="y6U-7a-bnX" userLabel="Syncing">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="4U1-fe-PIb" detailTextLabel="kLY-5g-v8n" style="IBUITableViewCellStyleValue1" id="bwW-PG-BcV">
|
||||
<rect key="frame" x="0.0" y="611" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bwW-PG-BcV" id="RNA-99-evH">
|
||||
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Service" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4U1-fe-PIb">
|
||||
<rect key="frame" x="16" y="12" width="54" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Google Drive" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="kLY-5g-v8n">
|
||||
<rect key="frame" x="246" y="12" width="94" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" red="0.5568627451" green="0.5568627451" blue="0.57647058819999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="R9m-rV-VgV" kind="show" id="MAM-GM-FlH"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="BadgeCell" textLabel="1u0-gh-zP7" style="IBUITableViewCellStyleDefault" id="JPg-6O-DRe" customClass="BadgedTableViewCell" customModule="Delta" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="655" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="JPg-6O-DRe" id="zcZ-QR-Nno">
|
||||
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Status" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="1u0-gh-zP7">
|
||||
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="vVg-ci-JRa" kind="show" id="Hfy-lJ-4tW"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="3D Touch" id="fdp-8c-oOc">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="c5i-qG-ir9" style="IBUITableViewCellStyleDefault" id="SSL-t4-QZj">
|
||||
<rect key="frame" x="0.0" y="611" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="755" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="SSL-t4-QZj" id="hQB-Iy-bzy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="App Icon Shortcuts" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="c5i-qG-ir9">
|
||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
||||
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
@ -262,6 +312,7 @@
|
||||
<connections>
|
||||
<outlet property="controllerOpacityLabel" destination="zaz-yD-CYG" id="eUW-u9-xxx"/>
|
||||
<outlet property="controllerOpacitySlider" destination="whi-If-wFf" id="6Cx-HY-xLG"/>
|
||||
<outlet property="syncingServiceLabel" destination="kLY-5g-v8n" id="zzx-qM-q1g"/>
|
||||
<outlet property="versionLabel" destination="Str-BY-agW" id="gU2-L0-pYt"/>
|
||||
<segue destination="uBz-mm-mXr" kind="show" identifier="controllersSegue" id="MLY-hF-UB8"/>
|
||||
<segue destination="56e-ul-z6v" kind="show" identifier="controllerSkinsSegue" id="GNM-Gt-YFf"/>
|
||||
@ -342,7 +393,7 @@
|
||||
</connections>
|
||||
</tapGestureRecognizer>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3809" y="471"/>
|
||||
<point key="canvasLocation" x="4102" y="-244"/>
|
||||
</scene>
|
||||
<!--Controllers-->
|
||||
<scene sceneID="swa-DT-VKS">
|
||||
@ -391,7 +442,7 @@
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="owG-Kh-rfn" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2221" y="471"/>
|
||||
<point key="canvasLocation" x="2513" y="-244"/>
|
||||
</scene>
|
||||
<!--Game Boy Advance-->
|
||||
<scene sceneID="pkL-Te-puh">
|
||||
@ -463,7 +514,7 @@
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Nx1-Ly-oRu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2220" y="1181.5592203898052"/>
|
||||
<point key="canvasLocation" x="2513" y="466"/>
|
||||
</scene>
|
||||
<!--App Icon Shortcuts-->
|
||||
<scene sceneID="e9N-fv-yuQ">
|
||||
@ -482,7 +533,7 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4bh-Lf-zgk">
|
||||
<rect key="frame" x="310" y="6" width="51" height="31"/>
|
||||
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="switchGameShortcutsModeWith:" destination="yXS-6u-1AN" eventType="valueChanged" id="bX8-gd-h4g"/>
|
||||
</connections>
|
||||
@ -508,7 +559,7 @@
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Lzk-4m-LKw" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2220" y="1897"/>
|
||||
<point key="canvasLocation" x="2513" y="1182"/>
|
||||
</scene>
|
||||
<!--Controller Skins-->
|
||||
<scene sceneID="IN0-an-SWm">
|
||||
@ -556,7 +607,7 @@
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="54U-JB-wBG" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2976.8000000000002" y="1181.5592203898052"/>
|
||||
<point key="canvasLocation" x="3270" y="466"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="8qd-VB-Uy5">
|
||||
@ -574,7 +625,7 @@
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HMI-Ep-MdI" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="889" y="471"/>
|
||||
<point key="canvasLocation" x="708" y="471"/>
|
||||
</scene>
|
||||
<!--Grid Menu View Controller-->
|
||||
<scene sceneID="Lgi-Ii-M1W">
|
||||
@ -608,7 +659,7 @@
|
||||
</collectionViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="pRg-BA-3KK" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4566" y="-216"/>
|
||||
<point key="canvasLocation" x="4858" y="-931"/>
|
||||
</scene>
|
||||
<!--Game View Controller-->
|
||||
<scene sceneID="qAz-yz-iOc">
|
||||
@ -626,7 +677,7 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="uQK-ch-9AG" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4566" y="471"/>
|
||||
<point key="canvasLocation" x="4858" y="-244"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="bwW-s2-fcE">
|
||||
@ -645,7 +696,538 @@
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="D4f-Fb-zfa" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2977" y="471"/>
|
||||
<point key="canvasLocation" x="3270" y="-244"/>
|
||||
</scene>
|
||||
<!--Syncing Service-->
|
||||
<scene sceneID="8nM-uV-t0b">
|
||||
<objects>
|
||||
<tableViewController title="Syncing Service" id="R9m-rV-VgV" customClass="SyncingServicesViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="Zsb-6q-tLe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Service" id="mIB-Au-dYz">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="QAp-WA-1g3" style="IBUITableViewCellStyleDefault" id="vkb-8K-t7E">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vkb-8K-t7E" id="YcK-vq-ABN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="None" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QAp-WA-1g3">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="4fb-TC-FrG" style="IBUITableViewCellStyleDefault" id="hBZ-Fp-9Kh">
|
||||
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hBZ-Fp-9Kh" id="rfN-2N-L43">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Google Drive" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4fb-TC-FrG">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Account" id="1Wk-cG-HDE">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" textLabel="AYq-XK-j5L" style="IBUITableViewCellStyleDefault" id="nrN-mu-0HX">
|
||||
<rect key="frame" x="0.0" y="199.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="nrN-mu-0HX" id="lHU-qJ-uhj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="riley@rileytestut.com" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="AYq-XK-j5L">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="" id="Jnq-12-IOu">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" textLabel="4TQ-cm-2sN" style="IBUITableViewCellStyleDefault" id="wRv-En-k1Y">
|
||||
<rect key="frame" x="0.0" y="279.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="wRv-En-k1Y" id="7QF-ID-Gu2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Sign Out" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="4TQ-cm-2sN">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.99101024869999998" green="0.27251276369999999" blue="0.0051303170620000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="R9m-rV-VgV" id="PXt-S6-euC"/>
|
||||
<outlet property="delegate" destination="R9m-rV-VgV" id="qqu-iI-H9F"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="uqz-XU-aTr" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2513" y="1872"/>
|
||||
</scene>
|
||||
<!--Sync Status-->
|
||||
<scene sceneID="IYe-DI-U7i">
|
||||
<objects>
|
||||
<tableViewController title="Sync Status" id="vVg-ci-JRa" customClass="SyncStatusViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Nza-ON-XbS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="AWh-ik-Gvu" style="IBUITableViewCellStyleDefault" id="wpv-cf-duw" customClass="BadgedTableViewCell" customModule="Delta" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="wpv-cf-duw" id="ehC-Qx-Zx4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="AWh-ik-Gvu">
|
||||
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="OnX-sX-bHK" kind="show" identifier="showGame" id="vUN-0T-oaK"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="PreviousSyncCell" textLabel="Piz-MQ-rK5" style="IBUITableViewCellStyleDefault" id="tdO-nZ-AtU">
|
||||
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="tdO-nZ-AtU" id="Ecj-ul-03q">
|
||||
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Previous Sync" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Piz-MQ-rK5">
|
||||
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" name="Purple"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="NCG-b5-gRT" kind="show" identifier="showPreviousSyncResults" id="e0p-do-4bJ"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="vVg-ci-JRa" id="Ch3-KN-Vuq"/>
|
||||
<outlet property="delegate" destination="vVg-ci-JRa" id="WMN-hE-f2P"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="kT7-Ig-nyf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3351" y="1872"/>
|
||||
</scene>
|
||||
<!--syncResultViewController-->
|
||||
<scene sceneID="p0H-JM-YTw">
|
||||
<objects>
|
||||
<viewControllerPlaceholder storyboardName="SyncResultsViewController" referencedIdentifier="syncResultViewController" id="NCG-b5-gRT" sceneMemberID="viewController"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="X4J-BU-m3d" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3350" y="2281"/>
|
||||
</scene>
|
||||
<!--Game Sync Status View Controller-->
|
||||
<scene sceneID="iQk-cq-qsQ">
|
||||
<objects>
|
||||
<tableViewController id="OnX-sX-bHK" customClass="GameSyncStatusViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="AFt-Hn-fzR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="RWL-4W-NpH" style="IBUITableViewCellStyleDefault" id="q5G-Db-MXt">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="q5G-Db-MXt" id="Ric-Km-AWj">
|
||||
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Game" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RWL-4W-NpH">
|
||||
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="kBh-Lp-rBr" kind="show" identifier="showRecord" id="6lK-rC-DrT"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<sections/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="OnX-sX-bHK" id="TuS-hI-T9X"/>
|
||||
<outlet property="delegate" destination="OnX-sX-bHK" id="vII-vu-IIw"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="FHk-WU-IoC" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="4206" y="1872"/>
|
||||
</scene>
|
||||
<!--Record Sync Status View Controller-->
|
||||
<scene sceneID="GG8-80-sV4">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="recordSyncStatusViewController" id="kBh-Lp-rBr" customClass="RecordSyncStatusViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="k6O-hT-4zC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection id="6pR-j6-CoP">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="SwitchCell" textLabel="QBt-p1-jpG" rowHeight="44" style="IBUITableViewCellStyleDefault" id="4jH-oK-0Z2">
|
||||
<rect key="frame" x="0.0" y="35" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4jH-oK-0Z2" id="SMG-3T-Z4f">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="K0v-VD-uUj">
|
||||
<rect key="frame" x="163" y="6" width="49" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="onTintColor" name="Purple"/>
|
||||
<connections>
|
||||
<action selector="toggleSyncingEnabled:" destination="kBh-Lp-rBr" eventType="primaryActionTriggered" id="OoQ-XU-uYY"/>
|
||||
</connections>
|
||||
</switch>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Syncing Enabled" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QBt-p1-jpG">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accessoryView" destination="K0v-VD-uUj" id="7H4-Y4-jCe"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Local" id="vpJ-Pg-nU9">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" textLabel="npF-wl-PPC" detailTextLabel="SYD-cR-5TY" style="IBUITableViewCellStyleValue1" id="9Dq-cm-tka">
|
||||
<rect key="frame" x="0.0" y="135" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="9Dq-cm-tka" id="KqI-RZ-PSb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Status" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="npF-wl-PPC">
|
||||
<rect key="frame" x="16" y="12" width="49.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Updated" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="SYD-cR-5TY">
|
||||
<rect key="frame" x="292.5" y="12" width="66.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" textLabel="EFZ-Om-S4W" detailTextLabel="D5a-Na-NTh" style="IBUITableViewCellStyleValue1" id="1tW-nf-sYt">
|
||||
<rect key="frame" x="0.0" y="179" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="1tW-nf-sYt" id="Unw-0A-JBl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="EFZ-Om-S4W">
|
||||
<rect key="frame" x="16" y="12" width="36" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="October 7, 1995" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="D5a-Na-NTh">
|
||||
<rect key="frame" x="236.5" y="12" width="122.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Remote" id="Bct-0y-ptf">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" textLabel="aeh-me-gZl" detailTextLabel="0Rm-b2-HX5" style="IBUITableViewCellStyleValue1" id="djI-O4-xho">
|
||||
<rect key="frame" x="0.0" y="279" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="djI-O4-xho" id="tAf-KE-rZV">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Status" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="aeh-me-gZl">
|
||||
<rect key="frame" x="16" y="12" width="49.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Updated" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0Rm-b2-HX5">
|
||||
<rect key="frame" x="292.5" y="12" width="66.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" textLabel="jzK-yT-69q" detailTextLabel="b43-lW-Enx" style="IBUITableViewCellStyleValue1" id="gAa-cx-WYJ">
|
||||
<rect key="frame" x="0.0" y="323" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="gAa-cx-WYJ" id="ZsM-y1-TiF">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Date" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="jzK-yT-69q">
|
||||
<rect key="frame" x="16" y="12" width="36" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="October 7, 1995" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="b43-lW-Enx">
|
||||
<rect key="frame" x="236.5" y="12" width="122.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" textLabel="r04-kp-xeN" detailTextLabel="VIa-dS-7SR" style="IBUITableViewCellStyleValue1" id="oac-qK-Po9">
|
||||
<rect key="frame" x="0.0" y="367" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="oac-qK-Po9" id="EDS-kC-xOc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Device" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="r04-kp-xeN">
|
||||
<rect key="frame" x="16" y="12" width="52.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Riley's iPhone 7" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VIa-dS-7SR">
|
||||
<rect key="frame" x="239.5" y="12" width="119.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="DP4-vw-Pfm">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="gray" indentationWidth="10" reuseIdentifier="VersionsCell" textLabel="8zd-va-DMf" style="IBUITableViewCellStyleDefault" id="Mzg-ek-npi">
|
||||
<rect key="frame" x="0.0" y="447" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Mzg-ek-npi" id="Xw1-8U-8dU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="View Versions" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8zd-va-DMf">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<segue destination="zfS-39-NF3" kind="presentation" identifier="showVersions" id="xvh-Yg-gc5"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="kBh-Lp-rBr" id="QaZ-pX-7Yr"/>
|
||||
<outlet property="delegate" destination="kBh-Lp-rBr" id="Kl3-Sd-SAS"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<connections>
|
||||
<outlet property="localDateLabel" destination="D5a-Na-NTh" id="i8q-2d-4hk"/>
|
||||
<outlet property="localStatusLabel" destination="SYD-cR-5TY" id="GOK-j1-D0y"/>
|
||||
<outlet property="remoteDateLabel" destination="b43-lW-Enx" id="IYb-bX-oZJ"/>
|
||||
<outlet property="remoteDeviceLabel" destination="VIa-dS-7SR" id="Cy1-pu-0vk"/>
|
||||
<outlet property="remoteStatusLabel" destination="0Rm-b2-HX5" id="PUq-1s-SGy"/>
|
||||
<outlet property="syncingEnabledSwitch" destination="K0v-VD-uUj" id="BSt-cy-p4F"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="fmY-uC-Naa" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="5047" y="1872"/>
|
||||
</scene>
|
||||
<!--Versions-->
|
||||
<scene sceneID="xaH-AW-NqP">
|
||||
<objects>
|
||||
<tableViewController id="PGW-Yp-czd" customClass="RecordVersionsViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="9fJ-qb-tfO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="gray" indentationWidth="10" reuseIdentifier="Cell" textLabel="Itj-QW-4dw" detailTextLabel="2fS-9A-FKk" style="IBUITableViewCellStyleSubtitle" id="kAh-xQ-U0r">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="kAh-xQ-U0r" id="Uoy-xs-wjL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Itj-QW-4dw">
|
||||
<rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2fS-9A-FKk">
|
||||
<rect key="frame" x="16" y="25.5" width="44" height="14.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="LoadingCell" id="lkg-MY-0hB">
|
||||
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lkg-MY-0hB" id="TM3-U4-9sx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="hJz-RO-Pi8">
|
||||
<rect key="frame" x="177.5" y="12" width="20" height="20"/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="hJz-RO-Pi8" firstAttribute="centerX" secondItem="TM3-U4-9sx" secondAttribute="centerX" id="dwn-pH-e6v"/>
|
||||
<constraint firstItem="hJz-RO-Pi8" firstAttribute="centerY" secondItem="TM3-U4-9sx" secondAttribute="centerY" id="yIw-Ou-nWN"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="gray" indentationWidth="10" reuseIdentifier="ConfirmCell" textLabel="saf-eQ-eJc" style="IBUITableViewCellStyleDefault" id="x0b-KE-P94">
|
||||
<rect key="frame" x="0.0" y="143.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="x0b-KE-P94" id="ufh-yE-uv2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Restore Version" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="saf-eQ-eJc">
|
||||
<rect key="frame" x="16" y="0.0" width="343" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="PGW-Yp-czd" id="O27-2u-XUE"/>
|
||||
<outlet property="delegate" destination="PGW-Yp-czd" id="qqj-9a-7id"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Versions" id="GLC-Lc-6VM">
|
||||
<barButtonItem key="rightBarButtonItem" style="done" systemItem="done" id="LMc-KQ-vea">
|
||||
<connections>
|
||||
<segue destination="oyk-u7-Dn0" kind="unwind" unwindAction="unwindToRecordSyncStatusViewController:" id="JOO-Bj-i1E"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="p6Y-9f-cL2" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
<exit id="oyk-u7-Dn0" userLabel="Exit" sceneMemberID="exit"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="5807" y="1182"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="J41-NY-ZAz">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="zfS-39-NF3" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="iJQ-8h-Ho6">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="PGW-Yp-czd" kind="relationship" relationship="rootViewController" id="ybZ-FQ-HsP"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bJV-tR-yMT" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="5046" y="1182"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<namedColor name="Purple">
|
||||
<color red="0.54509803921568623" green="0.15686274509803921" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</namedColor>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
19
Delta/Components/Box.swift
Normal file
19
Delta/Components/Box.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// Box.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/28/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class Box<T>
|
||||
{
|
||||
let value: T
|
||||
|
||||
init(_ value: T)
|
||||
{
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
73
Delta/Components/Table View/BadgedTableViewCell.swift
Normal file
73
Delta/Components/Table View/BadgedTableViewCell.swift
Normal file
@ -0,0 +1,73 @@
|
||||
//
|
||||
// BadgedTableViewCell.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/20/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class BadgedTableViewCell: UITableViewCell
|
||||
{
|
||||
let badgeLabel = UILabel()
|
||||
|
||||
required init?(coder aDecoder: NSCoder)
|
||||
{
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
self.initialize()
|
||||
}
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
|
||||
{
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
|
||||
self.initialize()
|
||||
}
|
||||
|
||||
private func initialize()
|
||||
{
|
||||
self.badgeLabel.clipsToBounds = true
|
||||
self.badgeLabel.textAlignment = .center
|
||||
self.badgeLabel.backgroundColor = .red
|
||||
self.badgeLabel.font = UIFont.boldSystemFont(ofSize: 14)
|
||||
self.badgeLabel.textColor = .white
|
||||
self.contentView.addSubview(self.badgeLabel)
|
||||
}
|
||||
|
||||
override func layoutSubviews()
|
||||
{
|
||||
super.layoutSubviews()
|
||||
|
||||
guard let textLabel = self.textLabel else { return }
|
||||
|
||||
let spacing = 8 as CGFloat
|
||||
|
||||
var contentSize = self.badgeLabel.intrinsicContentSize
|
||||
contentSize.width += 10
|
||||
contentSize.height += 10
|
||||
contentSize.width = max(contentSize.width, contentSize.height)
|
||||
|
||||
var frame = CGRect(x: self.contentView.bounds.maxX - contentSize.width,
|
||||
y: self.contentView.bounds.midY - contentSize.height / 2,
|
||||
width: contentSize.width,
|
||||
height: contentSize.height)
|
||||
|
||||
if self.accessoryType == .none
|
||||
{
|
||||
frame.origin.x -= spacing
|
||||
}
|
||||
|
||||
self.badgeLabel.frame = frame
|
||||
self.badgeLabel.layer.cornerRadius = frame.height / 2
|
||||
|
||||
self.badgeLabel.backgroundColor = .red
|
||||
|
||||
let overlap = textLabel.frame.maxX - (frame.minX - spacing)
|
||||
if overlap > 0 && !self.badgeLabel.isHidden
|
||||
{
|
||||
textLabel.frame.size.width -= overlap
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,11 +11,10 @@ import CoreData
|
||||
|
||||
// Workspace
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
import Roxas
|
||||
import ZIPFoundation
|
||||
|
||||
// Pods
|
||||
import FileMD5Hash
|
||||
|
||||
extension DatabaseManager
|
||||
{
|
||||
enum ImportError: Error, Hashable
|
||||
@ -56,10 +55,12 @@ extension DatabaseManager
|
||||
}
|
||||
}
|
||||
|
||||
final class DatabaseManager: NSPersistentContainer
|
||||
final class DatabaseManager: RSTPersistentContainer
|
||||
{
|
||||
static let shared = DatabaseManager()
|
||||
|
||||
private(set) var isStarted = false
|
||||
|
||||
private var gamesDatabase: GamesDatabase? = nil
|
||||
|
||||
private var validationManagedObjectContext: NSManagedObjectContext?
|
||||
@ -68,30 +69,41 @@ final class DatabaseManager: NSPersistentContainer
|
||||
{
|
||||
guard
|
||||
let modelURL = Bundle(for: DatabaseManager.self).url(forResource: "Delta", withExtension: "momd"),
|
||||
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
|
||||
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL),
|
||||
let harmonyModel = NSManagedObjectModel.harmonyModel(byMergingWith: [managedObjectModel])
|
||||
else { fatalError("Core Data model cannot be found. Aborting.") }
|
||||
|
||||
super.init(name: "Delta", managedObjectModel: managedObjectModel)
|
||||
super.init(name: "Delta", managedObjectModel: harmonyModel)
|
||||
|
||||
self.viewContext.automaticallyMergesChangesFromParent = true
|
||||
self.shouldAddStoresAsynchronously = true
|
||||
}
|
||||
}
|
||||
|
||||
extension DatabaseManager
|
||||
{
|
||||
override func newBackgroundContext() -> NSManagedObjectContext
|
||||
func start(completionHandler: @escaping (Error?) -> Void)
|
||||
{
|
||||
let context = super.newBackgroundContext()
|
||||
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
return context
|
||||
}
|
||||
|
||||
override func loadPersistentStores(completionHandler block: @escaping (NSPersistentStoreDescription, Error?) -> Void)
|
||||
{
|
||||
super.loadPersistentStores { (description, error) in
|
||||
self.prepareDatabase {
|
||||
block(description, error)
|
||||
guard !self.isStarted else { return }
|
||||
|
||||
do
|
||||
{
|
||||
if !FileManager.default.fileExists(atPath: DatabaseManager.backupDirectoryURL.path)
|
||||
{
|
||||
try FileManager.default.copyItem(at: DatabaseManager.defaultDirectoryURL(), to: DatabaseManager.backupDirectoryURL)
|
||||
}
|
||||
|
||||
self.loadPersistentStores { (description, error) in
|
||||
guard error == nil else { return completionHandler(error) }
|
||||
|
||||
self.prepareDatabase {
|
||||
self.isStarted = true
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,7 +221,18 @@ extension DatabaseManager
|
||||
continue
|
||||
}
|
||||
|
||||
let identifier = FileHash.sha1HashOfFile(atPath: url.path) as String
|
||||
let identifier: String
|
||||
|
||||
do
|
||||
{
|
||||
identifier = try RSTHasher.sha1HashOfFile(at: url)
|
||||
}
|
||||
catch let error as NSError
|
||||
{
|
||||
errors.insert(.unknown(url, error))
|
||||
continue
|
||||
}
|
||||
|
||||
let filename = identifier + "." + url.pathExtension
|
||||
|
||||
let game = Game(context: context)
|
||||
@ -494,6 +517,12 @@ extension DatabaseManager
|
||||
let artworkURL = gameURL.deletingPathExtension().appendingPathExtension("jpg")
|
||||
return artworkURL
|
||||
}
|
||||
|
||||
class var backupDirectoryURL: URL
|
||||
{
|
||||
let backupDirectoryURL = FileManager.default.documentsDirectory.appendingPathComponent("Database-Backup")
|
||||
return backupDirectoryURL
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Notifications -
|
||||
|
||||
@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>Delta 2.xcdatamodel</string>
|
||||
<string>Delta 4.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13772" systemVersion="16G1114" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13240" systemVersion="16G29" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
||||
<entity name="Cheat" representedClassName="Cheat" syncable="YES">
|
||||
<attribute name="code" attributeType="String" syncable="YES"/>
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
@ -54,7 +54,6 @@
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="playedDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
@ -115,7 +114,7 @@
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Integer 16" defaultValueString="2" usesScalarValueType="NO" syncable="YES">
|
||||
<attribute name="type" attributeType="Integer 16" defaultValueString="1" usesScalarValueType="NO" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueScalarType" value="SaveStateType"/>
|
||||
</userInfo>
|
||||
@ -131,9 +130,9 @@
|
||||
<elements>
|
||||
<element name="Cheat" positionX="-198" positionY="-63" width="128" height="165"/>
|
||||
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="135"/>
|
||||
<element name="Game" positionX="-378" positionY="-54" width="128" height="195"/>
|
||||
<element name="Game" positionX="-378" positionY="-54" width="128" height="180"/>
|
||||
<element name="GameCollection" positionX="-585" positionY="-27" width="128" height="90"/>
|
||||
<element name="GameControllerInputMapping" positionX="-387" positionY="90" width="128" height="105"/>
|
||||
<element name="SaveState" positionX="-198" positionY="113" width="128" height="165"/>
|
||||
</elements>
|
||||
</model>
|
||||
</model>
|
||||
|
||||
@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13772" systemVersion="16G1114" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
||||
<entity name="Cheat" representedClassName="Cheat" syncable="YES">
|
||||
<attribute name="code" attributeType="String" syncable="YES"/>
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="CheatType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="cheats" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="ControllerSkin" representedClassName="ControllerSkin" syncable="YES">
|
||||
<attribute name="filename" attributeType="String" syncable="YES"/>
|
||||
<attribute name="gameType" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isStandard" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="supportedConfigurations" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueScalarType" value="ControllerSkinConfigurations"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
<constraint value="gameType"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="Game" representedClassName="Game" syncable="YES">
|
||||
<attribute name="artworkURL" optional="YES" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="URL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="filename" attributeType="String" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="NSURL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="playedDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="cheats" toMany="YES" deletionRule="Cascade" destinationEntity="Cheat" inverseName="game" inverseEntity="Cheat" syncable="YES"/>
|
||||
<relationship name="gameCollections" toMany="YES" deletionRule="Nullify" destinationEntity="GameCollection" inverseName="games" inverseEntity="GameCollection" syncable="YES"/>
|
||||
<relationship name="previewSaveState" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SaveState" inverseName="previewGame" inverseEntity="SaveState" syncable="YES"/>
|
||||
<relationship name="saveStates" toMany="YES" deletionRule="Cascade" destinationEntity="SaveState" inverseName="game" inverseEntity="SaveState" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameCollection" representedClassName="GameCollection" syncable="YES">
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="index" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="games" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="gameCollections" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameControllerInputMapping" representedClassName="GameControllerInputMapping" syncable="YES">
|
||||
<attribute name="deltaCoreInputMapping" attributeType="Transformable" valueTransformerName="GameControllerInputMappingTransformer" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="Any"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameControllerInputType" attributeType="Transformable" valueTransformerName="" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameControllerInputType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameType" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="playerIndex" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="gameControllerInputType"/>
|
||||
<constraint value="gameType"/>
|
||||
<constraint value="playerIndex"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="SaveState" representedClassName="SaveState" versionHashModifier="quick" syncable="YES">
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="filename" attributeType="String" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="NSURL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Integer 16" defaultValueString="2" usesScalarValueType="NO" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueScalarType" value="SaveStateType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="saveStates" inverseEntity="Game" syncable="YES"/>
|
||||
<relationship name="previewGame" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="previewSaveState" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Cheat" positionX="-198" positionY="-63" width="128" height="165"/>
|
||||
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="135"/>
|
||||
<element name="Game" positionX="-378" positionY="-54" width="128" height="195"/>
|
||||
<element name="GameCollection" positionX="-585" positionY="-27" width="128" height="90"/>
|
||||
<element name="GameControllerInputMapping" positionX="-387" positionY="90" width="128" height="105"/>
|
||||
<element name="SaveState" positionX="-198" positionY="113" width="128" height="165"/>
|
||||
</elements>
|
||||
</model>
|
||||
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14460.32" systemVersion="18C54" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
||||
<entity name="Cheat" representedClassName="Cheat" syncable="YES">
|
||||
<attribute name="code" attributeType="String" syncable="YES"/>
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isEnabled" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="CheatType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="cheats" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="ControllerSkin" representedClassName="ControllerSkin" syncable="YES">
|
||||
<attribute name="filename" attributeType="String" syncable="YES"/>
|
||||
<attribute name="gameType" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isStandard" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="supportedConfigurations" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueScalarType" value="ControllerSkinConfigurations"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
<constraint value="gameType"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="Game" representedClassName="Game" syncable="YES">
|
||||
<attribute name="artworkURL" optional="YES" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="URL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="filename" attributeType="String" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="NSURL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="playedDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="cheats" toMany="YES" deletionRule="Cascade" destinationEntity="Cheat" inverseName="game" inverseEntity="Cheat" syncable="YES"/>
|
||||
<relationship name="gameCollection" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="GameCollection" inverseName="games" inverseEntity="GameCollection" syncable="YES"/>
|
||||
<relationship name="gameSave" optional="YES" maxCount="1" deletionRule="Cascade" destinationEntity="GameSave" inverseName="game" inverseEntity="GameSave" syncable="YES"/>
|
||||
<relationship name="previewSaveState" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SaveState" inverseName="previewGame" inverseEntity="SaveState" syncable="YES"/>
|
||||
<relationship name="saveStates" toMany="YES" deletionRule="Cascade" destinationEntity="SaveState" inverseName="game" inverseEntity="SaveState" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameCollection" representedClassName="GameCollection" syncable="YES">
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="index" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="games" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="gameCollection" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameControllerInputMapping" representedClassName="GameControllerInputMapping" syncable="YES">
|
||||
<attribute name="deltaCoreInputMapping" attributeType="Transformable" valueTransformerName="GameControllerInputMappingTransformer" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="Any"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameControllerInputType" attributeType="Transformable" valueTransformerName="" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameControllerInputType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameType" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="playerIndex" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="gameControllerInputType"/>
|
||||
<constraint value="gameType"/>
|
||||
<constraint value="playerIndex"/>
|
||||
</uniquenessConstraint>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameSave" representedClassName="GameSave" syncable="YES">
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="gameSave" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="SaveState" representedClassName="SaveState" versionHashModifier="quick" syncable="YES">
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="filename" attributeType="String" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="NSURL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Integer 16" defaultValueString="2" usesScalarValueType="NO" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueScalarType" value="SaveStateType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="saveStates" inverseEntity="Game" syncable="YES"/>
|
||||
<relationship name="previewGame" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="previewSaveState" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Cheat" positionX="-198" positionY="-63" width="128" height="165"/>
|
||||
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="135"/>
|
||||
<element name="Game" positionX="-378" positionY="-54" width="128" height="30"/>
|
||||
<element name="GameCollection" positionX="-585" positionY="-27" width="128" height="90"/>
|
||||
<element name="GameControllerInputMapping" positionX="-387" positionY="90" width="128" height="120"/>
|
||||
<element name="GameSave" positionX="-387" positionY="90" width="128" height="90"/>
|
||||
<element name="SaveState" positionX="-198" positionY="113" width="128" height="165"/>
|
||||
</elements>
|
||||
</model>
|
||||
@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
@objc(Cheat)
|
||||
public class Cheat: _Cheat, CheatProtocol
|
||||
@ -29,3 +30,27 @@ public class Cheat: _Cheat, CheatProtocol
|
||||
self.primitiveModifiedDate = date
|
||||
}
|
||||
}
|
||||
|
||||
extension Cheat: Syncable
|
||||
{
|
||||
public static var syncablePrimaryKey: AnyKeyPath {
|
||||
return \Cheat.identifier
|
||||
}
|
||||
|
||||
public var syncableKeys: Set<AnyKeyPath> {
|
||||
return [\Cheat.code, \Cheat.creationDate, \Cheat.modifiedDate, \Cheat.name, \Cheat.type]
|
||||
}
|
||||
|
||||
public var syncableRelationships: Set<AnyKeyPath> {
|
||||
return [\Cheat.game as AnyKeyPath]
|
||||
}
|
||||
|
||||
public var syncableMetadata: [HarmonyMetadataKey : String] {
|
||||
guard let game = self.game else { return [:] }
|
||||
return [.gameID: game.identifier, .gameName: game.name]
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.name
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
extension ControllerSkinConfigurations
|
||||
{
|
||||
@ -90,3 +91,26 @@ extension ControllerSkin: ControllerSkinProtocol
|
||||
return self.controllerSkin?.aspectRatio(for: traits)
|
||||
}
|
||||
}
|
||||
|
||||
extension ControllerSkin: Syncable
|
||||
{
|
||||
public static var syncablePrimaryKey: AnyKeyPath {
|
||||
return \ControllerSkin.identifier
|
||||
}
|
||||
|
||||
public var syncableKeys: Set<AnyKeyPath> {
|
||||
return [\ControllerSkin.filename, \ControllerSkin.gameType, \ControllerSkin.name, \ControllerSkin.supportedConfigurations]
|
||||
}
|
||||
|
||||
public var syncableFiles: Set<File> {
|
||||
return [File(identifier: "skin", fileURL: self.fileURL)]
|
||||
}
|
||||
|
||||
public var isSyncingEnabled: Bool {
|
||||
return !self.isStandard
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.name
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
@objc(Game)
|
||||
public class Game: _Game, GameProtocol
|
||||
@ -90,7 +91,7 @@ extension Game
|
||||
print(error)
|
||||
}
|
||||
|
||||
for collection in self.gameCollections where collection.games.count == 1
|
||||
if let collection = self.gameCollection, collection.games.count == 1
|
||||
{
|
||||
// Once this game is deleted, collection will have 0 games, so we should delete it
|
||||
managedObjectContext.delete(collection)
|
||||
@ -109,3 +110,31 @@ extension Game
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Game: Syncable
|
||||
{
|
||||
public static var syncablePrimaryKey: AnyKeyPath {
|
||||
return \Game.identifier
|
||||
}
|
||||
|
||||
public var syncableKeys: Set<AnyKeyPath> {
|
||||
return [\Game.artworkURL, \Game.filename, \Game.name, \Game.type]
|
||||
}
|
||||
|
||||
public var syncableFiles: Set<File> {
|
||||
let gameFile = File(identifier: "game", fileURL: self.fileURL)
|
||||
|
||||
let artworkURL = DatabaseManager.artworkURL(for: self)
|
||||
let artworkFile = File(identifier: "artwork", fileURL: artworkURL)
|
||||
|
||||
return [gameFile, artworkFile]
|
||||
}
|
||||
|
||||
public var syncableRelationships: Set<AnyKeyPath> {
|
||||
return [\Game.gameCollection]
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.name
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,15 +9,16 @@
|
||||
import CoreData
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
@objc(GameCollection)
|
||||
public class GameCollection: _GameCollection
|
||||
{
|
||||
var name: String {
|
||||
@objc var name: String {
|
||||
return self.system?.localizedName ?? NSLocalizedString("Unknown", comment: "")
|
||||
}
|
||||
|
||||
var shortName: String {
|
||||
@objc var shortName: String {
|
||||
return self.system?.localizedShortName ?? NSLocalizedString("Unknown", comment: "")
|
||||
}
|
||||
|
||||
@ -28,3 +29,18 @@ public class GameCollection: _GameCollection
|
||||
return system
|
||||
}
|
||||
}
|
||||
|
||||
extension GameCollection: Syncable
|
||||
{
|
||||
public static var syncablePrimaryKey: AnyKeyPath {
|
||||
return \GameCollection.identifier
|
||||
}
|
||||
|
||||
public var syncableKeys: Set<AnyKeyPath> {
|
||||
return [\GameCollection.index as AnyKeyPath]
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.name
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
@objc(GameControllerInputMapping)
|
||||
public class GameControllerInputMapping: _GameControllerInputMapping
|
||||
@ -24,6 +25,13 @@ public class GameControllerInputMapping: _GameControllerInputMapping
|
||||
|
||||
self.inputMapping = inputMapping
|
||||
}
|
||||
|
||||
public override func awakeFromInsert()
|
||||
{
|
||||
super.awakeFromInsert()
|
||||
|
||||
self.identifier = UUID().uuidString
|
||||
}
|
||||
}
|
||||
|
||||
extension GameControllerInputMapping
|
||||
@ -75,3 +83,21 @@ extension GameControllerInputMapping: GameControllerInputMappingProtocol
|
||||
self.inputMapping.set(input, forControllerInput: controllerInput)
|
||||
}
|
||||
}
|
||||
|
||||
extension GameControllerInputMapping: Syncable
|
||||
{
|
||||
public static var syncablePrimaryKey: AnyKeyPath {
|
||||
return \GameControllerInputMapping.identifier
|
||||
}
|
||||
|
||||
public var syncableKeys: Set<AnyKeyPath> {
|
||||
return [\GameControllerInputMapping.deltaCoreInputMapping,
|
||||
\GameControllerInputMapping.gameControllerInputType,
|
||||
\GameControllerInputMapping.gameType,
|
||||
\GameControllerInputMapping.playerIndex]
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.name
|
||||
}
|
||||
}
|
||||
|
||||
60
Delta/Database/Model/Human/GameSave.swift
Normal file
60
Delta/Database/Model/Human/GameSave.swift
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// GameSave.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 8/30/16.
|
||||
// Copyright (c) 2016 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import Harmony
|
||||
|
||||
@objc(GameSave)
|
||||
public class GameSave: _GameSave
|
||||
{
|
||||
public override func awakeFromInsert()
|
||||
{
|
||||
super.awakeFromInsert()
|
||||
|
||||
self.modifiedDate = Date()
|
||||
}
|
||||
}
|
||||
|
||||
extension GameSave: Syncable
|
||||
{
|
||||
public static var syncablePrimaryKey: AnyKeyPath {
|
||||
return \GameSave.identifier
|
||||
}
|
||||
|
||||
public var syncableKeys: Set<AnyKeyPath> {
|
||||
return [\GameSave.modifiedDate]
|
||||
}
|
||||
|
||||
public var syncableRelationships: Set<AnyKeyPath> {
|
||||
return [\GameSave.game]
|
||||
}
|
||||
|
||||
public var syncableFiles: Set<File> {
|
||||
guard let game = self.game else { return [] }
|
||||
|
||||
var files: Set<File> = [File(identifier: "gameSave", fileURL: game.gameSaveURL)]
|
||||
|
||||
if game.type == .gbc
|
||||
{
|
||||
let gameTimeSaveURL = game.gameSaveURL.deletingPathExtension().appendingPathExtension("rtc")
|
||||
files.insert(File(identifier: "gameTimeSave", fileURL: gameTimeSaveURL))
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
public var syncableMetadata: [HarmonyMetadataKey : String] {
|
||||
guard let game = self.game else { return [:] }
|
||||
return [.gameID: game.identifier, .gameName: game.name]
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.game?.name
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
@objc public enum SaveStateType: Int16
|
||||
{
|
||||
@ -21,6 +22,13 @@ import DeltaCore
|
||||
@objc(SaveState)
|
||||
public class SaveState: _SaveState, SaveStateProtocol
|
||||
{
|
||||
public static let localizedDateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.timeStyle = .short
|
||||
dateFormatter.dateStyle = .short
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
public var fileURL: URL {
|
||||
let fileURL = DatabaseManager.saveStatesDirectoryURL(for: self.game!).appendingPathComponent(self.filename)
|
||||
return fileURL
|
||||
@ -36,6 +44,11 @@ public class SaveState: _SaveState, SaveStateProtocol
|
||||
return self.game!.type
|
||||
}
|
||||
|
||||
public var localizedName: String {
|
||||
let localizedName = self.name ?? SaveState.localizedDateFormatter.string(from: self.modifiedDate)
|
||||
return localizedName
|
||||
}
|
||||
|
||||
@NSManaged private var primitiveFilename: String
|
||||
@NSManaged private var primitiveIdentifier: String
|
||||
@NSManaged private var primitiveCreationDate: Date
|
||||
@ -90,3 +103,35 @@ public class SaveState: _SaveState, SaveStateProtocol
|
||||
return fetchRequest
|
||||
}
|
||||
}
|
||||
|
||||
extension SaveState: Syncable
|
||||
{
|
||||
public static var syncablePrimaryKey: AnyKeyPath {
|
||||
return \SaveState.identifier
|
||||
}
|
||||
|
||||
public var syncableKeys: Set<AnyKeyPath> {
|
||||
return [\SaveState.creationDate, \SaveState.filename, \SaveState.modifiedDate, \SaveState.name, \SaveState.type]
|
||||
}
|
||||
|
||||
public var syncableFiles: Set<File> {
|
||||
return [File(identifier: "saveState", fileURL: self.fileURL), File(identifier: "thumbnail", fileURL: self.imageFileURL)]
|
||||
}
|
||||
|
||||
public var syncableRelationships: Set<AnyKeyPath> {
|
||||
return [\SaveState.game]
|
||||
}
|
||||
|
||||
public var isSyncingEnabled: Bool {
|
||||
return self.type != .auto && self.type != .quick
|
||||
}
|
||||
|
||||
public var syncableMetadata: [HarmonyMetadataKey : String] {
|
||||
guard let game = self.game else { return [:] }
|
||||
return [.gameID: game.identifier, .gameName: game.name]
|
||||
}
|
||||
|
||||
public var syncableLocalizedName: String? {
|
||||
return self.localizedName
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ public class _Cheat: NSManagedObject
|
||||
|
||||
@NSManaged public var modifiedDate: Date
|
||||
|
||||
@NSManaged public var name: String?
|
||||
@NSManaged public var name: String
|
||||
|
||||
@NSManaged public var type: CheatType
|
||||
|
||||
|
||||
@ -30,7 +30,9 @@ public class _Game: NSManagedObject
|
||||
|
||||
@NSManaged public var cheats: Set<Cheat>
|
||||
|
||||
@NSManaged public var gameCollections: Set<GameCollection>
|
||||
@NSManaged public var gameCollection: GameCollection?
|
||||
|
||||
@NSManaged public var gameSave: GameSave?
|
||||
|
||||
@NSManaged public var previewSaveState: SaveState?
|
||||
|
||||
|
||||
@ -20,6 +20,8 @@ public class _GameControllerInputMapping: NSManagedObject
|
||||
|
||||
@NSManaged public var gameType: GameType
|
||||
|
||||
@NSManaged public var identifier: String
|
||||
|
||||
@NSManaged public var playerIndex: Int16
|
||||
|
||||
// MARK: - Relationships
|
||||
|
||||
26
Delta/Database/Model/Machine/_GameSave.swift
Normal file
26
Delta/Database/Model/Machine/_GameSave.swift
Normal file
@ -0,0 +1,26 @@
|
||||
// DO NOT EDIT. This file is machine-generated and constantly overwritten.
|
||||
// Make changes to GameSave.swift instead.
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
import DeltaCore
|
||||
|
||||
public class _GameSave: NSManagedObject
|
||||
{
|
||||
@nonobjc public class func fetchRequest() -> NSFetchRequest<GameSave> {
|
||||
return NSFetchRequest<GameSave>(entityName: "GameSave")
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
@NSManaged public var identifier: String
|
||||
|
||||
@NSManaged public var modifiedDate: Date
|
||||
|
||||
// MARK: - Relationships
|
||||
|
||||
@NSManaged public var game: Game?
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,19 @@
|
||||
//
|
||||
// GameControllerInputMappingMigrationPolicy.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 1/30/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
|
||||
@objc(GameControllerInputMappingMigrationPolicy)
|
||||
class GameControllerInputMappingMigrationPolicy: NSEntityMigrationPolicy
|
||||
{
|
||||
@objc(migrateIdentifier)
|
||||
func migrateIdentifier() -> String
|
||||
{
|
||||
return UUID().uuidString
|
||||
}
|
||||
}
|
||||
@ -66,6 +66,8 @@ class GameViewController: DeltaCore.GameViewController
|
||||
let game = self.game as? Game
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.managedObjectContextDidChange(with:)), name: .NSManagedObjectContextObjectsDidChange, object: game?.managedObjectContext)
|
||||
|
||||
self.emulatorCore?.saveHandler = { [weak self] _ in self?.updateGameSave() }
|
||||
|
||||
self.updateControllerSkin()
|
||||
self.updateControllers()
|
||||
}
|
||||
@ -501,6 +503,40 @@ private extension GameViewController
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Game Saves -
|
||||
/// Game Saves
|
||||
private extension GameViewController
|
||||
{
|
||||
func updateGameSave()
|
||||
{
|
||||
guard let game = self.game as? Game else { return }
|
||||
|
||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||
let game = context.object(with: game.objectID) as! Game
|
||||
|
||||
if let gameSave = game.gameSave
|
||||
{
|
||||
gameSave.modifiedDate = Date()
|
||||
}
|
||||
else
|
||||
{
|
||||
let gameSave = GameSave(context: context)
|
||||
gameSave.identifier = game.identifier
|
||||
game.gameSave = gameSave
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
try context.save()
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Error updating game save.", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Save States -
|
||||
/// Save States
|
||||
extension GameViewController: SaveStatesViewControllerDelegate
|
||||
@ -906,6 +942,8 @@ private extension GameViewController
|
||||
}
|
||||
|
||||
case .translucentControllerSkinOpacity: self.controllerView.translucentControllerSkinOpacity = Settings.translucentControllerSkinOpacity
|
||||
|
||||
case .syncingService: break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,11 +18,11 @@ extension EmulatorCore
|
||||
}
|
||||
catch EmulatorCore.CheatError.invalid
|
||||
{
|
||||
print("Invalid cheat:", cheat.name ?? "Unnamed Cheat", cheat.code)
|
||||
print("Invalid cheat:", cheat.name, cheat.code)
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Unknown Cheat Error:", error, cheat.name ?? "Unnamed Cheat", cheat.code)
|
||||
print("Unknown Cheat Error:", error, cheat.name, cheat.code)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
Delta/Extensions/HarmonyMetadataKey+Keys.swift
Normal file
15
Delta/Extensions/HarmonyMetadataKey+Keys.swift
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// HarmonyMetadataKey+Keys.swift
|
||||
// Harmony
|
||||
//
|
||||
// Created by Riley Testut on 11/5/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Harmony
|
||||
|
||||
extension HarmonyMetadataKey
|
||||
{
|
||||
static let gameID = HarmonyMetadataKey("gameID")
|
||||
static let gameName = HarmonyMetadataKey("gameName")
|
||||
}
|
||||
32
Delta/Extensions/UIAlertController+Error.swift
Normal file
32
Delta/Extensions/UIAlertController+Error.swift
Normal file
@ -0,0 +1,32 @@
|
||||
//
|
||||
// UIAlertController+Error.swift
|
||||
// INLINE
|
||||
//
|
||||
// Created by Riley Testut on 11/27/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Roxas
|
||||
import Harmony
|
||||
|
||||
extension UIAlertController
|
||||
{
|
||||
convenience init(title: String, error: Error)
|
||||
{
|
||||
let message: String
|
||||
|
||||
if let error = error as? HarmonyError, let reason = error.failureReason
|
||||
{
|
||||
message = reason
|
||||
}
|
||||
else
|
||||
{
|
||||
message = error.localizedDescription
|
||||
}
|
||||
|
||||
self.init(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
self.addAction(.ok)
|
||||
}
|
||||
}
|
||||
@ -10,13 +10,6 @@ import UIKit
|
||||
|
||||
extension UIColor
|
||||
{
|
||||
class var deltaPurple: UIColor
|
||||
{
|
||||
return UIColor(red: 139.0/255.0, green: 40.0/255.0, blue: 247.0/255.0, alpha: 1.0)
|
||||
}
|
||||
|
||||
class var deltaDarkGray: UIColor
|
||||
{
|
||||
return UIColor(white: 0.15, alpha: 1.0)
|
||||
}
|
||||
static let deltaPurple = UIColor(named: "Purple")!
|
||||
static let deltaDarkGray = UIColor(named: "DarkGray")!
|
||||
}
|
||||
|
||||
@ -12,9 +12,19 @@ import MobileCoreServices
|
||||
import DeltaCore
|
||||
|
||||
import Roxas
|
||||
import Harmony
|
||||
|
||||
import SDWebImage
|
||||
|
||||
extension GameCollectionViewController
|
||||
{
|
||||
private enum LaunchError: Error
|
||||
{
|
||||
case alreadyRunning
|
||||
case downloadingGameSave
|
||||
}
|
||||
}
|
||||
|
||||
class GameCollectionViewController: UICollectionViewController
|
||||
{
|
||||
var gameCollection: GameCollection? {
|
||||
@ -189,8 +199,8 @@ private extension GameCollectionViewController
|
||||
//MARK: - Data Source
|
||||
func prepareDataSource()
|
||||
{
|
||||
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
||||
self.configure(cell as! GridCollectionViewCell, for: indexPath)
|
||||
self.dataSource.cellConfigurationHandler = { [weak self] (cell, item, indexPath) in
|
||||
self?.configure(cell as! GridCollectionViewCell, for: indexPath)
|
||||
}
|
||||
|
||||
self.dataSource.prefetchHandler = { (game, indexPath, completionHandler) in
|
||||
@ -219,7 +229,7 @@ private extension GameCollectionViewController
|
||||
|
||||
if let gameCollection = self.gameCollection
|
||||
{
|
||||
fetchRequest.predicate = NSPredicate(format: "ANY %K == %@", #keyPath(Game.gameCollections), gameCollection)
|
||||
fetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(Game.gameCollection), gameCollection)
|
||||
}
|
||||
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Game.name), ascending: true)]
|
||||
@ -252,14 +262,113 @@ private extension GameCollectionViewController
|
||||
}
|
||||
|
||||
//MARK: - Emulation
|
||||
func launchGame(withSender sender: AnyObject?, clearScreen: Bool)
|
||||
func launchGame(at indexPath: IndexPath, clearScreen: Bool, ignoreAlreadyRunningError: Bool = false)
|
||||
{
|
||||
if clearScreen
|
||||
func launchGame(ignoringErrors ignoredErrors: [Error])
|
||||
{
|
||||
self.activeEmulatorCore?.gameViews.forEach { $0.inputImage = nil }
|
||||
let game = self.dataSource.item(at: indexPath)
|
||||
|
||||
do
|
||||
{
|
||||
try self.validateLaunchingGame(game, ignoringErrors: ignoredErrors)
|
||||
|
||||
if clearScreen
|
||||
{
|
||||
self.activeEmulatorCore?.gameViews.forEach { $0.inputImage = nil }
|
||||
}
|
||||
|
||||
let cell = self.collectionView.cellForItem(at: indexPath)
|
||||
self.performSegue(withIdentifier: "unwindFromGames", sender: cell)
|
||||
}
|
||||
catch LaunchError.alreadyRunning
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Game Paused", comment: ""), message: NSLocalizedString("Would you like to resume where you left off, or restart the game?", comment: ""), preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Resume", comment: ""), style: .default, handler: { (action) in
|
||||
|
||||
let fetchRequest = SaveState.rst_fetchRequest() as! NSFetchRequest<SaveState>
|
||||
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %d", #keyPath(SaveState.game), game, #keyPath(SaveState.type), SaveStateType.auto.rawValue)
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(SaveState.creationDate), ascending: true)]
|
||||
|
||||
do
|
||||
{
|
||||
let saveStates = try game.managedObjectContext?.fetch(fetchRequest)
|
||||
self.activeSaveState = saveStates?.last
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
// Disable videoManager to prevent flash of black
|
||||
self.activeEmulatorCore?.videoManager.isEnabled = false
|
||||
|
||||
launchGame(ignoringErrors: [LaunchError.alreadyRunning])
|
||||
|
||||
// The game hasn't changed, so the activeEmulatorCore is the same as before, so we need to enable videoManager it again
|
||||
self.activeEmulatorCore?.videoManager.isEnabled = true
|
||||
}))
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Restart", comment: ""), style: .destructive, handler: { (action) in
|
||||
launchGame(ignoringErrors: [LaunchError.alreadyRunning])
|
||||
}))
|
||||
self.present(alertController, animated: true)
|
||||
}
|
||||
catch LaunchError.downloadingGameSave
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Downloading Save File", comment: ""), message: NSLocalizedString("Please wait until after this game's save file has been downloaded before playing to prevent losing save data.", comment: ""), preferredStyle: .alert)
|
||||
alertController.addAction(.ok)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
catch
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Unable to Launch Game", comment: ""), error: error)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
self.performSegue(withIdentifier: "unwindFromGames", sender: sender)
|
||||
if ignoreAlreadyRunningError
|
||||
{
|
||||
launchGame(ignoringErrors: [LaunchError.alreadyRunning])
|
||||
}
|
||||
else
|
||||
{
|
||||
launchGame(ignoringErrors: [])
|
||||
}
|
||||
}
|
||||
|
||||
func validateLaunchingGame(_ game: Game, ignoringErrors ignoredErrors: [Error]) throws
|
||||
{
|
||||
let ignoredErrors = ignoredErrors.map { $0 as NSError }
|
||||
|
||||
if !ignoredErrors.contains(where: { $0.domain == (LaunchError.alreadyRunning as NSError).domain && $0.code == (LaunchError.alreadyRunning as NSError).code })
|
||||
{
|
||||
guard game.fileURL != self.activeEmulatorCore?.game.fileURL else { throw LaunchError.alreadyRunning }
|
||||
}
|
||||
|
||||
if SyncManager.shared.syncCoordinator.isSyncing
|
||||
{
|
||||
if let gameSave = game.gameSave
|
||||
{
|
||||
do
|
||||
{
|
||||
if let record = try SyncManager.shared.recordController.fetchRecords(for: [gameSave]).first
|
||||
{
|
||||
if record.isSyncingEnabled && !record.isConflicted && (record.localStatus == nil || record.remoteStatus == .updated)
|
||||
{
|
||||
throw LaunchError.downloadingGameSave
|
||||
}
|
||||
}
|
||||
}
|
||||
catch let error as LaunchError
|
||||
{
|
||||
throw error
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Error fetching record for game save.", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,8 +572,7 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
|
||||
let game = gameViewController.game as! Game
|
||||
|
||||
let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: game)!
|
||||
let cell = self.collectionView?.cellForItem(at: indexPath)
|
||||
|
||||
|
||||
let fileURL = FileManager.default.uniqueTemporaryURL()
|
||||
self.activeSaveState = gameViewController.emulatorCore?.saveSaveState(to: fileURL)
|
||||
|
||||
@ -472,7 +580,7 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
|
||||
|
||||
_performing3DTouchTransition = true
|
||||
|
||||
self.launchGame(withSender: cell, clearScreen: true)
|
||||
self.launchGame(at: indexPath, clearScreen: true, ignoreAlreadyRunningError: true)
|
||||
|
||||
do
|
||||
{
|
||||
@ -499,9 +607,7 @@ extension GameCollectionViewController: SaveStatesViewControllerDelegate
|
||||
|
||||
self.dismiss(animated: true) {
|
||||
let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: saveStatesViewController.game)!
|
||||
let cell = self.collectionView?.cellForItem(at: indexPath)
|
||||
|
||||
self.launchGame(withSender: cell, clearScreen: false)
|
||||
self.launchGame(at: indexPath, clearScreen: false, ignoreAlreadyRunningError: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -555,14 +661,14 @@ extension GameCollectionViewController: ImportControllerDelegate
|
||||
|
||||
if let imageURL = imageURL
|
||||
{
|
||||
// Remove previous artwork from cache.
|
||||
self.dataSource.prefetchItemCache.removeObject(forKey: game)
|
||||
|
||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||
let temporaryGame = context.object(with: game.objectID) as! Game
|
||||
temporaryGame.artworkURL = imageURL
|
||||
context.saveWithErrorLogging()
|
||||
|
||||
// Local image URLs may not change despite being a different image, so manually mark record as updated.
|
||||
SyncManager.shared.recordController.updateRecord(for: temporaryGame)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.presentedViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
@ -604,46 +710,7 @@ extension GameCollectionViewController
|
||||
{
|
||||
guard self.gameCollection?.identifier != GameType.unknown.rawValue else { return }
|
||||
|
||||
let cell = collectionView.cellForItem(at: indexPath)
|
||||
let game = self.dataSource.item(at: indexPath)
|
||||
|
||||
if game.fileURL == self.activeEmulatorCore?.game.fileURL
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Game Paused", comment: ""), message: NSLocalizedString("Would you like to resume where you left off, or restart the game?", comment: ""), preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Resume", comment: ""), style: .default, handler: { (action) in
|
||||
|
||||
let fetchRequest = SaveState.rst_fetchRequest() as! NSFetchRequest<SaveState>
|
||||
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %d", #keyPath(SaveState.game), game, #keyPath(SaveState.type), SaveStateType.auto.rawValue)
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(SaveState.creationDate), ascending: true)]
|
||||
|
||||
do
|
||||
{
|
||||
let saveStates = try game.managedObjectContext?.fetch(fetchRequest)
|
||||
self.activeSaveState = saveStates?.last
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
// Disable videoManager to prevent flash of black
|
||||
self.activeEmulatorCore?.videoManager.isEnabled = false
|
||||
|
||||
self.launchGame(withSender: cell, clearScreen: false)
|
||||
|
||||
// The game hasn't changed, so the activeEmulatorCore is the same as before, so we need to enable videoManager it again
|
||||
self.activeEmulatorCore?.videoManager.isEnabled = true
|
||||
}))
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Restart", comment: ""), style: .destructive, handler: { (action) in
|
||||
self.launchGame(withSender: cell, clearScreen: true)
|
||||
}))
|
||||
self.present(alertController, animated: true)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.launchGame(withSender: cell, clearScreen: true)
|
||||
}
|
||||
self.launchGame(at: indexPath, clearScreen: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ import MobileCoreServices
|
||||
import DeltaCore
|
||||
|
||||
import Roxas
|
||||
import Harmony
|
||||
|
||||
class GamesViewController: UIViewController
|
||||
{
|
||||
@ -61,6 +62,9 @@ class GamesViewController: UIViewController
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
self.fetchedResultsController.delegate = self
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(GamesViewController.syncingDidStart(_:)), name: SyncCoordinator.didStartSyncingNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(GamesViewController.syncingDidFinish(_:)), name: SyncCoordinator.didFinishSyncingNotification, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,6 +113,8 @@ extension GamesViewController
|
||||
DispatchQueue.global().async {
|
||||
self.activeEmulatorCore?.stop()
|
||||
}
|
||||
|
||||
self.sync()
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning()
|
||||
@ -251,6 +257,11 @@ private extension GamesViewController
|
||||
|
||||
}
|
||||
|
||||
if self.pageViewController.viewControllers?.count == 0
|
||||
{
|
||||
resetPageViewController = true
|
||||
}
|
||||
|
||||
self.navigationController?.setToolbarHidden(sections < 2, animated: animated)
|
||||
|
||||
if sections > 0
|
||||
@ -351,6 +362,11 @@ extension GamesViewController: ImportControllerDelegate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func sync()
|
||||
{
|
||||
SyncManager.shared.sync()
|
||||
}
|
||||
}
|
||||
|
||||
private extension GamesViewController
|
||||
@ -375,6 +391,42 @@ private extension GamesViewController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func syncingDidStart(_ notification: Notification)
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
let toastView = RSTToastView(text: NSLocalizedString("Syncing...", comment: ""), detailText: nil)
|
||||
toastView.activityIndicatorView.startAnimating()
|
||||
toastView.show(in: self.view)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func syncingDidFinish(_ notification: Notification)
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
guard let result = notification.userInfo?[SyncCoordinator.syncResultKey] as? SyncResult else { return }
|
||||
|
||||
let toastView: RSTToastView
|
||||
|
||||
switch result
|
||||
{
|
||||
case .success: toastView = RSTToastView(text: NSLocalizedString("Sync Complete", comment: ""), detailText: nil)
|
||||
case .failure(let error): toastView = RSTToastView(text: NSLocalizedString("Sync Failed", comment: ""), detailText: error.failureReason)
|
||||
}
|
||||
|
||||
toastView.addTarget(self, action: #selector(GamesViewController.presentSyncResultsViewController), for: .touchUpInside)
|
||||
|
||||
toastView.show(in: self.view, duration: 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
@objc func presentSyncResultsViewController()
|
||||
{
|
||||
guard let result = SyncManager.shared.previousSyncResult else { return }
|
||||
|
||||
let navigationController = SyncResultViewController.make(result: result)
|
||||
self.present(navigationController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - UIPageViewController -
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
import UIKit
|
||||
import Roxas
|
||||
|
||||
class LaunchViewController: UIViewController
|
||||
class LaunchViewController: RSTLaunchViewController
|
||||
{
|
||||
@IBOutlet private var gameViewContainerView: UIView!
|
||||
private var gameViewController: GameViewController!
|
||||
@ -18,6 +18,8 @@ class LaunchViewController: UIViewController
|
||||
|
||||
private var applicationLaunchDeepLinkGame: Game?
|
||||
|
||||
private var didAttemptStartingSyncManager = false
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.gameViewController?.preferredStatusBarStyle ?? .lightContent
|
||||
}
|
||||
@ -37,40 +39,6 @@ class LaunchViewController: UIViewController
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(LaunchViewController.deepLinkControllerLaunchGame(with:)), name: .deepLinkControllerLaunchGame, object: nil)
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool)
|
||||
{
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if !self.presentedGameViewController
|
||||
{
|
||||
self.presentedGameViewController = true
|
||||
|
||||
func showGameViewController()
|
||||
{
|
||||
self.view.bringSubviewToFront(self.gameViewContainerView)
|
||||
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
self.setNeedsUpdateOfHomeIndicatorAutoHidden()
|
||||
}
|
||||
|
||||
if let game = self.applicationLaunchDeepLinkGame
|
||||
{
|
||||
self.gameViewController.game = game
|
||||
|
||||
UIView.transition(with: self.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
|
||||
showGameViewController()
|
||||
}, completion: nil)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.gameViewController.performSegue(withIdentifier: "showInitialGamesViewController", sender: nil)
|
||||
self.transitionCoordinator?.animate(alongsideTransition: nil, completion: { (context) in
|
||||
showGameViewController()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||
{
|
||||
guard segue.identifier == "embedGameViewController" else { return }
|
||||
@ -79,6 +47,76 @@ class LaunchViewController: UIViewController
|
||||
}
|
||||
}
|
||||
|
||||
extension LaunchViewController
|
||||
{
|
||||
override var launchConditions: [RSTLaunchCondition] {
|
||||
let isDatabaseManagerStarted = RSTLaunchCondition(condition: { DatabaseManager.shared.isStarted }) { (completionHandler) in
|
||||
DatabaseManager.shared.start(completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
let isSyncingManagerStarted = RSTLaunchCondition(condition: { self.didAttemptStartingSyncManager }) { (completionHandler) in
|
||||
SyncManager.shared.syncCoordinator.start { (error) in
|
||||
self.didAttemptStartingSyncManager = true
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
let isRecordControllerSeeded = RSTLaunchCondition(condition: { SyncManager.shared.syncCoordinator.recordController.isSeeded }) { (completionHandler) in
|
||||
SyncManager.shared.syncCoordinator.recordController.seedFromPersistentContainer() { (result) in
|
||||
switch result
|
||||
{
|
||||
case .success: completionHandler(nil)
|
||||
case .failure(let error): completionHandler(error.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [isDatabaseManagerStarted, isSyncingManagerStarted, isRecordControllerSeeded]
|
||||
}
|
||||
|
||||
override func handleLaunchError(_ error: Error)
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Unable to Launch Delta", comment: ""), message: error.localizedDescription, preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Retry", comment: ""), style: .default, handler: { (action) in
|
||||
self.handleLaunchConditions()
|
||||
}))
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
override func finishLaunching()
|
||||
{
|
||||
super.finishLaunching()
|
||||
|
||||
guard !self.presentedGameViewController else { return }
|
||||
|
||||
self.presentedGameViewController = true
|
||||
|
||||
func showGameViewController()
|
||||
{
|
||||
self.view.bringSubviewToFront(self.gameViewContainerView)
|
||||
|
||||
self.setNeedsStatusBarAppearanceUpdate()
|
||||
self.setNeedsUpdateOfHomeIndicatorAutoHidden()
|
||||
}
|
||||
|
||||
if let game = self.applicationLaunchDeepLinkGame
|
||||
{
|
||||
self.gameViewController.game = game
|
||||
|
||||
UIView.transition(with: self.view, duration: 0.3, options: [.transitionCrossDissolve], animations: {
|
||||
showGameViewController()
|
||||
}, completion: nil)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.gameViewController.performSegue(withIdentifier: "showInitialGamesViewController", sender: nil)
|
||||
self.transitionCoordinator?.animate(alongsideTransition: nil, completion: { (context) in
|
||||
showGameViewController()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension LaunchViewController
|
||||
{
|
||||
@objc func deepLinkControllerLaunchGame(with notification: Notification)
|
||||
|
||||
@ -16,6 +16,7 @@ extension CheatValidator
|
||||
{
|
||||
case invalidCode
|
||||
case invalidName
|
||||
case invalidGame
|
||||
case duplicateName
|
||||
case duplicateCode
|
||||
}
|
||||
@ -28,7 +29,10 @@ struct CheatValidator
|
||||
|
||||
func validate(_ cheat: Cheat) throws
|
||||
{
|
||||
guard let name = cheat.name, let game = cheat.game else { throw Error.invalidName }
|
||||
let name = cheat.name
|
||||
guard !name.isEmpty else { throw Error.invalidName }
|
||||
|
||||
guard let game = cheat.game else { throw Error.invalidGame }
|
||||
|
||||
let code = cheat.code
|
||||
|
||||
|
||||
@ -70,16 +70,10 @@ class SaveStatesViewController: UICollectionViewController
|
||||
|
||||
private var emulatorCoreSaveState: SaveStateProtocol?
|
||||
|
||||
private let dateFormatter: DateFormatter
|
||||
|
||||
required init?(coder aDecoder: NSCoder)
|
||||
{
|
||||
self.dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<SaveState, UIImage>(fetchedResultsController: NSFetchedResultsController())
|
||||
|
||||
self.dateFormatter = DateFormatter()
|
||||
self.dateFormatter.timeStyle = .short
|
||||
self.dateFormatter.dateStyle = .short
|
||||
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
self.prepareDataSource()
|
||||
@ -255,9 +249,7 @@ private extension SaveStatesViewController
|
||||
cell.maximumImageSize = CGSize(width: self.prototypeCellWidthConstraint.constant, height: (self.prototypeCellWidthConstraint.constant / dimensions.width) * dimensions.height)
|
||||
|
||||
cell.textLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
|
||||
|
||||
let name = saveState.name ?? self.dateFormatter.string(from: saveState.modifiedDate)
|
||||
cell.textLabel.text = name
|
||||
cell.textLabel.text = saveState.localizedName
|
||||
}
|
||||
|
||||
func configure(_ headerView: SaveStatesCollectionHeaderView, forSection section: Int)
|
||||
|
||||
@ -32,6 +32,7 @@ extension Settings
|
||||
case localControllerPlayerIndex
|
||||
case translucentControllerSkinOpacity
|
||||
case preferredControllerSkin
|
||||
case syncingService
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,6 +46,15 @@ extension Settings
|
||||
}
|
||||
|
||||
struct Settings
|
||||
{
|
||||
static func registerDefaults()
|
||||
{
|
||||
let defaults = [#keyPath(UserDefaults.translucentControllerSkinOpacity): 0.7, #keyPath(UserDefaults.gameShortcutsMode): GameShortcutsMode.recent.rawValue] as [String : Any]
|
||||
UserDefaults.standard.register(defaults: defaults)
|
||||
}
|
||||
}
|
||||
|
||||
extension Settings
|
||||
{
|
||||
/// Controllers
|
||||
static var localControllerPlayerIndex: Int? = 0 {
|
||||
@ -120,10 +130,12 @@ struct Settings
|
||||
}
|
||||
}
|
||||
|
||||
static func registerDefaults()
|
||||
{
|
||||
let defaults = [#keyPath(UserDefaults.translucentControllerSkinOpacity): 0.7, #keyPath(UserDefaults.gameShortcutsMode): GameShortcutsMode.recent.rawValue] as [String : Any]
|
||||
UserDefaults.standard.register(defaults: defaults)
|
||||
static var syncingService: SyncingService {
|
||||
get { return SyncingService(rawValue: UserDefaults.standard.syncingService) ?? .none }
|
||||
set {
|
||||
UserDefaults.standard.syncingService = newValue.rawValue
|
||||
NotificationCenter.default.post(name: .settingsDidChange, object: nil, userInfo: [NotificationUserInfoKey.name: Name.syncingService])
|
||||
}
|
||||
}
|
||||
|
||||
static func preferredControllerSkin(for system: System, traits: DeltaCore.ControllerSkin.Traits) -> ControllerSkin?
|
||||
@ -220,4 +232,6 @@ private extension UserDefaults
|
||||
|
||||
@NSManaged var gameShortcutsMode: String
|
||||
@NSManaged var gameShortcutIdentifiers: [String]
|
||||
|
||||
@NSManaged var syncingService: String
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ private extension SettingsViewController
|
||||
case controllers
|
||||
case controllerSkins
|
||||
case controllerOpacity
|
||||
case syncing
|
||||
case threeDTouch
|
||||
}
|
||||
|
||||
@ -34,22 +35,34 @@ private extension SettingsViewController
|
||||
case gba
|
||||
case gbc
|
||||
}
|
||||
|
||||
enum SyncingRow: Int, CaseIterable
|
||||
{
|
||||
case service
|
||||
case status
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsViewController: UITableViewController
|
||||
{
|
||||
@IBOutlet private var controllerOpacityLabel: UILabel!
|
||||
@IBOutlet private var controllerOpacitySlider: UISlider!
|
||||
|
||||
@IBOutlet private var versionLabel: UILabel!
|
||||
|
||||
@IBOutlet private var syncingServiceLabel: UILabel!
|
||||
|
||||
private var selectionFeedbackGenerator: UISelectionFeedbackGenerator?
|
||||
|
||||
private var previousSelectedRowIndexPath: IndexPath?
|
||||
|
||||
private var syncingConflictsCount = 0
|
||||
|
||||
required init?(coder aDecoder: NSCoder)
|
||||
{
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.settingsDidChange(with:)), name: .settingsDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.externalGameControllerDidConnect(_:)), name: .externalGameControllerDidConnect, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.externalGameControllerDidDisconnect(_:)), name: .externalGameControllerDidDisconnect, object: nil)
|
||||
}
|
||||
@ -58,9 +71,6 @@ class SettingsViewController: UITableViewController
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.controllerOpacitySlider.value = Float(Settings.translucentControllerSkinOpacity)
|
||||
self.updateControllerOpacityLabel()
|
||||
|
||||
if let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
||||
{
|
||||
self.versionLabel.text = NSLocalizedString(String(format: "Delta %@", version), comment: "Delta Version")
|
||||
@ -86,6 +96,8 @@ class SettingsViewController: UITableViewController
|
||||
|
||||
self.tableView.deselectRow(at: indexPath, animated: true)
|
||||
}
|
||||
|
||||
self.update()
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning()
|
||||
@ -126,6 +138,26 @@ class SettingsViewController: UITableViewController
|
||||
|
||||
private extension SettingsViewController
|
||||
{
|
||||
func update()
|
||||
{
|
||||
self.controllerOpacitySlider.value = Float(Settings.translucentControllerSkinOpacity)
|
||||
self.updateControllerOpacityLabel()
|
||||
|
||||
self.syncingServiceLabel.text = Settings.syncingService.localizedName
|
||||
|
||||
do
|
||||
{
|
||||
let records = try SyncManager.shared.recordController.fetchConflictedRecords()
|
||||
self.syncingConflictsCount = records.count
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
func updateControllerOpacityLabel()
|
||||
{
|
||||
let percentage = String(format: "%.f", Settings.translucentControllerSkinOpacity * 100) + "%"
|
||||
@ -173,6 +205,26 @@ private extension SettingsViewController
|
||||
|
||||
private extension SettingsViewController
|
||||
{
|
||||
@objc func settingsDidChange(with notification: Notification)
|
||||
{
|
||||
guard let settingsName = notification.userInfo?[Settings.NotificationUserInfoKey.name] as? Settings.Name else { return }
|
||||
|
||||
switch settingsName
|
||||
{
|
||||
case .localControllerPlayerIndex, .preferredControllerSkin, .translucentControllerSkinOpacity: break
|
||||
case .syncingService:
|
||||
let selectedIndexPath = self.tableView.indexPathForSelectedRow
|
||||
|
||||
let indexPath = IndexPath(row: SyncingRow.service.rawValue, section: Section.syncing.rawValue)
|
||||
self.tableView.reloadRows(at: [indexPath], with: .none)
|
||||
|
||||
if indexPath == selectedIndexPath
|
||||
{
|
||||
self.tableView.selectRow(at: selectedIndexPath, animated: true, scrollPosition: .none)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func externalGameControllerDidConnect(_ notification: Notification)
|
||||
{
|
||||
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
|
||||
@ -228,7 +280,18 @@ extension SettingsViewController
|
||||
}
|
||||
|
||||
case .controllerSkins: cell.textLabel?.text = System.supportedSystems[indexPath.row].localizedName
|
||||
default: break
|
||||
case .syncing:
|
||||
switch SyncingRow.allCases[indexPath.row]
|
||||
{
|
||||
case .status:
|
||||
let cell = cell as! BadgedTableViewCell
|
||||
cell.badgeLabel.text = self.syncingConflictsCount.description
|
||||
cell.badgeLabel.isHidden = (self.syncingConflictsCount == 0)
|
||||
|
||||
case .service: break
|
||||
}
|
||||
|
||||
case .controllerOpacity, .threeDTouch: break
|
||||
}
|
||||
|
||||
return cell
|
||||
@ -241,10 +304,9 @@ extension SettingsViewController
|
||||
|
||||
switch section
|
||||
{
|
||||
case Section.controllers: self.performSegue(withIdentifier: Segue.controllers.rawValue, sender: cell)
|
||||
case Section.controllerSkins: self.performSegue(withIdentifier: Segue.controllerSkins.rawValue, sender: cell)
|
||||
case Section.controllerOpacity: break
|
||||
case Section.threeDTouch: break
|
||||
case .controllers: self.performSegue(withIdentifier: Segue.controllers.rawValue, sender: cell)
|
||||
case .controllerSkins: self.performSegue(withIdentifier: Segue.controllerSkins.rawValue, sender: cell)
|
||||
case .controllerOpacity, .threeDTouch, .syncing: break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
171
Delta/Settings/Syncing/GameSyncStatusViewController.swift
Normal file
171
Delta/Settings/Syncing/GameSyncStatusViewController.swift
Normal file
@ -0,0 +1,171 @@
|
||||
//
|
||||
// GameSyncStatusViewController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/20/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Roxas
|
||||
import Harmony
|
||||
|
||||
extension GameSyncStatusViewController
|
||||
{
|
||||
private enum Section: Int, CaseIterable
|
||||
{
|
||||
case game
|
||||
case saveStates
|
||||
case cheats
|
||||
}
|
||||
}
|
||||
|
||||
class GameSyncStatusViewController: UITableViewController
|
||||
{
|
||||
var game: Game!
|
||||
|
||||
private lazy var dataSource = self.makeDataSource()
|
||||
|
||||
private var recordsByObjectURI = [URL: Record<NSManagedObject>]()
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.title = self.game.name
|
||||
|
||||
self.tableView.dataSource = self.dataSource
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
self.fetchRecords()
|
||||
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||
{
|
||||
guard segue.identifier == "showRecord" else { return }
|
||||
|
||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
||||
|
||||
let recordedObject = self.dataSource.item(at: indexPath) as! SyncableManagedObject
|
||||
|
||||
do
|
||||
{
|
||||
let records = try SyncManager.shared.recordController.fetchRecords(for: [recordedObject])
|
||||
|
||||
let recordSyncStatusViewController = segue.destination as! RecordSyncStatusViewController
|
||||
recordSyncStatusViewController.record = records.first
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension GameSyncStatusViewController
|
||||
{
|
||||
private func makeDataSource() -> RSTCompositeTableViewDataSource<NSManagedObject>
|
||||
{
|
||||
func configure(_ cell: UITableViewCell, recordedObject: NSManagedObject)
|
||||
{
|
||||
if let record = self.recordsByObjectURI[recordedObject.objectID.uriRepresentation()], record.isConflicted
|
||||
{
|
||||
cell.textLabel?.textColor = .red
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel?.textColor = .darkText
|
||||
}
|
||||
}
|
||||
|
||||
let gameDataSource = RSTArrayTableViewDataSource<NSManagedObject>(items: [self.game, self.game.gameSave].compactMap { $0 })
|
||||
gameDataSource.cellConfigurationHandler = { (cell, item, indexPath) in
|
||||
if item is Game
|
||||
{
|
||||
cell.textLabel?.text = NSLocalizedString("Game", comment: "")
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel?.text = NSLocalizedString("Game Save", comment: "")
|
||||
}
|
||||
|
||||
configure(cell, recordedObject: item)
|
||||
}
|
||||
|
||||
let saveStatesFetchRequest = SaveState.fetchRequest() as NSFetchRequest<SaveState>
|
||||
saveStatesFetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K != %@ AND %K != %@",
|
||||
#keyPath(SaveState.game), self.game,
|
||||
#keyPath(SaveState.type), NSNumber(value: SaveStateType.auto.rawValue),
|
||||
#keyPath(SaveState.type), NSNumber(value: SaveStateType.quick.rawValue))
|
||||
saveStatesFetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \SaveState.creationDate, ascending: true)]
|
||||
|
||||
let saveStatesDataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: saveStatesFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||
saveStatesDataSource.cellConfigurationHandler = { (cell, saveState, indexPath) in
|
||||
cell.textLabel?.text = saveState.localizedName
|
||||
configure(cell, recordedObject: saveState)
|
||||
}
|
||||
|
||||
let cheatsFetchRequest = Cheat.fetchRequest() as NSFetchRequest<Cheat>
|
||||
cheatsFetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(Cheat.game), self.game)
|
||||
cheatsFetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Cheat.name, ascending: true)]
|
||||
|
||||
let cheatsDataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: cheatsFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||
cheatsDataSource.cellConfigurationHandler = { (cell, cheat, indexPath) in
|
||||
cell.textLabel?.text = cheat.name
|
||||
|
||||
configure(cell, recordedObject: cheat)
|
||||
}
|
||||
|
||||
let dataSources = [gameDataSource, saveStatesDataSource, cheatsDataSource] as! [RSTArrayTableViewDataSource<NSManagedObject>]
|
||||
|
||||
let dataSource = RSTCompositeTableViewDataSource(dataSources: dataSources)
|
||||
dataSource.proxy = self
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func fetchRecords()
|
||||
{
|
||||
var recordsByObjectURI = [URL: Record<NSManagedObject>]()
|
||||
|
||||
do
|
||||
{
|
||||
let recordedObjects = ([self.game, self.game.gameSave].compactMap { $0 } + Array(self.game.saveStates) + Array(self.game.cheats)) as! [SyncableManagedObject]
|
||||
let records = try SyncManager.shared.recordController.fetchRecords(for: recordedObjects)
|
||||
|
||||
for record in records
|
||||
{
|
||||
guard let recordedObject = record.recordedObject else { continue }
|
||||
|
||||
recordsByObjectURI[recordedObject.objectID.uriRepresentation()] = record
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
self.recordsByObjectURI = recordsByObjectURI
|
||||
}
|
||||
}
|
||||
|
||||
extension GameSyncStatusViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||
{
|
||||
guard self.dataSource.tableView(self.tableView, numberOfRowsInSection: section) > 0 else { return nil }
|
||||
|
||||
switch Section.allCases[section]
|
||||
{
|
||||
case .game: return nil
|
||||
case .saveStates: return NSLocalizedString("Save States", comment: "")
|
||||
case .cheats: return NSLocalizedString("Cheats", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
174
Delta/Settings/Syncing/RecordSyncStatusViewController.swift
Normal file
174
Delta/Settings/Syncing/RecordSyncStatusViewController.swift
Normal file
@ -0,0 +1,174 @@
|
||||
//
|
||||
// RecordSyncStatusViewController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/20/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Harmony
|
||||
|
||||
extension RecordStatus
|
||||
{
|
||||
fileprivate var localizedDescription: String {
|
||||
switch self
|
||||
{
|
||||
case .normal: return NSLocalizedString("Normal", comment: "")
|
||||
case .updated: return NSLocalizedString("Updated", comment: "")
|
||||
case .deleted: return NSLocalizedString("Deleted", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension RecordSyncStatusViewController
|
||||
{
|
||||
private enum Section: Int, CaseIterable
|
||||
{
|
||||
case syncingEnabled
|
||||
case localStatus
|
||||
case remoteStatus
|
||||
case versions
|
||||
}
|
||||
}
|
||||
|
||||
class RecordSyncStatusViewController: UITableViewController
|
||||
{
|
||||
var record: Record<NSManagedObject>?
|
||||
|
||||
private let dateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.timeStyle = .short
|
||||
dateFormatter.dateStyle = .short
|
||||
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
@IBOutlet private var syncingEnabledSwitch: UISwitch!
|
||||
|
||||
@IBOutlet private var localStatusLabel: UILabel!
|
||||
@IBOutlet private var localDateLabel: UILabel!
|
||||
|
||||
@IBOutlet private var remoteStatusLabel: UILabel!
|
||||
@IBOutlet private var remoteDateLabel: UILabel!
|
||||
@IBOutlet private var remoteDeviceLabel: UILabel!
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.update()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
extension RecordSyncStatusViewController
|
||||
{
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||
{
|
||||
guard segue.identifier == "showVersions" else { return }
|
||||
|
||||
let navigationController = segue.destination as! UINavigationController
|
||||
|
||||
let recordVersionsViewController = navigationController.viewControllers[0] as! RecordVersionsViewController
|
||||
recordVersionsViewController.record = self.record
|
||||
}
|
||||
|
||||
@IBAction private func unwindToRecordSyncStatusViewController(_ segue: UIStoryboardSegue)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private extension RecordSyncStatusViewController
|
||||
{
|
||||
func update()
|
||||
{
|
||||
if let record = self.record
|
||||
{
|
||||
self.syncingEnabledSwitch.isEnabled = !record.isConflicted
|
||||
self.syncingEnabledSwitch.isOn = record.isSyncingEnabled
|
||||
|
||||
self.localStatusLabel.text = record.localStatus?.localizedDescription ?? "-"
|
||||
self.remoteStatusLabel.text = record.remoteStatus?.localizedDescription ?? "-"
|
||||
|
||||
self.remoteDeviceLabel.text = record.remoteAuthor ?? "-"
|
||||
|
||||
if let version = record.remoteVersion
|
||||
{
|
||||
self.remoteDateLabel.text = self.dateFormatter.string(from: version.date)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.remoteDateLabel.text = "-"
|
||||
}
|
||||
|
||||
if let date = record.localModificationDate
|
||||
{
|
||||
self.localDateLabel.text = self.dateFormatter.string(from: date)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.localDateLabel.text = "-"
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.syncingEnabledSwitch.isEnabled = false
|
||||
self.syncingEnabledSwitch.isOn = false
|
||||
|
||||
self.localStatusLabel.text = "-"
|
||||
self.localDateLabel.text = "-"
|
||||
|
||||
self.remoteStatusLabel.text = "-"
|
||||
self.remoteDateLabel.text = "-"
|
||||
self.remoteDeviceLabel.text = "-"
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func toggleSyncingEnabled(_ sender: UISwitch)
|
||||
{
|
||||
do
|
||||
{
|
||||
try self.record?.setSyncingEnabled(sender.isOn)
|
||||
}
|
||||
catch
|
||||
{
|
||||
let title = sender.isOn ? NSLocalizedString("Failed to Enable Syncing", comment: "") : NSLocalizedString("Failed to Disable Syncing", comment: "")
|
||||
|
||||
let alertController = UIAlertController(title: title, error: error)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
extension RecordSyncStatusViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
|
||||
{
|
||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
|
||||
switch Section.allCases[indexPath.section]
|
||||
{
|
||||
case .versions:
|
||||
cell.textLabel?.alpha = (self.record != nil) ? 1.0 : 0.33
|
||||
|
||||
if self.record?.isConflicted == true
|
||||
{
|
||||
cell.textLabel?.text = NSLocalizedString("Resolve Conflict", comment: "")
|
||||
cell.textLabel?.textColor = .red
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel?.text = NSLocalizedString("View Versions", comment: "")
|
||||
cell.textLabel?.textColor = .deltaPurple
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
}
|
||||
394
Delta/Settings/Syncing/RecordVersionsViewController.swift
Normal file
394
Delta/Settings/Syncing/RecordVersionsViewController.swift
Normal file
@ -0,0 +1,394 @@
|
||||
//
|
||||
// RecordVersionsViewController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/20/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Roxas
|
||||
import Harmony
|
||||
|
||||
extension RecordVersionsViewController
|
||||
{
|
||||
private enum Section: Int, CaseIterable
|
||||
{
|
||||
case local
|
||||
case remote
|
||||
case confirm
|
||||
}
|
||||
|
||||
private enum Mode
|
||||
{
|
||||
case restoreVersion
|
||||
case resolveConflict
|
||||
}
|
||||
}
|
||||
|
||||
private class Version
|
||||
{
|
||||
let version: Harmony.Version
|
||||
|
||||
init(_ version: Harmony.Version)
|
||||
{
|
||||
self.version = version
|
||||
}
|
||||
}
|
||||
|
||||
class RecordVersionsViewController: UITableViewController
|
||||
{
|
||||
var record: Record<NSManagedObject>! {
|
||||
didSet {
|
||||
self.mode = self.record.isConflicted ? .resolveConflict : .restoreVersion
|
||||
}
|
||||
}
|
||||
|
||||
private var mode = Mode.restoreVersion {
|
||||
didSet {
|
||||
switch self.mode
|
||||
{
|
||||
case .restoreVersion: self._selectedVersionIndexPath = IndexPath(item: 0, section: Section.local.rawValue)
|
||||
case .resolveConflict: self._selectedVersionIndexPath = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var versions: [Version]?
|
||||
|
||||
private lazy var dataSource = self.makeDataSource()
|
||||
private var remoteVersionsDataSource: RSTArrayTableViewDataSource<Version> {
|
||||
let compositeDataSource = self.dataSource.dataSources[1] as! RSTCompositeTableViewDataSource
|
||||
|
||||
let dataSource = compositeDataSource.dataSources[1] as! RSTArrayTableViewDataSource<Version>
|
||||
return dataSource
|
||||
}
|
||||
|
||||
private let dateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.timeStyle = .short
|
||||
dateFormatter.dateStyle = .short
|
||||
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
private var isSyncingRecord = false
|
||||
private var _selectedVersionIndexPath: IndexPath?
|
||||
|
||||
private var progressView: UIProgressView!
|
||||
|
||||
private var _progressObservation: NSKeyValueObservation?
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.progressView = UIProgressView(progressViewStyle: .bar)
|
||||
self.progressView.translatesAutoresizingMaskIntoConstraints = false
|
||||
self.progressView.progress = 0
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar
|
||||
{
|
||||
navigationBar.addSubview(self.progressView)
|
||||
|
||||
NSLayoutConstraint.activate([self.progressView.leadingAnchor.constraint(equalTo: navigationBar.leadingAnchor),
|
||||
self.progressView.trailingAnchor.constraint(equalTo: navigationBar.trailingAnchor),
|
||||
self.progressView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)])
|
||||
}
|
||||
|
||||
self.tableView.dataSource = self.dataSource
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.fetchVersions()
|
||||
}
|
||||
}
|
||||
|
||||
private extension RecordVersionsViewController
|
||||
{
|
||||
func makeDataSource() -> RSTCompositeTableViewDataSource<Version>
|
||||
{
|
||||
func configure(_ cell: UITableViewCell, isSelected: Bool, isEnabled: Bool)
|
||||
{
|
||||
cell.accessoryType = isSelected ? .checkmark : .none
|
||||
|
||||
if isEnabled
|
||||
{
|
||||
cell.textLabel?.alpha = 1.0
|
||||
cell.detailTextLabel?.alpha = 1.0
|
||||
cell.selectionStyle = .gray
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel?.alpha = 0.33
|
||||
cell.detailTextLabel?.alpha = 0.33
|
||||
cell.selectionStyle = .none
|
||||
}
|
||||
}
|
||||
|
||||
let localVersionsDataSource = RSTDynamicTableViewDataSource<Version>()
|
||||
localVersionsDataSource.numberOfSectionsHandler = { 1 }
|
||||
localVersionsDataSource.numberOfItemsHandler = { _ in self.record.localModificationDate != nil ? 1 : 0 }
|
||||
localVersionsDataSource.cellConfigurationHandler = { [weak self] (cell, _, indexPath) in
|
||||
guard let `self` = self else { return }
|
||||
|
||||
let date = self.record.localModificationDate!
|
||||
cell.textLabel?.text = self.dateFormatter.string(from: date)
|
||||
cell.detailTextLabel?.text = nil
|
||||
|
||||
let isSelected = (indexPath == self._selectedVersionIndexPath)
|
||||
configure(cell, isSelected: isSelected, isEnabled: !self.isSyncingRecord)
|
||||
}
|
||||
|
||||
let remoteVersionsDataSource = RSTArrayTableViewDataSource<Version>(items: [])
|
||||
remoteVersionsDataSource.cellConfigurationHandler = { [weak self] (cell, version, indexPath) in
|
||||
guard let `self` = self else { return }
|
||||
|
||||
cell.textLabel?.text = self.dateFormatter.string(from: version.version.date)
|
||||
cell.detailTextLabel?.text = (version.version.identifier == self.record.remoteVersion?.identifier) ? self.record.remoteAuthor : nil
|
||||
|
||||
let isSelected = (self._selectedVersionIndexPath?.section == Section.remote.rawValue && self._selectedVersionIndexPath?.row == indexPath.row)
|
||||
configure(cell, isSelected: isSelected, isEnabled: !self.isSyncingRecord)
|
||||
}
|
||||
|
||||
let loadingDataSource = RSTDynamicTableViewDataSource<Version>()
|
||||
loadingDataSource.numberOfSectionsHandler = { 1 }
|
||||
loadingDataSource.numberOfItemsHandler = { _ in (self.versions == nil) ? 1 : 0 }
|
||||
loadingDataSource.cellIdentifierHandler = { _ in "LoadingCell" }
|
||||
loadingDataSource.cellConfigurationHandler = { (_, _, _) in }
|
||||
|
||||
let remoteVersionsLoadingDataSource = RSTCompositeTableViewDataSource(dataSources: [loadingDataSource, remoteVersionsDataSource])
|
||||
remoteVersionsLoadingDataSource.shouldFlattenSections = true
|
||||
|
||||
let restoreVersionDataSource = RSTDynamicTableViewDataSource<Version>()
|
||||
restoreVersionDataSource.numberOfSectionsHandler = { 1 }
|
||||
restoreVersionDataSource.numberOfItemsHandler = { _ in 1}
|
||||
restoreVersionDataSource.cellIdentifierHandler = { _ in "ConfirmCell" }
|
||||
restoreVersionDataSource.cellConfigurationHandler = { [weak self] (cell, _, indexPath) in
|
||||
guard let `self` = self else { return }
|
||||
|
||||
switch self.mode
|
||||
{
|
||||
case .restoreVersion:
|
||||
cell.textLabel?.text = NSLocalizedString("Restore Version", comment: "")
|
||||
cell.textLabel?.textColor = .deltaPurple
|
||||
|
||||
let isEnabled = (self._selectedVersionIndexPath?.section == Section.remote.rawValue && !self.isSyncingRecord)
|
||||
configure(cell, isSelected: false, isEnabled: isEnabled)
|
||||
|
||||
case .resolveConflict:
|
||||
cell.textLabel?.text = NSLocalizedString("Resolve Conflict", comment: "")
|
||||
cell.textLabel?.textColor = .red
|
||||
|
||||
let isEnabled = (self._selectedVersionIndexPath != nil && !self.isSyncingRecord)
|
||||
configure(cell, isSelected: false, isEnabled: isEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
let dataSource = RSTCompositeTableViewDataSource(dataSources: [localVersionsDataSource, remoteVersionsLoadingDataSource, restoreVersionDataSource])
|
||||
dataSource.proxy = self
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func fetchVersions()
|
||||
{
|
||||
SyncManager.shared.syncCoordinator.fetchVersions(for: self.record) { (result) in
|
||||
do
|
||||
{
|
||||
let versions = try result.value().map(Version.init)
|
||||
self.versions = versions
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let count = self.tableView.numberOfRows(inSection: Section.remote.rawValue)
|
||||
|
||||
let deletions = (0 ..< count).map { (row) -> RSTCellContentChange in
|
||||
let change = RSTCellContentChange(type: .delete,
|
||||
currentIndexPath: IndexPath(row: row, section: 0),
|
||||
destinationIndexPath: nil)
|
||||
change.rowAnimation = .fade
|
||||
return change
|
||||
}
|
||||
|
||||
let inserts = (0 ..< versions.count).map { (row) -> RSTCellContentChange in
|
||||
let change = RSTCellContentChange(type: .insert,
|
||||
currentIndexPath: nil,
|
||||
destinationIndexPath: IndexPath(row: row, section: 0))
|
||||
change.rowAnimation = .fade
|
||||
return change
|
||||
}
|
||||
|
||||
let changes = deletions + inserts
|
||||
self.remoteVersionsDataSource.setItems(versions, with: changes)
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Failed to Fetch Record Versions", comment: ""), message: error.localizedDescription, preferredStyle: .alert)
|
||||
alertController.addAction(.ok)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func restoreVersion()
|
||||
{
|
||||
guard !self.isSyncingRecord else { return }
|
||||
|
||||
guard let indexPath = self._selectedVersionIndexPath else { return }
|
||||
|
||||
func finish<T: Error>(_ result: Result<AnyRecord, T>)
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
|
||||
CATransaction.begin()
|
||||
|
||||
CATransaction.setCompletionBlock {
|
||||
self.isSyncingRecord = false
|
||||
self._progressObservation = nil
|
||||
|
||||
self.progressView.setHidden(true, animated: true)
|
||||
|
||||
self.tableView.reloadData()
|
||||
|
||||
self.navigationItem.rightBarButtonItem?.isIndicatingActivity = false
|
||||
|
||||
switch result
|
||||
{
|
||||
case .success: self.fetchVersions()
|
||||
case .failure: break
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
let record = try result.value()
|
||||
self.record = record
|
||||
|
||||
self.progressView.setProgress(1.0, animated: true)
|
||||
}
|
||||
catch
|
||||
{
|
||||
let title: String
|
||||
|
||||
switch self.mode
|
||||
{
|
||||
case .restoreVersion: title = NSLocalizedString("Failed to Restore Version", comment: "")
|
||||
case .resolveConflict: title = NSLocalizedString("Failed to Resolve Conflict", comment: "")
|
||||
}
|
||||
|
||||
let alertController = UIAlertController(title: title, message: error.localizedDescription, preferredStyle: .alert)
|
||||
alertController.addAction(.ok)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
|
||||
let progress: Progress
|
||||
|
||||
switch (self.mode, Section.allCases[indexPath.section])
|
||||
{
|
||||
case (.restoreVersion, _):
|
||||
let version = self.dataSource.item(at: indexPath)
|
||||
|
||||
progress = SyncManager.shared.syncCoordinator.restore(self.record, to: version.version) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case (.resolveConflict, .local):
|
||||
progress = SyncManager.shared.syncCoordinator.resolveConflictedRecord(self.record, resolution: .local) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case (.resolveConflict, .remote):
|
||||
let version = self.dataSource.item(at: indexPath)
|
||||
|
||||
progress = SyncManager.shared.syncCoordinator.resolveConflictedRecord(self.record, resolution: .remote(version.version)) { (result) in
|
||||
finish(result)
|
||||
}
|
||||
|
||||
case (.resolveConflict, .confirm): return
|
||||
}
|
||||
|
||||
self.isSyncingRecord = true
|
||||
self.navigationItem.rightBarButtonItem?.isIndicatingActivity = true
|
||||
|
||||
self.progressView.progress = 0
|
||||
self.progressView.isHidden = false
|
||||
|
||||
self._progressObservation = progress.observe(\.fractionCompleted) { [weak progressView] (_, change) in
|
||||
DispatchQueue.main.async {
|
||||
progressView?.setProgress(Float(progress.fractionCompleted), animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
extension RecordVersionsViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||
{
|
||||
switch Section.allCases[section]
|
||||
{
|
||||
case .local: return NSLocalizedString("Local", comment: "")
|
||||
case .remote: return NSLocalizedString("Remote", comment: "")
|
||||
case .confirm: return nil
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||
{
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
|
||||
guard let cell = tableView.cellForRow(at: indexPath), cell.selectionStyle != .none else { return }
|
||||
|
||||
switch Section.allCases[indexPath.section]
|
||||
{
|
||||
case .local, .remote:
|
||||
let indexPaths = [indexPath, self._selectedVersionIndexPath, IndexPath(item: 0, section: Section.confirm.rawValue)].compactMap { $0 }
|
||||
self._selectedVersionIndexPath = indexPath
|
||||
|
||||
tableView.reloadRows(at: indexPaths, with: .none)
|
||||
|
||||
case .confirm:
|
||||
let message: String
|
||||
let actionTitle: String
|
||||
|
||||
switch self.mode
|
||||
{
|
||||
case .restoreVersion:
|
||||
message = NSLocalizedString("Restoring a remote version will cause any local changes to be lost.", comment: "")
|
||||
actionTitle = NSLocalizedString("Restore Version", comment: "")
|
||||
|
||||
case .resolveConflict:
|
||||
if self._selectedVersionIndexPath?.section == Section.local.rawValue
|
||||
{
|
||||
message = NSLocalizedString("The local version will be uploaded and synced to your other devices.", comment: "")
|
||||
}
|
||||
else
|
||||
{
|
||||
message = NSLocalizedString("Selecting a remote version will cause any local changes to be lost.", comment: "")
|
||||
}
|
||||
|
||||
actionTitle = NSLocalizedString("Resolve Conflict", comment: "")
|
||||
}
|
||||
|
||||
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .actionSheet)
|
||||
alertController.addAction(.cancel)
|
||||
alertController.addAction(UIAlertAction(title: actionTitle, style: .destructive) { (action) in
|
||||
self.restoreVersion()
|
||||
})
|
||||
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
184
Delta/Settings/Syncing/SyncStatusViewController.swift
Normal file
184
Delta/Settings/Syncing/SyncStatusViewController.swift
Normal file
@ -0,0 +1,184 @@
|
||||
//
|
||||
// SyncStatusViewController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/15/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Roxas
|
||||
|
||||
class SyncStatusViewController: UITableViewController
|
||||
{
|
||||
private lazy var dataSource = self.makeDataSource()
|
||||
|
||||
private var gameConflictsCount: [URL: Int]?
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.tableView.dataSource = self.dataSource
|
||||
self.navigationItem.searchController = self.dataSource.searchController
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.fetchConflictedRecords()
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||
{
|
||||
guard let identifier = segue.identifier else { return }
|
||||
|
||||
switch identifier
|
||||
{
|
||||
case "showGame":
|
||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
||||
|
||||
let game = self.dataSource.item(at: indexPath)
|
||||
|
||||
let gameSyncStatusViewController = segue.destination as! GameSyncStatusViewController
|
||||
gameSyncStatusViewController.game = game
|
||||
|
||||
case "showPreviousSyncResults":
|
||||
let syncResultViewController = segue.destination as! SyncResultViewController
|
||||
syncResultViewController.result = SyncManager.shared.previousSyncResult
|
||||
|
||||
default: break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SyncStatusViewController
|
||||
{
|
||||
func makeDataSource() -> RSTCompositeTableViewDataSource<Game>
|
||||
{
|
||||
let fetchRequest = Game.fetchRequest() as NSFetchRequest<Game>
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Game.gameCollection?.index, ascending: true), NSSortDescriptor(key: #keyPath(Game.name), ascending: true)]
|
||||
|
||||
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(Game.gameCollection.name), cacheName: nil)
|
||||
|
||||
let fetchedDataSource = RSTFetchedResultsTableViewDataSource(fetchedResultsController: fetchedResultsController)
|
||||
fetchedDataSource.searchController.searchableKeyPaths = [#keyPath(Game.name)]
|
||||
fetchedDataSource.cellConfigurationHandler = { (cell, game, indexPath) in
|
||||
let cell = cell as! BadgedTableViewCell
|
||||
cell.textLabel?.text = game.name
|
||||
cell.textLabel?.numberOfLines = 0
|
||||
|
||||
if let gameConflictsCount = self.gameConflictsCount
|
||||
{
|
||||
if let count = gameConflictsCount[game.objectID.uriRepresentation()], count > 0
|
||||
{
|
||||
cell.badgeLabel.text = String(describing: count)
|
||||
cell.badgeLabel.isHidden = false
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.badgeLabel.isHidden = true
|
||||
}
|
||||
|
||||
cell.accessoryType = .disclosureIndicator
|
||||
cell.accessoryView = nil
|
||||
}
|
||||
else
|
||||
{
|
||||
let activityIndicatorView = UIActivityIndicatorView(style: .gray)
|
||||
activityIndicatorView.startAnimating()
|
||||
|
||||
cell.accessoryType = .none
|
||||
cell.accessoryView = activityIndicatorView
|
||||
|
||||
cell.badgeLabel.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
let dynamicDataSource = RSTDynamicTableViewDataSource<Game>()
|
||||
dynamicDataSource.numberOfSectionsHandler = { (SyncManager.shared.previousSyncResult != nil) ? 1 : 0 }
|
||||
dynamicDataSource.numberOfItemsHandler = { _ in 1 }
|
||||
dynamicDataSource.cellIdentifierHandler = { _ in "PreviousSyncCell" }
|
||||
dynamicDataSource.cellConfigurationHandler = { (cell, _, indexPath) in }
|
||||
|
||||
let dataSource = RSTCompositeTableViewDataSource(dataSources: [dynamicDataSource, fetchedDataSource])
|
||||
dataSource.proxy = self
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func fetchConflictedRecords()
|
||||
{
|
||||
DispatchQueue.global().async {
|
||||
do
|
||||
{
|
||||
var gameConflictsCount = [URL: Int]()
|
||||
|
||||
let records = try SyncManager.shared.recordController.fetchConflictedRecords()
|
||||
|
||||
for record in records
|
||||
{
|
||||
guard let recordedObject = record.recordedObject else { continue }
|
||||
recordedObject.managedObjectContext?.performAndWait {
|
||||
let conflictedGame: Game?
|
||||
|
||||
switch recordedObject
|
||||
{
|
||||
case let game as Game: conflictedGame = game
|
||||
case let saveState as SaveState: conflictedGame = saveState.game
|
||||
case let cheat as Cheat: conflictedGame = cheat.game
|
||||
case let gameSave as GameSave: conflictedGame = gameSave.game
|
||||
default: conflictedGame = nil
|
||||
}
|
||||
|
||||
guard let game = conflictedGame else { return }
|
||||
|
||||
gameConflictsCount[game.objectID.uriRepresentation(), default: 0] += 1
|
||||
}
|
||||
}
|
||||
|
||||
self.gameConflictsCount = gameConflictsCount
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
|
||||
self.gameConflictsCount = [:]
|
||||
|
||||
DispatchQueue.main.async {
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Failed to Get Sync Status", comment: ""),
|
||||
message: error.localizedDescription,
|
||||
preferredStyle: .alert)
|
||||
alertController.addAction(.ok)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncStatusViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||
{
|
||||
var section = section
|
||||
|
||||
if SyncManager.shared.previousSyncResult != nil
|
||||
{
|
||||
guard section > 0 else { return nil }
|
||||
|
||||
section -= 1
|
||||
}
|
||||
|
||||
let dataSource = self.dataSource.dataSources[1] as! RSTFetchedResultsTableViewDataSource
|
||||
|
||||
let sectionInfo = dataSource.fetchedResultsController.sections?[section]
|
||||
return sectionInfo?.name
|
||||
}
|
||||
}
|
||||
204
Delta/Settings/Syncing/SyncingServicesViewController.swift
Normal file
204
Delta/Settings/Syncing/SyncingServicesViewController.swift
Normal file
@ -0,0 +1,204 @@
|
||||
//
|
||||
// SyncingServicesViewController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 6/27/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Harmony
|
||||
import Harmony_Drive
|
||||
|
||||
import Roxas
|
||||
|
||||
enum SyncingService: String, CaseIterable
|
||||
{
|
||||
case none
|
||||
case googleDrive
|
||||
|
||||
var localizedName: String {
|
||||
switch self
|
||||
{
|
||||
case .none: return NSLocalizedString("None", comment: "")
|
||||
case .googleDrive: return NSLocalizedString("Google Drive", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncingServicesViewController
|
||||
{
|
||||
enum Section: Int, CaseIterable
|
||||
{
|
||||
case service
|
||||
case account
|
||||
case authenticate
|
||||
}
|
||||
}
|
||||
|
||||
class SyncingServicesViewController: UITableViewController
|
||||
{
|
||||
func isSectionHidden(_ section: Section) -> Bool
|
||||
{
|
||||
switch section
|
||||
{
|
||||
case .account: return SyncManager.shared.syncCoordinator.account == nil
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncingServicesViewController
|
||||
{
|
||||
override func numberOfSections(in tableView: UITableView) -> Int
|
||||
{
|
||||
guard Settings.syncingService != .none else { return 1 }
|
||||
|
||||
return super.numberOfSections(in: tableView)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
|
||||
{
|
||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
|
||||
switch Section.allCases[indexPath.section]
|
||||
{
|
||||
case .service:
|
||||
let service = SyncingService.allCases[indexPath.row]
|
||||
cell.accessoryType = (service == Settings.syncingService) ? .checkmark : .none
|
||||
|
||||
case .account:
|
||||
cell.textLabel?.text = SyncManager.shared.syncCoordinator.account?.name ?? NSLocalizedString("Unknown Account", comment: "")
|
||||
|
||||
case .authenticate:
|
||||
if SyncManager.shared.syncCoordinator.isAuthenticated
|
||||
{
|
||||
cell.textLabel?.textColor = .red
|
||||
cell.textLabel?.text = NSLocalizedString("Sign Out", comment: "")
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel?.textColor = .deltaPurple
|
||||
cell.textLabel?.text = NSLocalizedString("Sign In", comment: "")
|
||||
}
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||
{
|
||||
switch Section.allCases[indexPath.section]
|
||||
{
|
||||
case .service:
|
||||
Settings.syncingService = SyncingService.allCases[indexPath.row]
|
||||
|
||||
if Settings.syncingService == .none && self.tableView.numberOfSections > 1
|
||||
{
|
||||
self.tableView.deleteSections(IndexSet(integersIn: Section.account.rawValue ... Section.authenticate.rawValue), with: .fade)
|
||||
}
|
||||
else if Settings.syncingService != .none && self.tableView.numberOfSections == 1
|
||||
{
|
||||
self.tableView.insertSections(IndexSet(integersIn: Section.account.rawValue ... Section.authenticate.rawValue), with: .fade)
|
||||
}
|
||||
|
||||
self.tableView.reloadSections(IndexSet(integer: Section.service.rawValue), with: .none)
|
||||
|
||||
case .account: break
|
||||
|
||||
case .authenticate:
|
||||
if SyncManager.shared.syncCoordinator.isAuthenticated
|
||||
{
|
||||
SyncManager.shared.syncCoordinator.deauthenticate { (result) in
|
||||
DispatchQueue.main.async {
|
||||
do
|
||||
{
|
||||
try result.verify()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
catch
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Failed to Sign Out", comment: ""), error: error)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SyncManager.shared.syncCoordinator.authenticate(presentingViewController: self) { (result) in
|
||||
DispatchQueue.main.async {
|
||||
do
|
||||
{
|
||||
try result.verify()
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Failed to Sign In", comment: ""), error: error)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||
{
|
||||
let section = Section.allCases[section]
|
||||
|
||||
if self.isSectionHidden(section)
|
||||
{
|
||||
return 0
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.tableView(tableView, numberOfRowsInSection: section.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||
{
|
||||
let section = Section.allCases[section]
|
||||
|
||||
if self.isSectionHidden(section)
|
||||
{
|
||||
return nil
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.tableView(tableView, titleForHeaderInSection: section.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
|
||||
{
|
||||
let section = Section.allCases[section]
|
||||
|
||||
if self.isSectionHidden(section)
|
||||
{
|
||||
return 1
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.tableView(tableView, heightForHeaderInSection: section.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat
|
||||
{
|
||||
let section = Section.allCases[section]
|
||||
|
||||
if self.isSectionHidden(section)
|
||||
{
|
||||
return 1
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.tableView(tableView, heightForFooterInSection: section.rawValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
15
Delta/Supporting Files/GoogleService-Info.plist
Normal file
15
Delta/Supporting Files/GoogleService-Info.plist
Normal file
@ -0,0 +1,15 @@
|
||||
<?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>CLIENT_ID</key>
|
||||
<string>457607414709-5puj6lcv779gpu3ql43e6k3smjj40dmu.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.457607414709-5puj6lcv779gpu3ql43e6k3smjj40dmu</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.rileytestut.Delta</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -89,6 +89,17 @@
|
||||
<string>0.6.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.457607414709-5puj6lcv779gpu3ql43e6k3smjj40dmu</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>6</string>
|
||||
<key>Fabric</key>
|
||||
|
||||
88
Delta/Syncing/SyncManager.swift
Normal file
88
Delta/Syncing/SyncManager.swift
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// SyncManager.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/12/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Harmony
|
||||
import Harmony_Drive
|
||||
|
||||
extension SyncManager
|
||||
{
|
||||
enum RecordType: String, Hashable
|
||||
{
|
||||
case game = "Game"
|
||||
case gameCollection = "GameCollection"
|
||||
case cheat = "Cheat"
|
||||
case saveState = "SaveState"
|
||||
case controllerSkin = "ControllerSkin"
|
||||
case gameControllerInputMapping = "GameControllerInputMapping"
|
||||
case gameSave = "GameSave"
|
||||
|
||||
var localizedName: String {
|
||||
switch self
|
||||
{
|
||||
case .game: return NSLocalizedString("Game", comment: "")
|
||||
case .gameCollection: return NSLocalizedString("Game Collection", comment: "")
|
||||
case .cheat: return NSLocalizedString("Cheat", comment: "")
|
||||
case .saveState: return NSLocalizedString("Save State", comment: "")
|
||||
case .controllerSkin: return NSLocalizedString("Controller Skin", comment: "")
|
||||
case .gameControllerInputMapping: return NSLocalizedString("Game Controller Input Mapping", comment: "")
|
||||
case .gameSave: return NSLocalizedString("Game Save", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Syncable where Self: NSManagedObject
|
||||
{
|
||||
var recordType: SyncManager.RecordType {
|
||||
let recordType = SyncManager.RecordType(rawValue: self.syncableType)!
|
||||
return recordType
|
||||
}
|
||||
}
|
||||
|
||||
final class SyncManager
|
||||
{
|
||||
static let shared = SyncManager()
|
||||
|
||||
var service: Service {
|
||||
return self.syncCoordinator.service
|
||||
}
|
||||
|
||||
var recordController: RecordController {
|
||||
return self.syncCoordinator.recordController
|
||||
}
|
||||
|
||||
private(set) var previousSyncResult: SyncResult?
|
||||
|
||||
let syncCoordinator = SyncCoordinator(service: DriveService.shared, persistentContainer: DatabaseManager.shared)
|
||||
|
||||
private init()
|
||||
{
|
||||
DriveService.shared.clientID = "457607414709-5puj6lcv779gpu3ql43e6k3smjj40dmu.apps.googleusercontent.com"
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(SyncManager.syncingDidFinish(_:)), name: SyncCoordinator.didFinishSyncingNotification, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncManager
|
||||
{
|
||||
func sync()
|
||||
{
|
||||
guard Settings.syncingService != .none else { return }
|
||||
|
||||
self.syncCoordinator.sync()
|
||||
}
|
||||
}
|
||||
|
||||
private extension SyncManager
|
||||
{
|
||||
@objc func syncingDidFinish(_ notification: Notification)
|
||||
{
|
||||
guard let result = notification.userInfo?[SyncCoordinator.syncResultKey] as? SyncResult else { return }
|
||||
self.previousSyncResult = result
|
||||
}
|
||||
}
|
||||
354
Delta/Syncing/SyncResultViewController.swift
Normal file
354
Delta/Syncing/SyncResultViewController.swift
Normal file
@ -0,0 +1,354 @@
|
||||
//
|
||||
// SyncResultViewController.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 11/28/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import Roxas
|
||||
import Harmony
|
||||
|
||||
@objc(SyncResultTableViewCell)
|
||||
private class SyncResultTableViewCell: UITableViewCell
|
||||
{
|
||||
@IBOutlet var nameLabel: UILabel!
|
||||
@IBOutlet var errorLabel: UILabel!
|
||||
}
|
||||
|
||||
extension SyncResultViewController
|
||||
{
|
||||
private enum Group: Hashable
|
||||
{
|
||||
case game(RecordID)
|
||||
case saveState(gameID: RecordID)
|
||||
case cheat(gameID: RecordID)
|
||||
case controllerSkin
|
||||
case gameControllerInputMapping
|
||||
case gameCollection
|
||||
case other
|
||||
|
||||
var sortIndex: Int {
|
||||
switch self
|
||||
{
|
||||
case .game: return 0
|
||||
case .saveState: return 1
|
||||
case .cheat: return 2
|
||||
case .controllerSkin: return 3
|
||||
case .gameControllerInputMapping: return 4
|
||||
case .gameCollection: return 5
|
||||
case .other: return 6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SyncResultViewController: UITableViewController
|
||||
{
|
||||
var result: Result<[Record<NSManagedObject>: Result<Void, RecordError>], SyncError>!
|
||||
|
||||
private lazy var dataSource = self.makeDataSource()
|
||||
|
||||
private lazy var sortedErrors = self.processResults()
|
||||
private lazy var gameNamesByRecordID = self.fetchGameNames()
|
||||
|
||||
private init()
|
||||
{
|
||||
super.init(style: .grouped)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder)
|
||||
{
|
||||
super.init(coder: aDecoder)
|
||||
}
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.tableView.dataSource = self.dataSource
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||
{
|
||||
guard segue.identifier == "showRecordStatus" else { return }
|
||||
|
||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
||||
|
||||
guard let recordError = self.dataSource.item(at: indexPath).value as? RecordError else { return }
|
||||
|
||||
let recordSyncStatusViewController = segue.destination as! RecordSyncStatusViewController
|
||||
recordSyncStatusViewController.record = recordError.record
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncResultViewController
|
||||
{
|
||||
class func make(result: Result<[Record<NSManagedObject>: Result<Void, RecordError>], SyncError>) -> UINavigationController
|
||||
{
|
||||
let storyboard = UIStoryboard(name: "SyncResultsViewController", bundle: nil)
|
||||
|
||||
let navigationController = storyboard.instantiateInitialViewController() as! UINavigationController
|
||||
|
||||
let syncResultViewController = navigationController.viewControllers[0] as! SyncResultViewController
|
||||
syncResultViewController.result = result
|
||||
|
||||
return navigationController
|
||||
}
|
||||
}
|
||||
|
||||
private extension SyncResultViewController
|
||||
{
|
||||
func makeDataSource() -> RSTCompositeTableViewDataSource<Box<Error>>
|
||||
{
|
||||
let dataSources = self.sortedErrors.map { (_, errors) -> RSTArrayTableViewDataSource<Box<Error>> in
|
||||
let dataSource = RSTArrayTableViewDataSource<Box<Error>>(items: errors.map(Box.init))
|
||||
dataSource.cellConfigurationHandler = { (cell, error, indexPath) in
|
||||
let cell = cell as! SyncResultTableViewCell
|
||||
|
||||
let title: String?
|
||||
let errorMessage: String?
|
||||
|
||||
switch error.value
|
||||
{
|
||||
case let error as RecordError:
|
||||
guard let recordType = SyncManager.RecordType(rawValue: error.record.recordID.type) else { return }
|
||||
|
||||
switch recordType
|
||||
{
|
||||
case .game: title = NSLocalizedString("Game", comment: "")
|
||||
case .gameSave: title = NSLocalizedString("Game Save", comment: "")
|
||||
case .saveState, .cheat, .controllerSkin, .gameCollection, .gameControllerInputMapping: title = error.record.localizedName ?? recordType.localizedName
|
||||
}
|
||||
|
||||
switch error
|
||||
{
|
||||
case .filesFailed(_, let errors):
|
||||
var messages = [String]()
|
||||
|
||||
for error in errors
|
||||
{
|
||||
messages.append(error.localizedDescription)
|
||||
}
|
||||
|
||||
errorMessage = messages.joined(separator: "\n")
|
||||
|
||||
default: errorMessage = error.failureReason
|
||||
}
|
||||
|
||||
case let error as HarmonyError:
|
||||
title = error.failureDescription
|
||||
errorMessage = error.failureReason
|
||||
|
||||
case let error:
|
||||
assertionFailure("Only HarmonyErrors should be thrown by syncing logic.")
|
||||
|
||||
title = nil
|
||||
errorMessage = error.localizedDescription
|
||||
}
|
||||
|
||||
cell.nameLabel.text = title
|
||||
cell.errorLabel.text = errorMessage
|
||||
}
|
||||
|
||||
return dataSource
|
||||
}
|
||||
|
||||
let placeholderView = RSTPlaceholderView()
|
||||
placeholderView.textLabel.text = NSLocalizedString("Sync Successful", comment: "")
|
||||
placeholderView.detailTextLabel.text = NSLocalizedString("There were no errors during last sync.", comment: "")
|
||||
|
||||
let compositeDataSource = RSTCompositeTableViewDataSource(dataSources: dataSources)
|
||||
compositeDataSource.proxy = self
|
||||
compositeDataSource.placeholderView = placeholderView
|
||||
return compositeDataSource
|
||||
}
|
||||
|
||||
private func processResults() -> [(group: Group, errors: [Error])]
|
||||
{
|
||||
var errors = [Error]()
|
||||
|
||||
do
|
||||
{
|
||||
try self.result.verify()
|
||||
}
|
||||
catch SyncError.partial(let recordResults)
|
||||
{
|
||||
for (_, result) in recordResults
|
||||
{
|
||||
guard case .failure(let error) = result else { continue }
|
||||
errors.append(error)
|
||||
}
|
||||
}
|
||||
catch SyncError.other(.cancelled)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
catch let error as SyncError
|
||||
{
|
||||
let error = error.underlyingError ?? error
|
||||
errors.append(error)
|
||||
}
|
||||
catch
|
||||
{
|
||||
assertionFailure("Non-SyncError thrown by sync result.")
|
||||
errors.append(error)
|
||||
}
|
||||
|
||||
var errorsByGroup = [Group: [Error]]()
|
||||
|
||||
for error in errors
|
||||
{
|
||||
let group: Group
|
||||
|
||||
switch error
|
||||
{
|
||||
case let error as RecordError:
|
||||
guard let recordType = SyncManager.RecordType(rawValue: error.record.recordID.type) else { continue }
|
||||
|
||||
switch recordType
|
||||
{
|
||||
case .game: group = .game(error.record.recordID)
|
||||
case .gameSave: group = .game(error.record.recordID)
|
||||
case .gameCollection: group = .gameCollection
|
||||
case .controllerSkin: group = .controllerSkin
|
||||
case .gameControllerInputMapping: group = .gameControllerInputMapping
|
||||
|
||||
case .saveState:
|
||||
guard let gameID = error.record.metadata?[.gameID] else { continue }
|
||||
|
||||
let recordID = RecordID(type: SyncManager.RecordType.game.rawValue, identifier: gameID)
|
||||
group = .saveState(gameID: recordID)
|
||||
|
||||
case .cheat:
|
||||
guard let gameID = error.record.metadata?[.gameID] else { continue }
|
||||
|
||||
let recordID = RecordID(type: SyncManager.RecordType.game.rawValue, identifier: gameID)
|
||||
group = .cheat(gameID: recordID)
|
||||
}
|
||||
|
||||
default: group = .other
|
||||
}
|
||||
|
||||
errorsByGroup[group, default: []].append(error)
|
||||
}
|
||||
|
||||
let sortedErrors = errorsByGroup.sorted { (a, b) in
|
||||
let groupA = a.key
|
||||
let groupB = b.key
|
||||
|
||||
// Sort initially by game, then sort by type.
|
||||
// This way games and their associated records (such as save states) are visually grouped together.
|
||||
switch (groupA, groupB)
|
||||
{
|
||||
// Game-related records, but different game identifiers, so sort by game identifiers (implicitly grouping related game records together).
|
||||
// Using `fallthrough` for these cases seg faults the compiler (as of Swift 4.2.1), so we just duplicate the return expression.
|
||||
case (.game(let a), .game(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.game(let a), .saveState(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.game(let a), .cheat(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.saveState(let a), .game(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.saveState(let a), .saveState(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.saveState(let a), .cheat(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.cheat(let a), .game(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.cheat(let a), .saveState(let b)) where a != b: return a.identifier < b.identifier
|
||||
case (.cheat(let a), .cheat(let b)) where a != b: return a.identifier < b.identifier
|
||||
|
||||
// Otherwise, just return their relative ordering.
|
||||
case (.game, _): fallthrough
|
||||
case (.saveState, _): fallthrough
|
||||
case (.cheat, _): fallthrough
|
||||
case (.controllerSkin, _): fallthrough
|
||||
case (.gameControllerInputMapping, _): fallthrough
|
||||
case (.gameCollection, _): fallthrough
|
||||
case (.other, _): return groupA.sortIndex < groupB.sortIndex
|
||||
}
|
||||
}
|
||||
|
||||
return sortedErrors.map { (group: $0.key, errors: $0.value) }
|
||||
}
|
||||
|
||||
func fetchGameNames() -> [RecordID: String]
|
||||
{
|
||||
let fetchRequest = Game.fetchRequest() as NSFetchRequest<Game>
|
||||
fetchRequest.propertiesToFetch = [#keyPath(Game.name), #keyPath(Game.identifier)]
|
||||
|
||||
do
|
||||
{
|
||||
let games = try DatabaseManager.shared.viewContext.fetch(fetchRequest)
|
||||
|
||||
let gameNames = Dictionary(uniqueKeysWithValues: games.map { (RecordID(type: SyncManager.RecordType.game.rawValue, identifier: $0.identifier), $0.name) })
|
||||
return gameNames
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to fetch game names.", error)
|
||||
|
||||
return [:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension SyncResultViewController
|
||||
{
|
||||
@IBAction func dismiss()
|
||||
{
|
||||
self.presentingViewController?.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension SyncResultViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||
{
|
||||
let section = self.sortedErrors[section]
|
||||
|
||||
switch section.group
|
||||
{
|
||||
case .controllerSkin: return NSLocalizedString("Controller Skins", comment: "")
|
||||
case .gameCollection: return NSLocalizedString("Game Collections", comment: "")
|
||||
case .gameControllerInputMapping: return NSLocalizedString("Input Mappings", comment: "")
|
||||
case .other: return NSLocalizedString("Misc.", comment: "")
|
||||
|
||||
case .game:
|
||||
guard let error = section.errors.first as? RecordError else { return nil }
|
||||
return error.record.localizedName
|
||||
|
||||
case .saveState(let gameID):
|
||||
guard let error = section.errors.first as? RecordError else { return nil }
|
||||
|
||||
if let gameName = self.gameNamesByRecordID[gameID] ?? error.record.metadata?[.gameName]
|
||||
{
|
||||
return gameName + " - " + NSLocalizedString("Save States", comment: "")
|
||||
}
|
||||
else
|
||||
{
|
||||
return NSLocalizedString("Save States", comment: "")
|
||||
}
|
||||
|
||||
case .cheat(let gameID):
|
||||
guard let error = section.errors.first as? RecordError else { return nil }
|
||||
|
||||
if let gameName = self.gameNamesByRecordID[gameID] ?? error.record.metadata?[.gameName]
|
||||
{
|
||||
return gameName + " - " + NSLocalizedString("Cheats", comment: "")
|
||||
}
|
||||
else
|
||||
{
|
||||
return NSLocalizedString("Cheats", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath?
|
||||
{
|
||||
let section = self.sortedErrors[indexPath.section]
|
||||
|
||||
switch section.group
|
||||
{
|
||||
case .other: return nil
|
||||
default: return indexPath
|
||||
}
|
||||
}
|
||||
}
|
||||
102
Delta/Syncing/SyncResultsViewController.storyboard
Normal file
102
Delta/Syncing/SyncResultsViewController.storyboard
Normal file
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="PiK-Yl-5r8">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Results-->
|
||||
<scene sceneID="bU4-NU-gHn">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="syncResultViewController" id="Vv7-67-y3h" customClass="SyncResultViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="Mge-4Z-RnG">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" rowHeight="81" id="LBH-gN-Qjn" customClass="SyncResultTableViewCell">
|
||||
<rect key="frame" x="0.0" y="55.5" width="375" height="81"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="LBH-gN-Qjn" id="qeU-Pt-UrC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="80.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="4.5" translatesAutoresizingMaskIntoConstraints="NO" id="sqc-Zb-cJa">
|
||||
<rect key="frame" x="16" y="11" width="343" height="59"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Title Screen" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="E5a-nn-ak9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="343" height="20.5"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="The record could not be uploaded because an error occured." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzf-2D-v9F">
|
||||
<rect key="frame" x="0.0" y="25" width="343" height="34"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleFootnote"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="sqc-Zb-cJa" secondAttribute="trailing" id="uaR-cD-kNU"/>
|
||||
<constraint firstItem="sqc-Zb-cJa" firstAttribute="leading" secondItem="qeU-Pt-UrC" secondAttribute="leadingMargin" id="xp4-eH-qd4"/>
|
||||
<constraint firstItem="sqc-Zb-cJa" firstAttribute="top" secondItem="qeU-Pt-UrC" secondAttribute="topMargin" id="z2y-9Y-d1U"/>
|
||||
<constraint firstItem="sqc-Zb-cJa" firstAttribute="bottom" secondItem="qeU-Pt-UrC" secondAttribute="bottomMargin" id="zT9-af-Xcc"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="errorLabel" destination="gzf-2D-v9F" id="QL8-uA-FvI"/>
|
||||
<outlet property="nameLabel" destination="E5a-nn-ak9" id="iBv-cv-b6G"/>
|
||||
<segue destination="Cj8-UD-irg" kind="show" identifier="showRecordStatus" id="bKJ-Fa-4lQ"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<sections/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="Vv7-67-y3h" id="9Yf-Hl-bIM"/>
|
||||
<outlet property="delegate" destination="Vv7-67-y3h" id="fDj-g9-Whw"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Results" id="Qzj-eH-8qF">
|
||||
<barButtonItem key="rightBarButtonItem" style="done" systemItem="done" id="s6x-aF-rco">
|
||||
<connections>
|
||||
<action selector="dismiss" destination="Vv7-67-y3h" id="gW2-WJ-4ii"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="x5W-HZ-Oy6" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="365.60000000000002" y="-179.46026986506749"/>
|
||||
</scene>
|
||||
<!--recordSyncStatusViewController-->
|
||||
<scene sceneID="rqP-Wm-wlP">
|
||||
<objects>
|
||||
<viewControllerPlaceholder storyboardName="Settings" referencedIdentifier="recordSyncStatusViewController" id="Cj8-UD-irg" sceneMemberID="viewController"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="xWn-zn-1t0" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1177" y="-179"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="d6h-g9-dij">
|
||||
<objects>
|
||||
<navigationController id="PiK-Yl-5r8" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="EvV-dw-xfY">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="Vv7-67-y3h" kind="relationship" relationship="rootViewController" id="pdH-jQ-yc1"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="ems-7x-mVT" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-457" y="-178"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
1
External/Harmony
vendored
Submodule
1
External/Harmony
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 2c8225d359528177eaf698cb01afaeb399bd37ce
|
||||
2
External/Roxas
vendored
2
External/Roxas
vendored
@ -1 +1 @@
|
||||
Subproject commit 8b544e7328de5f8481b5571ebf8e0a5312d1b74a
|
||||
Subproject commit 0c1e8f6948c2c99844d05db79e8bf4e55d3211ba
|
||||
1
Podfile
1
Podfile
@ -4,7 +4,6 @@ use_frameworks!
|
||||
inhibit_all_warnings!
|
||||
|
||||
target 'Delta' do
|
||||
pod 'FileMD5Hash', '~> 2.0.0'
|
||||
pod 'SQLite.swift', '~> 0.11.0'
|
||||
pod 'SDWebImage', '~> 3.8'
|
||||
pod 'Fabric', '~> 1.6.0'
|
||||
|
||||
17
Podfile.lock
17
Podfile.lock
@ -2,7 +2,6 @@ PODS:
|
||||
- Crashlytics (3.8.6):
|
||||
- Fabric (~> 1.6.3)
|
||||
- Fabric (1.6.13)
|
||||
- FileMD5Hash (2.0.0)
|
||||
- SDWebImage (3.8.2):
|
||||
- SDWebImage/Core (= 3.8.2)
|
||||
- SDWebImage/Core (3.8.2)
|
||||
@ -14,19 +13,25 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- Crashlytics (~> 3.8.0)
|
||||
- Fabric (~> 1.6.0)
|
||||
- FileMD5Hash (~> 2.0.0)
|
||||
- SDWebImage (~> 3.8)
|
||||
- SMCalloutView
|
||||
- SQLite.swift (~> 0.11.0)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- Crashlytics
|
||||
- Fabric
|
||||
- SDWebImage
|
||||
- SMCalloutView
|
||||
- SQLite.swift
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Crashlytics: 95d05f4e4c19a771250c4bd9ce344d996de32bbf
|
||||
Fabric: 2fb5676bc811af011a04513451f463dac6803206
|
||||
FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0
|
||||
SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c'
|
||||
SDWebImage: 098e97e6176540799c27e804c96653ee0833d13c
|
||||
SMCalloutView: 5c0ee363dc8e7204b2fda17dfad38c93e9e23481
|
||||
SQLite.swift: 3e3bee21da701b5b9f87c4a672cb54f233505692
|
||||
|
||||
PODFILE CHECKSUM: a1fb0ce1f1bb0e73380460cc4f946d297a4d5ff1
|
||||
PODFILE CHECKSUM: 1d7f9ff69da571c7991621312e07aa4b16a0a074
|
||||
|
||||
COCOAPODS: 1.2.1
|
||||
COCOAPODS: 1.5.3
|
||||
|
||||
202
Pods/FileMD5Hash/LICENSE
generated
202
Pods/FileMD5Hash/LICENSE
generated
@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
29
Pods/FileMD5Hash/Library/FileHash.h
generated
29
Pods/FileMD5Hash/Library/FileHash.h
generated
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* FileHash.h
|
||||
* FileMD5Hash
|
||||
*
|
||||
* Copyright © 2010-2014 Joel Lopes Da Silva. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface FileHash : NSObject
|
||||
|
||||
+ (NSString *)md5HashOfFileAtPath:(NSString *)filePath;
|
||||
+ (NSString *)sha1HashOfFileAtPath:(NSString *)filePath;
|
||||
+ (NSString *)sha512HashOfFileAtPath:(NSString *)filePath;
|
||||
|
||||
@end
|
||||
127
Pods/FileMD5Hash/Library/FileHash.m
generated
127
Pods/FileMD5Hash/Library/FileHash.m
generated
@ -1,127 +0,0 @@
|
||||
/*
|
||||
* FileHash.c
|
||||
* FileMD5Hash
|
||||
*
|
||||
* Copyright © 2010-2014 Joel Lopes Da Silva. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Header file
|
||||
#import "FileHash.h"
|
||||
|
||||
// System framework and libraries
|
||||
#include <CommonCrypto/CommonDigest.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Constants
|
||||
static const size_t FileHashDefaultChunkSizeForReadingData = 4096;
|
||||
|
||||
// Function pointer types for functions used in the computation
|
||||
// of a cryptographic hash.
|
||||
typedef int (*FileHashInitFunction) (uint8_t *hashObjectPointer[]);
|
||||
typedef int (*FileHashUpdateFunction) (uint8_t *hashObjectPointer[], const void *data, CC_LONG len);
|
||||
typedef int (*FileHashFinalFunction) (unsigned char *md, uint8_t *hashObjectPointer[]);
|
||||
|
||||
// Structure used to describe a hash computation context.
|
||||
typedef struct _FileHashComputationContext {
|
||||
FileHashInitFunction initFunction;
|
||||
FileHashUpdateFunction updateFunction;
|
||||
FileHashFinalFunction finalFunction;
|
||||
size_t digestLength;
|
||||
uint8_t **hashObjectPointer;
|
||||
} FileHashComputationContext;
|
||||
|
||||
#define FileHashComputationContextInitialize(context, hashAlgorithmName) \
|
||||
CC_##hashAlgorithmName##_CTX hashObjectFor##hashAlgorithmName; \
|
||||
context.initFunction = (FileHashInitFunction)&CC_##hashAlgorithmName##_Init; \
|
||||
context.updateFunction = (FileHashUpdateFunction)&CC_##hashAlgorithmName##_Update; \
|
||||
context.finalFunction = (FileHashFinalFunction)&CC_##hashAlgorithmName##_Final; \
|
||||
context.digestLength = CC_##hashAlgorithmName##_DIGEST_LENGTH; \
|
||||
context.hashObjectPointer = (uint8_t **)&hashObjectFor##hashAlgorithmName
|
||||
|
||||
|
||||
@implementation FileHash
|
||||
|
||||
+ (NSString *)hashOfFileAtPath:(NSString *)filePath withComputationContext:(FileHashComputationContext *)context {
|
||||
NSString *result = nil;
|
||||
CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filePath, kCFURLPOSIXPathStyle, (Boolean)false);
|
||||
CFReadStreamRef readStream = fileURL ? CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL) : NULL;
|
||||
BOOL didSucceed = readStream ? (BOOL)CFReadStreamOpen(readStream) : NO;
|
||||
if (didSucceed) {
|
||||
|
||||
// Use default value for the chunk size for reading data.
|
||||
const size_t chunkSizeForReadingData = FileHashDefaultChunkSizeForReadingData;
|
||||
|
||||
// Initialize the hash object
|
||||
(*context->initFunction)(context->hashObjectPointer);
|
||||
|
||||
// Feed the data to the hash object.
|
||||
BOOL hasMoreData = YES;
|
||||
while (hasMoreData) {
|
||||
uint8_t buffer[chunkSizeForReadingData];
|
||||
CFIndex readBytesCount = CFReadStreamRead(readStream, (UInt8 *)buffer, (CFIndex)sizeof(buffer));
|
||||
if (readBytesCount == -1) {
|
||||
break;
|
||||
} else if (readBytesCount == 0) {
|
||||
hasMoreData = NO;
|
||||
} else {
|
||||
(*context->updateFunction)(context->hashObjectPointer, (const void *)buffer, (CC_LONG)readBytesCount);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the hash digest
|
||||
unsigned char digest[context->digestLength];
|
||||
(*context->finalFunction)(digest, context->hashObjectPointer);
|
||||
|
||||
// Close the read stream.
|
||||
CFReadStreamClose(readStream);
|
||||
|
||||
// Proceed if the read operation succeeded.
|
||||
didSucceed = !hasMoreData;
|
||||
if (didSucceed) {
|
||||
char hash[2 * sizeof(digest) + 1];
|
||||
for (size_t i = 0; i < sizeof(digest); ++i) {
|
||||
snprintf(hash + (2 * i), 3, "%02x", (int)(digest[i]));
|
||||
}
|
||||
result = [NSString stringWithUTF8String:hash];
|
||||
}
|
||||
|
||||
}
|
||||
if (readStream) CFRelease(readStream);
|
||||
if (fileURL) CFRelease(fileURL);
|
||||
return result;
|
||||
}
|
||||
|
||||
+ (NSString *)md5HashOfFileAtPath:(NSString *)filePath {
|
||||
FileHashComputationContext context;
|
||||
FileHashComputationContextInitialize(context, MD5);
|
||||
return [self hashOfFileAtPath:filePath withComputationContext:&context];
|
||||
}
|
||||
|
||||
+ (NSString *)sha1HashOfFileAtPath:(NSString *)filePath {
|
||||
FileHashComputationContext context;
|
||||
FileHashComputationContextInitialize(context, SHA1);
|
||||
return [self hashOfFileAtPath:filePath withComputationContext:&context];
|
||||
}
|
||||
|
||||
+ (NSString *)sha512HashOfFileAtPath:(NSString *)filePath {
|
||||
FileHashComputationContext context;
|
||||
FileHashComputationContextInitialize(context, SHA512);
|
||||
return [self hashOfFileAtPath:filePath withComputationContext:&context];
|
||||
}
|
||||
|
||||
@end
|
||||
3
Pods/FileMD5Hash/README.md
generated
3
Pods/FileMD5Hash/README.md
generated
@ -1,3 +0,0 @@
|
||||
Taken from [Joel's Writings](http://www.joel.lopes-da-silva.com/2010/09/07/compute-md5-or-sha-hash-of-large-file-efficiently-on-ios-and-mac-os-x/).
|
||||
|
||||
For more information and instructions on how to use, please refer to the [original blog post describing FileMD5Hash](http://www.joel.lopes-da-silva.com/2010/09/07/compute-md5-or-sha-hash-of-large-file-efficiently-on-ios-and-mac-os-x/).
|
||||
17
Pods/Manifest.lock
generated
17
Pods/Manifest.lock
generated
@ -2,7 +2,6 @@ PODS:
|
||||
- Crashlytics (3.8.6):
|
||||
- Fabric (~> 1.6.3)
|
||||
- Fabric (1.6.13)
|
||||
- FileMD5Hash (2.0.0)
|
||||
- SDWebImage (3.8.2):
|
||||
- SDWebImage/Core (= 3.8.2)
|
||||
- SDWebImage/Core (3.8.2)
|
||||
@ -14,19 +13,25 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- Crashlytics (~> 3.8.0)
|
||||
- Fabric (~> 1.6.0)
|
||||
- FileMD5Hash (~> 2.0.0)
|
||||
- SDWebImage (~> 3.8)
|
||||
- SMCalloutView
|
||||
- SQLite.swift (~> 0.11.0)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/cocoapods/specs.git:
|
||||
- Crashlytics
|
||||
- Fabric
|
||||
- SDWebImage
|
||||
- SMCalloutView
|
||||
- SQLite.swift
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
Crashlytics: 95d05f4e4c19a771250c4bd9ce344d996de32bbf
|
||||
Fabric: 2fb5676bc811af011a04513451f463dac6803206
|
||||
FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0
|
||||
SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c'
|
||||
SDWebImage: 098e97e6176540799c27e804c96653ee0833d13c
|
||||
SMCalloutView: 5c0ee363dc8e7204b2fda17dfad38c93e9e23481
|
||||
SQLite.swift: 3e3bee21da701b5b9f87c4a672cb54f233505692
|
||||
|
||||
PODFILE CHECKSUM: a1fb0ce1f1bb0e73380460cc4f946d297a4d5ff1
|
||||
PODFILE CHECKSUM: 1d7f9ff69da571c7991621312e07aa4b16a0a074
|
||||
|
||||
COCOAPODS: 1.2.1
|
||||
COCOAPODS: 1.5.3
|
||||
|
||||
1573
Pods/Pods.xcodeproj/project.pbxproj
generated
1573
Pods/Pods.xcodeproj/project.pbxproj
generated
File diff suppressed because it is too large
Load Diff
@ -1,5 +0,0 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_FileMD5Hash : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_FileMD5Hash
|
||||
@end
|
||||
@ -1,12 +0,0 @@
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#import "FileHash.h"
|
||||
|
||||
FOUNDATION_EXPORT double FileMD5HashVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char FileMD5HashVersionString[];
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
framework module FileMD5Hash {
|
||||
umbrella header "FileMD5Hash-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
@ -1,9 +0,0 @@
|
||||
CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/FileMD5Hash
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Crashlytics" "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
PODS_BUILD_DIR = $BUILD_DIR
|
||||
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/FileMD5Hash
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
26
Pods/Target Support Files/FileMD5Hash/Info.plist
generated
26
Pods/Target Support Files/FileMD5Hash/Info.plist
generated
@ -1,26 +0,0 @@
|
||||
<?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>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -9,212 +9,6 @@ Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is
|
||||
|
||||
Fabric: Copyright 2017 Google, Inc. All Rights Reserved. Use of this software is subject to the terms and conditions of the Fabric Software and Services Agreement located at https://fabric.io/terms. OSS: http://get.fabric.io/terms/opensource.txt
|
||||
|
||||
## FileMD5Hash
|
||||
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
## SDWebImage
|
||||
|
||||
Copyright (c) 2016 Olivier Poitrey rs@dailymotion.com
|
||||
|
||||
@ -32,218 +32,6 @@
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>Apache License, Version 2.0</string>
|
||||
<key>Title</key>
|
||||
<string>FileMD5Hash</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Copyright (c) 2016 Olivier Poitrey rs@dailymotion.com
|
||||
|
||||
@ -1,11 +1,28 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then
|
||||
# If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy
|
||||
# frameworks to, so exit 0 (signalling the script phase was successful).
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
|
||||
COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}"
|
||||
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
|
||||
|
||||
# Used as a return value for each invocation of `strip_invalid_archs` function.
|
||||
STRIP_BINARY_RETVAL=0
|
||||
|
||||
# This protects against multiple targets copying the same framework dependency at the same time. The solution
|
||||
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
|
||||
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
|
||||
|
||||
# Copies and strips a vendored framework
|
||||
install_framework()
|
||||
{
|
||||
if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then
|
||||
@ -23,9 +40,9 @@ install_framework()
|
||||
source="$(readlink "${source}")"
|
||||
fi
|
||||
|
||||
# use filter instead of exclude so missing patterns dont' throw errors
|
||||
echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
|
||||
rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
|
||||
# Use filter instead of exclude so missing patterns don't throw errors.
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\""
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}"
|
||||
|
||||
local basename
|
||||
basename="$(basename -s .framework "$1")"
|
||||
@ -54,12 +71,40 @@ install_framework()
|
||||
fi
|
||||
}
|
||||
|
||||
# Copies and strips a vendored dSYM
|
||||
install_dsym() {
|
||||
local source="$1"
|
||||
if [ -r "$source" ]; then
|
||||
# Copy the dSYM into a the targets temp dir.
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\""
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}"
|
||||
|
||||
local basename
|
||||
basename="$(basename -s .framework.dSYM "$source")"
|
||||
binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}"
|
||||
|
||||
# Strip invalid architectures so "fat" simulator / device frameworks work on device
|
||||
if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then
|
||||
strip_invalid_archs "$binary"
|
||||
fi
|
||||
|
||||
if [[ $STRIP_BINARY_RETVAL == 1 ]]; then
|
||||
# Move the stripped file into its final destination.
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\""
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}"
|
||||
else
|
||||
# The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing.
|
||||
touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Signs a framework with the provided identity
|
||||
code_sign_if_enabled() {
|
||||
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
|
||||
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then
|
||||
# Use the current code_sign_identitiy
|
||||
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
|
||||
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'"
|
||||
local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'"
|
||||
|
||||
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
|
||||
code_sign_cmd="$code_sign_cmd &"
|
||||
@ -72,11 +117,19 @@ code_sign_if_enabled() {
|
||||
# Strip invalid architectures
|
||||
strip_invalid_archs() {
|
||||
binary="$1"
|
||||
# Get architectures for current file
|
||||
archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)"
|
||||
# Get architectures for current target binary
|
||||
binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)"
|
||||
# Intersect them with the architectures we are building for
|
||||
intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)"
|
||||
# If there are no archs supported by this binary then warn the user
|
||||
if [[ -z "$intersected_archs" ]]; then
|
||||
echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)."
|
||||
STRIP_BINARY_RETVAL=0
|
||||
return
|
||||
fi
|
||||
stripped=""
|
||||
for arch in $archs; do
|
||||
if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then
|
||||
for arch in $binary_archs; do
|
||||
if ! [[ "${ARCHS}" == *"$arch"* ]]; then
|
||||
# Strip non-valid architectures in-place
|
||||
lipo -remove "$arch" -output "$binary" "$binary" || exit 1
|
||||
stripped="$stripped $arch"
|
||||
@ -85,20 +138,19 @@ strip_invalid_archs() {
|
||||
if [[ "$stripped" ]]; then
|
||||
echo "Stripped $binary of architectures:$stripped"
|
||||
fi
|
||||
STRIP_BINARY_RETVAL=1
|
||||
}
|
||||
|
||||
|
||||
if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||
install_framework "$BUILT_PRODUCTS_DIR/FileMD5Hash/FileMD5Hash.framework"
|
||||
install_framework "$BUILT_PRODUCTS_DIR/SDWebImage/SDWebImage.framework"
|
||||
install_framework "$BUILT_PRODUCTS_DIR/SMCalloutView/SMCalloutView.framework"
|
||||
install_framework "$BUILT_PRODUCTS_DIR/SQLite.swift/SQLite.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SMCalloutView/SMCalloutView.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SQLite.swift/SQLite.framework"
|
||||
fi
|
||||
if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||
install_framework "$BUILT_PRODUCTS_DIR/FileMD5Hash/FileMD5Hash.framework"
|
||||
install_framework "$BUILT_PRODUCTS_DIR/SDWebImage/SDWebImage.framework"
|
||||
install_framework "$BUILT_PRODUCTS_DIR/SMCalloutView/SMCalloutView.framework"
|
||||
install_framework "$BUILT_PRODUCTS_DIR/SQLite.swift/SQLite.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SDWebImage/SDWebImage.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SMCalloutView/SMCalloutView.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SQLite.swift/SQLite.framework"
|
||||
fi
|
||||
if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then
|
||||
wait
|
||||
|
||||
@ -1,5 +1,13 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then
|
||||
# If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy
|
||||
# resources to, so exit 0 (signalling the script phase was successful).
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
|
||||
@ -8,7 +16,11 @@ RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt
|
||||
|
||||
XCASSET_FILES=()
|
||||
|
||||
case "${TARGETED_DEVICE_FAMILY}" in
|
||||
# This protects against multiple targets copying the same framework dependency at the same time. The solution
|
||||
# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html
|
||||
RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????")
|
||||
|
||||
case "${TARGETED_DEVICE_FAMILY:-}" in
|
||||
1,2)
|
||||
TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone"
|
||||
;;
|
||||
@ -44,29 +56,29 @@ EOM
|
||||
fi
|
||||
case $RESOURCE_PATH in
|
||||
*.storyboard)
|
||||
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
|
||||
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
|
||||
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
|
||||
;;
|
||||
*.xib)
|
||||
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}"
|
||||
echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true
|
||||
ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS}
|
||||
;;
|
||||
*.framework)
|
||||
echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
|
||||
mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true
|
||||
rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
|
||||
;;
|
||||
*.xcdatamodel)
|
||||
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\""
|
||||
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true
|
||||
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom"
|
||||
;;
|
||||
*.xcdatamodeld)
|
||||
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\""
|
||||
echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true
|
||||
xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd"
|
||||
;;
|
||||
*.xcmappingmodel)
|
||||
echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\""
|
||||
echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true
|
||||
xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm"
|
||||
;;
|
||||
*.xcassets)
|
||||
@ -74,7 +86,7 @@ EOM
|
||||
XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE")
|
||||
;;
|
||||
*)
|
||||
echo "$RESOURCE_PATH"
|
||||
echo "$RESOURCE_PATH" || true
|
||||
echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY"
|
||||
;;
|
||||
esac
|
||||
@ -88,7 +100,7 @@ if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then
|
||||
fi
|
||||
rm -f "$RESOURCES_TO_COPY"
|
||||
|
||||
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ]
|
||||
if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ]
|
||||
then
|
||||
# Find all other xcassets (this unfortunately includes those of path pods and other targets).
|
||||
OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d)
|
||||
@ -98,5 +110,9 @@ then
|
||||
fi
|
||||
done <<<"$OTHER_XCASSETS"
|
||||
|
||||
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then
|
||||
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}"
|
||||
else
|
||||
printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist"
|
||||
fi
|
||||
fi
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/FileMD5Hash" "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage" "$PODS_CONFIGURATION_BUILD_DIR/SMCalloutView" "$PODS_CONFIGURATION_BUILD_DIR/SQLite.swift" "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView" "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift" "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Crashlytics" "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/FileMD5Hash/FileMD5Hash.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage/SDWebImage.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SMCalloutView/SMCalloutView.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SQLite.swift/SQLite.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Crashlytics" -isystem "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"z" -framework "Crashlytics" -framework "Fabric" -framework "FileMD5Hash" -framework "SDWebImage" -framework "SMCalloutView" -framework "SQLite" -framework "Security" -framework "SystemConfiguration" -framework "UIKit"
|
||||
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView/SMCalloutView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift/SQLite.framework/Headers"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"z" -framework "Crashlytics" -framework "Fabric" -framework "SDWebImage" -framework "SMCalloutView" -framework "SQLite" -framework "Security" -framework "SystemConfiguration" -framework "UIKit"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_BUILD_DIR = $BUILD_DIR
|
||||
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/FileMD5Hash" "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage" "$PODS_CONFIGURATION_BUILD_DIR/SMCalloutView" "$PODS_CONFIGURATION_BUILD_DIR/SQLite.swift" "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView" "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift" "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Crashlytics" "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/FileMD5Hash/FileMD5Hash.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SDWebImage/SDWebImage.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SMCalloutView/SMCalloutView.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SQLite.swift/SQLite.framework/Headers" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/Crashlytics" -isystem "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"z" -framework "Crashlytics" -framework "Fabric" -framework "FileMD5Hash" -framework "SDWebImage" -framework "SMCalloutView" -framework "SQLite" -framework "Security" -framework "SystemConfiguration" -framework "UIKit"
|
||||
OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView/SMCalloutView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift/SQLite.framework/Headers"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"z" -framework "Crashlytics" -framework "Fabric" -framework "SDWebImage" -framework "SMCalloutView" -framework "SQLite" -framework "Security" -framework "SystemConfiguration" -framework "UIKit"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS"
|
||||
PODS_BUILD_DIR = $BUILD_DIR
|
||||
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
|
||||
PODS_ROOT = ${SRCROOT}/Pods
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SDWebImage
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Crashlytics" "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
OTHER_LDFLAGS = -framework "ImageIO"
|
||||
PODS_BUILD_DIR = $BUILD_DIR
|
||||
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SDWebImage
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SMCalloutView
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Crashlytics" "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
PODS_BUILD_DIR = $BUILD_DIR
|
||||
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SMCalloutView
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SQLite.swift
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/Crashlytics" "${PODS_ROOT}/Headers/Public/Fabric"
|
||||
OTHER_LDFLAGS = -l"sqlite3"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" "-suppress-warnings"
|
||||
PODS_BUILD_DIR = $BUILD_DIR
|
||||
PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/SQLite.swift
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
|
||||
6
Resources/Assets.xcassets/Colors/Contents.json
Normal file
6
Resources/Assets.xcassets/Colors/Contents.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0.150",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.150",
|
||||
"green" : "0.150"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "139",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "247",
|
||||
"green" : "40"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user