[AltWidget] Initial version

This commit is contained in:
Riley Testut
2020-09-15 13:51:29 -07:00
parent 669c6f5bf4
commit 5abf7a5a11
21 changed files with 1042 additions and 0 deletions

View File

@@ -31,6 +31,7 @@
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E3129229F474900370A3C /* RequestHandler.swift */; };
BF29012F2318F6B100D88A45 /* AppBannerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF29012E2318F6B100D88A45 /* AppBannerView.xib */; };
BF2901312318F7A800D88A45 /* AppBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2901302318F7A800D88A45 /* AppBannerView.swift */; };
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; };
BF3432FB246B894F0052F4A1 /* BackupAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3432FA246B894F0052F4A1 /* BackupAppOperation.swift */; };
BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3BEFBE2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift */; };
BF3BEFC124086A1E00DE7D55 /* RefreshAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3BEFC024086A1E00DE7D55 /* RefreshAppOperation.swift */; };
@@ -40,6 +41,7 @@
BF3F786422CAA41E008FBD20 /* ALTDeviceManager+Installation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3F786322CAA41E008FBD20 /* ALTDeviceManager+Installation.swift */; };
BF41B806233423AE00C593A3 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF41B805233423AE00C593A3 /* TabBarController.swift */; };
BF41B808233433C100C593A3 /* LoadingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF41B807233433C100C593A3 /* LoadingState.swift */; };
BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF42345825101C1D006D1EB2 /* WidgetView.swift */; };
BF42345C251024B0006D1EB2 /* AltSign-Static in Frameworks */ = {isa = PBXBuildFile; productRef = BF42345B251024B0006D1EB2 /* AltSign-Static */; };
BF42345D25102688006D1EB2 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF088D322501A4FF008082D9 /* OpenSSL.xcframework */; };
BF44CC6C232AEB90004DA9C3 /* LaunchAtLogin.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */; };
@@ -193,6 +195,10 @@
BF8CAE4E248AEABA004D6CCE /* UIDevice+Jailbreak.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */; };
BF8F69C222E659F700049BA1 /* AppContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C122E659F700049BA1 /* AppContentViewController.swift */; };
BF8F69C422E662D300049BA1 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F69C322E662D300049BA1 /* AppViewController.swift */; };
BF989171250AABF4002ACF50 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF989170250AABF4002ACF50 /* Assets.xcassets */; };
BF989177250AABF4002ACF50 /* AltWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF98917C250AAC4F002ACF50 /* Countdown.swift */; };
BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF98917D250AAC4F002ACF50 /* AltWidget.swift */; };
BF989184250AACFC002ACF50 /* Date+RelativeDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB5B1522EE90D300F74113 /* Date+RelativeDate.swift */; };
BF989185250AAD1D002ACF50 /* UIColor+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */; };
BF9ABA4522DCFF43008935CF /* BrowseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */; };
@@ -223,6 +229,7 @@
BFB6B21E231870160022A802 /* NewsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21D231870160022A802 /* NewsViewController.swift */; };
BFB6B220231870B00022A802 /* NewsCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21F231870B00022A802 /* NewsCollectionViewCell.swift */; };
BFB6B22423187A3D0022A802 /* NewsCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFB6B22323187A3D0022A802 /* NewsCollectionViewCell.xib */; };
BFBE0004250ACFFB0080826E /* ViewApp.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */; settings = {ATTRIBUTES = (no_codegen, ); }; };
BFC1F38D22AEE3A4003AC21A /* DownloadAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F38C22AEE3A4003AC21A /* DownloadAppOperation.swift */; };
BFC57A652416C72400EB891E /* DeactivateAppOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A642416C72400EB891E /* DeactivateAppOperation.swift */; };
BFC57A6E2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC57A6D2416FC5D00EB891E /* InstalledAppsCollectionHeaderView.swift */; };
@@ -318,6 +325,7 @@
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; };
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */; };
BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */; };
BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; };
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; };
BFFCFA582488648D0077BFCE /* LocalConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF10EB3124870B3F0055E6DB /* LocalConnectionHandler.swift */; };
/* End PBXBuildFile section */
@@ -337,6 +345,13 @@
remoteGlobalIDString = BF66EE7D2501AE50007EE018;
remoteInfo = AltStoreCore;
};
BF989175250AABF4002ACF50 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFD247622284B9A500981D42 /* Project object */;
proxyType = 1;
remoteGlobalIDString = BF989166250AABF3002ACF50;
remoteInfo = AltWidgetExtension;
};
BFBFFB262380C72F00993A4A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFD247622284B9A500981D42 /* Project object */;
@@ -344,6 +359,13 @@
remoteGlobalIDString = BF5C5FC4237DF5AE00EDD0C6;
remoteInfo = AltPlugin;
};
BFF615AA2510042B00484D3B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFD247622284B9A500981D42 /* Project object */;
proxyType = 1;
remoteGlobalIDString = BF66EE7D2501AE50007EE018;
remoteInfo = AltStoreCore;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -382,6 +404,17 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
BF98917B250AABF4002ACF50 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
BF989177250AABF4002ACF50 /* AltWidgetExtension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@@ -425,6 +458,7 @@
BF3F786322CAA41E008FBD20 /* ALTDeviceManager+Installation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTDeviceManager+Installation.swift"; sourceTree = "<group>"; };
BF41B805233423AE00C593A3 /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = "<group>"; };
BF41B807233433C100C593A3 /* LoadingState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingState.swift; sourceTree = "<group>"; };
BF42345825101C1D006D1EB2 /* WidgetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetView.swift; sourceTree = "<group>"; };
BF44CC6A232AEB74004DA9C3 /* LaunchAtLogin.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LaunchAtLogin.framework; path = Carthage/Build/Mac/LaunchAtLogin.framework; sourceTree = "<group>"; };
BF44EEEF246B08BA002A52F2 /* BackupController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupController.swift; sourceTree = "<group>"; };
BF44EEF2246B3A17002A52F2 /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; path = AltBackup.ipa; sourceTree = "<group>"; };
@@ -592,6 +626,7 @@
BF770E6622BD57C3002A40FE /* BackgroundTaskManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundTaskManager.swift; sourceTree = "<group>"; };
BF770E6822BD57DD002A40FE /* Silence.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; path = Silence.m4a; sourceTree = "<group>"; };
BF88F97124F8727D00BB75DF /* AppManagerErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManagerErrors.swift; sourceTree = "<group>"; };
BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltWidgetExtension.entitlements; sourceTree = "<group>"; };
BF8CAE422489E772004D6CCE /* AnisetteDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnisetteDataManager.swift; sourceTree = "<group>"; };
BF8CAE432489E772004D6CCE /* AppManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
BF8CAE442489E772004D6CCE /* RequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestHandler.swift; sourceTree = "<group>"; };
@@ -599,6 +634,12 @@
BF8CAE4D248AEABA004D6CCE /* UIDevice+Jailbreak.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Jailbreak.swift"; sourceTree = "<group>"; };
BF8F69C122E659F700049BA1 /* AppContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContentViewController.swift; sourceTree = "<group>"; };
BF8F69C322E662D300049BA1 /* AppViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = "<group>"; };
BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AltWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
BF989170250AABF4002ACF50 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
BF989172250AABF4002ACF50 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BF98917C250AAC4F002ACF50 /* Countdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Countdown.swift; sourceTree = "<group>"; };
BF98917D250AAC4F002ACF50 /* AltWidget.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AltWidget.swift; sourceTree = "<group>"; };
BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; path = ViewApp.intentdefinition; sourceTree = "<group>"; };
BF9ABA4422DCFF43008935CF /* BrowseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowseViewController.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>"; };
@@ -759,6 +800,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
BF989164250AABF3002ACF50 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFD247672284B9A500981D42 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -1054,6 +1103,7 @@
BF66EEE32501AED0007EE018 /* Extensions */,
BF66EEAA2501AECA007EE018 /* Model */,
BF66EE9F2501AEC5007EE018 /* Patreon */,
BF98918B250AAE18002ACF50 /* Intents */,
BF66EE9A2501AEC1007EE018 /* Protocols */,
BF66EE8D2501AEBC007EE018 /* Types */,
BF66EE812501AE50007EE018 /* Info.plist */,
@@ -1217,6 +1267,27 @@
name = "Supporting Files";
sourceTree = "<group>";
};
BF98916C250AABF3002ACF50 /* AltWidget */ = {
isa = PBXGroup;
children = (
BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */,
BF98917D250AAC4F002ACF50 /* AltWidget.swift */,
BF42345825101C1D006D1EB2 /* WidgetView.swift */,
BF98917C250AAC4F002ACF50 /* Countdown.swift */,
BF989170250AABF4002ACF50 /* Assets.xcassets */,
BF989172250AABF4002ACF50 /* Info.plist */,
);
path = AltWidget;
sourceTree = "<group>";
};
BF98918B250AAE18002ACF50 /* Intents */ = {
isa = PBXGroup;
children = (
BF989191250AAE86002ACF50 /* ViewApp.intentdefinition */,
);
path = Intents;
sourceTree = "<group>";
};
BF9ABA4322DCFF33008935CF /* Browse */ = {
isa = PBXGroup;
children = (
@@ -1288,6 +1359,7 @@
BF5C5FC6237DF5AE00EDD0C6 /* AltPlugin */,
BF58047C246A28F7008AE704 /* AltBackup */,
BF18BFE824857D7900DD5981 /* AltDaemon */,
BF98916C250AABF3002ACF50 /* AltWidget */,
BFD247852284BB3300981D42 /* Frameworks */,
BFD2476B2284B9A500981D42 /* Products */,
4460E048E3AC1C9708C4FA33 /* Pods */,
@@ -1304,6 +1376,7 @@
BF58047B246A28F7008AE704 /* AltBackup.app */,
BF18BFE724857D7900DD5981 /* AltDaemon */,
BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */,
BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -1716,6 +1789,24 @@
productReference = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */;
productType = "com.apple.product-type.framework";
};
BF989166250AABF3002ACF50 /* AltWidgetExtension */ = {
isa = PBXNativeTarget;
buildConfigurationList = BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "AltWidgetExtension" */;
buildPhases = (
BF989163250AABF3002ACF50 /* Sources */,
BF989164250AABF3002ACF50 /* Frameworks */,
BF989165250AABF3002ACF50 /* Resources */,
);
buildRules = (
);
dependencies = (
BFF615AB2510042B00484D3B /* PBXTargetDependency */,
);
name = AltWidgetExtension;
productName = AltWidgetExtension;
productReference = BF989167250AABF3002ACF50 /* AltWidgetExtension.appex */;
productType = "com.apple.product-type.app-extension";
};
BFD247692284B9A500981D42 /* AltStore */ = {
isa = PBXNativeTarget;
buildConfigurationList = BFD2477E2284B9A700981D42 /* Build configuration list for PBXNativeTarget "AltStore" */;
@@ -1726,11 +1817,13 @@
BFD247682284B9A500981D42 /* Resources */,
BF088D2B2501A087008082D9 /* Embed Frameworks */,
744AE3B03F6BF664FC5705C5 /* [CP] Embed Pods Frameworks */,
BF98917B250AABF4002ACF50 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
BF66EE842501AE50007EE018 /* PBXTargetDependency */,
BF989176250AABF4002ACF50 /* PBXTargetDependency */,
);
name = AltStore;
productName = AltStore;
@@ -1776,6 +1869,10 @@
BF66EE7D2501AE50007EE018 = {
CreatedOnToolsVersion = 12.0;
};
BF989166250AABF3002ACF50 = {
CreatedOnToolsVersion = 12.0;
LastSwiftMigration = 1200;
};
BFD247692284B9A500981D42 = {
CreatedOnToolsVersion = 10.2.1;
LastSwiftMigration = 1020;
@@ -1810,6 +1907,7 @@
BF58047A246A28F7008AE704 /* AltBackup */,
BF18BFE624857D7900DD5981 /* AltDaemon */,
BF66EE7D2501AE50007EE018 /* AltStoreCore */,
BF989166250AABF3002ACF50 /* AltWidgetExtension */,
);
};
/* End PBXProject section */
@@ -1848,6 +1946,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
BF989165250AABF3002ACF50 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BF989171250AABF4002ACF50 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFD247682284B9A500981D42 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -2155,6 +2261,7 @@
BFAECC5B2501B0A400528F27 /* Bundle+AltStore.swift in Sources */,
BF66EECD2501AECA007EE018 /* StoreAppPolicy.swift in Sources */,
BF66EEE82501AED0007EE018 /* UserDefaults+AltStore.swift in Sources */,
BF340E9A250AD39500A192CB /* ViewApp.intentdefinition in Sources */,
BFAECC522501B0A400528F27 /* CodableServerError.swift in Sources */,
BF66EE9E2501AEC1007EE018 /* Fetchable.swift in Sources */,
BF66EEDF2501AECA007EE018 /* PatreonAccount.swift in Sources */,
@@ -2204,6 +2311,16 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
BF989163250AABF3002ACF50 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */,
BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */,
BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFD247662284B9A500981D42 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -2281,6 +2398,7 @@
BFB3645A2325985F00CD0EB1 /* FindServerOperation.swift in Sources */,
BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */,
BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */,
BFBE0004250ACFFB0080826E /* ViewApp.intentdefinition in Sources */,
BF770E5622BC3C03002A40FE /* Server.swift in Sources */,
BFA8172923C56042001B5953 /* ServerConnection.swift in Sources */,
BF56D2AF23DF9E310006506D /* AppIDsViewController.swift in Sources */,
@@ -2300,11 +2418,21 @@
target = BF66EE7D2501AE50007EE018 /* AltStoreCore */;
targetProxy = BF66EE832501AE50007EE018 /* PBXContainerItemProxy */;
};
BF989176250AABF4002ACF50 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = BF989166250AABF3002ACF50 /* AltWidgetExtension */;
targetProxy = BF989175250AABF4002ACF50 /* PBXContainerItemProxy */;
};
BFBFFB272380C72F00993A4A /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = BF5C5FC4237DF5AE00EDD0C6 /* AltPlugin */;
targetProxy = BFBFFB262380C72F00993A4A /* PBXContainerItemProxy */;
};
BFF615AB2510042B00484D3B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = BF66EE7D2501AE50007EE018 /* AltStoreCore */;
targetProxy = BFF615AA2510042B00484D3B /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -2772,6 +2900,72 @@
};
name = Release;
};
BF989179250AABF4002ACF50 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = 6XVY5G3U44;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/iossimulator",
"$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/ios",
);
INFOPLIST_FILE = AltWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore.AltWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
BF98917A250AABF4002ACF50 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6XVY5G3U44;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/iossimulator",
"$(PROJECT_DIR)/Dependencies/AltSign/Dependencies/OpenSSL/Frameworks/ios",
);
INFOPLIST_FILE = AltWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.AltStore.AltWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
BFD2477C2284B9A700981D42 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -3014,6 +3208,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "AltWidgetExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BF989179250AABF4002ACF50 /* Debug */,
BF98917A250AABF4002ACF50 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
BFD247652284B9A500981D42 /* Build configuration list for PBXProject "AltStore" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -12,6 +12,7 @@ import UserNotifications
import MobileCoreServices
import Intents
import Combine
import WidgetKit
import AltStoreCore
import AltSign
@@ -1346,6 +1347,16 @@ private extension AppManager
AnalyticsManager.shared.trackEvent(event)
}
if #available(iOS 14, *)
{
WidgetCenter.shared.getCurrentConfigurations { (result) in
guard case .success(let widgets) = result else { return }
guard let widget = widgets.first(where: { $0.configuration is ViewAppIntent }) else { return }
WidgetCenter.shared.reloadTimelines(ofKind: widget.kind)
}
}
do { try installedApp.managedObjectContext?.save() }
catch { print("Error saving installed app.", error) }
}

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>INEnums</key>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.2</string>
<key>INIntentDefinitionNamespace</key>
<string>TPucbY</string>
<key>INIntentDefinitionSystemVersion</key>
<string>20A5354i</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>12A8189n</string>
<key>INIntentDefinitionToolsVersion</key>
<string>12.0</string>
<key>INIntents</key>
<array>
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentConfigurable</key>
<true/>
<key>INIntentDescription</key>
<string>Select App</string>
<key>INIntentDescriptionID</key>
<string>sb9c7F</string>
<key>INIntentEligibleForWidgets</key>
<true/>
<key>INIntentIneligibleForSuggestions</key>
<true/>
<key>INIntentLastParameterTag</key>
<integer>2</integer>
<key>INIntentManagedParameterCombinations</key>
<dict>
<key>app</key>
<dict>
<key>INIntentParameterCombinationSupportsBackgroundExecution</key>
<true/>
<key>INIntentParameterCombinationUpdatesLinked</key>
<true/>
</dict>
</dict>
<key>INIntentName</key>
<string>ViewApp</string>
<key>INIntentParameters</key>
<array>
<dict>
<key>INIntentParameterConfigurable</key>
<true/>
<key>INIntentParameterDisplayName</key>
<string>App</string>
<key>INIntentParameterDisplayNameID</key>
<string>QwLCXY</string>
<key>INIntentParameterDisplayPriority</key>
<integer>1</integer>
<key>INIntentParameterName</key>
<string>app</string>
<key>INIntentParameterObjectType</key>
<string>App</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>TPucbY</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Configuration</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
</array>
<key>INIntentParameterSupportsDynamicEnumeration</key>
<true/>
<key>INIntentParameterTag</key>
<integer>2</integer>
<key>INIntentParameterType</key>
<string>Object</string>
</dict>
</array>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
<dict>
<key>INIntentResponseCodeName</key>
<string>failure</string>
</dict>
</array>
</dict>
<key>INIntentTitle</key>
<string>View App</string>
<key>INIntentTitleID</key>
<string>7aGoWn</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>View</string>
</dict>
</array>
<key>INTypes</key>
<array>
<dict>
<key>INTypeDisplayName</key>
<string>App</string>
<key>INTypeDisplayNameID</key>
<string>cUl1NZ</string>
<key>INTypeLastPropertyTag</key>
<integer>99</integer>
<key>INTypeName</key>
<string>App</string>
<key>INTypeProperties</key>
<array>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>1</integer>
<key>INTypePropertyName</key>
<string>identifier</string>
<key>INTypePropertyTag</key>
<integer>1</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>2</integer>
<key>INTypePropertyName</key>
<string>displayString</string>
<key>INTypePropertyTag</key>
<integer>2</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>3</integer>
<key>INTypePropertyName</key>
<string>pronunciationHint</string>
<key>INTypePropertyTag</key>
<integer>3</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>4</integer>
<key>INTypePropertyName</key>
<string>alternativeSpeakableMatches</string>
<key>INTypePropertySupportsMultipleValues</key>
<true/>
<key>INTypePropertyTag</key>
<integer>4</integer>
<key>INTypePropertyType</key>
<string>SpeakableString</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -145,6 +145,8 @@ private extension DatabaseManager
{
func prepareDatabase(completionHandler: @escaping (Result<Void, Error>) -> Void)
{
guard !Bundle.isAppExtension() else { return completionHandler(.success(())) }
let context = self.persistentContainer.newBackgroundContext()
context.performAndWait {
guard let localApp = ALTApplication(fileURL: Bundle.main.bundleURL) else { return }

173
AltWidget/AltWidget.swift Normal file
View File

@@ -0,0 +1,173 @@
//
// AltWidget.swift
// AltWidget
//
// Created by Riley Testut on 6/26/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import SwiftUI
import WidgetKit
import UIKit
import CoreData
import AltStoreCore
import AltSign
struct AppEntry: TimelineEntry
{
var date: Date
var relevance: TimelineEntryRelevance?
var app: AppSnapshot?
var isPlaceholder: Bool = false
}
struct AppSnapshot
{
var name: String
var bundleIdentifier: String
var expirationDate: Date
var refreshedDate: Date
var tintColor: UIColor?
var icon: UIImage?
}
extension AppSnapshot
{
// Declared in extension so we retain synthesized initializer.
init(installedApp: InstalledApp)
{
self.name = installedApp.name
self.bundleIdentifier = installedApp.bundleIdentifier
self.expirationDate = installedApp.expirationDate
self.refreshedDate = installedApp.refreshedDate
self.tintColor = installedApp.storeApp?.tintColor
let application = ALTApplication(fileURL: installedApp.fileURL)
self.icon = application?.icon?.resizing(toFill: CGSize(width: 180, height: 180))
}
}
struct Provider: IntentTimelineProvider
{
typealias Intent = ViewAppIntent
typealias Entry = AppEntry
func placeholder(in context: Context) -> AppEntry
{
return AppEntry(date: Date(), app: nil, isPlaceholder: true)
}
func getSnapshot(for configuration: ViewAppIntent, in context: Context, completion: @escaping (AppEntry) -> Void)
{
self.prepare { (result) in
do
{
let context = try result.get()
let snapshot = InstalledApp.fetchAltStore(in: context).map(AppSnapshot.init)
let entry = AppEntry(date: Date(), app: snapshot)
completion(entry)
}
catch
{
print("Error preparing widget snapshot:", error)
let entry = AppEntry(date: Date(), app: nil)
completion(entry)
}
}
}
func getTimeline(for configuration: ViewAppIntent, in context: Context, completion: @escaping (Timeline<AppEntry>) -> Void) {
self.prepare { (result) in
autoreleasepool {
do
{
let context = try result.get()
let installedApp: InstalledApp?
if let identifier = configuration.app?.identifier
{
let app = InstalledApp.first(satisfying: NSPredicate(format: "%K == %@", #keyPath(InstalledApp.bundleIdentifier), identifier),
in: context)
installedApp = app
}
else
{
installedApp = InstalledApp.fetchAltStore(in: context)
}
let snapshot = installedApp.map(AppSnapshot.init)
var entries: [AppEntry] = []
// Generate a timeline consisting of one entry per day.
if let snapshot = snapshot
{
let currentDate = Calendar.current.startOfDay(for: Date())
let numberOfDays = snapshot.expirationDate.numberOfCalendarDays(since: currentDate)
for dayOffset in 0 ..< min(numberOfDays, 7)
{
guard let entryDate = Calendar.current.date(byAdding: .day, value: dayOffset, to: currentDate) else { continue }
let score = Float(dayOffset + 1) / Float(numberOfDays)
let entry = AppEntry(date: entryDate, relevance: TimelineEntryRelevance(score: score), app: snapshot)
entries.append(entry)
}
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
catch
{
print("Error preparing widget timeline:", error)
let entry = AppEntry(date: Date(), app: nil)
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
}
}
private func prepare(completion: @escaping (Result<NSManagedObjectContext, Error>) -> Void)
{
DatabaseManager.shared.start { (error) in
if let error = error
{
completion(.failure(error))
}
else
{
DatabaseManager.shared.viewContext.perform {
completion(.success(DatabaseManager.shared.viewContext))
}
}
}
}
}
@main
struct AltWidget: Widget
{
private let kind: String = "AppDetail"
public var body: some WidgetConfiguration {
return IntentConfiguration(kind: kind,
intent: ViewAppIntent.self,
provider: Provider()) { (entry) in
WidgetView(entry: entry)
}
.supportedFamilies([.systemSmall])
.configurationDisplayName("AltWidget")
.description("View remaining days until your sideloaded apps expire.")
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.rileytestut.AltStore</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Group 23_120.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Group 23_180.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "group16Copy2.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "icon-120.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "icon-180.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

79
AltWidget/Countdown.swift Normal file
View File

@@ -0,0 +1,79 @@
//
// Countdown.swift
// AltWidgetExtension
//
// Created by Riley Testut on 7/6/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import SwiftUI
import WidgetKit
struct Countdown: View
{
let startDate: Date?
let endDate: Date?
@Environment(\.font) private var font
private var numberOfDays: Int {
guard let date = self.endDate else { return 0 }
let numberOfDays = date.numberOfCalendarDays(since: Date())
return numberOfDays
}
private var fractionComplete: CGFloat {
guard let startDate = self.startDate, let endDate = self.endDate else { return 1.0 }
let totalNumberOfDays = endDate.numberOfCalendarDays(since: startDate)
let fractionComplete = CGFloat(self.numberOfDays) / CGFloat(totalNumberOfDays)
return fractionComplete
}
@ViewBuilder
private func overlay(progress: CGFloat) -> some View
{
let strokeStyle = StrokeStyle(lineWidth: 4.0, lineCap: .round, lineJoin: .round)
if self.numberOfDays > 9 || self.numberOfDays < 0 {
Capsule(style: .continuous)
.trim(from: 0.0, to: progress)
.stroke(style: strokeStyle)
}
else {
Circle()
.trim(from: 0.0, to: progress)
.rotation(Angle(degrees: -90), anchor: .center)
.stroke(style: strokeStyle)
}
}
var body: some View {
Text("\(numberOfDays)")
.font((font ?? .title).monospacedDigit())
.bold()
.opacity(endDate != nil ? 1 : 0)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.overlay(
ZStack {
overlay(progress: 1.0)
.opacity(0.3)
overlay(progress: fractionComplete)
}
)
}
}
struct Countdown_Previews: PreviewProvider {
static var previews: some View {
let startDate = Calendar.current.date(byAdding: .day, value: -2, to: Date()) ?? Date()
Group {
Countdown(startDate: startDate, endDate: Calendar.current.date(byAdding: .day, value: 7, to: startDate))
Countdown(startDate: startDate, endDate: Calendar.current.date(byAdding: .day, value: 365, to: startDate))
}
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}

33
AltWidget/Info.plist Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ALTAppGroups</key>
<array>
<string>group.com.rileytestut.AltStore</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>AltWidget</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>

170
AltWidget/WidgetView.swift Normal file
View File

@@ -0,0 +1,170 @@
//
// WidgetView.swift
// AltStore
//
// Created by Riley Testut on 9/14/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import WidgetKit
import SwiftUI
import AltStoreCore
import AltSign
import CoreData
struct WidgetView : View
{
var entry: AppEntry
var body: some View {
Group {
if let app = self.entry.app
{
let daysRemaining = app.expirationDate.numberOfCalendarDays(since: Date())
GeometryReader { (geometry) in
Group {
VStack(alignment: .leading) {
VStack(alignment: .leading, spacing: 5) {
let imageHeight = geometry.size.height * 0.45
app.icon.map {
Image(uiImage: $0)
.resizable()
.aspectRatio(CGSize(width: 1, height: 1), contentMode: .fit)
.frame(height: imageHeight)
.mask(RoundedRectangle(cornerRadius: imageHeight / 5.0, style: .continuous))
}
Text(app.name.uppercased())
.font(.system(size: 12, weight: .semibold, design: .rounded))
.foregroundColor(.white)
.lineLimit(1)
.minimumScaleFactor(0.5)
}
HStack(alignment: .bottom) {
(
Text("Expires in\n")
.font(.system(size: 13, weight: .semibold, design: .rounded))
.foregroundColor(Color.white.opacity(0.45)) +
Text(daysRemaining == 1 ? "1 day" : "\(daysRemaining) days")
.font(.system(size: 15, weight: .semibold, design: .rounded))
.foregroundColor(.white)
)
.lineLimit(2)
.lineSpacing(1.0)
.minimumScaleFactor(0.5)
Spacer()
Countdown(startDate: app.refreshedDate, endDate: app.expirationDate)
.font(.system(size: 20, weight: .semibold, design: .rounded))
.foregroundColor(Color.white)
.opacity(0.8)
.fixedSize(horizontal: true, vertical: false)
.offset(x: 5)
}
.offset(y: 5) // Offset so we don't affect layout, but still leave space between app name and Countdown.
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
.padding()
}
}
else
{
VStack {
// Put conditional inside VStack, or else an empty view will be returned
// if isPlaceholder == false, which messes up layout.
if !entry.isPlaceholder
{
Text("App Not Found")
.font(.system(.body, design: .rounded))
.fontWeight(.semibold)
.foregroundColor(Color.white.opacity(0.4))
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
.background(backgroundView(icon: entry.app?.icon, tintColor: entry.app?.tintColor))
}
}
private extension WidgetView
{
func backgroundView(icon: UIImage? = nil, tintColor: UIColor? = nil) -> some View
{
let icon = icon ?? UIImage(named: "AltStore")!
let tintColor = tintColor ?? .altPrimary
let imageHeight = 60 as CGFloat
let saturation = 1.8
let blurRadius = 5 as CGFloat
let tintOpacity = 0.45
return ZStack(alignment: .topTrailing) {
// Blurred Image
GeometryReader { geometry in
ZStack {
Image(uiImage: icon)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: imageHeight, height: imageHeight, alignment: .center)
.saturation(saturation)
.blur(radius: blurRadius, opaque: true)
.scaleEffect(geometry.size.width / imageHeight, anchor: .center)
Color(tintColor)
.opacity(tintOpacity)
}
}
Image("Badge")
.resizable()
.frame(width: 26, height: 26)
.padding()
}
}
}
struct WidgetView_Previews: PreviewProvider {
static var previews: some View {
let shortRefreshedDate = Calendar.current.date(byAdding: .day, value: -2, to: Date()) ?? Date()
let shortExpirationDate = Calendar.current.date(byAdding: .day, value: 7, to: shortRefreshedDate) ?? Date()
let longRefreshedDate = Calendar.current.date(byAdding: .day, value: -100, to: Date()) ?? Date()
let longExpirationDate = Calendar.current.date(byAdding: .day, value: 365, to: longRefreshedDate) ?? Date()
let altstore = AppSnapshot(name: "AltStore",
bundleIdentifier: "com.rileytestut.AltStore",
expirationDate: shortExpirationDate,
refreshedDate: shortRefreshedDate,
tintColor: .altPrimary,
icon: UIImage(named: "AltStore"))
let delta = AppSnapshot(name: "Delta",
bundleIdentifier: "com.rileytestut.Delta",
expirationDate: longExpirationDate,
refreshedDate: longRefreshedDate,
tintColor: .deltaPrimary,
icon: UIImage(named: "Delta"))
return Group {
WidgetView(entry: AppEntry(date: Date(), app: altstore))
.previewContext(WidgetPreviewContext(family: .systemSmall))
WidgetView(entry: AppEntry(date: Date(), app: delta))
.previewContext(WidgetPreviewContext(family: .systemSmall))
WidgetView(entry: AppEntry(date: Date(), app: nil))
.previewContext(WidgetPreviewContext(family: .systemSmall))
WidgetView(entry: AppEntry(date: Date(), app: nil, isPlaceholder: true))
.previewContext(WidgetPreviewContext(family: .systemSmall))
}
}
}