mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-18 19:23:43 +01:00
Merge branch 'develop' into develop
Signed-off-by: Spidy123222 <64176728+Spidy123222@users.noreply.github.com>
This commit is contained in:
25
.github/workflows/build.yml
vendored
25
.github/workflows/build.yml
vendored
@@ -1,9 +1,26 @@
|
|||||||
name: Build and Upload SideStore
|
name: Build and Upload SideStore
|
||||||
on: [push, pull_request]
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- develop
|
||||||
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: Build and upload SideStore
|
name: Build and upload SideStore
|
||||||
runs-on: macos-11
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- os: 'macos-12'
|
||||||
|
version: '14.0-beta'
|
||||||
|
- os: 'macos-12'
|
||||||
|
version: '13.4.1'
|
||||||
|
- os: 'macos-11'
|
||||||
|
version: '13.2.1'
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -11,6 +28,10 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: brew install ldid
|
run: brew install ldid
|
||||||
|
- name: Setup Xcode
|
||||||
|
uses: maxim-lobanov/setup-xcode@v1.4.1
|
||||||
|
with:
|
||||||
|
xcode-version: ${{ matrix.version }}
|
||||||
- name: Build SideStore
|
- name: Build SideStore
|
||||||
run: |
|
run: |
|
||||||
rm -rf ~/Library/Developer/Xcode/DerivedData/
|
rm -rf ~/Library/Developer/Xcode/DerivedData/
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
@interface AKDevice
|
@interface AKDevice
|
||||||
+ (AKDevice *)currentDevice;
|
+ (AKDevice *)currentDevice;
|
||||||
- (NSString *)uniqueDeviceIdentifier;
|
- (NSString *)uniqueDeviceIdentifier;
|
||||||
- (NSString *)serialNumber;
|
- (nullable NSString *)serialNumber;
|
||||||
- (NSString *)serverFriendlyDescription;
|
- (NSString *)serverFriendlyDescription;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
localUserID:headers[@"X-Apple-I-MD-LU"]
|
localUserID:headers[@"X-Apple-I-MD-LU"]
|
||||||
routingInfo:[headers[@"X-Apple-I-MD-RINFO"] longLongValue]
|
routingInfo:[headers[@"X-Apple-I-MD-RINFO"] longLongValue]
|
||||||
deviceUniqueIdentifier:device.uniqueDeviceIdentifier
|
deviceUniqueIdentifier:device.uniqueDeviceIdentifier
|
||||||
deviceSerialNumber:device.serialNumber
|
deviceSerialNumber:device.serialNumber ?: @"C02LKHBBFD57" // serialNumber can be nil, so provide valid fallback serial number.
|
||||||
deviceDescription:device.serverFriendlyDescription
|
deviceDescription:device.serverFriendlyDescription
|
||||||
date:date
|
date:date
|
||||||
locale:[NSLocale currentLocale]
|
locale:[NSLocale currentLocale]
|
||||||
|
|||||||
@@ -348,9 +348,11 @@
|
|||||||
BFF7C90F257844C900E55F36 /* AltXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = BFF7C904257844C900E55F36 /* AltXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
BFF7C90F257844C900E55F36 /* AltXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = BFF7C904257844C900E55F36 /* AltXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
BFF7C920257844FA00E55F36 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; };
|
BFF7C920257844FA00E55F36 /* ALTPluginService.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5C5FCE237DF69100EDD0C6 /* ALTPluginService.m */; };
|
||||||
BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; };
|
BFF7C9342578492100E55F36 /* ALTAnisetteData.m in Sources */ = {isa = PBXBuildFile; fileRef = BFB49AA823834CF900D542D9 /* ALTAnisetteData.m */; };
|
||||||
|
D504F42628AD72C50014BB5D /* ProgressRing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D504F42528AD72C50014BB5D /* ProgressRing.swift */; };
|
||||||
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8B62727841800A9B5DD /* libAppleArchive.tbd */; settings = {ATTRIBUTES = (Weak, ); }; };
|
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8B62727841800A9B5DD /* libAppleArchive.tbd */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||||
D533E8BC2727BBEE00A9B5DD /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8BB2727BBEE00A9B5DD /* libfragmentzip.a */; };
|
D533E8BC2727BBEE00A9B5DD /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8BB2727BBEE00A9B5DD /* libfragmentzip.a */; };
|
||||||
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8BD2727BBF800A9B5DD /* libcurl.a */; };
|
D533E8BE2727BBF800A9B5DD /* libcurl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8BD2727BBF800A9B5DD /* libcurl.a */; };
|
||||||
|
D55E163728776CB700A627A1 /* ComplicationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55E163528776CB000A627A1 /* ComplicationView.swift */; };
|
||||||
D57DF638271E32F000677701 /* PatchApp.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D57DF637271E32F000677701 /* PatchApp.storyboard */; };
|
D57DF638271E32F000677701 /* PatchApp.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D57DF637271E32F000677701 /* PatchApp.storyboard */; };
|
||||||
D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D57DF63E271E51E400677701 /* ALTAppPatcher.m */; };
|
D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = D57DF63E271E51E400677701 /* ALTAppPatcher.m */; };
|
||||||
D57F2C9126E0070200B9FA39 /* EnableJITOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */; };
|
D57F2C9126E0070200B9FA39 /* EnableJITOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57F2C9026E0070200B9FA39 /* EnableJITOperation.swift */; };
|
||||||
@@ -834,10 +836,12 @@
|
|||||||
BFF7C93B257849C600E55F36 /* AltXPC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltXPC.entitlements; sourceTree = "<group>"; };
|
BFF7C93B257849C600E55F36 /* AltXPC.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltXPC.entitlements; sourceTree = "<group>"; };
|
||||||
BFF7EC4C25081E9300BDE521 /* AltStore 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 8.xcdatamodel"; sourceTree = "<group>"; };
|
BFF7EC4C25081E9300BDE521 /* AltStore 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "AltStore 8.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
BFFCFA45248835530077BFCE /* AltDaemon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltDaemon.entitlements; sourceTree = "<group>"; };
|
BFFCFA45248835530077BFCE /* AltDaemon.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AltDaemon.entitlements; sourceTree = "<group>"; };
|
||||||
|
D504F42528AD72C50014BB5D /* ProgressRing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressRing.swift; sourceTree = "<group>"; };
|
||||||
D533E8B62727841800A9B5DD /* libAppleArchive.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libAppleArchive.tbd; path = usr/lib/libAppleArchive.tbd; sourceTree = SDKROOT; };
|
D533E8B62727841800A9B5DD /* libAppleArchive.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libAppleArchive.tbd; path = usr/lib/libAppleArchive.tbd; sourceTree = SDKROOT; };
|
||||||
D533E8B82727B61400A9B5DD /* fragmentzip.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fragmentzip.h; sourceTree = "<group>"; };
|
D533E8B82727B61400A9B5DD /* fragmentzip.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fragmentzip.h; sourceTree = "<group>"; };
|
||||||
D533E8BB2727BBEE00A9B5DD /* libfragmentzip.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libfragmentzip.a; path = Dependencies/fragmentzip/libfragmentzip.a; sourceTree = SOURCE_ROOT; };
|
D533E8BB2727BBEE00A9B5DD /* libfragmentzip.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libfragmentzip.a; path = Dependencies/fragmentzip/libfragmentzip.a; sourceTree = SOURCE_ROOT; };
|
||||||
D533E8BD2727BBF800A9B5DD /* libcurl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcurl.a; path = Dependencies/libcurl/libcurl.a; sourceTree = SOURCE_ROOT; };
|
D533E8BD2727BBF800A9B5DD /* libcurl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcurl.a; path = Dependencies/libcurl/libcurl.a; sourceTree = SOURCE_ROOT; };
|
||||||
|
D55E163528776CB000A627A1 /* ComplicationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationView.swift; sourceTree = "<group>"; };
|
||||||
D57DF637271E32F000677701 /* PatchApp.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = PatchApp.storyboard; sourceTree = "<group>"; };
|
D57DF637271E32F000677701 /* PatchApp.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = PatchApp.storyboard; sourceTree = "<group>"; };
|
||||||
D57DF63D271E51E400677701 /* ALTAppPatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTAppPatcher.h; sourceTree = "<group>"; };
|
D57DF63D271E51E400677701 /* ALTAppPatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ALTAppPatcher.h; sourceTree = "<group>"; };
|
||||||
D57DF63E271E51E400677701 /* ALTAppPatcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTAppPatcher.m; sourceTree = "<group>"; };
|
D57DF63E271E51E400677701 /* ALTAppPatcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ALTAppPatcher.m; sourceTree = "<group>"; };
|
||||||
@@ -1417,6 +1421,8 @@
|
|||||||
BF98917D250AAC4F002ACF50 /* AltWidget.swift */,
|
BF98917D250AAC4F002ACF50 /* AltWidget.swift */,
|
||||||
BF42345825101C1D006D1EB2 /* WidgetView.swift */,
|
BF42345825101C1D006D1EB2 /* WidgetView.swift */,
|
||||||
BF98917C250AAC4F002ACF50 /* Countdown.swift */,
|
BF98917C250AAC4F002ACF50 /* Countdown.swift */,
|
||||||
|
D55E163528776CB000A627A1 /* ComplicationView.swift */,
|
||||||
|
D504F42528AD72C50014BB5D /* ProgressRing.swift */,
|
||||||
BF989170250AABF4002ACF50 /* Assets.xcassets */,
|
BF989170250AABF4002ACF50 /* Assets.xcassets */,
|
||||||
BF989172250AABF4002ACF50 /* Info.plist */,
|
BF989172250AABF4002ACF50 /* Info.plist */,
|
||||||
);
|
);
|
||||||
@@ -2467,7 +2473,9 @@
|
|||||||
files = (
|
files = (
|
||||||
BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */,
|
BF98917E250AAC4F002ACF50 /* Countdown.swift in Sources */,
|
||||||
BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */,
|
BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */,
|
||||||
|
D55E163728776CB700A627A1 /* ComplicationView.swift in Sources */,
|
||||||
BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */,
|
BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */,
|
||||||
|
D504F42628AD72C50014BB5D /* ProgressRing.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ extension AppDelegate
|
|||||||
static let openPatreonSettingsDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.OpenPatreonSettingsDeepLinkNotification")
|
static let openPatreonSettingsDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.OpenPatreonSettingsDeepLinkNotification")
|
||||||
static let importAppDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.ImportAppDeepLinkNotification")
|
static let importAppDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.ImportAppDeepLinkNotification")
|
||||||
static let addSourceDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.AddSourceDeepLinkNotification")
|
static let addSourceDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.AddSourceDeepLinkNotification")
|
||||||
|
|
||||||
static let appBackupDidFinish = Notification.Name("com.rileytestut.AltStore.AppBackupDidFinish")
|
static let appBackupDidFinish = Notification.Name("com.rileytestut.AltStore.AppBackupDidFinish")
|
||||||
|
|
||||||
static let importAppDeepLinkURLKey = "fileURL"
|
static let importAppDeepLinkURLKey = "fileURL"
|
||||||
static let appBackupResultKey = "result"
|
static let appBackupResultKey = "result"
|
||||||
static let addSourceDeepLinkURLKey = "sourceURL"
|
static let addSourceDeepLinkURLKey = "sourceURL"
|
||||||
@@ -32,15 +32,34 @@ extension AppDelegate
|
|||||||
class AppDelegate: UIResponder, UIApplicationDelegate {
|
class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
|
|
||||||
private lazy var intentHandler = IntentHandler()
|
@available(iOS 14, *)
|
||||||
private lazy var viewAppIntentHandler = ViewAppIntentHandler()
|
private var intentHandler: IntentHandler {
|
||||||
|
get { _intentHandler as! IntentHandler }
|
||||||
|
set { _intentHandler = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 14, *)
|
||||||
|
private var viewAppIntentHandler: ViewAppIntentHandler {
|
||||||
|
get { _viewAppIntentHandler as! ViewAppIntentHandler }
|
||||||
|
set { _viewAppIntentHandler = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var _intentHandler: Any = {
|
||||||
|
guard #available(iOS 14, *) else { fatalError() }
|
||||||
|
return IntentHandler()
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var _viewAppIntentHandler: Any = {
|
||||||
|
guard #available(iOS 14, *) else { fatalError() }
|
||||||
|
return ViewAppIntentHandler()
|
||||||
|
}()
|
||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
||||||
{
|
{
|
||||||
// Register default settings before doing anything else.
|
// Register default settings before doing anything else.
|
||||||
UserDefaults.registerDefaults()
|
UserDefaults.registerDefaults()
|
||||||
|
|
||||||
DatabaseManager.shared.start { (error) in
|
DatabaseManager.shared.start { (error) in
|
||||||
if let error = error
|
if let error = error
|
||||||
{
|
{
|
||||||
@@ -51,32 +70,32 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
print("Started DatabaseManager.")
|
print("Started DatabaseManager.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalyticsManager.shared.start()
|
AnalyticsManager.shared.start()
|
||||||
|
|
||||||
self.setTintColor()
|
self.setTintColor()
|
||||||
|
|
||||||
ServerManager.shared.startDiscovering()
|
ServerManager.shared.startDiscovering()
|
||||||
|
|
||||||
SecureValueTransformer.register()
|
SecureValueTransformer.register()
|
||||||
|
|
||||||
if UserDefaults.standard.firstLaunch == nil
|
if UserDefaults.standard.firstLaunch == nil
|
||||||
{
|
{
|
||||||
Keychain.shared.reset()
|
Keychain.shared.reset()
|
||||||
UserDefaults.standard.firstLaunch = Date()
|
UserDefaults.standard.firstLaunch = Date()
|
||||||
}
|
}
|
||||||
|
|
||||||
UserDefaults.standard.preferredServerID = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.serverID) as? String
|
UserDefaults.standard.preferredServerID = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.serverID) as? String
|
||||||
|
|
||||||
#if DEBUG || BETA
|
#if DEBUG || BETA
|
||||||
UserDefaults.standard.isDebugModeEnabled = true
|
UserDefaults.standard.isDebugModeEnabled = true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
self.prepareForBackgroundFetch()
|
self.prepareForBackgroundFetch()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidEnterBackground(_ application: UIApplication)
|
func applicationDidEnterBackground(_ application: UIApplication)
|
||||||
{
|
{
|
||||||
ServerManager.shared.stopDiscovering()
|
ServerManager.shared.stopDiscovering()
|
||||||
@@ -86,19 +105,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
{
|
{
|
||||||
AppManager.shared.update()
|
AppManager.shared.update()
|
||||||
ServerManager.shared.startDiscovering()
|
ServerManager.shared.startDiscovering()
|
||||||
|
|
||||||
PatreonAPI.shared.refreshPatreonAccount()
|
PatreonAPI.shared.refreshPatreonAccount()
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool
|
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool
|
||||||
{
|
{
|
||||||
return self.open(url)
|
return self.open(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any?
|
func application(_ application: UIApplication, handlerFor intent: INIntent) -> Any?
|
||||||
{
|
{
|
||||||
guard #available(iOS 14, *) else { return nil }
|
guard #available(iOS 14, *) else { return nil }
|
||||||
|
|
||||||
switch intent
|
switch intent
|
||||||
{
|
{
|
||||||
case is RefreshAllIntent: return self.intentHandler
|
case is RefreshAllIntent: return self.intentHandler
|
||||||
@@ -117,7 +136,7 @@ extension AppDelegate
|
|||||||
// Use this method to select a configuration to create the new scene with.
|
// Use this method to select a configuration to create the new scene with.
|
||||||
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)
|
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)
|
||||||
{
|
{
|
||||||
// Called when the user discards a scene session.
|
// Called when the user discards a scene session.
|
||||||
@@ -132,36 +151,36 @@ private extension AppDelegate
|
|||||||
{
|
{
|
||||||
self.window?.tintColor = .altPrimary
|
self.window?.tintColor = .altPrimary
|
||||||
}
|
}
|
||||||
|
|
||||||
func open(_ url: URL) -> Bool
|
func open(_ url: URL) -> Bool
|
||||||
{
|
{
|
||||||
if url.isFileURL
|
if url.isFileURL
|
||||||
{
|
{
|
||||||
guard url.pathExtension.lowercased() == "ipa" else { return false }
|
guard url.pathExtension.lowercased() == "ipa" else { return false }
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: url])
|
NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: url])
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false }
|
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false }
|
||||||
guard let host = components.host?.lowercased() else { return false }
|
guard let host = components.host?.lowercased() else { return false }
|
||||||
|
|
||||||
switch host
|
switch host
|
||||||
{
|
{
|
||||||
case "patreon":
|
case "patreon":
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
NotificationCenter.default.post(name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
NotificationCenter.default.post(name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case "appbackupresponse":
|
case "appbackupresponse":
|
||||||
let result: Result<Void, Error>
|
let result: Result<Void, Error>
|
||||||
|
|
||||||
switch url.path.lowercased()
|
switch url.path.lowercased()
|
||||||
{
|
{
|
||||||
case "/success": result = .success(())
|
case "/success": result = .success(())
|
||||||
@@ -172,37 +191,37 @@ private extension AppDelegate
|
|||||||
let errorCodeString = queryItems["errorCode"], let errorCode = Int(errorCodeString),
|
let errorCodeString = queryItems["errorCode"], let errorCode = Int(errorCodeString),
|
||||||
let errorDescription = queryItems["errorDescription"]
|
let errorDescription = queryItems["errorDescription"]
|
||||||
else { return false }
|
else { return false }
|
||||||
|
|
||||||
let error = NSError(domain: errorDomain, code: errorCode, userInfo: [NSLocalizedDescriptionKey: errorDescription])
|
let error = NSError(domain: errorDomain, code: errorCode, userInfo: [NSLocalizedDescriptionKey: errorDescription])
|
||||||
result = .failure(error)
|
result = .failure(error)
|
||||||
|
|
||||||
default: return false
|
default: return false
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationCenter.default.post(name: AppDelegate.appBackupDidFinish, object: nil, userInfo: [AppDelegate.appBackupResultKey: result])
|
NotificationCenter.default.post(name: AppDelegate.appBackupDidFinish, object: nil, userInfo: [AppDelegate.appBackupResultKey: result])
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case "install":
|
case "install":
|
||||||
let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:]
|
let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:]
|
||||||
guard let downloadURLString = queryItems["url"], let downloadURL = URL(string: downloadURLString) else { return false }
|
guard let downloadURLString = queryItems["url"], let downloadURL = URL(string: downloadURLString) else { return false }
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: downloadURL])
|
NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: downloadURL])
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case "source":
|
case "source":
|
||||||
let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:]
|
let queryItems = components.queryItems?.reduce(into: [String: String]()) { $0[$1.name.lowercased()] = $1.value } ?? [:]
|
||||||
guard let sourceURLString = queryItems["url"], let sourceURL = URL(string: sourceURLString) else { return false }
|
guard let sourceURLString = queryItems["url"], let sourceURL = URL(string: sourceURLString) else { return false }
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
NotificationCenter.default.post(name: AppDelegate.addSourceDeepLinkNotification, object: nil, userInfo: [AppDelegate.addSourceDeepLinkURLKey: sourceURL])
|
NotificationCenter.default.post(name: AppDelegate.addSourceDeepLinkNotification, object: nil, userInfo: [AppDelegate.addSourceDeepLinkURLKey: sourceURL])
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
default: return false
|
default: return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -215,47 +234,47 @@ extension AppDelegate
|
|||||||
{
|
{
|
||||||
// "Fetch" every hour, but then refresh only those that need to be refreshed (so we don't drain the battery).
|
// "Fetch" every hour, but then refresh only those that need to be refreshed (so we don't drain the battery).
|
||||||
UIApplication.shared.setMinimumBackgroundFetchInterval(1 * 60 * 60)
|
UIApplication.shared.setMinimumBackgroundFetchInterval(1 * 60 * 60)
|
||||||
|
|
||||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
|
||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
UIApplication.shared.registerForRemoteNotifications()
|
UIApplication.shared.registerForRemoteNotifications()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
|
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
|
||||||
{
|
{
|
||||||
let tokenParts = deviceToken.map { data -> String in
|
let tokenParts = deviceToken.map { data -> String in
|
||||||
return String(format: "%02.2hhx", data)
|
return String(format: "%02.2hhx", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = tokenParts.joined()
|
let token = tokenParts.joined()
|
||||||
print("Push Token:", token)
|
print("Push Token:", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
|
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
|
||||||
{
|
{
|
||||||
self.application(application, performFetchWithCompletionHandler: completionHandler)
|
self.application(application, performFetchWithCompletionHandler: completionHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, performFetchWithCompletionHandler backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void)
|
func application(_ application: UIApplication, performFetchWithCompletionHandler backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void)
|
||||||
{
|
{
|
||||||
if UserDefaults.standard.isBackgroundRefreshEnabled && !UserDefaults.standard.presentedLaunchReminderNotification
|
if UserDefaults.standard.isBackgroundRefreshEnabled && !UserDefaults.standard.presentedLaunchReminderNotification
|
||||||
{
|
{
|
||||||
let threeHours: TimeInterval = 3 * 60 * 60
|
let threeHours: TimeInterval = 3 * 60 * 60
|
||||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: threeHours, repeats: false)
|
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: threeHours, repeats: false)
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = NSLocalizedString("App Refresh Tip", comment: "")
|
content.title = NSLocalizedString("App Refresh Tip", comment: "")
|
||||||
content.body = NSLocalizedString("The more you open AltStore, the more chances it's given to refresh apps in the background.", comment: "")
|
content.body = NSLocalizedString("The more you open AltStore, the more chances it's given to refresh apps in the background.", comment: "")
|
||||||
|
|
||||||
let request = UNNotificationRequest(identifier: "background-refresh-reminder5", content: content, trigger: trigger)
|
let request = UNNotificationRequest(identifier: "background-refresh-reminder5", content: content, trigger: trigger)
|
||||||
UNUserNotificationCenter.current().add(request)
|
UNUserNotificationCenter.current().add(request)
|
||||||
|
|
||||||
UserDefaults.standard.presentedLaunchReminderNotification = true
|
UserDefaults.standard.presentedLaunchReminderNotification = true
|
||||||
}
|
}
|
||||||
|
|
||||||
BackgroundTaskManager.shared.performExtendedBackgroundTask { (taskResult, taskCompletionHandler) in
|
BackgroundTaskManager.shared.performExtendedBackgroundTask { (taskResult, taskCompletionHandler) in
|
||||||
if let error = taskResult.error
|
if let error = taskResult.error
|
||||||
{
|
{
|
||||||
@@ -264,7 +283,7 @@ extension AppDelegate
|
|||||||
taskCompletionHandler()
|
taskCompletionHandler()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !DatabaseManager.shared.isStarted
|
if !DatabaseManager.shared.isStarted
|
||||||
{
|
{
|
||||||
DatabaseManager.shared.start() { (error) in
|
DatabaseManager.shared.start() { (error) in
|
||||||
@@ -293,7 +312,7 @@ extension AppDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func performBackgroundFetch(backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void,
|
func performBackgroundFetch(backgroundFetchCompletionHandler: @escaping (UIBackgroundFetchResult) -> Void,
|
||||||
refreshAppsCompletionHandler: @escaping (Result<[String: Result<InstalledApp, Error>], Error>) -> Void)
|
refreshAppsCompletionHandler: @escaping (Result<[String: Result<InstalledApp, Error>], Error>) -> Void)
|
||||||
{
|
{
|
||||||
@@ -303,15 +322,15 @@ extension AppDelegate
|
|||||||
case .failure: backgroundFetchCompletionHandler(.failed)
|
case .failure: backgroundFetchCompletionHandler(.failed)
|
||||||
case .success: backgroundFetchCompletionHandler(.newData)
|
case .success: backgroundFetchCompletionHandler(.newData)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !UserDefaults.standard.isBackgroundRefreshEnabled
|
if !UserDefaults.standard.isBackgroundRefreshEnabled
|
||||||
{
|
{
|
||||||
refreshAppsCompletionHandler(.success([:]))
|
refreshAppsCompletionHandler(.success([:]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard UserDefaults.standard.isBackgroundRefreshEnabled else { return }
|
guard UserDefaults.standard.isBackgroundRefreshEnabled else { return }
|
||||||
|
|
||||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||||
let installedApps = InstalledApp.fetchAppsForBackgroundRefresh(in: context)
|
let installedApps = InstalledApp.fetchAppsForBackgroundRefresh(in: context)
|
||||||
AppManager.shared.backgroundRefresh(installedApps, completionHandler: refreshAppsCompletionHandler)
|
AppManager.shared.backgroundRefresh(installedApps, completionHandler: refreshAppsCompletionHandler)
|
||||||
@@ -327,49 +346,49 @@ private extension AppDelegate
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
let (sources, context) = try result.get()
|
let (sources, context) = try result.get()
|
||||||
|
|
||||||
let previousUpdatesFetchRequest = InstalledApp.updatesFetchRequest() as! NSFetchRequest<NSFetchRequestResult>
|
let previousUpdatesFetchRequest = InstalledApp.updatesFetchRequest() as! NSFetchRequest<NSFetchRequestResult>
|
||||||
previousUpdatesFetchRequest.includesPendingChanges = false
|
previousUpdatesFetchRequest.includesPendingChanges = false
|
||||||
previousUpdatesFetchRequest.resultType = .dictionaryResultType
|
previousUpdatesFetchRequest.resultType = .dictionaryResultType
|
||||||
previousUpdatesFetchRequest.propertiesToFetch = [#keyPath(InstalledApp.bundleIdentifier)]
|
previousUpdatesFetchRequest.propertiesToFetch = [#keyPath(InstalledApp.bundleIdentifier)]
|
||||||
|
|
||||||
let previousNewsItemsFetchRequest = NewsItem.fetchRequest() as NSFetchRequest<NSFetchRequestResult>
|
let previousNewsItemsFetchRequest = NewsItem.fetchRequest() as NSFetchRequest<NSFetchRequestResult>
|
||||||
previousNewsItemsFetchRequest.includesPendingChanges = false
|
previousNewsItemsFetchRequest.includesPendingChanges = false
|
||||||
previousNewsItemsFetchRequest.resultType = .dictionaryResultType
|
previousNewsItemsFetchRequest.resultType = .dictionaryResultType
|
||||||
previousNewsItemsFetchRequest.propertiesToFetch = [#keyPath(NewsItem.identifier)]
|
previousNewsItemsFetchRequest.propertiesToFetch = [#keyPath(NewsItem.identifier)]
|
||||||
|
|
||||||
let previousUpdates = try context.fetch(previousUpdatesFetchRequest) as! [[String: String]]
|
let previousUpdates = try context.fetch(previousUpdatesFetchRequest) as! [[String: String]]
|
||||||
let previousNewsItems = try context.fetch(previousNewsItemsFetchRequest) as! [[String: String]]
|
let previousNewsItems = try context.fetch(previousNewsItemsFetchRequest) as! [[String: String]]
|
||||||
|
|
||||||
try context.save()
|
try context.save()
|
||||||
|
|
||||||
let updatesFetchRequest = InstalledApp.updatesFetchRequest()
|
let updatesFetchRequest = InstalledApp.updatesFetchRequest()
|
||||||
let newsItemsFetchRequest = NewsItem.fetchRequest() as NSFetchRequest<NewsItem>
|
let newsItemsFetchRequest = NewsItem.fetchRequest() as NSFetchRequest<NewsItem>
|
||||||
|
|
||||||
let updates = try context.fetch(updatesFetchRequest)
|
let updates = try context.fetch(updatesFetchRequest)
|
||||||
let newsItems = try context.fetch(newsItemsFetchRequest)
|
let newsItems = try context.fetch(newsItemsFetchRequest)
|
||||||
|
|
||||||
for update in updates
|
for update in updates
|
||||||
{
|
{
|
||||||
guard !previousUpdates.contains(where: { $0[#keyPath(InstalledApp.bundleIdentifier)] == update.bundleIdentifier }) else { continue }
|
guard !previousUpdates.contains(where: { $0[#keyPath(InstalledApp.bundleIdentifier)] == update.bundleIdentifier }) else { continue }
|
||||||
guard let storeApp = update.storeApp else { continue }
|
guard let storeApp = update.storeApp else { continue }
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = NSLocalizedString("New Update Available", comment: "")
|
content.title = NSLocalizedString("New Update Available", comment: "")
|
||||||
content.body = String(format: NSLocalizedString("%@ %@ is now available for download.", comment: ""), update.name, storeApp.version)
|
content.body = String(format: NSLocalizedString("%@ %@ is now available for download.", comment: ""), update.name, storeApp.version)
|
||||||
content.sound = .default
|
content.sound = .default
|
||||||
|
|
||||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||||
UNUserNotificationCenter.current().add(request)
|
UNUserNotificationCenter.current().add(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
for newsItem in newsItems
|
for newsItem in newsItems
|
||||||
{
|
{
|
||||||
guard !previousNewsItems.contains(where: { $0[#keyPath(NewsItem.identifier)] == newsItem.identifier }) else { continue }
|
guard !previousNewsItems.contains(where: { $0[#keyPath(NewsItem.identifier)] == newsItem.identifier }) else { continue }
|
||||||
guard !newsItem.isSilent else { continue }
|
guard !newsItem.isSilent else { continue }
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
|
|
||||||
if let app = newsItem.storeApp
|
if let app = newsItem.storeApp
|
||||||
{
|
{
|
||||||
content.title = String(format: NSLocalizedString("%@ News", comment: ""), app.name)
|
content.title = String(format: NSLocalizedString("%@ News", comment: ""), app.name)
|
||||||
@@ -378,10 +397,10 @@ private extension AppDelegate
|
|||||||
{
|
{
|
||||||
content.title = NSLocalizedString("AltStore News", comment: "")
|
content.title = NSLocalizedString("AltStore News", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
content.body = newsItem.title
|
content.body = newsItem.title
|
||||||
content.sound = .default
|
content.sound = .default
|
||||||
|
|
||||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||||
UNUserNotificationCenter.current().add(request)
|
UNUserNotificationCenter.current().add(request)
|
||||||
}
|
}
|
||||||
@@ -389,7 +408,7 @@ private extension AppDelegate
|
|||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
UIApplication.shared.applicationIconBadgeNumber = updates.count
|
UIApplication.shared.applicationIconBadgeNumber = updates.count
|
||||||
}
|
}
|
||||||
|
|
||||||
completionHandler(.success(sources))
|
completionHandler(.success(sources))
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|||||||
@@ -46,9 +46,6 @@ class AppManager
|
|||||||
{
|
{
|
||||||
static let shared = AppManager()
|
static let shared = AppManager()
|
||||||
|
|
||||||
@available(iOS 13, *)
|
|
||||||
private(set) lazy var publisher: AppManagerPublisher = AppManagerPublisher()
|
|
||||||
|
|
||||||
private(set) var updatePatronsResult: Result<Void, Error>?
|
private(set) var updatePatronsResult: Result<Void, Error>?
|
||||||
|
|
||||||
private let operationQueue = OperationQueue()
|
private let operationQueue = OperationQueue()
|
||||||
@@ -67,8 +64,27 @@ class AppManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 13.0, *)
|
@available(iOS 13, *)
|
||||||
private lazy var cancellables = Set<AnyCancellable>()
|
private(set) var publisher: AppManagerPublisher {
|
||||||
|
get { _publisher as! AppManagerPublisher }
|
||||||
|
set { _publisher = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13, *)
|
||||||
|
private(set) var cancellables: Set<AnyCancellable> {
|
||||||
|
get { _cancellables as! Set<AnyCancellable> }
|
||||||
|
set { _cancellables = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var _publisher: Any = {
|
||||||
|
guard #available(iOS 13, *) else { fatalError() }
|
||||||
|
return AppManagerPublisher()
|
||||||
|
}()
|
||||||
|
|
||||||
|
private lazy var _cancellables: Any = {
|
||||||
|
guard #available(iOS 13, *) else { fatalError() }
|
||||||
|
return Set<AnyCancellable>()
|
||||||
|
}()
|
||||||
|
|
||||||
private init()
|
private init()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,14 +7,14 @@
|
|||||||
"name": "AltStore",
|
"name": "AltStore",
|
||||||
"bundleIdentifier": "com.rileytestut.AltStore",
|
"bundleIdentifier": "com.rileytestut.AltStore",
|
||||||
"developerName": "Riley Testut",
|
"developerName": "Riley Testut",
|
||||||
"version": "1.5",
|
"version": "1.5.1",
|
||||||
"versionDate": "2022-05-06T08:00:00-07:00",
|
"versionDate": "2022-07-14T12:00:00-05:00",
|
||||||
"versionDescription": "TRUSTED SOURCES\n• Download and update certain third party apps entirely through AltStore\n• Add Trusted Sources directly from the new Sources page\n• Trusted Sources are sources we’ve explicitly approved that meet our safety standards\n• You can still sideload any .ipa you want via the \"+\" button in My Apps \n• Support for adding any source by URL will be coming later\n\nALTJIT\n• Allows sideloaded apps to use Just-in-Time (JIT) compilation\n• Long-press an app in My Apps, then choose “Enable JIT”\n• JIT will remain enabled until the app is quit\n• Requires iOS 14 or later\n• Requires AltServer 1.5 or later\n\nIMPROVED\n• Adds Shane to credits and Patreon screen\n• Loads “Friend Tier” Patrons much faster on Patreon screen\n\nFIXED\n• Fixed a potential crash when sideloading certain apps\n• Fixed some apps having access to AltStore’s app group after being (de-)activated\n• Fixed Core Data error when not connected to internet",
|
"versionDescription": "This update fixes the following issues:\n\n• Using Apple IDs that contain capital letters\n• Using Apple IDs with 2FA enabled without any trusted devices\n• Repeatedly asking some users to sign in every refresh\n• \"Incorrect Apple ID or password\" error after changing Apple ID email address\n• “Application is missing application-identifier” error when sideloading or (de-)activating certain apps\n• Potential crash when receiving unknown error codes from AltServer",
|
||||||
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_5.ipa",
|
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/altstore/1_5_1.ipa",
|
||||||
"localizedDescription": "AltStore is an alternative app store for non-jailbroken devices. \n\nThis version of AltStore allows you to install Delta, an all-in-one emulator for iOS, as well as sideload other .ipa files from the Files app.",
|
"localizedDescription": "AltStore is an alternative app store for non-jailbroken devices. \n\nThis version of AltStore allows you to install Delta, an all-in-one emulator for iOS, as well as sideload other .ipa files from the Files app.",
|
||||||
"iconURL": "https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png",
|
"iconURL": "https://user-images.githubusercontent.com/705880/65270980-1eb96f80-dad1-11e9-9367-78ccd25ceb02.png",
|
||||||
"tintColor": "018084",
|
"tintColor": "018084",
|
||||||
"size": 5459395,
|
"size": 5465976,
|
||||||
"screenshotURLs": [
|
"screenshotURLs": [
|
||||||
"https://user-images.githubusercontent.com/705880/78942028-acf54300-7a6d-11ea-821c-5bb7a9b3e73a.PNG",
|
"https://user-images.githubusercontent.com/705880/78942028-acf54300-7a6d-11ea-821c-5bb7a9b3e73a.PNG",
|
||||||
"https://user-images.githubusercontent.com/705880/78942222-0fe6da00-7a6e-11ea-9f2a-dda16157583c.PNG",
|
"https://user-images.githubusercontent.com/705880/78942222-0fe6da00-7a6e-11ea-9f2a-dda16157583c.PNG",
|
||||||
@@ -92,14 +92,14 @@
|
|||||||
"bundleIdentifier": "com.rileytestut.Delta.Beta",
|
"bundleIdentifier": "com.rileytestut.Delta.Beta",
|
||||||
"developerName": "Riley Testut",
|
"developerName": "Riley Testut",
|
||||||
"subtitle": "Classic games in your pocket.",
|
"subtitle": "Classic games in your pocket.",
|
||||||
"version": "1.4b1",
|
"version": "1.4b2",
|
||||||
"versionDate": "2022-06-02T09:00:00-07:00",
|
"versionDate": "2022-08-16T08:00:00-05:00",
|
||||||
"versionDescription": "NEW\n• iPad-optimized UI\n• Redesigned iPad controller skins for NES, SNES, and DS games\n• Temporary iPad controller skins for GBC, GBA, Genesis, and N64 games\n\nIMPROVED\n• Modern menu UI when importing games\n• Updated \"Copy Deep Link\" icon when sharing a game\n\nFIXED\n• Fixed keyboard support on iOS 15+\n• Fixed invisible \"Games\" navigation bar and toolbar on iOS 15+\n• Fixed error sharing games via certain share methods\n• Fixed exporting save files for games with names containing special characters\n• Fixed UI bug where previous game controller would remaining selected after switching\n• Fixed game controller input mapping UI on iPad\n• Fixed crash importing games on iPad\n• Fixed crash sharing games on iPad",
|
"versionDescription": "NEW\n• Supports Split View and Stage Manager multitasking on iPad\n• Automatically pauses + resumes emulation when switching between foreground apps with Stage Manager\n• Optimized full screen-width controller skins when using Split View, Slide Over, or Stage Manager\n• Supports controller skins with new `placement` parameter\n• Supports controller skins with custom screens that don’t have explicit `outputFrame`\n\nFIXED\n• Fixed not detecting keyboard presses when remapping inputs\n• Fixed potential crash rendering game screen after changing EAGLContext\n• Fixed incorrect game screen frame when software keyboard appears on iOS 16\n• Fixed software keyboard sometimes appearing when not emulating anything",
|
||||||
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_4_b1.ipa",
|
"downloadURL": "https://cdn.altstore.io/file/altstore/apps/delta/1_4_b2.ipa",
|
||||||
"localizedDescription": "The next consoles for Delta are coming: this beta version of Delta brings support for playing Nintendo DS and Sega Genesis games!\n\nPlease report any issues you find to support@altstore.io. Thanks!",
|
"localizedDescription": "The next consoles for Delta are coming: this beta version of Delta brings support for playing Nintendo DS and Sega Genesis games!\n\nPlease report any issues you find to support@altstore.io. Thanks!",
|
||||||
"iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
|
"iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
|
||||||
"tintColor": "8A28F7",
|
"tintColor": "8A28F7",
|
||||||
"size": 38346715,
|
"size": 42968657,
|
||||||
"beta": true,
|
"beta": true,
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@@ -186,15 +186,6 @@
|
|||||||
"date": "2019-09-28",
|
"date": "2019-09-28",
|
||||||
"notify": true
|
"notify": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"title": "Welcome to AltStore",
|
|
||||||
"identifier": "welcome-to-altstore",
|
|
||||||
"caption": "Please read the FAQ for help with installing apps.",
|
|
||||||
"tintColor": "018084",
|
|
||||||
"url": "https://altstore.io/faq/",
|
|
||||||
"date": "2019-09-28",
|
|
||||||
"notify": false
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"title": "Sideloading is Here!",
|
"title": "Sideloading is Here!",
|
||||||
"identifier": "sideloading-is-here",
|
"identifier": "sideloading-is-here",
|
||||||
@@ -261,6 +252,15 @@
|
|||||||
"url": "https://faq.altstore.io/release-notes/altstore",
|
"url": "https://faq.altstore.io/release-notes/altstore",
|
||||||
"date": "2022-05-05",
|
"date": "2022-05-05",
|
||||||
"notify": true
|
"notify": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "New to AltStore?",
|
||||||
|
"identifier": "updated-faq",
|
||||||
|
"caption": "Check out our updated guide to learn how to sideload apps!",
|
||||||
|
"tintColor": "018084",
|
||||||
|
"url": "https://faq.altstore.io",
|
||||||
|
"date": "2022-07-28",
|
||||||
|
"notify": false
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"userInfo": {
|
"userInfo": {
|
||||||
|
|||||||
@@ -173,8 +173,7 @@ struct Provider: IntentTimelineProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@main
|
struct HomeScreenWidget: Widget
|
||||||
struct AltWidget: Widget
|
|
||||||
{
|
{
|
||||||
private let kind: String = "AppDetail"
|
private let kind: String = "AppDetail"
|
||||||
|
|
||||||
@@ -189,3 +188,35 @@ struct AltWidget: Widget
|
|||||||
.description("View remaining days until your sideloaded apps expire.")
|
.description("View remaining days until your sideloaded apps expire.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LockScreenWidget: Widget
|
||||||
|
{
|
||||||
|
private let kind: String = "LockAppDetail"
|
||||||
|
|
||||||
|
public var body: some WidgetConfiguration {
|
||||||
|
if #available(iOSApplicationExtension 16, *)
|
||||||
|
{
|
||||||
|
return IntentConfiguration(kind: kind,
|
||||||
|
intent: ViewAppIntent.self,
|
||||||
|
provider: Provider()) { (entry) in
|
||||||
|
ComplicationView(entry: entry)
|
||||||
|
}
|
||||||
|
.supportedFamilies([.accessoryCircular])
|
||||||
|
.configurationDisplayName("AltWidget")
|
||||||
|
.description("View remaining days until AltStore expires.")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return EmptyWidgetConfiguration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct AltWidgets: WidgetBundle
|
||||||
|
{
|
||||||
|
var body: some Widget {
|
||||||
|
HomeScreenWidget()
|
||||||
|
LockScreenWidget()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
86
AltWidget/ComplicationView.swift
Normal file
86
AltWidget/ComplicationView.swift
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// ComplicationView.swift
|
||||||
|
// AltStore
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 7/7/22.
|
||||||
|
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import WidgetKit
|
||||||
|
|
||||||
|
@available(iOS 16, *)
|
||||||
|
struct ComplicationView: View
|
||||||
|
{
|
||||||
|
let entry: AppEntry
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let refreshedDate = self.entry.app?.refreshedDate ?? .now
|
||||||
|
let expirationDate = self.entry.app?.expirationDate ?? .now
|
||||||
|
|
||||||
|
let totalDays = expirationDate.numberOfCalendarDays(since: refreshedDate)
|
||||||
|
let daysRemaining = expirationDate.numberOfCalendarDays(since: self.entry.date)
|
||||||
|
|
||||||
|
let progress = Double(daysRemaining) / Double(totalDays)
|
||||||
|
|
||||||
|
ZStack(alignment: .center) {
|
||||||
|
ProgressRing(progress: progress) {
|
||||||
|
if daysRemaining < 0
|
||||||
|
{
|
||||||
|
Text("Expired")
|
||||||
|
.font(.system(size: 10, weight: .bold))
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VStack(spacing: -1) {
|
||||||
|
Text("\(daysRemaining)")
|
||||||
|
.font(.system(size: 20, weight: .bold, design: .rounded))
|
||||||
|
|
||||||
|
Text(daysRemaining == 1 ? "DAY" : "DAYS")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
.offset(y: -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unredacted()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 16, *)
|
||||||
|
struct ComplicationView_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 expiredDate = shortExpirationDate.addingTimeInterval(1 * 60 * 60 * 24)
|
||||||
|
|
||||||
|
let weekAltstore = AppSnapshot(name: "AltStore",
|
||||||
|
bundleIdentifier: "com.rileytestut.AltStore",
|
||||||
|
expirationDate: shortExpirationDate,
|
||||||
|
refreshedDate: shortRefreshedDate,
|
||||||
|
tintColor: .altPrimary,
|
||||||
|
icon: UIImage(named: "AltStore"))
|
||||||
|
|
||||||
|
let yearAltstore = AppSnapshot(name: "AltStore",
|
||||||
|
bundleIdentifier: "com.rileytestut.AltStore",
|
||||||
|
expirationDate: longExpirationDate,
|
||||||
|
refreshedDate: longRefreshedDate,
|
||||||
|
tintColor: .altPrimary,
|
||||||
|
icon: UIImage(named: "AltStore"))
|
||||||
|
|
||||||
|
return Group {
|
||||||
|
ComplicationView(entry: AppEntry(date: Date(), app: weekAltstore))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .accessoryCircular))
|
||||||
|
|
||||||
|
ComplicationView(entry: AppEntry(date: expiredDate, app: weekAltstore))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .accessoryCircular))
|
||||||
|
|
||||||
|
ComplicationView(entry: AppEntry(date: longRefreshedDate, app: yearAltstore))
|
||||||
|
.previewContext(WidgetPreviewContext(family: .accessoryCircular))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
AltWidget/ProgressRing.swift
Normal file
54
AltWidget/ProgressRing.swift
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// ProgressRing.swift
|
||||||
|
// AltWidgetExtension
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 8/17/22.
|
||||||
|
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import WidgetKit
|
||||||
|
|
||||||
|
struct ProgressRing<Content: View>: View
|
||||||
|
{
|
||||||
|
let progress: Double
|
||||||
|
|
||||||
|
private let content: Content
|
||||||
|
|
||||||
|
init(progress: Double, @ViewBuilder content: () -> Content)
|
||||||
|
{
|
||||||
|
self.progress = progress
|
||||||
|
self.content = content()
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack(alignment: .center) {
|
||||||
|
ring(progress: 1.0)
|
||||||
|
.opacity(0.3)
|
||||||
|
|
||||||
|
ring(progress: self.progress)
|
||||||
|
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private func ring(progress: Double) -> some View {
|
||||||
|
let strokeStyle = StrokeStyle(lineWidth: 4.0, lineCap: .round, lineJoin: .round)
|
||||||
|
|
||||||
|
Circle()
|
||||||
|
.inset(by: 2.0)
|
||||||
|
.trim(from: 0.0, to: progress)
|
||||||
|
.rotation(Angle(degrees: -90), anchor: .center)
|
||||||
|
.stroke(style: strokeStyle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProgressRing_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ProgressRing(progress: 0.5) {
|
||||||
|
EmptyView()
|
||||||
|
}
|
||||||
|
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user