mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-19 11:43:24 +01:00
[AltStore] Adds redesigned MyAppsViewController to refresh/update installed apps
This commit is contained in:
@@ -11,7 +11,10 @@
|
|||||||
BF0201BB22C2EFA3000B93E4 /* AltSign.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF0201BB22C2EFA3000B93E4 /* AltSign.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
BF0201BD22C2EFBC000B93E4 /* openssl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF4713A422976CFC00784A2F /* openssl.framework */; };
|
BF0201BD22C2EFBC000B93E4 /* openssl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF4713A422976CFC00784A2F /* openssl.framework */; };
|
||||||
BF0201BE22C2EFBC000B93E4 /* openssl.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF4713A422976CFC00784A2F /* openssl.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF0201BE22C2EFBC000B93E4 /* openssl.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF4713A422976CFC00784A2F /* openssl.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
BF08858322DE795100DE9F1E /* MyAppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF08858222DE795100DE9F1E /* MyAppsViewController.swift */; };
|
||||||
|
BF08858522DE7EC800DE9F1E /* UpdateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF08858422DE7EC800DE9F1E /* UpdateCollectionViewCell.swift */; };
|
||||||
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */; };
|
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */; };
|
||||||
|
BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18B0F022E25DF9005C4CF5 /* ToastView.swift */; };
|
||||||
BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* ConnectionManager.swift */; };
|
BF1E312B229F474900370A3C /* ConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* ConnectionManager.swift */; };
|
||||||
BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; };
|
BF1E315722A061F500370A3C /* ServerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3128229F474900370A3C /* ServerProtocol.swift */; };
|
||||||
BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; };
|
BF1E315822A061F900370A3C /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAC8852295C90300587369 /* Result+Conveniences.swift */; };
|
||||||
@@ -104,19 +107,17 @@
|
|||||||
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */; };
|
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */; };
|
||||||
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */; };
|
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */; };
|
||||||
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4A22DD137F008935CF /* NavigationBar.swift */; };
|
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4A22DD137F008935CF /* NavigationBar.swift */; };
|
||||||
BF9ABA4D22DD16DE008935CF /* ProgressButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4C22DD16DE008935CF /* ProgressButton.swift */; };
|
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4C22DD16DE008935CF /* PillButton.swift */; };
|
||||||
BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */; };
|
BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */; };
|
||||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB11691229322E400BB457C /* DatabaseManager.swift */; };
|
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB11691229322E400BB457C /* DatabaseManager.swift */; };
|
||||||
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */; };
|
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */; };
|
||||||
BFB1169D22932DB100BB457C /* Apps-Dev.json in Resources */ = {isa = PBXBuildFile; fileRef = BFB1169C22932DB100BB457C /* Apps-Dev.json */; };
|
BFB1169D22932DB100BB457C /* Apps-Dev.json in Resources */ = {isa = PBXBuildFile; fileRef = BFB1169C22932DB100BB457C /* Apps-Dev.json */; };
|
||||||
BFB116A022933DEB00BB457C /* UpdatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB1169F22933DEB00BB457C /* UpdatesViewController.swift */; };
|
BFB4323F22DE852000B7F8BC /* UpdateCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFB4323E22DE852000B7F8BC /* UpdateCollectionViewCell.xib */; };
|
||||||
BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */; };
|
BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */; };
|
||||||
BFBBE2DF22931F73002097FA /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DE22931F73002097FA /* App.swift */; };
|
BFBBE2DF22931F73002097FA /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DE22931F73002097FA /* App.swift */; };
|
||||||
BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2E022931F81002097FA /* InstalledApp.swift */; };
|
BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2E022931F81002097FA /* InstalledApp.swift */; };
|
||||||
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */; };
|
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */; };
|
||||||
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476D2284B9A500981D42 /* AppDelegate.swift */; };
|
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476D2284B9A500981D42 /* AppDelegate.swift */; };
|
||||||
BFD247702284B9A500981D42 /* AppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476F2284B9A500981D42 /* AppsViewController.swift */; };
|
|
||||||
BFD247722284B9A500981D42 /* MyAppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD247712284B9A500981D42 /* MyAppsViewController.swift */; };
|
|
||||||
BFD247752284B9A500981D42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247732284B9A500981D42 /* Main.storyboard */; };
|
BFD247752284B9A500981D42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247732284B9A500981D42 /* Main.storyboard */; };
|
||||||
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFD247762284B9A700981D42 /* Assets.xcassets */; };
|
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFD247762284B9A700981D42 /* Assets.xcassets */; };
|
||||||
BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247782284B9A700981D42 /* LaunchScreen.storyboard */; };
|
BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247782284B9A700981D42 /* LaunchScreen.storyboard */; };
|
||||||
@@ -124,7 +125,6 @@
|
|||||||
BFD247882284BB4200981D42 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFD247862284BB3B00981D42 /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BFD247882284BB4200981D42 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFD247862284BB3B00981D42 /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
BFD2478C2284C4C300981D42 /* AppIconImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478B2284C4C300981D42 /* AppIconImageView.swift */; };
|
BFD2478C2284C4C300981D42 /* AppIconImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478B2284C4C300981D42 /* AppIconImageView.swift */; };
|
||||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478E2284C8F900981D42 /* Button.swift */; };
|
BFD2478F2284C8F900981D42 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478E2284C8F900981D42 /* Button.swift */; };
|
||||||
BFD247932284D4B700981D42 /* AppTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD247922284D4B700981D42 /* AppTableViewCell.swift */; };
|
|
||||||
BFD2479C2284E19A00981D42 /* AppDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */; };
|
BFD2479C2284E19A00981D42 /* AppDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */; };
|
||||||
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */; };
|
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */; };
|
||||||
BFD52BD422A0800A000B7ED1 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD52BD322A0800A000B7ED1 /* ServerManager.swift */; };
|
BFD52BD422A0800A000B7ED1 /* ServerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD52BD322A0800A000B7ED1 /* ServerManager.swift */; };
|
||||||
@@ -159,6 +159,7 @@
|
|||||||
BFD52C2022A1A9EC000B7ED1 /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1D22A1A9EC000B7ED1 /* node.c */; };
|
BFD52C2022A1A9EC000B7ED1 /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1D22A1A9EC000B7ED1 /* node.c */; };
|
||||||
BFD52C2122A1A9EC000B7ED1 /* node_list.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1E22A1A9EC000B7ED1 /* node_list.c */; };
|
BFD52C2122A1A9EC000B7ED1 /* node_list.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1E22A1A9EC000B7ED1 /* node_list.c */; };
|
||||||
BFD52C2222A1A9EC000B7ED1 /* cnary.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1F22A1A9EC000B7ED1 /* cnary.c */; };
|
BFD52C2222A1A9EC000B7ED1 /* cnary.c in Sources */ = {isa = PBXBuildFile; fileRef = BFD52C1F22A1A9EC000B7ED1 /* cnary.c */; };
|
||||||
|
BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */; };
|
||||||
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */; };
|
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */; };
|
||||||
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */; };
|
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */; };
|
||||||
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */; };
|
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */; };
|
||||||
@@ -239,7 +240,10 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1039C07E517311FC499A0B64 /* Pods_AltStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
1039C07E517311FC499A0B64 /* Pods_AltStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AltStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A136EE677716B80768E9F0A2 /* Pods-AltStore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStore.release.xcconfig"; path = "Target Support Files/Pods-AltStore/Pods-AltStore.release.xcconfig"; sourceTree = "<group>"; };
|
A136EE677716B80768E9F0A2 /* Pods-AltStore.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AltStore.release.xcconfig"; path = "Target Support Files/Pods-AltStore/Pods-AltStore.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
BF08858222DE795100DE9F1E /* MyAppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF08858422DE7EC800DE9F1E /* UpdateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
|
BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
|
||||||
|
BF18B0F022E25DF9005C4CF5 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = "<group>"; };
|
||||||
BF1E3128229F474900370A3C /* ServerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerProtocol.swift; sourceTree = "<group>"; };
|
BF1E3128229F474900370A3C /* ServerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerProtocol.swift; sourceTree = "<group>"; };
|
||||||
BF1E3129229F474900370A3C /* ConnectionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = "<group>"; };
|
BF1E3129229F474900370A3C /* ConnectionManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionManager.swift; sourceTree = "<group>"; };
|
||||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+AltStore.swift"; sourceTree = "<group>"; };
|
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+AltStore.swift"; sourceTree = "<group>"; };
|
||||||
@@ -338,13 +342,13 @@
|
|||||||
BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseCollectionViewCell.swift; sourceTree = "<group>"; };
|
BF9ABA4622DD0638008935CF /* BrowseCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotCollectionViewCell.swift; sourceTree = "<group>"; };
|
BF9ABA4822DD0742008935CF /* ScreenshotCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenshotCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
BF9ABA4A22DD137F008935CF /* NavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = "<group>"; };
|
BF9ABA4A22DD137F008935CF /* NavigationBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = "<group>"; };
|
||||||
BF9ABA4C22DD16DE008935CF /* ProgressButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressButton.swift; sourceTree = "<group>"; };
|
BF9ABA4C22DD16DE008935CF /* PillButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillButton.swift; sourceTree = "<group>"; };
|
||||||
BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = "<group>"; };
|
BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Hex.swift"; sourceTree = "<group>"; };
|
||||||
BF9B63C5229DD44D002F0A62 /* AltSign.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AltSign.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BF9B63C5229DD44D002F0A62 /* AltSign.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AltSign.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFB11691229322E400BB457C /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
|
BFB11691229322E400BB457C /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
|
||||||
BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+ManagedObjectContext.swift"; sourceTree = "<group>"; };
|
BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+ManagedObjectContext.swift"; sourceTree = "<group>"; };
|
||||||
BFB1169C22932DB100BB457C /* Apps-Dev.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Apps-Dev.json"; sourceTree = "<group>"; };
|
BFB1169C22932DB100BB457C /* Apps-Dev.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "Apps-Dev.json"; sourceTree = "<group>"; };
|
||||||
BFB1169F22933DEB00BB457C /* UpdatesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdatesViewController.swift; sourceTree = "<group>"; };
|
BFB4323E22DE852000B7F8BC /* UpdateCollectionViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UpdateCollectionViewCell.xib; sourceTree = "<group>"; };
|
||||||
BFBAC8852295C90300587369 /* Result+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Conveniences.swift"; sourceTree = "<group>"; };
|
BFBAC8852295C90300587369 /* Result+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Result+Conveniences.swift"; sourceTree = "<group>"; };
|
||||||
BFBBE2DC22931B20002097FA /* AltStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AltStore.xcdatamodel; sourceTree = "<group>"; };
|
BFBBE2DC22931B20002097FA /* AltStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AltStore.xcdatamodel; sourceTree = "<group>"; };
|
||||||
BFBBE2DE22931F73002097FA /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
BFBBE2DE22931F73002097FA /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
||||||
@@ -352,8 +356,6 @@
|
|||||||
BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadAppOperation.swift; sourceTree = "<group>"; };
|
BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadAppOperation.swift; sourceTree = "<group>"; };
|
||||||
BFD2476A2284B9A500981D42 /* AltStore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AltStore.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFD2476A2284B9A500981D42 /* AltStore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AltStore.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFD2476D2284B9A500981D42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
BFD2476D2284B9A500981D42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
BFD2476F2284B9A500981D42 /* AppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsViewController.swift; sourceTree = "<group>"; };
|
|
||||||
BFD247712284B9A500981D42 /* MyAppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewController.swift; sourceTree = "<group>"; };
|
|
||||||
BFD247742284B9A500981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
BFD247742284B9A500981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
BFD247762284B9A700981D42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
BFD247762284B9A700981D42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
BFD247792284B9A700981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
BFD247792284B9A700981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
@@ -361,7 +363,6 @@
|
|||||||
BFD247862284BB3B00981D42 /* Roxas.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFD247862284BB3B00981D42 /* Roxas.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFD2478B2284C4C300981D42 /* AppIconImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconImageView.swift; sourceTree = "<group>"; };
|
BFD2478B2284C4C300981D42 /* AppIconImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconImageView.swift; sourceTree = "<group>"; };
|
||||||
BFD2478E2284C8F900981D42 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
BFD2478E2284C8F900981D42 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
||||||
BFD247922284D4B700981D42 /* AppTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTableViewCell.swift; sourceTree = "<group>"; };
|
|
||||||
BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailViewController.swift; sourceTree = "<group>"; };
|
BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailViewController.swift; sourceTree = "<group>"; };
|
||||||
BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+AltStore.swift"; sourceTree = "<group>"; };
|
BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+AltStore.swift"; sourceTree = "<group>"; };
|
||||||
BFD52BD222A06EFB000B7ED1 /* AltKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AltKit.h; sourceTree = "<group>"; };
|
BFD52BD222A06EFB000B7ED1 /* AltKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AltKit.h; sourceTree = "<group>"; };
|
||||||
@@ -397,6 +398,7 @@
|
|||||||
BFD52C1D22A1A9EC000B7ED1 /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node.c; path = Dependencies/libplist/libcnary/node.c; sourceTree = SOURCE_ROOT; };
|
BFD52C1D22A1A9EC000B7ED1 /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node.c; path = Dependencies/libplist/libcnary/node.c; sourceTree = SOURCE_ROOT; };
|
||||||
BFD52C1E22A1A9EC000B7ED1 /* node_list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node_list.c; path = Dependencies/libplist/libcnary/node_list.c; sourceTree = SOURCE_ROOT; };
|
BFD52C1E22A1A9EC000B7ED1 /* node_list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = node_list.c; path = Dependencies/libplist/libcnary/node_list.c; sourceTree = SOURCE_ROOT; };
|
||||||
BFD52C1F22A1A9EC000B7ED1 /* cnary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cnary.c; path = Dependencies/libplist/libcnary/cnary.c; sourceTree = SOURCE_ROOT; };
|
BFD52C1F22A1A9EC000B7ED1 /* cnary.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = cnary.c; path = Dependencies/libplist/libcnary/cnary.c; sourceTree = SOURCE_ROOT; };
|
||||||
|
BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsComponents.swift; sourceTree = "<group>"; };
|
||||||
BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
|
BFDB69FC22A9A7B7007EA6D6 /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = "<group>"; };
|
||||||
BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = "<group>"; };
|
BFDB6A0422A9AFB2007EA6D6 /* Fetchable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fetchable.swift; sourceTree = "<group>"; };
|
||||||
BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignAppOperation.swift; sourceTree = "<group>"; };
|
BFDB6A0722AAED73007EA6D6 /* ResignAppOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResignAppOperation.swift; sourceTree = "<group>"; };
|
||||||
@@ -667,18 +669,13 @@
|
|||||||
path = Browse;
|
path = Browse;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BFB1169E22933DDC00BB457C /* Updates */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
BFB1169F22933DEB00BB457C /* UpdatesViewController.swift */,
|
|
||||||
);
|
|
||||||
path = Updates;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
BFBBE2E2229320A2002097FA /* My Apps */ = {
|
BFBBE2E2229320A2002097FA /* My Apps */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BFD247712284B9A500981D42 /* MyAppsViewController.swift */,
|
BF08858222DE795100DE9F1E /* MyAppsViewController.swift */,
|
||||||
|
BFD6B03222DFF20800B86064 /* MyAppsComponents.swift */,
|
||||||
|
BF08858422DE7EC800DE9F1E /* UpdateCollectionViewCell.swift */,
|
||||||
|
BFB4323E22DE852000B7F8BC /* UpdateCollectionViewCell.xib */,
|
||||||
);
|
);
|
||||||
path = "My Apps";
|
path = "My Apps";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -726,7 +723,6 @@
|
|||||||
BF9ABA4322DCFF33008935CF /* Browse */,
|
BF9ABA4322DCFF33008935CF /* Browse */,
|
||||||
BFD2478A2284C49000981D42 /* Apps */,
|
BFD2478A2284C49000981D42 /* Apps */,
|
||||||
BFBBE2E2229320A2002097FA /* My Apps */,
|
BFBBE2E2229320A2002097FA /* My Apps */,
|
||||||
BFB1169E22933DDC00BB457C /* Updates */,
|
|
||||||
BFDB69FB22A9A7A6007EA6D6 /* Account */,
|
BFDB69FB22A9A7A6007EA6D6 /* Account */,
|
||||||
BFC51D7922972F1F00388324 /* Server */,
|
BFC51D7922972F1F00388324 /* Server */,
|
||||||
BFD247982284D7FC00981D42 /* Model */,
|
BFD247982284D7FC00981D42 /* Model */,
|
||||||
@@ -758,8 +754,6 @@
|
|||||||
BFD2478A2284C49000981D42 /* Apps */ = {
|
BFD2478A2284C49000981D42 /* Apps */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BFD2476F2284B9A500981D42 /* AppsViewController.swift */,
|
|
||||||
BFD247922284D4B700981D42 /* AppTableViewCell.swift */,
|
|
||||||
BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */,
|
BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */,
|
||||||
BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */,
|
BF0C4EBC22A1BD8B009A2DD7 /* AppManager.swift */,
|
||||||
);
|
);
|
||||||
@@ -774,7 +768,8 @@
|
|||||||
BF43002D22A714AF0051E2BC /* Keychain.swift */,
|
BF43002D22A714AF0051E2BC /* Keychain.swift */,
|
||||||
BF770E6622BD57C3002A40FE /* BackgroundTaskManager.swift */,
|
BF770E6622BD57C3002A40FE /* BackgroundTaskManager.swift */,
|
||||||
BF9ABA4A22DD137F008935CF /* NavigationBar.swift */,
|
BF9ABA4A22DD137F008935CF /* NavigationBar.swift */,
|
||||||
BF9ABA4C22DD16DE008935CF /* ProgressButton.swift */,
|
BF9ABA4C22DD16DE008935CF /* PillButton.swift */,
|
||||||
|
BF18B0F022E25DF9005C4CF5 /* ToastView.swift */,
|
||||||
);
|
);
|
||||||
path = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1079,6 +1074,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
BFB1169D22932DB100BB457C /* Apps-Dev.json in Resources */,
|
BFB1169D22932DB100BB457C /* Apps-Dev.json in Resources */,
|
||||||
|
BFB4323F22DE852000B7F8BC /* UpdateCollectionViewCell.xib in Resources */,
|
||||||
BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */,
|
BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */,
|
||||||
BF770E6922BD57DD002A40FE /* Silence.m4a in Resources */,
|
BF770E6922BD57DD002A40FE /* Silence.m4a in Resources */,
|
||||||
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */,
|
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */,
|
||||||
@@ -1231,7 +1227,6 @@
|
|||||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
||||||
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */,
|
BFDB69FD22A9A7B7007EA6D6 /* AccountViewController.swift in Sources */,
|
||||||
BFE6326A22A85DAF00F30809 /* ReplaceCertificateViewController.swift in Sources */,
|
BFE6326A22A85DAF00F30809 /* ReplaceCertificateViewController.swift in Sources */,
|
||||||
BFD247722284B9A500981D42 /* MyAppsViewController.swift in Sources */,
|
|
||||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */,
|
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */,
|
||||||
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */,
|
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */,
|
||||||
BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */,
|
BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */,
|
||||||
@@ -1244,16 +1239,17 @@
|
|||||||
BFE6326622A857C200F30809 /* Team.swift in Sources */,
|
BFE6326622A857C200F30809 /* Team.swift in Sources */,
|
||||||
BFD2479C2284E19A00981D42 /* AppDetailViewController.swift in Sources */,
|
BFD2479C2284E19A00981D42 /* AppDetailViewController.swift in Sources */,
|
||||||
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */,
|
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */,
|
||||||
BFD247702284B9A500981D42 /* AppsViewController.swift in Sources */,
|
|
||||||
BFDB6A0B22AAEDB7007EA6D6 /* Operation.swift in Sources */,
|
BFDB6A0B22AAEDB7007EA6D6 /* Operation.swift in Sources */,
|
||||||
BFB116A022933DEB00BB457C /* UpdatesViewController.swift in Sources */,
|
|
||||||
BF770E6722BD57C4002A40FE /* BackgroundTaskManager.swift in Sources */,
|
BF770E6722BD57C4002A40FE /* BackgroundTaskManager.swift in Sources */,
|
||||||
BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */,
|
BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */,
|
||||||
BFE6325C22A83C0100F30809 /* AuthenticationViewController.swift in Sources */,
|
BFE6325C22A83C0100F30809 /* AuthenticationViewController.swift in Sources */,
|
||||||
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */,
|
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */,
|
||||||
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */,
|
BF9ABA4722DD0638008935CF /* BrowseCollectionViewCell.swift in Sources */,
|
||||||
BFD247932284D4B700981D42 /* AppTableViewCell.swift in Sources */,
|
BFD6B03322DFF20800B86064 /* MyAppsComponents.swift in Sources */,
|
||||||
|
BF08858522DE7EC800DE9F1E /* UpdateCollectionViewCell.swift in Sources */,
|
||||||
BF770E5822BC3D0F002A40FE /* OperationGroup.swift in Sources */,
|
BF770E5822BC3D0F002A40FE /* OperationGroup.swift in Sources */,
|
||||||
|
BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */,
|
||||||
|
BF08858322DE795100DE9F1E /* MyAppsViewController.swift in Sources */,
|
||||||
BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */,
|
BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */,
|
||||||
BFD52BD422A0800A000B7ED1 /* ServerManager.swift in Sources */,
|
BFD52BD422A0800A000B7ED1 /* ServerManager.swift in Sources */,
|
||||||
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */,
|
BFDB6A0822AAED73007EA6D6 /* ResignAppOperation.swift in Sources */,
|
||||||
@@ -1261,7 +1257,7 @@
|
|||||||
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,
|
BF9ABA4B22DD1380008935CF /* NavigationBar.swift in Sources */,
|
||||||
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
||||||
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */,
|
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */,
|
||||||
BF9ABA4D22DD16DE008935CF /* ProgressButton.swift in Sources */,
|
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */,
|
||||||
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
||||||
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */,
|
BFDB6A0522A9AFB2007EA6D6 /* Fetchable.swift in Sources */,
|
||||||
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */,
|
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */,
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
//
|
|
||||||
// AppTableViewCell.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 5/9/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
@objc class AppTableViewCell: UITableViewCell
|
|
||||||
{
|
|
||||||
@IBOutlet var nameLabel: UILabel!
|
|
||||||
@IBOutlet var developerLabel: UILabel!
|
|
||||||
@IBOutlet var appIconImageView: UIImageView!
|
|
||||||
@IBOutlet var button: UIButton!
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.selectionStyle = .none
|
|
||||||
}
|
|
||||||
|
|
||||||
override func setHighlighted(_ highlighted: Bool, animated: Bool)
|
|
||||||
{
|
|
||||||
super.setHighlighted(highlighted, animated: animated)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func setSelected(_ selected: Bool, animated: Bool)
|
|
||||||
{
|
|
||||||
super.setSelected(selected, animated: animated)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AppTableViewCell
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
if self.isHighlighted || self.isSelected
|
|
||||||
{
|
|
||||||
self.contentView.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.contentView.backgroundColor = .white
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
//
|
|
||||||
// AppsViewController.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 5/9/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import Roxas
|
|
||||||
|
|
||||||
class AppsViewController: UITableViewController
|
|
||||||
{
|
|
||||||
private lazy var dataSource = self.makeDataSource()
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.tableView.dataSource = self.dataSource
|
|
||||||
|
|
||||||
// Hide trailing row separators.
|
|
||||||
self.tableView.tableFooterView = UIView()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
self.fetchApps()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
|
||||||
{
|
|
||||||
guard segue.identifier == "showAppDetail" else { return }
|
|
||||||
|
|
||||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
|
||||||
|
|
||||||
let app = self.dataSource.item(at: indexPath)
|
|
||||||
|
|
||||||
let appDetailViewController = segue.destination as! AppDetailViewController
|
|
||||||
appDetailViewController.app = app
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AppsViewController
|
|
||||||
{
|
|
||||||
func makeDataSource() -> RSTFetchedResultsTableViewDataSource<App>
|
|
||||||
{
|
|
||||||
let fetchRequest = App.fetchRequest() as NSFetchRequest<App>
|
|
||||||
fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(InstalledApp.app)]
|
|
||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \App.name, ascending: true)]
|
|
||||||
fetchRequest.predicate = NSPredicate(format: "%K != %@", #keyPath(App.identifier), App.altstoreAppID)
|
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
|
||||||
|
|
||||||
let dataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
|
||||||
dataSource.cellConfigurationHandler = { (cell, app, indexPath) in
|
|
||||||
let cell = cell as! AppTableViewCell
|
|
||||||
cell.nameLabel.text = app.name
|
|
||||||
cell.developerLabel.text = app.developerName
|
|
||||||
cell.appIconImageView.image = UIImage(named: app.iconName)
|
|
||||||
|
|
||||||
if app.installedApp != nil
|
|
||||||
{
|
|
||||||
cell.button.isEnabled = false
|
|
||||||
cell.button.setTitle(NSLocalizedString("Installed", comment: ""), for: .normal)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cell.button.isEnabled = true
|
|
||||||
cell.button.setTitle(NSLocalizedString("Download", comment: ""), for: .normal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataSource
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchApps()
|
|
||||||
{
|
|
||||||
AppManager.shared.fetchApps { (result) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let apps = try result.get()
|
|
||||||
try apps.first?.managedObjectContext?.save()
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let toastView = RSTToastView(text: NSLocalizedString("Failed to fetch apps", comment: ""), detailText: error.localizedDescription)
|
|
||||||
toastView.tintColor = .altPurple
|
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,80 +21,14 @@
|
|||||||
</tabBar>
|
</tabBar>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="faz-B4-Sub" kind="relationship" relationship="viewControllers" id="kCE-KJ-sWv"/>
|
<segue destination="faz-B4-Sub" kind="relationship" relationship="viewControllers" id="kCE-KJ-sWv"/>
|
||||||
<segue destination="XF0-gk-CxQ" kind="relationship" relationship="viewControllers" id="t3q-Lp-gsl"/>
|
<segue destination="3Ew-ox-i4n" kind="relationship" relationship="viewControllers" id="F8I-ea-yTZ"/>
|
||||||
<segue destination="v8c-d9-T9x" kind="relationship" relationship="viewControllers" id="DmU-oI-nsr"/>
|
<segue destination="MGm-Zy-ffn" kind="relationship" relationship="viewControllers" id="9m0-Rb-vjU"/>
|
||||||
<segue destination="gJ2-NJ-8DO" kind="relationship" relationship="viewControllers" id="WBS-ya-y8Z"/>
|
|
||||||
<segue destination="MGm-Zy-ffn" kind="relationship" relationship="viewControllers" id="wxx-9a-d9q"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
</tabBarController>
|
</tabBarController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="0.0" y="0.0"/>
|
<point key="canvasLocation" x="0.0" y="0.0"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--My Apps-->
|
|
||||||
<scene sceneID="eRf-VD-MSX">
|
|
||||||
<objects>
|
|
||||||
<tableViewController id="Xf1-LZ-1vU" customClass="MyAppsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="hkn-c3-iKp">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="kY8-9c-qLE" detailTextLabel="XWn-JG-SYe" style="IBUITableViewCellStyleSubtitle" id="Lwi-IB-I9S">
|
|
||||||
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Lwi-IB-I9S" id="4Vg-jI-f8V">
|
|
||||||
<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="kY8-9c-qLE">
|
|
||||||
<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="XWn-JG-SYe">
|
|
||||||
<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>
|
|
||||||
<connections>
|
|
||||||
<segue destination="hR3-go-2DG" kind="show" identifier="showAppDetail" id="VNt-zB-flO">
|
|
||||||
<segue key="commit" inheritsFrom="parent" id="8jj-zE-2hk"/>
|
|
||||||
<segue key="preview" inheritsFrom="commit" id="9iz-NY-nvu"/>
|
|
||||||
</segue>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dataSource" destination="Xf1-LZ-1vU" id="Lsq-Jy-ugM"/>
|
|
||||||
<outlet property="delegate" destination="Xf1-LZ-1vU" id="0YS-oa-D9d"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
<navigationItem key="navigationItem" title="My Apps" id="dz9-0e-LKa">
|
|
||||||
<barButtonItem key="rightBarButtonItem" title="Refresh All" id="0Ke-yl-tAg">
|
|
||||||
<connections>
|
|
||||||
<action selector="refreshAllApps:" destination="Xf1-LZ-1vU" id="GOS-gx-qKD"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
</navigationItem>
|
|
||||||
<connections>
|
|
||||||
<outlet property="progressView" destination="CuF-K7-fn8" id="SPP-VP-a9e"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nb5-5T-hHT" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progressViewStyle="bar" id="CuF-K7-fn8">
|
|
||||||
<rect key="frame" x="0.0" y="-1" width="150" height="2.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
</progressView>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1518" y="420"/>
|
|
||||||
</scene>
|
|
||||||
<!--Browse-->
|
<!--Browse-->
|
||||||
<scene sceneID="rXq-UR-qQp">
|
<scene sceneID="rXq-UR-qQp">
|
||||||
<objects>
|
<objects>
|
||||||
@@ -144,7 +78,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</subviews>
|
</subviews>
|
||||||
</stackView>
|
</stackView>
|
||||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xCW-7H-lc3" customClass="ProgressButton" customModule="AltStore" customModuleProvider="target">
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xCW-7H-lc3" customClass="PillButton" customModule="AltStore" customModuleProvider="target">
|
||||||
<rect key="frame" x="263" y="17" width="72" height="31"/>
|
<rect key="frame" x="263" y="17" width="72" height="31"/>
|
||||||
<color key="backgroundColor" red="0.22352941176470589" green="0.49411764705882355" blue="0.396078431372549" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="0.22352941176470589" green="0.49411764705882355" blue="0.396078431372549" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
@@ -210,7 +144,6 @@
|
|||||||
</view>
|
</view>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="P9V-7N-kV7" firstAttribute="leading" secondItem="ofi-ID-YeW" secondAttribute="leading" constant="15" id="WeY-x3-kYd"/>
|
<constraint firstItem="P9V-7N-kV7" firstAttribute="leading" secondItem="ofi-ID-YeW" secondAttribute="leading" constant="15" id="WeY-x3-kYd"/>
|
||||||
<constraint firstItem="P9V-7N-kV7" firstAttribute="top" secondItem="Qmy-fI-0do" secondAttribute="bottom" constant="15" id="b4n-9X-Sfq"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="Qmy-fI-0do" secondAttribute="trailing" constant="20" id="dar-0I-2Zt"/>
|
<constraint firstAttribute="trailing" secondItem="Qmy-fI-0do" secondAttribute="trailing" constant="20" id="dar-0I-2Zt"/>
|
||||||
<constraint firstAttribute="bottom" secondItem="P9V-7N-kV7" secondAttribute="bottom" constant="20" id="qxV-1c-5fe"/>
|
<constraint firstAttribute="bottom" secondItem="P9V-7N-kV7" secondAttribute="bottom" constant="20" id="qxV-1c-5fe"/>
|
||||||
<constraint firstItem="Qmy-fI-0do" firstAttribute="leading" secondItem="ofi-ID-YeW" secondAttribute="leading" constant="20" id="r1c-ZM-cIP"/>
|
<constraint firstItem="Qmy-fI-0do" firstAttribute="leading" secondItem="ofi-ID-YeW" secondAttribute="leading" constant="20" id="r1c-ZM-cIP"/>
|
||||||
@@ -239,104 +172,6 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1517.5999999999999" y="-1013.3433283358322"/>
|
<point key="canvasLocation" x="1517.5999999999999" y="-1013.3433283358322"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Apps-->
|
|
||||||
<scene sceneID="JlP-x7-lBT">
|
|
||||||
<objects>
|
|
||||||
<tableViewController id="q89-7o-3rs" customClass="AppsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="10Y-Ge-TH3">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" rowHeight="118" id="dJn-Mf-F1h" customClass="AppTableViewCell" customModule="AltStore" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="28" width="375" height="118"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="dJn-Mf-F1h" id="gzF-hx-pLZ">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="117.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" alignment="top" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="tdM-M1-iDO">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="117.5"/>
|
|
||||||
<subviews>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="gsm-sN-j56" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="15" y="15" width="88" height="88"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="88" id="azK-Tb-i28"/>
|
|
||||||
<constraint firstAttribute="height" constant="88" id="kNm-96-9zq"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" alignment="top" translatesAutoresizingMaskIntoConstraints="NO" id="703-qg-th2">
|
|
||||||
<rect key="frame" x="118" y="15" width="242" height="87.5"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="opz-OR-arv">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="199.5" height="43"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="1000" text="Delta" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d4v-IK-LuC">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="199.5" height="23"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="19"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="1000" text="All-in-one Nintendo emulator" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xnf-XX-iX9">
|
|
||||||
<rect key="frame" x="0.0" y="25" width="199.5" height="18"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
|
||||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="1000" verticalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ScM-Z2-rAe" customClass="Button" customModule="AltStore" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="57.5" width="72" height="30"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="30" id="71g-sg-TqR"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="15"/>
|
|
||||||
<state key="normal" title="Download"/>
|
|
||||||
</button>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="ScM-Z2-rAe" firstAttribute="top" relation="greaterThanOrEqual" secondItem="opz-OR-arv" secondAttribute="bottom" priority="900" constant="14" id="p93-Jj-26G"/>
|
|
||||||
</constraints>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="703-qg-th2" secondAttribute="bottom" constant="15" id="omo-uf-DZn"/>
|
|
||||||
</constraints>
|
|
||||||
<edgeInsets key="layoutMargins" top="15" left="15" bottom="14.5" right="15"/>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="tdM-M1-iDO" firstAttribute="top" secondItem="gzF-hx-pLZ" secondAttribute="top" id="Dph-Xu-UMN"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="tdM-M1-iDO" secondAttribute="trailing" id="ITi-qo-EuU"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="tdM-M1-iDO" secondAttribute="bottom" id="iyZ-M1-q9y"/>
|
|
||||||
<constraint firstItem="tdM-M1-iDO" firstAttribute="leading" secondItem="gzF-hx-pLZ" secondAttribute="leading" id="sig-Cf-p45"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<inset key="separatorInset" minX="118" minY="0.0" maxX="0.0" maxY="0.0"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="appIconImageView" destination="gsm-sN-j56" id="1ip-RW-xBe"/>
|
|
||||||
<outlet property="button" destination="ScM-Z2-rAe" id="VnW-vG-JCc"/>
|
|
||||||
<outlet property="developerLabel" destination="xnf-XX-iX9" id="CYk-ud-YHz"/>
|
|
||||||
<outlet property="nameLabel" destination="d4v-IK-LuC" id="wFw-Js-PXE"/>
|
|
||||||
<segue destination="hR3-go-2DG" kind="show" identifier="showAppDetail" id="F38-66-skN">
|
|
||||||
<segue key="commit" inheritsFrom="parent" id="K6B-kg-jJz"/>
|
|
||||||
<segue key="preview" inheritsFrom="commit" id="sJk-KX-3sw"/>
|
|
||||||
</segue>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dataSource" destination="q89-7o-3rs" id="HlP-Lu-70n"/>
|
|
||||||
<outlet property="delegate" destination="q89-7o-3rs" id="D5P-te-Kct"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
<navigationItem key="navigationItem" title="Apps" id="8hf-te-CkC"/>
|
|
||||||
</tableViewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="ogT-wc-s7p" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1519" y="-319"/>
|
|
||||||
</scene>
|
|
||||||
<!--App Detail View Controller-->
|
<!--App Detail View Controller-->
|
||||||
<scene sceneID="XfG-lM-QRu">
|
<scene sceneID="XfG-lM-QRu">
|
||||||
<objects>
|
<objects>
|
||||||
@@ -532,103 +367,7 @@
|
|||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="BLa-Qn-j83" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="BLa-Qn-j83" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="2626" y="420"/>
|
<point key="canvasLocation" x="2314" y="-279"/>
|
||||||
</scene>
|
|
||||||
<!--Apps-->
|
|
||||||
<scene sceneID="YAm-Ca-4vd">
|
|
||||||
<objects>
|
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="XF0-gk-CxQ" sceneMemberID="viewController">
|
|
||||||
<tabBarItem key="tabBarItem" title="Apps" image="first" id="cDk-uR-lGN"/>
|
|
||||||
<toolbarItems/>
|
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="w2b-o0-FNx">
|
|
||||||
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</navigationBar>
|
|
||||||
<nil name="viewControllers"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="q89-7o-3rs" kind="relationship" relationship="rootViewController" id="yGz-tn-mRJ"/>
|
|
||||||
</connections>
|
|
||||||
</navigationController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="4cQ-6T-2Cl" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="749.60000000000002" y="-318.89055472263868"/>
|
|
||||||
</scene>
|
|
||||||
<!--My Apps-->
|
|
||||||
<scene sceneID="Edw-HT-TTT">
|
|
||||||
<objects>
|
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="v8c-d9-T9x" sceneMemberID="viewController">
|
|
||||||
<tabBarItem key="tabBarItem" title="My Apps" image="second" id="Pld-qe-pas"/>
|
|
||||||
<toolbarItems/>
|
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="PKd-0h-fii">
|
|
||||||
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</navigationBar>
|
|
||||||
<nil name="viewControllers"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="Xf1-LZ-1vU" kind="relationship" relationship="rootViewController" id="uaQ-4h-SJu"/>
|
|
||||||
</connections>
|
|
||||||
</navigationController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="G7O-77-OQq" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="749.60000000000002" y="420.53973013493254"/>
|
|
||||||
</scene>
|
|
||||||
<!--Updates-->
|
|
||||||
<scene sceneID="TJO-Cq-tOL">
|
|
||||||
<objects>
|
|
||||||
<tableViewController id="u4s-6L-zyx" customClass="UpdatesViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="vfp-b5-W5p">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="ewc-hz-Cyc" detailTextLabel="CXo-DZ-vv6" style="IBUITableViewCellStyleSubtitle" id="IXp-LN-P7W">
|
|
||||||
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="IXp-LN-P7W" id="sO0-Yc-Rhv">
|
|
||||||
<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="ewc-hz-Cyc">
|
|
||||||
<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="CXo-DZ-vv6">
|
|
||||||
<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>
|
|
||||||
<connections>
|
|
||||||
<segue destination="hR3-go-2DG" kind="show" identifier="showAppDetail" id="miz-8X-gFg">
|
|
||||||
<segue key="commit" inheritsFrom="parent" id="fQ8-cb-1An"/>
|
|
||||||
<segue key="preview" inheritsFrom="commit" id="wBX-0o-Ywz"/>
|
|
||||||
</segue>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dataSource" destination="u4s-6L-zyx" id="eML-9F-BXh"/>
|
|
||||||
<outlet property="delegate" destination="u4s-6L-zyx" id="3AF-E2-2B6"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
<navigationItem key="navigationItem" title="Updates" id="hvX-Ly-Y2C"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="progressView" destination="QaM-dt-nHj" id="Tlq-vj-Bo9"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="H7V-ct-fO1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progressViewStyle="bar" id="QaM-dt-nHj">
|
|
||||||
<rect key="frame" x="0.0" y="-1" width="150" height="2.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
|
||||||
</progressView>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1518" y="1124"/>
|
|
||||||
</scene>
|
</scene>
|
||||||
<!--Account-->
|
<!--Account-->
|
||||||
<scene sceneID="GaO-Ug-BdZ">
|
<scene sceneID="GaO-Ug-BdZ">
|
||||||
@@ -645,7 +384,7 @@
|
|||||||
</navigationController>
|
</navigationController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="cM4-hZ-uHG" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="cM4-hZ-uHG" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="750" y="1846"/>
|
<point key="canvasLocation" x="750" y="515"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Account-->
|
<!--Account-->
|
||||||
<scene sceneID="Xdi-2V-rwM">
|
<scene sceneID="Xdi-2V-rwM">
|
||||||
@@ -762,26 +501,7 @@
|
|||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="BE4-68-0PU" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="BE4-68-0PU" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1518" y="1847"/>
|
<point key="canvasLocation" x="1518" y="515"/>
|
||||||
</scene>
|
|
||||||
<!--Updates-->
|
|
||||||
<scene sceneID="H4l-3g-QrV">
|
|
||||||
<objects>
|
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="gJ2-NJ-8DO" sceneMemberID="viewController">
|
|
||||||
<tabBarItem key="tabBarItem" title="Updates" image="first" id="n2m-w3-Ltw"/>
|
|
||||||
<toolbarItems/>
|
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="mb8-TA-qKp">
|
|
||||||
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</navigationBar>
|
|
||||||
<nil name="viewControllers"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="u4s-6L-zyx" kind="relationship" relationship="rootViewController" id="HlY-Wa-In2"/>
|
|
||||||
</connections>
|
|
||||||
</navigationController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dR5-RY-R8S" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="750" y="1124"/>
|
|
||||||
</scene>
|
</scene>
|
||||||
<!--Browse-->
|
<!--Browse-->
|
||||||
<scene sceneID="VHa-uP-bFU">
|
<scene sceneID="VHa-uP-bFU">
|
||||||
@@ -803,17 +523,152 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="750" y="-1013"/>
|
<point key="canvasLocation" x="750" y="-1013"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<!--My Apps-->
|
||||||
|
<scene sceneID="nhh-BJ-XiT">
|
||||||
|
<objects>
|
||||||
|
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="3Ew-ox-i4n" sceneMemberID="viewController">
|
||||||
|
<tabBarItem key="tabBarItem" title="My Apps" id="4gT-9u-k7y">
|
||||||
|
<color key="badgeColor" name="Green"/>
|
||||||
|
</tabBarItem>
|
||||||
|
<toolbarItems/>
|
||||||
|
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="CzO-Kt-BlZ" customClass="NavigationBar" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</navigationBar>
|
||||||
|
<nil name="viewControllers"/>
|
||||||
|
<connections>
|
||||||
|
<segue destination="hv7-Ar-voT" kind="relationship" relationship="rootViewController" id="rz5-8X-hPJ"/>
|
||||||
|
</connections>
|
||||||
|
</navigationController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="9Nj-f6-CAf" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="749.60000000000002" y="-279.31034482758622"/>
|
||||||
|
</scene>
|
||||||
|
<!--My Apps-->
|
||||||
|
<scene sceneID="EC8-Sf-AF9">
|
||||||
|
<objects>
|
||||||
|
<collectionViewController id="hv7-Ar-voT" customClass="MyAppsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" id="Jrp-gi-4Df">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="15" minimumInteritemSpacing="10" id="SB5-U0-jyy">
|
||||||
|
<size key="itemSize" width="375" height="60"/>
|
||||||
|
<size key="headerReferenceSize" width="50" height="50"/>
|
||||||
|
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
|
</collectionViewFlowLayout>
|
||||||
|
<cells>
|
||||||
|
<collectionViewCell opaque="NO" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AppCell" id="kMp-ym-2yu" customClass="InstalledAppCollectionViewCell" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="50" width="375" height="60"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="60"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="11" translatesAutoresizingMaskIntoConstraints="NO" id="d6d-uV-GFi" userLabel="App Info">
|
||||||
|
<rect key="frame" x="20" y="0.0" width="335" height="60"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="H12-ip-Bbl" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="60" height="60"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="60" id="SOy-Xe-y2x"/>
|
||||||
|
<constraint firstAttribute="width" secondItem="H12-ip-Bbl" secondAttribute="height" multiplier="1:1" id="ZIR-f8-Jc4"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="100" axis="vertical" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="7iy-Zp-LEj">
|
||||||
|
<rect key="frame" x="71" y="12" width="203" height="36"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="Short" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Nhl-6I-9gW">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="203" height="18"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="Developer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Hp4-uP-55T">
|
||||||
|
<rect key="frame" x="0.0" y="20" width="203" height="16"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||||
|
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dh4-fU-DFx" customClass="PillButton" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="285" y="15.5" width="50" height="29"/>
|
||||||
|
<color key="backgroundColor" red="0.22352941179999999" green="0.4941176471" blue="0.39607843139999999" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
|
||||||
|
<state key="normal" title="7 DAYS"/>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Expires in" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Kc-4f-KYr">
|
||||||
|
<rect key="frame" x="306.5" y="0.5" width="47" height="12"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="10"/>
|
||||||
|
<color key="textColor" red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.45000000000000001" colorSpace="calibratedRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="4Kc-4f-KYr" firstAttribute="centerX" secondItem="dh4-fU-DFx" secondAttribute="centerX" id="9Uf-Qu-bhZ"/>
|
||||||
|
<constraint firstItem="d6d-uV-GFi" firstAttribute="leading" secondItem="kMp-ym-2yu" secondAttribute="leading" constant="20" id="fV7-0C-Hop"/>
|
||||||
|
<constraint firstItem="d6d-uV-GFi" firstAttribute="top" secondItem="kMp-ym-2yu" secondAttribute="top" id="rCI-7z-0mR"/>
|
||||||
|
<constraint firstItem="dh4-fU-DFx" firstAttribute="top" secondItem="4Kc-4f-KYr" secondAttribute="bottom" constant="3" id="rmM-9v-G5C"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="d6d-uV-GFi" secondAttribute="trailing" constant="20" id="s7H-ei-AEn"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="appIconImageView" destination="H12-ip-Bbl" id="61F-4i-4Q3"/>
|
||||||
|
<outlet property="developerLabel" destination="Hp4-uP-55T" id="Cqx-3O-knq"/>
|
||||||
|
<outlet property="nameLabel" destination="Nhl-6I-9gW" id="lzd-pp-PEQ"/>
|
||||||
|
<outlet property="refreshButton" destination="dh4-fU-DFx" id="KWX-9y-2w8"/>
|
||||||
|
</connections>
|
||||||
|
</collectionViewCell>
|
||||||
|
</cells>
|
||||||
|
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="InstalledAppsHeader" id="Crb-NU-1Ye" customClass="InstalledAppsCollectionHeaderView" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Installed" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BDU-hM-rro">
|
||||||
|
<rect key="frame" x="20" y="21" width="97" height="29"/>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="24"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nxk-e8-ARx">
|
||||||
|
<rect key="frame" x="274" y="23" width="81" height="32"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
|
||||||
|
<state key="normal" title="Refresh All"/>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="BDU-hM-rro" secondAttribute="bottom" id="9iT-ur-A4W"/>
|
||||||
|
<constraint firstItem="BDU-hM-rro" firstAttribute="leading" secondItem="Crb-NU-1Ye" secondAttribute="leading" constant="20" id="F8e-9W-MC2"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="nxk-e8-ARx" secondAttribute="trailing" constant="20" id="WxV-85-RcK"/>
|
||||||
|
<constraint firstItem="nxk-e8-ARx" firstAttribute="firstBaseline" secondItem="BDU-hM-rro" secondAttribute="firstBaseline" id="lIO-3C-ZPH"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outlet property="button" destination="nxk-e8-ARx" id="gwj-97-LVi"/>
|
||||||
|
<outlet property="textLabel" destination="BDU-hM-rro" id="CQM-8K-bcH"/>
|
||||||
|
</connections>
|
||||||
|
</collectionReusableView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="hv7-Ar-voT" id="YOx-f4-chF"/>
|
||||||
|
<outlet property="delegate" destination="hv7-Ar-voT" id="1PN-pf-cZK"/>
|
||||||
|
</connections>
|
||||||
|
</collectionView>
|
||||||
|
<navigationItem key="navigationItem" title="My Apps" id="zLJ-Cg-ijh"/>
|
||||||
|
</collectionViewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="kiO-UO-esV" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="1517.5999999999999" y="-279.31034482758622"/>
|
||||||
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="DeltaIcon" width="512" height="512"/>
|
<image name="DeltaIcon" width="512" height="512"/>
|
||||||
<image name="first" width="30" height="30"/>
|
|
||||||
<image name="second" width="30" height="30"/>
|
<image name="second" width="30" height="30"/>
|
||||||
<namedColor name="Green">
|
<namedColor name="Green">
|
||||||
<color red="0.22352941176470589" green="0.49411764705882355" blue="0.396078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color red="0.22352941176470589" green="0.49411764705882355" blue="0.396078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
</namedColor>
|
</namedColor>
|
||||||
</resources>
|
</resources>
|
||||||
<inferredMetricsTieBreakers>
|
|
||||||
<segue reference="wBX-0o-Ywz"/>
|
|
||||||
</inferredMetricsTieBreakers>
|
|
||||||
<color key="tintColor" name="Green"/>
|
<color key="tintColor" name="Green"/>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import Roxas
|
|||||||
@IBOutlet var nameLabel: UILabel!
|
@IBOutlet var nameLabel: UILabel!
|
||||||
@IBOutlet var developerLabel: UILabel!
|
@IBOutlet var developerLabel: UILabel!
|
||||||
@IBOutlet var appIconImageView: UIImageView!
|
@IBOutlet var appIconImageView: UIImageView!
|
||||||
@IBOutlet var actionButton: ProgressButton!
|
@IBOutlet var actionButton: PillButton!
|
||||||
@IBOutlet var subtitleLabel: UILabel!
|
@IBOutlet var subtitleLabel: UILabel!
|
||||||
|
|
||||||
@IBOutlet private var screenshotsContentView: UIView!
|
@IBOutlet private var screenshotsContentView: UIView!
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ private extension BrowseViewController
|
|||||||
fetchRequest.returnsObjectsAsFaults = false
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
|
||||||
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<App, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<App, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||||
dataSource.cellConfigurationHandler = { [weak self] (cell, app, indexPath) in
|
dataSource.cellConfigurationHandler = { (cell, app, indexPath) in
|
||||||
guard let `self` = self else { return }
|
|
||||||
|
|
||||||
let cell = cell as! BrowseCollectionViewCell
|
let cell = cell as! BrowseCollectionViewCell
|
||||||
cell.nameLabel.text = app.name
|
cell.nameLabel.text = app.name
|
||||||
cell.developerLabel.text = app.developerName
|
cell.developerLabel.text = app.developerName
|
||||||
@@ -59,41 +57,28 @@ private extension BrowseViewController
|
|||||||
cell.imageNames = Array(app.screenshotNames.prefix(3))
|
cell.imageNames = Array(app.screenshotNames.prefix(3))
|
||||||
cell.appIconImageView.image = UIImage(named: app.iconName)
|
cell.appIconImageView.image = UIImage(named: app.iconName)
|
||||||
|
|
||||||
cell.actionButton.tag = indexPath.item
|
|
||||||
cell.actionButton.activityIndicatorView.style = .white
|
cell.actionButton.activityIndicatorView.style = .white
|
||||||
|
|
||||||
// Explicitly set to false to ensure we're starting from a non-activity indicating state.
|
// Explicitly set to false to ensure we're starting from a non-activity indicating state.
|
||||||
// Otherwise, cell reuse can mess up some cached values.
|
// Otherwise, cell reuse can mess up some cached values.
|
||||||
cell.actionButton.isIndicatingActivity = false
|
cell.actionButton.isIndicatingActivity = false
|
||||||
|
|
||||||
let tintColor = app.tintColor ?? self.collectionView.tintColor!
|
let tintColor = app.tintColor ?? .altGreen
|
||||||
cell.tintColor = tintColor
|
cell.tintColor = tintColor
|
||||||
cell.actionButton.progressTintColor = tintColor
|
|
||||||
|
|
||||||
if app.installedApp == nil
|
if app.installedApp == nil
|
||||||
{
|
{
|
||||||
cell.actionButton.setTitle(NSLocalizedString("FREE", comment: ""), for: .normal)
|
cell.actionButton.setTitle(NSLocalizedString("FREE", comment: ""), for: .normal)
|
||||||
cell.actionButton.setTitleColor(.altGreen, for: .normal)
|
|
||||||
cell.actionButton.backgroundColor = UIColor.altGreen.withAlphaComponent(0.1)
|
|
||||||
|
|
||||||
if let progress = AppManager.shared.installationProgress(for: app)
|
let progress = AppManager.shared.installationProgress(for: app)
|
||||||
{
|
cell.actionButton.progress = progress
|
||||||
cell.actionButton.progress = progress
|
cell.actionButton.isInverted = false
|
||||||
cell.actionButton.isIndicatingActivity = true
|
|
||||||
cell.actionButton.activityIndicatorView.isUserInteractionEnabled = false
|
|
||||||
cell.actionButton.isUserInteractionEnabled = true
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cell.actionButton.progress = nil
|
|
||||||
cell.actionButton.isIndicatingActivity = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cell.actionButton.setTitle(NSLocalizedString("OPEN", comment: ""), for: .normal)
|
cell.actionButton.setTitle(NSLocalizedString("OPEN", comment: ""), for: .normal)
|
||||||
cell.actionButton.setTitleColor(.white, for: .normal)
|
cell.actionButton.progress = nil
|
||||||
cell.actionButton.backgroundColor = .altGreen
|
cell.actionButton.isInverted = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,9 +107,11 @@ private extension BrowseViewController
|
|||||||
|
|
||||||
private extension BrowseViewController
|
private extension BrowseViewController
|
||||||
{
|
{
|
||||||
@IBAction func performAppAction(_ sender: ProgressButton)
|
@IBAction func performAppAction(_ sender: PillButton)
|
||||||
{
|
{
|
||||||
let indexPath = IndexPath(item: sender.tag, section: 0)
|
let point = self.collectionView.convert(sender.center, from: sender.superview)
|
||||||
|
guard let indexPath = self.collectionView.indexPathForItem(at: point) else { return }
|
||||||
|
|
||||||
let app = self.dataSource.item(at: indexPath)
|
let app = self.dataSource.item(at: indexPath)
|
||||||
|
|
||||||
if let installedApp = app.installedApp
|
if let installedApp = app.installedApp
|
||||||
@@ -155,7 +142,7 @@ private extension BrowseViewController
|
|||||||
toastView.tintColor = .altGreen
|
toastView.tintColor = .altGreen
|
||||||
toastView.show(in: self.navigationController!.view, duration: 2)
|
toastView.show(in: self.navigationController!.view, duration: 2)
|
||||||
|
|
||||||
case .success(let installedApp): print("Installed app:", installedApp.app.identifier)
|
case .success: print("Installed app:", app.identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.collectionView.reloadItems(at: [indexPath])
|
self.collectionView.reloadItems(at: [indexPath])
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class AppIconImageView: UIImageView
|
|||||||
self.contentMode = .scaleAspectFill
|
self.contentMode = .scaleAspectFill
|
||||||
self.clipsToBounds = true
|
self.clipsToBounds = true
|
||||||
|
|
||||||
|
self.backgroundColor = .white
|
||||||
|
|
||||||
self.layer.borderWidth = 0.5
|
self.layer.borderWidth = 0.5
|
||||||
self.layer.borderColor = UIColor.lightGray.cgColor
|
self.layer.borderColor = UIColor.lightGray.cgColor
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// ProgressButton.swift
|
// PillButton.swift
|
||||||
// AltStore
|
// AltStore
|
||||||
//
|
//
|
||||||
// Created by Riley Testut on 7/15/19.
|
// Created by Riley Testut on 7/15/19.
|
||||||
@@ -8,12 +8,16 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
class ProgressButton: UIButton
|
class PillButton: UIButton
|
||||||
{
|
{
|
||||||
var progress: Progress? {
|
var progress: Progress? {
|
||||||
didSet {
|
didSet {
|
||||||
self.progressView.progress = Float(self.progress?.fractionCompleted ?? 0)
|
self.progressView.progress = Float(self.progress?.fractionCompleted ?? 0)
|
||||||
self.progressView.observedProgress = self.progress
|
self.progressView.observedProgress = self.progress
|
||||||
|
|
||||||
|
let isUserInteractionEnabled = self.isUserInteractionEnabled
|
||||||
|
self.isIndicatingActivity = (self.progress != nil)
|
||||||
|
self.isUserInteractionEnabled = isUserInteractionEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,12 +30,18 @@ class ProgressButton: UIButton
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isInverted: Bool = false {
|
||||||
|
didSet {
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private let progressView = UIProgressView(progressViewStyle: .default)
|
private let progressView = UIProgressView(progressViewStyle: .default)
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
override var intrinsicContentSize: CGSize {
|
||||||
var size = super.intrinsicContentSize
|
var size = super.intrinsicContentSize
|
||||||
size.width += 32
|
size.width += 26
|
||||||
size.height += 4
|
size.height += 3
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,10 +51,15 @@ class ProgressButton: UIButton
|
|||||||
|
|
||||||
self.layer.masksToBounds = true
|
self.layer.masksToBounds = true
|
||||||
|
|
||||||
|
self.activityIndicatorView.style = .white
|
||||||
|
self.activityIndicatorView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
self.progressView.progress = 0
|
self.progressView.progress = 0
|
||||||
self.progressView.trackImage = UIImage()
|
self.progressView.trackImage = UIImage()
|
||||||
self.progressView.isUserInteractionEnabled = false
|
self.progressView.isUserInteractionEnabled = false
|
||||||
self.addSubview(self.progressView)
|
self.addSubview(self.progressView)
|
||||||
|
|
||||||
|
self.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func layoutSubviews()
|
override func layoutSubviews()
|
||||||
@@ -60,4 +75,30 @@ class ProgressButton: UIButton
|
|||||||
|
|
||||||
self.layer.cornerRadius = self.bounds.midY
|
self.layer.cornerRadius = self.bounds.midY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func tintColorDidChange()
|
||||||
|
{
|
||||||
|
super.tintColorDidChange()
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension PillButton
|
||||||
|
{
|
||||||
|
func update()
|
||||||
|
{
|
||||||
|
if self.isInverted
|
||||||
|
{
|
||||||
|
self.setTitleColor(.white, for: .normal)
|
||||||
|
self.backgroundColor = self.tintColor
|
||||||
|
self.progressView.progressTintColor = self.tintColor.withAlphaComponent(0.15)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.setTitleColor(self.tintColor, for: .normal)
|
||||||
|
self.backgroundColor = self.tintColor.withAlphaComponent(0.15)
|
||||||
|
self.progressView.progressTintColor = self.tintColor
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
19
AltStore/Components/ToastView.swift
Normal file
19
AltStore/Components/ToastView.swift
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// ToastView.swift
|
||||||
|
// AltStore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 7/19/19.
|
||||||
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
class ToastView: RSTToastView
|
||||||
|
{
|
||||||
|
override func layoutSubviews()
|
||||||
|
{
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
self.layer.cornerRadius = self.bounds.midY
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,4 +12,9 @@ extension UIColor
|
|||||||
{
|
{
|
||||||
static let altPurple = UIColor(named: "Purple")!
|
static let altPurple = UIColor(named: "Purple")!
|
||||||
static let altGreen = UIColor(named: "Green")!
|
static let altGreen = UIColor(named: "Green")!
|
||||||
|
|
||||||
|
static let refreshRed = UIColor(named: "RefreshRed")!
|
||||||
|
static let refreshOrange = UIColor(named: "RefreshOrange")!
|
||||||
|
static let refreshYellow = UIColor(named: "RefreshYellow")!
|
||||||
|
static let refreshGreen = UIColor(named: "RefreshGreen")!
|
||||||
}
|
}
|
||||||
|
|||||||
46
AltStore/My Apps/MyAppsComponents.swift
Normal file
46
AltStore/My Apps/MyAppsComponents.swift
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//
|
||||||
|
// MyAppsComponents.swift
|
||||||
|
// AltStore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 7/17/19.
|
||||||
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class InstalledAppCollectionViewCell: UICollectionViewCell
|
||||||
|
{
|
||||||
|
@IBOutlet var appIconImageView: UIImageView!
|
||||||
|
@IBOutlet var nameLabel: UILabel!
|
||||||
|
@IBOutlet var developerLabel: UILabel!
|
||||||
|
@IBOutlet var refreshButton: PillButton!
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstalledAppsCollectionHeaderView: UICollectionReusableView
|
||||||
|
{
|
||||||
|
@IBOutlet var textLabel: UILabel!
|
||||||
|
@IBOutlet var button: UIButton!
|
||||||
|
}
|
||||||
|
|
||||||
|
class UpdatesCollectionHeaderView: UICollectionReusableView
|
||||||
|
{
|
||||||
|
let button = PillButton(type: .system)
|
||||||
|
|
||||||
|
override init(frame: CGRect)
|
||||||
|
{
|
||||||
|
super.init(frame: frame)
|
||||||
|
|
||||||
|
self.button.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
self.button.setTitle(">", for: .normal)
|
||||||
|
self.addSubview(self.button)
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([self.button.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20),
|
||||||
|
self.button.topAnchor.constraint(equalTo: self.topAnchor),
|
||||||
|
self.button.widthAnchor.constraint(equalToConstant: 50),
|
||||||
|
self.button.heightAnchor.constraint(equalToConstant: 26)])
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,129 +2,222 @@
|
|||||||
// MyAppsViewController.swift
|
// MyAppsViewController.swift
|
||||||
// AltStore
|
// AltStore
|
||||||
//
|
//
|
||||||
// Created by Riley Testut on 5/9/19.
|
// Created by Riley Testut on 7/16/19.
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
import AltKit
|
||||||
import Roxas
|
import Roxas
|
||||||
|
|
||||||
import AltSign
|
private let maximumCollapsedUpdatesCount = 2
|
||||||
|
|
||||||
class MyAppsViewController: UITableViewController
|
extension MyAppsViewController
|
||||||
{
|
{
|
||||||
private var refreshErrors = [String: Error]()
|
private enum Section: Int, CaseIterable
|
||||||
|
{
|
||||||
|
case updates
|
||||||
|
case installedApps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Date
|
||||||
|
{
|
||||||
|
func numberOfCalendarDays(since date: Date) -> Int
|
||||||
|
{
|
||||||
|
let today = Calendar.current.startOfDay(for: self)
|
||||||
|
let previousDay = Calendar.current.startOfDay(for: date)
|
||||||
|
|
||||||
|
let components = Calendar.current.dateComponents([.day], from: previousDay, to: today)
|
||||||
|
return components.day!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyAppsViewController: UICollectionViewController
|
||||||
|
{
|
||||||
private lazy var dataSource = self.makeDataSource()
|
private lazy var dataSource = self.makeDataSource()
|
||||||
|
private lazy var updatesDataSource = self.makeUpdatesDataSource()
|
||||||
|
private lazy var installedAppsDataSource = self.makeInstalledAppsDataSource()
|
||||||
|
|
||||||
|
private var prototypeUpdateCell: UpdateCollectionViewCell!
|
||||||
|
|
||||||
|
// State
|
||||||
|
private var isUpdateSectionCollapsed = true
|
||||||
|
private var expandedAppUpdates = Set<String>()
|
||||||
|
private var isRefreshingAllApps = false
|
||||||
|
private var refreshGroup: OperationGroup?
|
||||||
|
|
||||||
|
// Cache
|
||||||
|
private var cachedUpdateSizes = [String: CGSize]()
|
||||||
|
|
||||||
private lazy var dateFormatter: DateFormatter = {
|
private lazy var dateFormatter: DateFormatter = {
|
||||||
let dateFormatter = DateFormatter()
|
let dateFormatter = DateFormatter()
|
||||||
dateFormatter.dateStyle = .short
|
dateFormatter.dateStyle = .medium
|
||||||
dateFormatter.timeStyle = .short
|
dateFormatter.timeStyle = .none
|
||||||
return dateFormatter
|
return dateFormatter
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private var refreshGroup: OperationGroup?
|
required init?(coder aDecoder: NSCoder)
|
||||||
|
{
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
@IBOutlet private var progressView: UIProgressView!
|
NotificationCenter.default.addObserver(self, selector: #selector(MyAppsViewController.didFetchApps(_:)), name: AppManager.didFetchAppsNotification, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad()
|
||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.tableView.dataSource = self.dataSource
|
self.collectionView.dataSource = self.dataSource
|
||||||
|
self.collectionView.prefetchDataSource = self.dataSource
|
||||||
|
|
||||||
if let navigationBar = self.navigationController?.navigationBar
|
self.prototypeUpdateCell = UpdateCollectionViewCell.instantiate(with: UpdateCollectionViewCell.nib!)
|
||||||
{
|
self.prototypeUpdateCell.translatesAutoresizingMaskIntoConstraints = false
|
||||||
self.progressView.translatesAutoresizingMaskIntoConstraints = false
|
self.prototypeUpdateCell.contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
navigationBar.addSubview(self.progressView)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.progressView.widthAnchor.constraint(equalTo: navigationBar.widthAnchor),
|
self.collectionView.register(UpdateCollectionViewCell.nib, forCellWithReuseIdentifier: "UpdateCell")
|
||||||
self.progressView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)])
|
self.collectionView.register(UpdatesCollectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "UpdatesHeader")
|
||||||
}
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
|
||||||
{
|
|
||||||
guard segue.identifier == "showAppDetail" else { return }
|
|
||||||
|
|
||||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
|
||||||
|
|
||||||
let installedApp = self.dataSource.item(at: indexPath)
|
|
||||||
guard let app = installedApp.app else { return }
|
|
||||||
|
|
||||||
let appDetailViewController = segue.destination as! AppDetailViewController
|
|
||||||
appDetailViewController.app = app
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MyAppsViewController
|
private extension MyAppsViewController
|
||||||
{
|
{
|
||||||
func makeDataSource() -> RSTFetchedResultsTableViewDataSource<InstalledApp>
|
func makeDataSource() -> RSTCompositeCollectionViewPrefetchingDataSource<InstalledApp, UIImage>
|
||||||
|
{
|
||||||
|
let dataSource = RSTCompositeCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(dataSources: [self.updatesDataSource, self.installedAppsDataSource])
|
||||||
|
dataSource.proxy = self
|
||||||
|
return dataSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUpdatesDataSource() -> RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>
|
||||||
{
|
{
|
||||||
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
||||||
fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(InstalledApp.app)]
|
fetchRequest.predicate = NSPredicate(format: "%K != %K", #keyPath(InstalledApp.version), #keyPath(InstalledApp.app.version))
|
||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.app?.name, ascending: true)]
|
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.app?.versionDate, ascending: true),
|
||||||
|
NSSortDescriptor(keyPath: \InstalledApp.app?.name, ascending: true)]
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
|
||||||
let dataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||||
dataSource.proxy = self
|
dataSource.liveFetchLimit = maximumCollapsedUpdatesCount
|
||||||
dataSource.cellConfigurationHandler = { [weak self] (cell, installedApp, indexPath) in
|
dataSource.cellIdentifierHandler = { _ in "UpdateCell" }
|
||||||
|
dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in
|
||||||
guard let app = installedApp.app else { return }
|
guard let app = installedApp.app else { return }
|
||||||
|
|
||||||
cell.textLabel?.text = app.name + " (\(installedApp.version))"
|
let cell = cell as! UpdateCollectionViewCell
|
||||||
|
cell.tintColor = app.tintColor ?? .altGreen
|
||||||
|
cell.nameLabel.text = app.name
|
||||||
|
cell.versionDescriptionTextView.text = app.versionDescription
|
||||||
|
cell.appIconImageView.image = UIImage(named: app.iconName)
|
||||||
|
|
||||||
let detailText =
|
cell.updateButton.isIndicatingActivity = false
|
||||||
"""
|
cell.updateButton.addTarget(self, action: #selector(MyAppsViewController.updateApp(_:)), for: .primaryActionTriggered)
|
||||||
Expires: \(self?.dateFormatter.string(from: installedApp.expirationDate) ?? "-")
|
|
||||||
"""
|
|
||||||
|
|
||||||
cell.detailTextLabel?.numberOfLines = 1
|
if self.expandedAppUpdates.contains(app.identifier)
|
||||||
cell.detailTextLabel?.text = detailText
|
|
||||||
cell.detailTextLabel?.textColor = .red
|
|
||||||
|
|
||||||
if let _ = self?.refreshErrors[installedApp.bundleIdentifier]
|
|
||||||
{
|
{
|
||||||
cell.accessoryType = .detailButton
|
cell.mode = .expanded
|
||||||
cell.tintColor = .red
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cell.accessoryType = .none
|
cell.mode = .collapsed
|
||||||
cell.tintColor = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cell.moreButton.addTarget(self, action: #selector(MyAppsViewController.toggleUpdateCellMode(_:)), for: .primaryActionTriggered)
|
||||||
|
|
||||||
|
let progress = AppManager.shared.installationProgress(for: app)
|
||||||
|
cell.updateButton.progress = progress
|
||||||
|
|
||||||
|
cell.dateLabel.text = self.dateFormatter.string(from: app.versionDate)
|
||||||
|
|
||||||
|
let numberOfDays = Date().numberOfCalendarDays(since: app.versionDate)
|
||||||
|
switch numberOfDays
|
||||||
|
{
|
||||||
|
case 0: cell.dateLabel.text = NSLocalizedString("Today", comment: "")
|
||||||
|
case 1: cell.dateLabel.text = NSLocalizedString("Yesterday", comment: "")
|
||||||
|
case 2...7: cell.dateLabel.text = String(format: NSLocalizedString("%@ days ago", comment: ""), NSNumber(value: numberOfDays))
|
||||||
|
default: cell.dateLabel.text = self.dateFormatter.string(from: app.versionDate)
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.setNeedsLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataSource
|
return dataSource
|
||||||
}
|
}
|
||||||
|
|
||||||
func update()
|
func makeInstalledAppsDataSource() -> RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>
|
||||||
{
|
{
|
||||||
self.navigationItem.rightBarButtonItem?.isEnabled = !(self.dataSource.fetchedResultsController.fetchedObjects?.isEmpty ?? true)
|
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
||||||
|
fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(InstalledApp.app)]
|
||||||
|
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.expirationDate, ascending: true),
|
||||||
|
NSSortDescriptor(keyPath: \InstalledApp.refreshedDate, ascending: false),
|
||||||
|
NSSortDescriptor(keyPath: \InstalledApp.app?.name, ascending: true)]
|
||||||
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
|
||||||
self.tableView.reloadData()
|
let dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<InstalledApp, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||||
|
dataSource.cellIdentifierHandler = { _ in "AppCell" }
|
||||||
|
dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in
|
||||||
|
guard let app = installedApp.app else { return }
|
||||||
|
|
||||||
|
let tintColor = app.tintColor ?? .altGreen
|
||||||
|
|
||||||
|
let cell = cell as! InstalledAppCollectionViewCell
|
||||||
|
cell.tintColor = tintColor
|
||||||
|
cell.appIconImageView.image = UIImage(named: app.iconName)
|
||||||
|
cell.refreshButton.isIndicatingActivity = false
|
||||||
|
cell.refreshButton.addTarget(self, action: #selector(MyAppsViewController.refreshApp(_:)), for: .primaryActionTriggered)
|
||||||
|
|
||||||
|
let currentDate = Date()
|
||||||
|
|
||||||
|
let numberOfDays = installedApp.expirationDate.numberOfCalendarDays(since: currentDate)
|
||||||
|
|
||||||
|
if numberOfDays == 1
|
||||||
|
{
|
||||||
|
cell.refreshButton.setTitle(NSLocalizedString("1 DAY", comment: ""), for: .normal)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.refreshButton.setTitle(String(format: NSLocalizedString("%@ DAYS", comment: ""), NSNumber(value: numberOfDays)), for: .normal)
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.nameLabel.text = app.name
|
||||||
|
cell.developerLabel.text = app.developerName
|
||||||
|
|
||||||
|
// Make sure refresh button is correct size.
|
||||||
|
cell.layoutIfNeeded()
|
||||||
|
|
||||||
|
switch numberOfDays
|
||||||
|
{
|
||||||
|
case 2...3: cell.refreshButton.tintColor = .refreshOrange
|
||||||
|
case 4...5: cell.refreshButton.tintColor = .refreshYellow
|
||||||
|
case 6...: cell.refreshButton.tintColor = .refreshGreen
|
||||||
|
default: cell.refreshButton.tintColor = .refreshRed
|
||||||
|
}
|
||||||
|
|
||||||
|
if let refreshGroup = self.refreshGroup, let progress = refreshGroup.progress(for: app), progress.fractionCompleted < 1.0
|
||||||
|
{
|
||||||
|
cell.refreshButton.progress = progress
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.refreshButton.progress = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataSource
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension MyAppsViewController
|
private extension MyAppsViewController
|
||||||
{
|
{
|
||||||
@IBAction func refreshAllApps(_ sender: UIBarButtonItem)
|
func update()
|
||||||
{
|
{
|
||||||
sender.isIndicatingActivity = true
|
if self.updatesDataSource.itemCount > 0
|
||||||
|
{
|
||||||
let installedApps = InstalledApp.fetchAppsForRefreshingAll(in: DatabaseManager.shared.viewContext)
|
self.navigationController?.tabBarItem.badgeValue = String(describing: self.updatesDataSource.itemCount)
|
||||||
|
}
|
||||||
self.refresh(installedApps) { (result) in
|
else
|
||||||
sender.isIndicatingActivity = false
|
{
|
||||||
|
self.navigationController?.tabBarItem.badgeValue = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,74 +225,60 @@ private extension MyAppsViewController
|
|||||||
{
|
{
|
||||||
func refresh()
|
func refresh()
|
||||||
{
|
{
|
||||||
if self.refreshGroup == nil
|
|
||||||
{
|
|
||||||
let toastView = RSTToastView(text: "Refreshing...", detailText: nil)
|
|
||||||
toastView.tintColor = .altPurple
|
|
||||||
toastView.activityIndicatorView.startAnimating()
|
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view)
|
|
||||||
}
|
|
||||||
|
|
||||||
let group = AppManager.shared.refresh(installedApps, presentingViewController: self, group: self.refreshGroup)
|
let group = AppManager.shared.refresh(installedApps, presentingViewController: self, group: self.refreshGroup)
|
||||||
group.completionHandler = { (result) in
|
group.completionHandler = { (result) in
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
let toastView = RSTToastView(text: error.localizedDescription, detailText: nil)
|
let toastView = ToastView(text: error.localizedDescription, detailText: nil)
|
||||||
toastView.tintColor = .red
|
toastView.tintColor = .refreshRed
|
||||||
|
toastView.setNeedsLayout()
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
||||||
|
|
||||||
self.refreshErrors = [:]
|
|
||||||
|
|
||||||
case .success(let results):
|
case .success(let results):
|
||||||
let failures = results.compactMapValues { $0.error }
|
let failures = results.compactMapValues { (result) -> Error? in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(OperationError.cancelled): return nil
|
||||||
|
case .failure(let error): return error
|
||||||
|
case .success: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if failures.isEmpty
|
guard !failures.isEmpty else { break }
|
||||||
|
|
||||||
|
let localizedText: String
|
||||||
|
if let failure = failures.first, failures.count == 1
|
||||||
{
|
{
|
||||||
let toastView = RSTToastView(text: NSLocalizedString("Successfully refreshed apps!", comment: ""), detailText: nil)
|
localizedText = failure.value.localizedDescription
|
||||||
toastView.tintColor = .altPurple
|
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
let localizedText: String
|
localizedText = String(format: NSLocalizedString("Failed to refresh %@ apps.", comment: ""), NSNumber(value: failures.count))
|
||||||
if failures.count == 1
|
|
||||||
{
|
|
||||||
localizedText = String(format: NSLocalizedString("Failed to refresh %@ app.", comment: ""), NSNumber(value: failures.count))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
localizedText = String(format: NSLocalizedString("Failed to refresh %@ apps.", comment: ""), NSNumber(value: failures.count))
|
|
||||||
}
|
|
||||||
|
|
||||||
let toastView = RSTToastView(text: localizedText, detailText: nil)
|
|
||||||
toastView.tintColor = .red
|
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.refreshErrors = failures
|
let toastView = ToastView(text: localizedText, detailText: nil)
|
||||||
|
toastView.tintColor = .refreshRed
|
||||||
|
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.progressView.observedProgress = nil
|
|
||||||
self.progressView.progress = 0.0
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
self.refreshGroup = nil
|
self.refreshGroup = nil
|
||||||
completionHandler(result)
|
completionHandler(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.progressView.observedProgress = group.progress
|
|
||||||
|
|
||||||
self.refreshGroup = group
|
self.refreshGroup = group
|
||||||
|
|
||||||
|
self.collectionView.reloadSections(IndexSet(integer: Section.installedApps.rawValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
if installedApps.contains(where: { $0.app.identifier == App.altstoreAppID })
|
if installedApps.contains(where: { $0.app.identifier == App.altstoreAppID })
|
||||||
{
|
{
|
||||||
let alertController = UIAlertController(title: NSLocalizedString("Refresh AltStore?", comment: ""), message: NSLocalizedString("AltStore will quit when it is finished refreshing.", comment: ""), preferredStyle: .alert)
|
let alertController = UIAlertController(title: NSLocalizedString("Refresh AltStore?", comment: ""), message: NSLocalizedString("AltStore will quit when it is finished refreshing.", comment: ""), preferredStyle: .alert)
|
||||||
alertController.addAction(.cancel)
|
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("Cancel"), style: .cancel) { (action) in
|
||||||
|
completionHandler(.failure(OperationError.cancelled))
|
||||||
|
})
|
||||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Refresh", comment: ""), style: .default) { (action) in
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Refresh", comment: ""), style: .default) { (action) in
|
||||||
refresh()
|
refresh()
|
||||||
})
|
})
|
||||||
@@ -212,50 +291,248 @@ private extension MyAppsViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MyAppsViewController
|
private extension MyAppsViewController
|
||||||
{
|
{
|
||||||
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
|
@IBAction func toggleAppUpdates(_ sender: UIButton)
|
||||||
{
|
{
|
||||||
let deleteAction = UITableViewRowAction(style: .destructive, title: "Delete") { (action, indexPath) in
|
let visibleCells = self.collectionView.visibleCells
|
||||||
let installedApp = self.dataSource.item(at: indexPath)
|
|
||||||
|
|
||||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
self.collectionView.performBatchUpdates({
|
||||||
let installedApp = context.object(with: installedApp.objectID) as! InstalledApp
|
|
||||||
context.delete(installedApp)
|
|
||||||
|
|
||||||
do
|
self.isUpdateSectionCollapsed.toggle()
|
||||||
|
|
||||||
|
UIView.animate(withDuration: 0.3, animations: {
|
||||||
|
if self.isUpdateSectionCollapsed
|
||||||
{
|
{
|
||||||
try context.save()
|
self.updatesDataSource.liveFetchLimit = maximumCollapsedUpdatesCount
|
||||||
|
self.expandedAppUpdates.removeAll()
|
||||||
|
|
||||||
|
for case let cell as UpdateCollectionViewCell in visibleCells
|
||||||
|
{
|
||||||
|
cell.mode = .collapsed
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cachedUpdateSizes.removeAll()
|
||||||
|
|
||||||
|
sender.titleLabel?.transform = .identity
|
||||||
}
|
}
|
||||||
catch
|
else
|
||||||
{
|
{
|
||||||
print("Failed to delete installed app.", error)
|
self.updatesDataSource.liveFetchLimit = 0
|
||||||
|
|
||||||
|
sender.titleLabel?.transform = CGAffineTransform.identity.rotated(by: .pi)
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
|
||||||
|
|
||||||
let refreshAction = UITableViewRowAction(style: .normal, title: "Refresh") { (action, indexPath) in
|
self.collectionView.collectionViewLayout.invalidateLayout()
|
||||||
let installedApp = self.dataSource.item(at: indexPath)
|
|
||||||
self.refresh([installedApp]) { (result) in
|
|
||||||
print("Refreshed", installedApp.app.identifier)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [deleteAction, refreshAction]
|
}, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
|
@IBAction func toggleUpdateCellMode(_ sender: UIButton)
|
||||||
{
|
{
|
||||||
}
|
let point = self.collectionView.convert(sender.center, from: sender.superview)
|
||||||
|
guard let indexPath = self.collectionView.indexPathForItem(at: point) else { return }
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let installedApp = self.dataSource.item(at: indexPath)
|
let installedApp = self.dataSource.item(at: indexPath)
|
||||||
|
|
||||||
guard let error = self.refreshErrors[installedApp.bundleIdentifier] else { return }
|
let cell = self.collectionView.cellForItem(at: indexPath) as? UpdateCollectionViewCell
|
||||||
|
|
||||||
let alertController = UIAlertController(title: "Failed to Refresh \(installedApp.app.name)", message: error.localizedDescription, preferredStyle: .alert)
|
if self.expandedAppUpdates.contains(installedApp.app.identifier)
|
||||||
alertController.addAction(.ok)
|
{
|
||||||
self.present(alertController, animated: true, completion: nil)
|
self.expandedAppUpdates.remove(installedApp.app.identifier)
|
||||||
|
cell?.mode = .collapsed
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.expandedAppUpdates.insert(installedApp.app.identifier)
|
||||||
|
cell?.mode = .expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cachedUpdateSizes[installedApp.app.identifier] = nil
|
||||||
|
|
||||||
|
self.collectionView.performBatchUpdates({
|
||||||
|
self.collectionView.collectionViewLayout.invalidateLayout()
|
||||||
|
}, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func refreshApp(_ sender: UIButton)
|
||||||
|
{
|
||||||
|
let point = self.collectionView.convert(sender.center, from: sender.superview)
|
||||||
|
guard let indexPath = self.collectionView.indexPathForItem(at: point) else { return }
|
||||||
|
|
||||||
|
let installedApp = self.dataSource.item(at: indexPath)
|
||||||
|
|
||||||
|
let previousProgress = AppManager.shared.refreshProgress(for: installedApp.app)
|
||||||
|
guard previousProgress == nil else {
|
||||||
|
previousProgress?.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.refresh([installedApp]) { (result) in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.collectionView.reloadSections(IndexSet(integer: Section.installedApps.rawValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func refreshAllApps(_ sender: UIBarButtonItem)
|
||||||
|
{
|
||||||
|
self.isRefreshingAllApps = true
|
||||||
|
self.collectionView.collectionViewLayout.invalidateLayout()
|
||||||
|
|
||||||
|
let installedApps = InstalledApp.fetchAppsForRefreshingAll(in: DatabaseManager.shared.viewContext)
|
||||||
|
|
||||||
|
self.refresh(installedApps) { (result) in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.isRefreshingAllApps = false
|
||||||
|
self.collectionView.reloadSections(IndexSet(integer: Section.installedApps.rawValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func updateApp(_ sender: UIButton)
|
||||||
|
{
|
||||||
|
let point = self.collectionView.convert(sender.center, from: sender.superview)
|
||||||
|
guard let indexPath = self.collectionView.indexPathForItem(at: point) else { return }
|
||||||
|
|
||||||
|
let app = self.dataSource.item(at: indexPath).app!
|
||||||
|
|
||||||
|
let previousProgress = AppManager.shared.installationProgress(for: app)
|
||||||
|
guard previousProgress == nil else {
|
||||||
|
previousProgress?.cancel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = AppManager.shared.install(app, presentingViewController: self) { (result) in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .failure(OperationError.cancelled):
|
||||||
|
self.collectionView.reloadItems(at: [indexPath])
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
let toastView = RSTToastView(text: "Failed to update \(app.name)", detailText: error.localizedDescription)
|
||||||
|
toastView.tintColor = .altGreen
|
||||||
|
toastView.show(in: self.navigationController!.view, duration: 2)
|
||||||
|
|
||||||
|
self.collectionView.reloadItems(at: [indexPath])
|
||||||
|
|
||||||
|
case .success:
|
||||||
|
print("Updated app:", app.identifier)
|
||||||
|
// No need to reload, since the the update cell is gone now.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.collectionView.reloadItems(at: [indexPath])
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func didFetchApps(_ notification: Notification)
|
||||||
|
{
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
if self.updatesDataSource.fetchedResultsController.fetchedObjects == nil
|
||||||
|
{
|
||||||
|
do { try self.updatesDataSource.fetchedResultsController.performFetch() }
|
||||||
|
catch { print("Error fetching:", error) }
|
||||||
|
}
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MyAppsViewController
|
||||||
|
{
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView
|
||||||
|
{
|
||||||
|
if indexPath.section == 0
|
||||||
|
{
|
||||||
|
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "UpdatesHeader", for: indexPath) as! UpdatesCollectionHeaderView
|
||||||
|
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
headerView.button.backgroundColor = UIColor.altGreen.withAlphaComponent(0.15)
|
||||||
|
headerView.button.setTitle("▾", for: .normal)
|
||||||
|
headerView.button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 28)
|
||||||
|
headerView.button.setTitleColor(.altGreen, for: .normal)
|
||||||
|
headerView.button.addTarget(self, action: #selector(MyAppsViewController.toggleAppUpdates), for: .primaryActionTriggered)
|
||||||
|
|
||||||
|
headerView.button.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerView
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let headerView = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "InstalledAppsHeader", for: indexPath) as! InstalledAppsCollectionHeaderView
|
||||||
|
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
headerView.textLabel.text = NSLocalizedString("Installed", comment: "")
|
||||||
|
|
||||||
|
headerView.button.isIndicatingActivity = false
|
||||||
|
headerView.button.activityIndicatorView.color = .altGreen
|
||||||
|
headerView.button.setTitle(NSLocalizedString("Refresh All", comment: ""), for: .normal)
|
||||||
|
headerView.button.addTarget(self, action: #selector(MyAppsViewController.refreshAllApps(_:)), for: .primaryActionTriggered)
|
||||||
|
headerView.button.isIndicatingActivity = self.isRefreshingAllApps
|
||||||
|
|
||||||
|
headerView.button.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerView
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MyAppsViewController: UICollectionViewDelegateFlowLayout
|
||||||
|
{
|
||||||
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
|
||||||
|
{
|
||||||
|
let section = Section.allCases[indexPath.section]
|
||||||
|
switch section
|
||||||
|
{
|
||||||
|
case .updates:
|
||||||
|
let item = self.dataSource.item(at: indexPath)
|
||||||
|
|
||||||
|
if let previousHeight = self.cachedUpdateSizes[item.app!.identifier]
|
||||||
|
{
|
||||||
|
return previousHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
let padding = 30 as CGFloat
|
||||||
|
let width = collectionView.bounds.width - padding
|
||||||
|
|
||||||
|
let widthConstraint = self.prototypeUpdateCell.contentView.widthAnchor.constraint(equalToConstant: width)
|
||||||
|
NSLayoutConstraint.activate([widthConstraint])
|
||||||
|
defer { NSLayoutConstraint.deactivate([widthConstraint]) }
|
||||||
|
|
||||||
|
self.dataSource.cellConfigurationHandler(self.prototypeUpdateCell, item, indexPath)
|
||||||
|
|
||||||
|
let size = self.prototypeUpdateCell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
|
||||||
|
self.cachedUpdateSizes[item.app!.identifier] = size
|
||||||
|
return size
|
||||||
|
|
||||||
|
case .installedApps:
|
||||||
|
return CGSize(width: collectionView.bounds.width, height: 60)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize
|
||||||
|
{
|
||||||
|
let section = Section.allCases[section]
|
||||||
|
switch section
|
||||||
|
{
|
||||||
|
case .updates: return CGSize(width: collectionView.bounds.width, height: 26)
|
||||||
|
case .installedApps: return CGSize(width: collectionView.bounds.width, height: 29)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets
|
||||||
|
{
|
||||||
|
let section = Section.allCases[section]
|
||||||
|
switch section
|
||||||
|
{
|
||||||
|
case .updates: return UIEdgeInsets(top: 12, left: 15, bottom: 20, right: 15)
|
||||||
|
case .installedApps: return UIEdgeInsets(top: 13, left: 0, bottom: 20, right: 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
121
AltStore/My Apps/UpdateCollectionViewCell.swift
Normal file
121
AltStore/My Apps/UpdateCollectionViewCell.swift
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
//
|
||||||
|
// UpdateCollectionViewCell.swift
|
||||||
|
// AltStore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 7/16/19.
|
||||||
|
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UpdateCollectionViewCell
|
||||||
|
{
|
||||||
|
enum Mode
|
||||||
|
{
|
||||||
|
case collapsed
|
||||||
|
case expanded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc class UpdateCollectionViewCell: UICollectionViewCell
|
||||||
|
{
|
||||||
|
var mode: Mode = .expanded {
|
||||||
|
didSet {
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet var appIconImageView: UIImageView!
|
||||||
|
@IBOutlet var nameLabel: UILabel!
|
||||||
|
@IBOutlet var dateLabel: UILabel!
|
||||||
|
@IBOutlet var updateButton: PillButton!
|
||||||
|
@IBOutlet var versionDescriptionTitleLabel: UILabel!
|
||||||
|
@IBOutlet var versionDescriptionTextView: UITextView!
|
||||||
|
|
||||||
|
@IBOutlet var moreButton: UIButton!
|
||||||
|
|
||||||
|
override func awakeFromNib()
|
||||||
|
{
|
||||||
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
self.contentView.layer.cornerRadius = 20
|
||||||
|
self.contentView.layer.masksToBounds = true
|
||||||
|
|
||||||
|
self.versionDescriptionTextView.textContainerInset = .zero
|
||||||
|
self.versionDescriptionTextView.textContainer.lineFragmentPadding = 0
|
||||||
|
self.versionDescriptionTextView.textContainer.lineBreakMode = .byTruncatingTail
|
||||||
|
self.versionDescriptionTextView.textContainer.heightTracksTextView = true
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tintColorDidChange()
|
||||||
|
{
|
||||||
|
super.tintColorDidChange()
|
||||||
|
|
||||||
|
self.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func layoutSubviews()
|
||||||
|
{
|
||||||
|
super.layoutSubviews()
|
||||||
|
|
||||||
|
let textContainer = self.versionDescriptionTextView.textContainer
|
||||||
|
|
||||||
|
switch self.mode
|
||||||
|
{
|
||||||
|
case .collapsed:
|
||||||
|
// Extra wide to make sure it wraps to next line.
|
||||||
|
let frame = CGRect(x: textContainer.size.width - self.moreButton.bounds.width - 8,
|
||||||
|
y: textContainer.size.height - 4,
|
||||||
|
width: textContainer.size.width,
|
||||||
|
height: textContainer.size.height)
|
||||||
|
|
||||||
|
textContainer.maximumNumberOfLines = 2
|
||||||
|
textContainer.exclusionPaths = [UIBezierPath(rect: frame)]
|
||||||
|
|
||||||
|
if let font = self.versionDescriptionTextView.font, self.versionDescriptionTextView.bounds.height > font.lineHeight * 1.5
|
||||||
|
{
|
||||||
|
self.moreButton.isHidden = false
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// One (or less) lines, so hide more button.
|
||||||
|
self.moreButton.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
case .expanded:
|
||||||
|
textContainer.maximumNumberOfLines = 10
|
||||||
|
textContainer.exclusionPaths = []
|
||||||
|
|
||||||
|
self.moreButton.isHidden = true
|
||||||
|
}
|
||||||
|
|
||||||
|
self.versionDescriptionTextView.invalidateIntrinsicContentSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes)
|
||||||
|
{
|
||||||
|
// Animates transition to new attributes.
|
||||||
|
let animator = UIViewPropertyAnimator(springTimingParameters: UISpringTimingParameters()) {
|
||||||
|
self.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
animator.startAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension UpdateCollectionViewCell
|
||||||
|
{
|
||||||
|
func update()
|
||||||
|
{
|
||||||
|
self.versionDescriptionTitleLabel.textColor = self.tintColor
|
||||||
|
self.contentView.backgroundColor = self.tintColor.withAlphaComponent(0.1)
|
||||||
|
|
||||||
|
self.updateButton.setTitleColor(self.tintColor, for: .normal)
|
||||||
|
self.updateButton.backgroundColor = self.tintColor.withAlphaComponent(0.15)
|
||||||
|
self.updateButton.progressTintColor = self.tintColor
|
||||||
|
|
||||||
|
self.setNeedsLayout()
|
||||||
|
self.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
}
|
||||||
136
AltStore/My Apps/UpdateCollectionViewCell.xib
Normal file
136
AltStore/My Apps/UpdateCollectionViewCell.xib
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||||
|
<device id="retina6_1" orientation="portrait">
|
||||||
|
<adaptation id="fullscreen"/>
|
||||||
|
</device>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||||
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="UpdateCell" id="Kqf-Pv-ca3" customClass="UpdateCollectionViewCell" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="133.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="133.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dmf-hv-bwx">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="133.5"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="57X-Ep-rfq">
|
||||||
|
<rect key="frame" x="20" y="20" width="340" height="93.5"/>
|
||||||
|
<subviews>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="11" translatesAutoresizingMaskIntoConstraints="NO" id="H0T-dR-3In" userLabel="App Info">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="340" height="65"/>
|
||||||
|
<subviews>
|
||||||
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="jg6-wi-ngb" customClass="AppIconImageView" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="65" height="65"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="65" id="W3C-hH-1Ii"/>
|
||||||
|
<constraint firstAttribute="width" secondItem="jg6-wi-ngb" secondAttribute="height" multiplier="1:1" id="vt3-Qt-m21"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="100" axis="vertical" spacing="2" translatesAutoresizingMaskIntoConstraints="NO" id="2Ii-Hu-4ru">
|
||||||
|
<rect key="frame" x="76" y="14" width="172" height="37"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="Short" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qmI-m4-Mra">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="172" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="100" verticalHuggingPriority="251" text="Developer" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xaB-Kc-Par">
|
||||||
|
<rect key="frame" x="0.0" y="22.5" width="172" height="14.5"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
|
||||||
|
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OSL-U2-BKa" customClass="PillButton" customModule="AltStore" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="259" y="17" width="81" height="31"/>
|
||||||
|
<color key="backgroundColor" red="0.22352941179999999" green="0.4941176471" blue="0.39607843139999999" alpha="0.10000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="81" id="3yj-p0-NuE"/>
|
||||||
|
<constraint firstAttribute="height" constant="31" id="KbP-M6-N3w"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
|
||||||
|
<state key="normal" title="UPDATE"/>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
<stackView opaque="NO" contentMode="scaleToFill" alignment="firstBaseline" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="RSR-5W-7tt" userLabel="Release Notes">
|
||||||
|
<rect key="frame" x="0.0" y="79" width="340" height="14.5"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="1000" verticalHuggingPriority="251" text="What's New" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="h1u-nj-qsP">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="65" height="13.5"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="65" id="C7Y-nh-TKJ"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||||
|
<nil key="textColor"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="Version Notes" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="rNs-2O-k3V">
|
||||||
|
<rect key="frame" x="75" y="-10" width="265" height="24.5"/>
|
||||||
|
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="13"/>
|
||||||
|
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||||
|
</textView>
|
||||||
|
</subviews>
|
||||||
|
</stackView>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="H0T-dR-3In" firstAttribute="width" secondItem="57X-Ep-rfq" secondAttribute="width" id="DYI-fa-Egk"/>
|
||||||
|
<constraint firstItem="RSR-5W-7tt" firstAttribute="width" secondItem="57X-Ep-rfq" secondAttribute="width" id="d3x-mH-ODQ"/>
|
||||||
|
</constraints>
|
||||||
|
</stackView>
|
||||||
|
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QTy-uj-lKA">
|
||||||
|
<rect key="frame" x="323" y="99" width="32" height="14.5"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="14.5" id="pWr-Y1-ZW8"/>
|
||||||
|
</constraints>
|
||||||
|
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="13"/>
|
||||||
|
<state key="normal" title="More"/>
|
||||||
|
</button>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="57X-Ep-rfq" secondAttribute="bottom" constant="20" id="ArC-R2-jtc"/>
|
||||||
|
<constraint firstAttribute="trailingMargin" secondItem="QTy-uj-lKA" secondAttribute="trailing" id="Otb-0P-uKP"/>
|
||||||
|
<constraint firstItem="57X-Ep-rfq" firstAttribute="leading" secondItem="mdL-JE-wCe" secondAttribute="leading" constant="20" id="PvV-gg-7us"/>
|
||||||
|
<constraint firstItem="57X-Ep-rfq" firstAttribute="top" secondItem="dmf-hv-bwx" secondAttribute="top" constant="20" id="QHM-k8-g0x"/>
|
||||||
|
<constraint firstItem="QTy-uj-lKA" firstAttribute="bottom" secondItem="rNs-2O-k3V" secondAttribute="bottom" id="mF3-ad-Fl5"/>
|
||||||
|
<constraint firstItem="mdL-JE-wCe" firstAttribute="trailing" secondItem="57X-Ep-rfq" secondAttribute="trailing" constant="15" id="sGL-bx-qIk"/>
|
||||||
|
</constraints>
|
||||||
|
<edgeInsets key="layoutMargins" top="20" left="20" bottom="20" right="20"/>
|
||||||
|
<viewLayoutGuide key="safeArea" id="mdL-JE-wCe"/>
|
||||||
|
</view>
|
||||||
|
</subviews>
|
||||||
|
</view>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="dmf-hv-bwx" firstAttribute="top" secondItem="Kqf-Pv-ca3" secondAttribute="top" id="7yY-05-eHt"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="dmf-hv-bwx" secondAttribute="bottom" id="Rrx-0k-6He"/>
|
||||||
|
<constraint firstItem="dmf-hv-bwx" firstAttribute="leading" secondItem="Kqf-Pv-ca3" secondAttribute="leading" id="W0V-sT-tXo"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="dmf-hv-bwx" secondAttribute="trailing" id="tgy-Zi-iZF"/>
|
||||||
|
</constraints>
|
||||||
|
<viewLayoutGuide key="safeArea" id="C6r-zO-INg"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="appIconImageView" destination="jg6-wi-ngb" id="j83-Dl-GT6"/>
|
||||||
|
<outlet property="dateLabel" destination="xaB-Kc-Par" id="mfG-3C-r7j"/>
|
||||||
|
<outlet property="moreButton" destination="QTy-uj-lKA" id="UME-5m-Dqe"/>
|
||||||
|
<outlet property="nameLabel" destination="qmI-m4-Mra" id="LQz-w7-HNb"/>
|
||||||
|
<outlet property="updateButton" destination="OSL-U2-BKa" id="WbI-96-Nel"/>
|
||||||
|
<outlet property="versionDescriptionTextView" destination="rNs-2O-k3V" id="4TC-A3-oxb"/>
|
||||||
|
<outlet property="versionDescriptionTitleLabel" destination="h1u-nj-qsP" id="dnz-Yv-BdY"/>
|
||||||
|
</connections>
|
||||||
|
<point key="canvasLocation" x="618.39999999999998" y="96.251874062968525"/>
|
||||||
|
</collectionViewCell>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
@@ -55,6 +55,16 @@ class InstallAppOperation: ResultOperation<Void>
|
|||||||
case .success:
|
case .success:
|
||||||
|
|
||||||
self.receive(from: connection, server: server) { (result) in
|
self.receive(from: connection, server: server) { (result) in
|
||||||
|
switch result
|
||||||
|
{
|
||||||
|
case .success:
|
||||||
|
installedApp.managedObjectContext?.performAndWait {
|
||||||
|
installedApp.refreshedDate = Date()
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure: break
|
||||||
|
}
|
||||||
|
|
||||||
self.finish(result)
|
self.finish(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +85,7 @@ class InstallAppOperation: ResultOperation<Void>
|
|||||||
}
|
}
|
||||||
else if response.progress == 1.0
|
else if response.progress == 1.0
|
||||||
{
|
{
|
||||||
|
self.progress.completedUnitCount = self.progress.totalUnitCount
|
||||||
self.finish(.success(()))
|
self.finish(.success(()))
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "52",
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "89",
|
||||||
|
"green" : "199"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "255",
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0",
|
||||||
|
"green" : "149"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "255",
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "48",
|
||||||
|
"green" : "59"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"red" : "255",
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0",
|
||||||
|
"green" : "204"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
//
|
|
||||||
// UpdatesViewController.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 5/20/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import Roxas
|
|
||||||
|
|
||||||
class UpdatesViewController: UITableViewController
|
|
||||||
{
|
|
||||||
private lazy var dataSource = self.makeDataSource()
|
|
||||||
|
|
||||||
private lazy var dateFormatter: DateFormatter = {
|
|
||||||
let dateFormatter = DateFormatter()
|
|
||||||
dateFormatter.dateStyle = .short
|
|
||||||
dateFormatter.timeStyle = .none
|
|
||||||
return dateFormatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
@IBOutlet private var progressView: UIProgressView!
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
|
||||||
{
|
|
||||||
super.init(coder: aDecoder)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(UpdatesViewController.didFetchApps(_:)), name: AppManager.didFetchAppsNotification, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.tableView.dataSource = self.dataSource
|
|
||||||
|
|
||||||
if let navigationBar = self.navigationController?.navigationBar
|
|
||||||
{
|
|
||||||
self.progressView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
navigationBar.addSubview(self.progressView)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.progressView.widthAnchor.constraint(equalTo: navigationBar.widthAnchor),
|
|
||||||
self.progressView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
|
||||||
{
|
|
||||||
guard segue.identifier == "showAppDetail" else { return }
|
|
||||||
|
|
||||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
|
||||||
|
|
||||||
let installedApp = self.dataSource.item(at: indexPath)
|
|
||||||
guard let app = installedApp.app else { return }
|
|
||||||
|
|
||||||
let appDetailViewController = segue.destination as! AppDetailViewController
|
|
||||||
appDetailViewController.app = app
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension UpdatesViewController
|
|
||||||
{
|
|
||||||
func makeDataSource() -> RSTFetchedResultsTableViewDataSource<InstalledApp>
|
|
||||||
{
|
|
||||||
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
|
||||||
fetchRequest.predicate = NSPredicate(format: "%K != %K", #keyPath(InstalledApp.version), #keyPath(InstalledApp.app.version))
|
|
||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.app?.versionDate, ascending: false)]
|
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
|
||||||
|
|
||||||
let dataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
|
||||||
dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in
|
|
||||||
guard let app = installedApp.app else { return }
|
|
||||||
|
|
||||||
cell.textLabel?.text = app.name + " (\(app.version))"
|
|
||||||
|
|
||||||
let detailText = self.dateFormatter.string(from: app.versionDate) + "\n\n" + (app.versionDescription ?? "No Update Description")
|
|
||||||
|
|
||||||
cell.detailTextLabel?.numberOfLines = 0
|
|
||||||
cell.detailTextLabel?.text = detailText
|
|
||||||
}
|
|
||||||
|
|
||||||
let placeholderView = RSTPlaceholderView()
|
|
||||||
placeholderView.textLabel.text = NSLocalizedString("No Updates", comment: "")
|
|
||||||
placeholderView.detailTextLabel.text = NSLocalizedString("There are no app updates at this time.", comment: "")
|
|
||||||
dataSource.placeholderView = placeholderView
|
|
||||||
|
|
||||||
return dataSource
|
|
||||||
}
|
|
||||||
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
if let count = self.dataSource.fetchedResultsController.fetchedObjects?.count, count > 0
|
|
||||||
{
|
|
||||||
self.navigationController?.tabBarItem.badgeValue = String(describing: count)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.navigationController?.tabBarItem.badgeValue = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension UpdatesViewController
|
|
||||||
{
|
|
||||||
func update(_ installedApp: InstalledApp)
|
|
||||||
{
|
|
||||||
func updateApp()
|
|
||||||
{
|
|
||||||
let toastView = RSTToastView(text: "Updating...", detailText: nil)
|
|
||||||
toastView.tintColor = .altPurple
|
|
||||||
toastView.activityIndicatorView.startAnimating()
|
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view)
|
|
||||||
|
|
||||||
let progress = AppManager.shared.install(installedApp.app, presentingViewController: self) { (result) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
_ = try result.get()
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let installedApp = DatabaseManager.shared.persistentContainer.viewContext.object(with: installedApp.objectID) as! InstalledApp
|
|
||||||
|
|
||||||
let toastView = RSTToastView(text: "Updated \(installedApp.app.name) to version \(installedApp.version)!", detailText: nil)
|
|
||||||
toastView.tintColor = .altPurple
|
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let toastView = RSTToastView(text: "Failed to update \(installedApp.app.name)", detailText: error.localizedDescription)
|
|
||||||
toastView.tintColor = .altPurple
|
|
||||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.progressView.observedProgress = nil
|
|
||||||
self.progressView.progress = 0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.progressView.observedProgress = progress
|
|
||||||
}
|
|
||||||
|
|
||||||
if installedApp.app.identifier == App.altstoreAppID
|
|
||||||
{
|
|
||||||
let alertController = UIAlertController(title: NSLocalizedString("Update AltStore?", comment: ""), message: NSLocalizedString("AltStore will quit upon completion.", comment: ""), preferredStyle: .alert)
|
|
||||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Update and Quit", comment: ""), style: .default, handler: { (action) in
|
|
||||||
updateApp()
|
|
||||||
}))
|
|
||||||
alertController.addAction(.cancel)
|
|
||||||
|
|
||||||
self.present(alertController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
updateApp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func didFetchApps(_ notification: Notification)
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if self.dataSource.fetchedResultsController.fetchedObjects == nil
|
|
||||||
{
|
|
||||||
do { try self.dataSource.fetchedResultsController.performFetch() }
|
|
||||||
catch { print("Error fetching:", error) }
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UpdatesViewController
|
|
||||||
{
|
|
||||||
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?
|
|
||||||
{
|
|
||||||
let updateAction = UITableViewRowAction(style: .normal, title: "Update") { [weak self] (action, indexPath) in
|
|
||||||
guard let installedApp = self?.dataSource.item(at: indexPath) else { return }
|
|
||||||
self?.update(installedApp)
|
|
||||||
}
|
|
||||||
updateAction.backgroundColor = .altPurple
|
|
||||||
|
|
||||||
return [updateAction]
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
Dependencies/Roxas
vendored
2
Dependencies/Roxas
vendored
Submodule Dependencies/Roxas updated: 7ceaa403a2...2642cb76c5
Reference in New Issue
Block a user