Compare commits

..

36 Commits

Author SHA1 Message Date
June
62faddf954 chore: Change deployment target to 18.0 2024-11-16 23:48:50 +09:00
Joe Mattiello
6a1181b21f Change gitignore, add package.resolve (#748) 2024-11-11 23:10:44 -05:00
kaoneko
c25ae10873 Update message about Disable Idle Timeout setting (#747) 2024-11-11 15:12:49 -05:00
Magesh K
2842c8f669 Widgets-Fix: Ported Widget compatibility for ios 18+ from altstore (#746) 2024-11-11 14:46:33 -05:00
Magesh K
3161892585 Background and shortcut refresh fix - remove only excess extensions from new package during install (#743) 2024-11-10 13:01:31 -05:00
Magesh K
489843f987 Partial-Fix for #723: Error on refresh operation must be set on self.context instead of group.context (#742) 2024-11-10 06:11:44 -05:00
Magesh K
dc0b30ab67 -[diagnostics]: Added diagnostics for RefreshAppOperation failure 2024-11-10 16:31:28 +05:30
Magesh K
c3235cc554 -[cleanup]: added guard check to remove file only if it exists 2024-11-10 16:27:29 +05:30
Magesh K
6568e5918a -[bug-fix]: UI Api invocation needs to be on UI Thread 2024-11-10 16:27:29 +05:30
Michael
91fba6db99 Add missing = sign (#739) 2024-11-09 22:20:23 -05:00
Stern
6b7e9a66f1 Merge pull request #738 from mahee96/develop
-[cleanup]: restore changes dropped from altstore-1.6.3 merge by 1713fcc
2024-11-09 19:05:59 -05:00
Magesh K
3682b65a4a restore changes dropped from altstore by 1713fcc 2024-11-10 03:04:24 +05:30
Magesh K
117412645b diagnostics: improved error logging for OperationError.invalidParameters (#736) 2024-11-09 04:05:18 -05:00
Stern
c784ff6925 chore: We need to add Homebrew to our path or Xcode will not find it.
Signed-off-by: Stern <stern@sidestore.io>
2024-11-08 23:56:27 -05:00
Stern
cf477024fc chore: Add checks to fetch-prebuilt script.
This adds a check that if for example wget isn't installed it'll install from brew via brew install.

Signed-off-by: Stern <stern@sidestore.io>
2024-11-08 23:24:46 -05:00
Magesh K
d595b7037f refresh-app: enhanced error logging when app extension validation check fails (#730)
refresh-app: enhanced error logging when app exenstion validation check fails
2024-11-04 04:05:13 -05:00
June Park
0c5007c8d8 Merge pull request #729 from mahee96/sidestore-crash-xcode
xcode: Debug dylib support is still broken in iOS 18 causing "No Entry Point Found. checked(null)"
2024-11-04 17:39:05 +09:00
Magesh K
8a87445d1f Fix-regression: restored update-check logic(2a392dd) in InstalledApp.swift which was dropped in (1713fcc) (#728) 2024-11-04 03:38:28 -05:00
Magesh K
76a693fae4 xcode: Debug dylib is still broken in iOS 18 causing "No Entry Point Found. checked(null)" 2024-11-04 12:53:30 +05:30
Magesh K
9c150d5f4a Fix for Refresh Operation causing renewal/install of provisioning profiles for removed app extensions (#727)
* Fix AppExtensions not being updated on Disk after db is updated in InstallAppOperation

* refresh-Extensions: Added check to ensure extensions in DB and DISK matches if not then cancel current refresh request
2024-11-03 17:30:39 -05:00
ny
e5febcdc6c More attempts at fixing app limit issues 2024-10-24 00:41:29 -04:00
ny
1e969a0888 Attempt to make the bypass more sane 2024-10-23 07:31:52 -04:00
ny
72bb549ea3 Clean/fix this implementation 2024-10-23 06:44:39 -04:00
ny
3c7cfdd91f Fix detection for SparseRestore patch 2024-10-23 06:07:58 -04:00
Moonsn
b21f80cdd7 feat: show custom anisette server list if set (#717) 2024-10-22 20:16:37 -04:00
nythepegasus
867a9c77e6 Actually fix deprecation warnings 2024-10-17 06:14:44 -04:00
nythepegasus
bc2d2c18fc Update GH Actions to fix deprecation warnings 2024-10-17 05:58:38 -04:00
nythepegasus
ab923d245d Try to fix refreshing and removing extensions
Silly fix

Signed-off-by: nythepegasus <me@nythepegas.us>
2024-10-17 05:18:10 -04:00
nythepegasus
fcf1b9ae03 Remove this
Should fix remove app extensions

Signed-off-by: nythepegasus <me@nythepegas.us>
2024-10-17 04:47:42 -04:00
ny
59896e4f89 Add a few more checks for app limits 2024-10-11 02:50:48 -04:00
ny
2a9f88c810 Add a max limit 2024-10-11 02:33:25 -04:00
ny
e98b0a3758 Fix these actions to point correctly 2024-10-11 02:24:36 -04:00
ny
0cb6da7be4 Fix up this so it actually sets the value 2024-10-11 01:48:01 -04:00
ny
fc3ff41fc4 Add bad app limit toggle implementation 2024-10-11 01:12:22 -04:00
Stern
719ddc8263 (fix:) Update Patreon Secret and links
Signed-off-by: Stern <stern@sidestore.io>
2024-09-16 07:18:41 -04:00
nythepegasus
9f6b1284bb [skipci] Bump to 0.5.9 for new nightly cycle
Signed-off-by: nythepegasus <me@nythepegas.us>
2024-09-07 14:44:14 -04:00
41 changed files with 668 additions and 152 deletions

View File

@@ -11,13 +11,13 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: 'macos-12' - os: 'macos-14'
version: '14.2' version: '15.4'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@@ -35,7 +35,7 @@ jobs:
run: echo "${{ steps.version.outputs.version }}" run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1
with: with:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
@@ -91,13 +91,13 @@ jobs:
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}.ipa name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload *.dSYM Artifact - name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./*.dSYM/ path: ./*.dSYM/

View File

@@ -14,13 +14,13 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: 'macos-12' - os: 'macos-14'
version: '14.2' version: '15.4'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@@ -28,7 +28,7 @@ jobs:
run: brew install ldid run: brew install ldid
- name: Cache .nightly-build-num - name: Cache .nightly-build-num
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
path: .nightly-build-num path: .nightly-build-num
key: nightly-build-num key: nightly-build-num
@@ -44,7 +44,7 @@ jobs:
run: echo "${{ steps.version.outputs.version }}" run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1.6.0
with: with:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
@@ -97,13 +97,13 @@ jobs:
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}.ipa name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload *.dSYM Artifact - name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./*.dSYM/ path: ./*.dSYM/

View File

@@ -9,13 +9,13 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: 'macos-12' - os: 'macos-14'
version: '14.2' version: '15.4'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@@ -35,7 +35,7 @@ jobs:
run: echo "${{ steps.version.outputs.version }}" run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1.6.0
with: with:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
@@ -58,13 +58,13 @@ jobs:
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}.ipa name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload *.dSYM Artifact - name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./*.dSYM/ path: ./*.dSYM/

View File

@@ -11,13 +11,13 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- os: 'macos-12' - os: 'macos-14'
version: '14.2' version: '15.4'
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v4
with: with:
submodules: recursive submodules: recursive
@@ -35,7 +35,7 @@ jobs:
run: echo "${{ steps.version.outputs.version }}" run: echo "${{ steps.version.outputs.version }}"
- name: Setup Xcode - name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1.4.1 uses: maxim-lobanov/setup-xcode@v1.6.0
with: with:
xcode-version: ${{ matrix.version }} xcode-version: ${{ matrix.version }}
@@ -87,13 +87,13 @@ jobs:
run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa run: mv SideStore.ipa SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload SideStore.ipa Artifact - name: Upload SideStore.ipa Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}.ipa name: SideStore-${{ steps.version.outputs.version }}.ipa
path: SideStore-${{ steps.version.outputs.version }}.ipa path: SideStore-${{ steps.version.outputs.version }}.ipa
- name: Upload *.dSYM Artifact - name: Upload *.dSYM Artifact
uses: actions/upload-artifact@v3.1.0 uses: actions/upload-artifact@v4
with: with:
name: SideStore-${{ steps.version.outputs.version }}-dSYM name: SideStore-${{ steps.version.outputs.version }}-dSYM
path: ./*.dSYM/ path: ./*.dSYM/

1
.gitignore vendored
View File

@@ -19,7 +19,6 @@ archive.xcarchive
*.perspectivev3 *.perspectivev3
!default.perspectivev3 !default.perspectivev3
xcuserdata xcuserdata
AltStore.xcodeproj/project.xcworkspace/xcshareddata/swiftpm
## Other ## Other
*.xccheckout *.xccheckout
*.moved-aside *.moved-aside

View File

@@ -0,0 +1,48 @@
//
// ErrorDetailsViewController.swift
// AltServer
//
// Created by Riley Testut on 10/4/22.
// Copyright © 2022 Riley Testut. All rights reserved.
//
import AppKit
class ErrorDetailsViewController: NSViewController
{
var error: NSError? {
didSet {
self.update()
}
}
@IBOutlet private var errorCodeLabel: NSTextField!
@IBOutlet private var detailedDescriptionLabel: NSTextField!
override func viewDidLoad()
{
super.viewDidLoad()
self.detailedDescriptionLabel.preferredMaxLayoutWidth = 800
}
}
private extension ErrorDetailsViewController
{
func update()
{
if !self.isViewLoaded
{
self.loadView()
}
guard let error = self.error else { return }
self.errorCodeLabel.stringValue = error.localizedErrorCode
let font = self.detailedDescriptionLabel.font ?? NSFont.systemFont(ofSize: 12)
let detailedDescription = error.formattedDetailedDescription(with: font)
self.detailedDescriptionLabel.attributedStringValue = detailedDescription
}
}

View File

@@ -10,6 +10,7 @@
03F06CD52942C27E001C4D68 /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; }; 03F06CD52942C27E001C4D68 /* Bundle+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */; };
0E05025A2BEC83C500879B5C /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0502592BEC83C500879B5C /* OperatingSystemVersion+Comparable.swift */; }; 0E05025A2BEC83C500879B5C /* OperatingSystemVersion+Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E0502592BEC83C500879B5C /* OperatingSystemVersion+Comparable.swift */; };
0E05025C2BEC947000879B5C /* String+SideStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E05025B2BEC947000879B5C /* String+SideStore.swift */; }; 0E05025C2BEC947000879B5C /* String+SideStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E05025B2BEC947000879B5C /* String+SideStore.swift */; };
0E13E5862CC8F55900E9C0DF /* ProcessInfo+SideStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E13E5852CC8F55900E9C0DF /* ProcessInfo+SideStore.swift */; };
0E1A1F912AE36A9700364CAD /* bytearray.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E1A1F902AE36A9600364CAD /* bytearray.c */; }; 0E1A1F912AE36A9700364CAD /* bytearray.c in Sources */ = {isa = PBXBuildFile; fileRef = 0E1A1F902AE36A9600364CAD /* bytearray.c */; };
0E764E172ADFF5740043DD4E /* AltBackup.ipa in Resources */ = {isa = PBXBuildFile; fileRef = 0E764E162ADFF5740043DD4E /* AltBackup.ipa */; }; 0E764E172ADFF5740043DD4E /* AltBackup.ipa in Resources */ = {isa = PBXBuildFile; fileRef = 0E764E162ADFF5740043DD4E /* AltBackup.ipa */; };
0EA1665B2ADFE0D2003015C1 /* out-limd.c in Sources */ = {isa = PBXBuildFile; fileRef = 0EA166472ADFE0D1003015C1 /* out-limd.c */; }; 0EA1665B2ADFE0D2003015C1 /* out-limd.c in Sources */ = {isa = PBXBuildFile; fileRef = 0EA166472ADFE0D1003015C1 /* out-limd.c */; };
@@ -59,6 +60,7 @@
99F87D0529D8B4E200B40039 /* minimuxer-helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */; }; 99F87D0529D8B4E200B40039 /* minimuxer-helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */; };
99F87D1829D8E4C900B40039 /* SwiftBridgeCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F87D1629D8E4C900B40039 /* SwiftBridgeCore.swift */; }; 99F87D1829D8E4C900B40039 /* SwiftBridgeCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F87D1629D8E4C900B40039 /* SwiftBridgeCore.swift */; };
99F87D1929D8E4C900B40039 /* minimuxer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F87D1729D8E4C900B40039 /* minimuxer.swift */; }; 99F87D1929D8E4C900B40039 /* minimuxer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99F87D1729D8E4C900B40039 /* minimuxer.swift */; };
A800F7042CE28E3800208744 /* View+AltWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = A800F7032CE28E2F00208744 /* View+AltWidget.swift */; };
B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; }; B3146ED2284F581E00BBC3FD /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3146ECD284F580500BBC3FD /* Roxas.framework */; };
B33FFBA8295F8E98002259E6 /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B343F894295F7F9B002B1159 /* libfragmentzip.a */; }; B33FFBA8295F8E98002259E6 /* libfragmentzip.a in Frameworks */ = {isa = PBXBuildFile; fileRef = B343F894295F7F9B002B1159 /* libfragmentzip.a */; };
B33FFBAA295F8F78002259E6 /* preboard.c in Sources */ = {isa = PBXBuildFile; fileRef = B33FFBA9295F8F78002259E6 /* preboard.c */; }; B33FFBAA295F8F78002259E6 /* preboard.c in Sources */ = {isa = PBXBuildFile; fileRef = B33FFBA9295F8F78002259E6 /* preboard.c */; };
@@ -509,6 +511,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
0E0502592BEC83C500879B5C /* OperatingSystemVersion+Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OperatingSystemVersion+Comparable.swift"; sourceTree = "<group>"; }; 0E0502592BEC83C500879B5C /* OperatingSystemVersion+Comparable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OperatingSystemVersion+Comparable.swift"; sourceTree = "<group>"; };
0E05025B2BEC947000879B5C /* String+SideStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+SideStore.swift"; sourceTree = "<group>"; }; 0E05025B2BEC947000879B5C /* String+SideStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+SideStore.swift"; sourceTree = "<group>"; };
0E13E5852CC8F55900E9C0DF /* ProcessInfo+SideStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+SideStore.swift"; sourceTree = "<group>"; };
0E1A1F902AE36A9600364CAD /* bytearray.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = bytearray.c; path = src/bytearray.c; sourceTree = "<group>"; }; 0E1A1F902AE36A9600364CAD /* bytearray.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = bytearray.c; path = src/bytearray.c; sourceTree = "<group>"; };
0E764E162ADFF5740043DD4E /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; name = AltBackup.ipa; path = AltStore/Resources/AltBackup.ipa; sourceTree = SOURCE_ROOT; }; 0E764E162ADFF5740043DD4E /* AltBackup.ipa */ = {isa = PBXFileReference; lastKnownFileType = file; name = AltBackup.ipa; path = AltStore/Resources/AltBackup.ipa; sourceTree = SOURCE_ROOT; };
0EA166412ADFE0D1003015C1 /* jplist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jplist.c; path = Dependencies/libplist/src/jplist.c; sourceTree = SOURCE_ROOT; }; 0EA166412ADFE0D1003015C1 /* jplist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = jplist.c; path = Dependencies/libplist/src/jplist.c; sourceTree = SOURCE_ROOT; };
@@ -557,6 +560,7 @@
9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "minimuxer-helpers.swift"; path = "Dependencies/minimuxer/minimuxer-helpers.swift"; sourceTree = SOURCE_ROOT; }; 9961EC2D29BE9F2E00AF2C6F /* minimuxer-helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = "minimuxer-helpers.swift"; path = "Dependencies/minimuxer/minimuxer-helpers.swift"; sourceTree = SOURCE_ROOT; };
99F87D1629D8E4C900B40039 /* SwiftBridgeCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftBridgeCore.swift; path = Dependencies/minimuxer/SwiftBridgeCore.swift; sourceTree = SOURCE_ROOT; }; 99F87D1629D8E4C900B40039 /* SwiftBridgeCore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftBridgeCore.swift; path = Dependencies/minimuxer/SwiftBridgeCore.swift; sourceTree = SOURCE_ROOT; };
99F87D1729D8E4C900B40039 /* minimuxer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = minimuxer.swift; path = Dependencies/minimuxer/minimuxer.swift; sourceTree = SOURCE_ROOT; }; 99F87D1729D8E4C900B40039 /* minimuxer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = minimuxer.swift; path = Dependencies/minimuxer/minimuxer.swift; sourceTree = SOURCE_ROOT; };
A800F7032CE28E2F00208744 /* View+AltWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+AltWidget.swift"; sourceTree = "<group>"; };
B3146EC6284F580500BBC3FD /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = "<group>"; }; B3146EC6284F580500BBC3FD /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = "<group>"; };
B33FFBA9295F8F78002259E6 /* preboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preboard.c; path = src/preboard.c; sourceTree = "<group>"; }; B33FFBA9295F8F78002259E6 /* preboard.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preboard.c; path = src/preboard.c; sourceTree = "<group>"; };
B33FFBAB295F8F98002259E6 /* companion_proxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = companion_proxy.c; path = src/companion_proxy.c; sourceTree = "<group>"; }; B33FFBAB295F8F98002259E6 /* companion_proxy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = companion_proxy.c; path = src/companion_proxy.c; sourceTree = "<group>"; };
@@ -1006,6 +1010,14 @@
name = Generated; name = Generated;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
A800F6FE2CE28DE300208744 /* Extensions */ = {
isa = PBXGroup;
children = (
A800F7032CE28E2F00208744 /* View+AltWidget.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
B3146EC7284F580500BBC3FD /* Products */ = { B3146EC7284F580500BBC3FD /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@@ -1463,6 +1475,7 @@
BF98916C250AABF3002ACF50 /* AltWidget */ = { BF98916C250AABF3002ACF50 /* AltWidget */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A800F6FE2CE28DE300208744 /* Extensions */,
BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */, BF8B17F0250AC62400F8157F /* AltWidgetExtension.entitlements */,
BF98917D250AAC4F002ACF50 /* AltWidget.swift */, BF98917D250AAC4F002ACF50 /* AltWidget.swift */,
BF42345825101C1D006D1EB2 /* WidgetView.swift */, BF42345825101C1D006D1EB2 /* WidgetView.swift */,
@@ -1678,6 +1691,7 @@
BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */, BFE00A1F2503097F00EB4D0C /* INInteraction+AltStore.swift */,
D57F2C9326E01BC700B9FA39 /* UIDevice+Vibration.swift */, D57F2C9326E01BC700B9FA39 /* UIDevice+Vibration.swift */,
B376FE3D29258C8900E18883 /* OSLog+SideStore.swift */, B376FE3D29258C8900E18883 /* OSLog+SideStore.swift */,
0E13E5852CC8F55900E9C0DF /* ProcessInfo+SideStore.swift */,
); );
path = Extensions; path = Extensions;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -2486,6 +2500,7 @@
BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */, BF42345A25101C35006D1EB2 /* WidgetView.swift in Sources */,
D55E163728776CB700A627A1 /* ComplicationView.swift in Sources */, D55E163728776CB700A627A1 /* ComplicationView.swift in Sources */,
BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */, BF98917F250AAC4F002ACF50 /* AltWidget.swift in Sources */,
A800F7042CE28E3800208744 /* View+AltWidget.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -2552,6 +2567,7 @@
BF770E5822BC3D0F002A40FE /* RefreshGroup.swift in Sources */, BF770E5822BC3D0F002A40FE /* RefreshGroup.swift in Sources */,
19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */, 19B9B7452845E6DF0076EF69 /* SelectTeamViewController.swift in Sources */,
99F87D0529D8B4E200B40039 /* minimuxer-helpers.swift in Sources */, 99F87D0529D8B4E200B40039 /* minimuxer-helpers.swift in Sources */,
0E13E5862CC8F55900E9C0DF /* ProcessInfo+SideStore.swift in Sources */,
BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */, BF18B0F122E25DF9005C4CF5 /* ToastView.swift in Sources */,
BF3D649F22E7B24C00E9056B /* CollapsingTextView.swift in Sources */, BF3D649F22E7B24C00E9056B /* CollapsingTextView.swift in Sources */,
BF02419622F2199300129732 /* RefreshAttemptsViewController.swift in Sources */, BF02419622F2199300129732 /* RefreshAttemptsViewController.swift in Sources */,
@@ -2997,7 +3013,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath"; DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = AltStoreCore/Info.plist; INFOPLIST_FILE = AltStoreCore/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -3035,7 +3051,7 @@
GCC_OPTIMIZATION_LEVEL = fast; GCC_OPTIMIZATION_LEVEL = fast;
INFOPLIST_FILE = AltStoreCore/Info.plist; INFOPLIST_FILE = AltStoreCore/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -3069,9 +3085,10 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
ENABLE_DEBUG_DYLIB = YES;
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = AltWidget/Info.plist; INFOPLIST_FILE = AltWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -3099,9 +3116,10 @@
CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
ENABLE_DEBUG_DYLIB = YES;
FRAMEWORK_SEARCH_PATHS = "$(inherited)"; FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = AltWidget/Info.plist; INFOPLIST_FILE = AltWidget/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
@@ -3168,7 +3186,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.2; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LLVM_LTO = YES_THIN; LLVM_LTO = YES_THIN;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
@@ -3234,7 +3252,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.2; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LLVM_LTO = YES_THIN; LLVM_LTO = YES_THIN;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
@@ -3265,9 +3283,10 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_DEBUG_DYLIB = NO;
GCC_UNROLL_LOOPS = YES; GCC_UNROLL_LOOPS = YES;
INFOPLIST_FILE = AltStore/Info.plist; INFOPLIST_FILE = AltStore/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LD_EXPORT_SYMBOLS = NO; LD_EXPORT_SYMBOLS = NO;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@@ -3304,10 +3323,11 @@
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_DEBUG_DYLIB = NO;
GCC_OPTIMIZATION_LEVEL = fast; GCC_OPTIMIZATION_LEVEL = fast;
GCC_UNROLL_LOOPS = YES; GCC_UNROLL_LOOPS = YES;
INFOPLIST_FILE = AltStore/Info.plist; INFOPLIST_FILE = AltStore/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0; IPHONEOS_DEPLOYMENT_TARGET = 18.0;
LD_EXPORT_SYMBOLS = NO; LD_EXPORT_SYMBOLS = NO;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",

View File

@@ -0,0 +1,96 @@
//
// ProcessInfo+SideStore.swift
// SideStore
//
// Created by ny on 10/23/24.
// Copyright © 2024 SideStore. All rights reserved.
//
import Foundation
fileprivate struct BuildVersion: Comparable {
let prefix: String
let numericPart: Int
let suffix: Character?
init?(_ buildString: String) {
// Initialize indices
var index = buildString.startIndex
// Extract prefix (letters before the numeric part)
while index < buildString.endIndex, !buildString[index].isNumber {
index = buildString.index(after: index)
}
guard index > buildString.startIndex else { return nil }
self.prefix = String(buildString[buildString.startIndex..<index])
// Extract numeric part
let startOfNumeric = index
while index < buildString.endIndex, buildString[index].isNumber {
index = buildString.index(after: index)
}
guard let numericValue = Int(buildString[startOfNumeric..<index]) else { return nil }
self.numericPart = numericValue
// Extract suffix (if any)
if index < buildString.endIndex {
self.suffix = buildString[index]
} else {
self.suffix = nil
}
}
// Implement Comparable protocol
static func < (lhs: BuildVersion, rhs: BuildVersion) -> Bool {
// Compare prefixes
if lhs.prefix != rhs.prefix {
return lhs.prefix < rhs.prefix
}
// Compare numeric parts
if lhs.numericPart != rhs.numericPart {
return lhs.numericPart < rhs.numericPart
}
// Compare suffixes
switch (lhs.suffix, rhs.suffix) {
case let (l?, r?):
return l < r
case (nil, _?):
return true // nil is considered less than any character
case (_?, nil):
return false
default:
return false // Both are nil and equal
}
}
static func == (lhs: BuildVersion, rhs: BuildVersion) -> Bool {
return lhs.prefix == rhs.prefix &&
lhs.numericPart == rhs.numericPart &&
lhs.suffix == rhs.suffix
}
}
extension ProcessInfo {
var shortVersion: String {
operatingSystemVersionString
.replacingOccurrences(of: "Version ", with: "")
.replacingOccurrences(of: "Build ", with: "")
}
var operatingSystemBuild: String {
if let start = shortVersion.range(of: "(")?.upperBound,
let end = shortVersion.range(of: ")")?.lowerBound {
shortVersion[start..<end].replacingOccurrences(of: "Build ", with: "")
} else { "???" }
}
var sparseRestorePatched: Bool {
if operatingSystemVersion < OperatingSystemVersion(majorVersion: 18, minorVersion: 1, patchVersion: 0) { false }
else if operatingSystemVersion > OperatingSystemVersion(majorVersion: 18, minorVersion: 1, patchVersion: 1) { true }
else if operatingSystemVersion >= OperatingSystemVersion(majorVersion: 18, minorVersion: 1, patchVersion: 0),
let currentBuild = BuildVersion(operatingSystemBuild),
let targetBuild = BuildVersion("22B5054e") {
currentBuild >= targetBuild
} else { false }
}
}

View File

@@ -118,11 +118,12 @@ private extension IntentHandler
{ {
// Queue response in case refreshing finishes after confirm() but before handle(). // Queue response in case refreshing finishes after confirm() but before handle().
self.queuedResponses[intent] = response self.queuedResponses[intent] = response
DispatchQueue.main.async {
UIApplication.shared.perform(#selector(NSXPCConnection.suspend)) UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
} }
} }
} }
}
func refreshApps(intent: RefreshAllIntent) func refreshApps(intent: RefreshAllIntent)
{ {

View File

@@ -240,7 +240,7 @@ extension AppManager
func deactivateApps(for app: ALTApplication, presentingViewController: UIViewController, completion: @escaping (Result<Void, Error>) -> Void) func deactivateApps(for app: ALTApplication, presentingViewController: UIViewController, completion: @escaping (Result<Void, Error>) -> Void)
{ {
guard let activeAppsLimit = UserDefaults.standard.activeAppsLimit else { return completion(.success(())) } guard !UserDefaults.standard.isAppLimitDisabled, let activeAppsLimit = UserDefaults.standard.activeAppsLimit else { return completion(.success(())) }
DispatchQueue.main.async { DispatchQueue.main.async {
let activeApps = InstalledApp.fetchActiveApps(in: DatabaseManager.shared.viewContext) let activeApps = InstalledApp.fetchActiveApps(in: DatabaseManager.shared.viewContext)
@@ -1015,8 +1015,42 @@ private extension AppManager
return group return group
} }
func removeAppExtensions(from application: ALTApplication, extensions: Set<ALTApplication>, _ presentingViewController: UIViewController, completion: @escaping (Result<Void, Error>) -> Void) func removeAppExtensions(from application: ALTApplication, existingApp: InstalledApp?, extensions: Set<ALTApplication>, _ presentingViewController: UIViewController?, completion: @escaping (Result<Void, Error>) -> Void)
{ {
//App-Extensions: Ensure existing app's extensions and currently installing app's extensions must match
let existingAppEx: Set<InstalledExtension> = existingApp?.appExtensions ?? Set()
let currentAppEx: Set<ALTApplication> = application.appExtensions
let currentAppExNames = currentAppEx.map{ appEx in appEx.bundleIdentifier}
let existingAppExNames = existingAppEx.map{ appEx in appEx.bundleIdentifier}
let excessExtensions = currentAppEx.filter{
!(existingAppExNames.contains($0.bundleIdentifier))
}
let isMatching = (currentAppEx.count == existingAppEx.count) && excessExtensions.isEmpty
let diagnosticsMsg = "AppManager.removeAppExtensions: App Extensions in existingApp and currentApp are matching: \(isMatching)\n"
+ "AppManager.removeAppExtensions: existingAppEx: \(existingAppExNames); currentAppEx: \(String(describing: currentAppExNames))\n"
print(diagnosticsMsg)
// if background mode, then remove only the excess extensions
guard let presentingViewController: UIViewController = presentingViewController else {
// perform silent extensions cleanup for those that aren't already present in existing app
print("\n Performing background mode Extensions removal \n")
print("AppManager.removeAppExtensions: Excess Extensions: \(excessExtensions)")
do {
for appExtension in excessExtensions {
print("Deleting extension \(appExtension.bundleIdentifier)")
try FileManager.default.removeItem(at: appExtension.fileURL)
}
return completion(.success(()))
} catch {
return completion(.failure(error))
}
}
guard !application.appExtensions.isEmpty else { return completion(.success(())) } guard !application.appExtensions.isEmpty else { return completion(.success(())) }
let firstSentence: String let firstSentence: String
@@ -1044,6 +1078,7 @@ private extension AppManager
{ {
for appExtension in application.appExtensions for appExtension in application.appExtensions
{ {
print("Deleting extension \(appExtension.bundleIdentifier)")
try FileManager.default.removeItem(at: appExtension.fileURL) try FileManager.default.removeItem(at: appExtension.fileURL)
} }
@@ -1178,18 +1213,26 @@ private extension AppManager
{ {
throw error throw error
} }
/*
guard case .install = appOperation else { guard case .install = appOperation else {
operation.finish() operation.finish()
return return
} }
*/
guard let extensions = context.app?.appExtensions else {
throw OperationError.invalidParameters("AppManager._install.removeAppExtensionsOperation: context.app?.appExtensions is nil")
}
guard let extensions = context.app?.appExtensions else { throw OperationError.invalidParameters } guard let currentApp = context.app else {
throw OperationError.invalidParameters("AppManager._install.removeAppExtensionsOperation: context.app is nil")
guard let app = context.app, let presentingViewController = context.authenticatedContext.presentingViewController else { throw OperationError.invalidParameters } }
self?.removeAppExtensions(from: app, extensions: extensions, presentingViewController) { result in self?.removeAppExtensions(from: currentApp,
existingApp: app as? InstalledApp,
extensions: extensions,
context.authenticatedContext.presentingViewController
) { result in
switch result { switch result {
case .success(): break case .success(): break
case .failure(let error): context.error = error case .failure(let error): context.error = error
@@ -1200,7 +1243,7 @@ private extension AppManager
} }
catch catch
{ {
group.context.error = error context.error = error
operation.finish() operation.finish()
} }
} }
@@ -1252,13 +1295,20 @@ private extension AppManager
throw error throw error
} }
guard let profiles = context.provisioningProfiles else { throw OperationError.invalidParameters } guard let profiles = context.provisioningProfiles else {
throw OperationError.invalidParameters("AppManager._install.deactivateAppsOperation: context.provisioningProfiles is nil")
}
if !profiles.contains(where: { $1.isFreeProvisioningProfile == true }) { if !profiles.contains(where: { $1.isFreeProvisioningProfile == true }) {
operation.finish() operation.finish()
return return
} }
guard let app = context.app, let presentingViewController = context.authenticatedContext.presentingViewController else { throw OperationError.invalidParameters } guard
let app = context.app,
let presentingViewController = context.authenticatedContext.presentingViewController
else {
throw OperationError.invalidParameters("AppManager._install.deactivateAppsOperation: self.context.app or context.authenticatedContext.presentingViewController is nil")
}
self?.deactivateApps(for: app, presentingViewController: presentingViewController) { result in self?.deactivateApps(for: app, presentingViewController: presentingViewController) { result in
switch result switch result
@@ -1297,7 +1347,9 @@ private extension AppManager
throw error throw error
} }
guard let app = context.app else { throw OperationError.invalidParameters } guard let app = context.app else {
throw OperationError.invalidParameters("AppManager._install.patchAppOperation: context.app is nil")
}
guard let isUntetherRequired = app.bundle.infoDictionary?[Bundle.Info.untetherRequired] as? Bool, guard let isUntetherRequired = app.bundle.infoDictionary?[Bundle.Info.untetherRequired] as? Bool,
let minimumiOSVersionString = app.bundle.infoDictionary?[Bundle.Info.untetherMinimumiOSVersion] as? String, let minimumiOSVersionString = app.bundle.infoDictionary?[Bundle.Info.untetherMinimumiOSVersion] as? String,
@@ -1416,6 +1468,25 @@ private extension AppManager
let context = AppOperationContext(bundleIdentifier: app.bundleIdentifier, authenticatedContext: group.context) let context = AppOperationContext(bundleIdentifier: app.bundleIdentifier, authenticatedContext: group.context)
context.app = ALTApplication(fileURL: app.fileURL) context.app = ALTApplication(fileURL: app.fileURL)
let validateAppExtensionsOperation = RSTAsyncBlockOperation {(op) in
//App-Extensions: Ensure DB data and disk state must match
let dbAppEx: Set<InstalledExtension> = app.appExtensions
let diskAppEx: Set<ALTApplication> = context.app!.appExtensions
let diskAppExNames = diskAppEx.map { $0.bundleIdentifier }
let dbAppExNames = dbAppEx.map{ $0.bundleIdentifier }
let isMatching = Set(dbAppExNames) == Set(diskAppExNames)
let errMessage = "AppManager.refresh: App Extensions in DB and Disk are matching: \(isMatching)\n"
+ "AppManager.refresh: dbAppEx: \(dbAppExNames); diskAppEx: \(String(describing: diskAppExNames))\n"
print(errMessage)
if(!isMatching){
completionHandler(.failure(OperationError.refreshAppFailed(message: errMessage)))
}
op.finish()
}
/* Fetch Provisioning Profiles */ /* Fetch Provisioning Profiles */
let fetchProvisioningProfilesOperation = FetchProvisioningProfilesOperation(context: context) let fetchProvisioningProfilesOperation = FetchProvisioningProfilesOperation(context: context)
fetchProvisioningProfilesOperation.resultHandler = { (result) in fetchProvisioningProfilesOperation.resultHandler = { (result) in
@@ -1426,6 +1497,8 @@ private extension AppManager
} }
} }
progress.addChild(fetchProvisioningProfilesOperation.progress, withPendingUnitCount: 60) progress.addChild(fetchProvisioningProfilesOperation.progress, withPendingUnitCount: 60)
fetchProvisioningProfilesOperation.addDependency(validateAppExtensionsOperation)
/* Refresh */ /* Refresh */
let refreshAppOperation = RefreshAppOperation(context: context) let refreshAppOperation = RefreshAppOperation(context: context)
@@ -1459,7 +1532,7 @@ private extension AppManager
progress.addChild(refreshAppOperation.progress, withPendingUnitCount: 40) progress.addChild(refreshAppOperation.progress, withPendingUnitCount: 40)
refreshAppOperation.addDependency(fetchProvisioningProfilesOperation) refreshAppOperation.addDependency(fetchProvisioningProfilesOperation)
let operations = [fetchProvisioningProfilesOperation, refreshAppOperation] let operations = [validateAppExtensionsOperation, fetchProvisioningProfilesOperation, refreshAppOperation]
group.add(operations) group.add(operations)
self.run(operations, context: group.context) self.run(operations, context: group.context)
@@ -1920,6 +1993,42 @@ private extension AppManager
UNUserNotificationCenter.current().add(request) UNUserNotificationCenter.current().add(request)
} }
func log(_ error: Error, for operation: AppOperation)
{
// Sanitize NSError on same thread before performing background task.
let sanitizedError = (error as NSError).sanitizedForSerialization()
let loggedErrorOperation: LoggedError.Operation = {
switch operation
{
case .install: return .install
case .update: return .update
case .refresh: return .refresh
case .activate: return .activate
case .deactivate: return .deactivate
case .backup: return .backup
case .restore: return .restore
}
}()
DatabaseManager.shared.persistentContainer.performBackgroundTask { context in
var app = operation.app
if let managedApp = app as? NSManagedObject, let tempApp = context.object(with: managedApp.objectID) as? AppProtocol
{
app = tempApp
}
do
{
_ = LoggedError(error: sanitizedError, app: app, operation: loggedErrorOperation, context: context)
try context.save()
}
catch let saveError
{
print("[ALTLog] Failed to log error \(sanitizedError.domain) code \(sanitizedError.code) for \(app.bundleIdentifier):", saveError)
}
}
}
func run(_ operations: [Foundation.Operation], context: OperationContext?, requiresSerialQueue: Bool = false) func run(_ operations: [Foundation.Operation], context: OperationContext?, requiresSerialQueue: Bool = false)
{ {

View File

@@ -791,7 +791,9 @@ private extension MyAppsViewController
throw error throw error
} }
guard let fileURL = context.fileURL else { throw OperationError.invalidParameters } guard let fileURL = context.fileURL else {
throw OperationError.invalidParameters("MyAppsViewController.sideloadApp.unzipAppOperation: context.fileURL is nil")
}
defer { defer {
try? FileManager.default.removeItem(at: fileURL) try? FileManager.default.removeItem(at: fileURL)
} }
@@ -825,7 +827,9 @@ private extension MyAppsViewController
throw error throw error
} }
guard let application = context.application else { throw OperationError.invalidParameters } guard let application = context.application else {
throw OperationError.invalidParameters("MyAppsViewController.sideloadApp.installAppOperation: context.application is nil")
}
let group = AppManager.shared.install(application, presentingViewController: self) { (result) in let group = AppManager.shared.install(application, presentingViewController: self) { (result) in
switch result switch result
@@ -994,7 +998,7 @@ private extension MyAppsViewController
} }
} }
if UserDefaults.standard.activeAppsLimit != nil, #available(iOS 13, *) if !UserDefaults.standard.isAppLimitDisabled && UserDefaults.standard.activeAppsLimit != nil, #available(iOS 13, *)
{ {
// UserDefaults.standard.activeAppsLimit is only non-nil on iOS 13.3.1 or later, so the #available check is just so we can use Combine. // UserDefaults.standard.activeAppsLimit is only non-nil on iOS 13.3.1 or later, so the #available check is just so we can use Combine.
@@ -1354,7 +1358,7 @@ extension MyAppsViewController
headerView.layoutMargins.left = self.view.layoutMargins.left headerView.layoutMargins.left = self.view.layoutMargins.left
headerView.layoutMargins.right = self.view.layoutMargins.right headerView.layoutMargins.right = self.view.layoutMargins.right
if UserDefaults.standard.activeAppsLimit == nil if UserDefaults.standard.activeAppsLimit == nil || UserDefaults.standard.isAppLimitDisabled
{ {
headerView.textLabel.text = NSLocalizedString("Installed", comment: "") headerView.textLabel.text = NSLocalizedString("Installed", comment: "")
} }
@@ -1753,7 +1757,7 @@ extension MyAppsViewController: UICollectionViewDragDelegate
return [] return []
case .activeApps, .inactiveApps: case .activeApps, .inactiveApps:
guard UserDefaults.standard.activeAppsLimit != nil else { return [] } guard UserDefaults.standard.activeAppsLimit != nil && !UserDefaults.standard.isAppLimitDisabled else { return [] }
guard let cell = collectionView.cellForItem(at: indexPath as IndexPath) as? InstalledAppCollectionViewCell else { return [] } guard let cell = collectionView.cellForItem(at: indexPath as IndexPath) as? InstalledAppCollectionViewCell else { return [] }
let item = self.dataSource.item(at: indexPath) let item = self.dataSource.item(at: indexPath)
@@ -1808,6 +1812,7 @@ extension MyAppsViewController: UICollectionViewDropDelegate
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal
{ {
guard guard
!UserDefaults.standard.isAppLimitDisabled,
let activeAppsLimit = UserDefaults.standard.activeAppsLimit, let activeAppsLimit = UserDefaults.standard.activeAppsLimit,
let installedApp = session.items.first?.localObject as? InstalledApp let installedApp = session.items.first?.localObject as? InstalledApp
else { return UICollectionViewDropProposal(operation: .cancel) } else { return UICollectionViewDropProposal(operation: .cancel) }

View File

@@ -241,12 +241,11 @@ final class AuthenticationOperation: ResultOperation<(ALTTeam, ALTCertificate, A
} }
let activeAppsMinimumVersion = OperatingSystemVersion(majorVersion: 13, minorVersion: 3, patchVersion: 1) let activeAppsMinimumVersion = OperatingSystemVersion(majorVersion: 13, minorVersion: 3, patchVersion: 1)
if team.type == .free, ProcessInfo.processInfo.isOperatingSystemAtLeast(activeAppsMinimumVersion) if team.type == .free, !UserDefaults.standard.isAppLimitDisabled, ProcessInfo().sparseRestorePatched {
{
UserDefaults.standard.activeAppsLimit = ALTActiveAppsLimit UserDefaults.standard.activeAppsLimit = ALTActiveAppsLimit
} } else if UserDefaults.standard.isAppLimitDisabled, !ProcessInfo().sparseRestorePatched {
else UserDefaults.standard.activeAppsLimit = 10
{ } else {
UserDefaults.standard.activeAppsLimit = nil UserDefaults.standard.activeAppsLimit = nil
} }

View File

@@ -48,7 +48,9 @@ class BackupAppOperation: ResultOperation<Void>
{ {
if let error = self.context.error { throw error } if let error = self.context.error { throw error }
guard let installedApp = self.context.installedApp, let context = installedApp.managedObjectContext else { throw OperationError.invalidParameters } guard let installedApp = self.context.installedApp, let context = installedApp.managedObjectContext else {
throw OperationError.invalidParameters("BackupAppOperation.main: self.context.installedApp or installedApp.managedObjectContext is nil")
}
context.perform { context.perform {
do do
{ {

View File

@@ -20,6 +20,7 @@ final class DownloadAppOperation: ResultOperation<ALTApplication>
private let appName: String private let appName: String
private let bundleIdentifier: String private let bundleIdentifier: String
private var sourceURL: URL?
private let destinationURL: URL private let destinationURL: URL
private let session = URLSession(configuration: .default) private let session = URLSession(configuration: .default)
@@ -32,6 +33,7 @@ final class DownloadAppOperation: ResultOperation<ALTApplication>
self.appName = app.name self.appName = app.name
self.bundleIdentifier = app.bundleIdentifier self.bundleIdentifier = app.bundleIdentifier
self.sourceURL = app.url
self.destinationURL = destinationURL self.destinationURL = destinationURL
super.init() super.init()
@@ -111,7 +113,7 @@ private extension DownloadAppOperation {
} }
func download(@Managed _ app: AppProtocol) { func download(@Managed _ app: AppProtocol) {
guard let sourceURL = $app.url else { return self.finish(.failure(OperationError.appNotFound(name: self.appName))) } guard let sourceURL = self.sourceURL else { return self.finish(.failure(OperationError.appNotFound(name: self.appName))) }
self.downloadIPA(from: sourceURL) { result in self.downloadIPA(from: sourceURL) { result in
do do

View File

@@ -50,7 +50,9 @@ final class EnableJITOperation<Context: EnableJITContext>: ResultOperation<Void>
return return
} }
guard let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) } guard let installedApp = self.context.installedApp else {
return self.finish(.failure(OperationError.invalidParameters("EnableJITOperation.main: self.context.installedApp is nil")))
}
if #available(iOS 17, *) { if #available(iOS 17, *) {
let sideJITenabled = UserDefaults.standard.sidejitenable let sideJITenabled = UserDefaults.standard.sidejitenable
let SideJITIP = UserDefaults.standard.textInputSideJITServerurl ?? "" let SideJITIP = UserDefaults.standard.textInputSideJITServerurl ?? ""

View File

@@ -39,7 +39,9 @@ final class FetchAppIDsOperation: ResultOperation<([AppID], NSManagedObjectConte
guard guard
let team = self.context.team, let team = self.context.team,
let session = self.context.session let session = self.context.session
else { return self.finish(.failure(OperationError.invalidParameters)) } else {
return self.finish(.failure(OperationError.invalidParameters("FetchAppIDsOperation.main: self.context.team or self.context.session is nil")))
}
ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in
self.managedObjectContext.perform { self.managedObjectContext.perform {

View File

@@ -43,7 +43,8 @@ final class FetchProvisioningProfilesOperation: ResultOperation<[String: ALTProv
guard guard
let team = self.context.team, let team = self.context.team,
let session = self.context.session let session = self.context.session
else { return self.finish(.failure(OperationError.invalidParameters)) } else {
return self.finish(.failure(OperationError.invalidParameters("FetchProvisioningProfilesOperation.main: self.context.team or self.context.session is nil"))) }
guard let app = self.context.app else { return self.finish(.failure(OperationError.appNotFound(name: nil))) } guard let app = self.context.app else { return self.finish(.failure(OperationError.appNotFound(name: nil))) }
@@ -262,6 +263,10 @@ extension FetchProvisioningProfilesOperation
{ {
throw OperationError.maximumAppIDLimitReached(appName: application.name, requiredAppIDs: requiredAppIDs, availableAppIDs: availableAppIDs, expirationDate: expirationDate) throw OperationError.maximumAppIDLimitReached(appName: application.name, requiredAppIDs: requiredAppIDs, availableAppIDs: availableAppIDs, expirationDate: expirationDate)
} }
else
{
throw ALTAppleAPIError(.maximumAppIDLimitReached)
}
} }
} }
//App ID name must be ascii. If the name is not ascii, using bundleID instead //App ID name must be ascii. If the name is not ascii, using bundleID instead

View File

@@ -43,7 +43,9 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
let certificate = self.context.certificate, let certificate = self.context.certificate,
let resignedApp = self.context.resignedApp, let resignedApp = self.context.resignedApp,
let provisioningProfiles = self.context.provisioningProfiles let provisioningProfiles = self.context.provisioningProfiles
else { return self.finish(.failure(OperationError.invalidParameters)) } else {
return self.finish(.failure(OperationError.invalidParameters("InstallAppOperation.main: self.context.certificate or self.context.resignedApp or self.context.provisioningProfiles is nil")))
}
let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext() let backgroundContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
backgroundContext.perform { backgroundContext.perform {
@@ -113,6 +115,22 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
installedApp.appExtensions = installedExtensions installedApp.appExtensions = installedExtensions
// Remove stale "PlugIns" (Extensions) from currently installed App
if let installedAppExns = ALTApplication(fileURL: installedApp.fileURL)?.appExtensions {
let currentAppExns = Set(installedApp.appExtensions).map{ $0.bundleIdentifier }
let staleAppExns = installedAppExns.filter{ !currentAppExns.contains($0.bundleIdentifier) }
for staleAppExn in staleAppExns {
do {
try FileManager.default.removeItem(at: staleAppExn.fileURL)
print("InstallAppOperation.appExtensions: removed stale app-extension: \(staleAppExn.fileURL)")
} catch {
print("InstallAppOperation.appExtensions processing error Error: \(error)")
}
}
}
self.context.beginInstallationHandler?(installedApp) self.context.beginInstallationHandler?(installedApp)
// Temporary directory and resigned .ipa no longer needed, so delete them now to ensure AltStore doesn't quit before we get the chance to. // Temporary directory and resigned .ipa no longer needed, so delete them now to ensure AltStore doesn't quit before we get the chance to.
@@ -222,9 +240,11 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
do do
{ {
if(FileManager.default.fileExists(atPath: fileURL.path)){
try FileManager.default.removeItem(at: fileURL) try FileManager.default.removeItem(at: fileURL)
print("Removed refreshed IPA") print("Removed refreshed IPA")
} }
}
catch catch
{ {
print("Failed to remove refreshed .ipa: \(error)") print("Failed to remove refreshed .ipa: \(error)")

View File

@@ -35,6 +35,7 @@ extension OperationError
case noSources case noSources
case openAppFailed//(name: String) case openAppFailed//(name: String)
case missingAppGroup case missingAppGroup
case refreshAppFailed
// Connection // Connection
case noWiFi = 1200 case noWiFi = 1200
@@ -55,7 +56,6 @@ extension OperationError
static let notAuthenticated: OperationError = .init(code: .notAuthenticated) static let notAuthenticated: OperationError = .init(code: .notAuthenticated)
static let unknownUDID: OperationError = .init(code: .unknownUDID) static let unknownUDID: OperationError = .init(code: .unknownUDID)
static let invalidApp: OperationError = .init(code: .invalidApp) static let invalidApp: OperationError = .init(code: .invalidApp)
static let invalidParameters: OperationError = .init(code: .invalidParameters)
static let noSources: OperationError = .init(code: .noSources) static let noSources: OperationError = .init(code: .noSources)
static let missingAppGroup: OperationError = .init(code: .missingAppGroup) static let missingAppGroup: OperationError = .init(code: .missingAppGroup)
@@ -78,6 +78,7 @@ extension OperationError
static func openAppFailed(name: String?) -> OperationError { static func openAppFailed(name: String?) -> OperationError {
OperationError(code: .openAppFailed, appName: name) OperationError(code: .openAppFailed, appName: name)
} }
static let domain = OperationError(code: .unknown)._domain
static func SideJITIssue(error: String?) -> OperationError { static func SideJITIssue(error: String?) -> OperationError {
var o = OperationError(code: .SideJITIssue) var o = OperationError(code: .SideJITIssue)
@@ -107,6 +108,13 @@ extension OperationError
OperationError(code: .anisetteV3Error, failureReason: message) OperationError(code: .anisetteV3Error, failureReason: message)
} }
static func refreshAppFailed(message: String) -> OperationError {
OperationError(code: .refreshAppFailed, failureReason: message)
}
static func invalidParameters(_ message: String? = nil) -> OperationError {
OperationError(code: .invalidParameters, failureReason: message)
}
} }
@@ -155,7 +163,6 @@ struct OperationError: ALTLocalizedError {
case .notAuthenticated: return NSLocalizedString("You are not signed in.", comment: "") case .notAuthenticated: return NSLocalizedString("You are not signed in.", comment: "")
case .unknownUDID: return NSLocalizedString("SideStore could not determine this device's UDID.", comment: "") case .unknownUDID: return NSLocalizedString("SideStore could not determine this device's UDID.", comment: "")
case .invalidApp: return NSLocalizedString("The app is in an invalid format.", comment: "") case .invalidApp: return NSLocalizedString("The app is in an invalid format.", comment: "")
case .invalidParameters: return NSLocalizedString("Invalid parameters.", comment: "")
case .maximumAppIDLimitReached: return NSLocalizedString("Cannot register more than 10 App IDs within a 7 day period.", comment: "") case .maximumAppIDLimitReached: return NSLocalizedString("Cannot register more than 10 App IDs within a 7 day period.", comment: "")
case .noSources: return NSLocalizedString("There are no SideStore sources.", comment: "") case .noSources: return NSLocalizedString("There are no SideStore sources.", comment: "")
case .missingAppGroup: return NSLocalizedString("SideStore's shared app group could not be accessed.", comment: "") case .missingAppGroup: return NSLocalizedString("SideStore's shared app group could not be accessed.", comment: "")
@@ -176,7 +183,16 @@ struct OperationError: ALTLocalizedError {
case .anisetteV3Error: return NSLocalizedString("An error occurred when getting anisette data from a V3 server: %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "") case .anisetteV3Error: return NSLocalizedString("An error occurred when getting anisette data from a V3 server: %@. Please try again. If the issue persists, report it on GitHub Issues!", comment: "")
case .cacheClearError: return NSLocalizedString("An error occurred while clearing cache: %@", comment: "") case .cacheClearError: return NSLocalizedString("An error occurred while clearing cache: %@", comment: "")
case .SideJITIssue: return NSLocalizedString("An error occurred while using SideJIT: %@", comment: "") case .SideJITIssue: return NSLocalizedString("An error occurred while using SideJIT: %@", comment: "")
case .refreshAppFailed:
let message = self._failureReason ?? ""
return String(format: NSLocalizedString("Unable to refresh App\n%@", comment: ""), message)
case .invalidParameters:
let message = self._failureReason.map { ": \n\($0)" } ?? "."
return String(format: NSLocalizedString("Invalid parameters%@", comment: ""), message)
} }
} }
var recoverySuggestion: String? { var recoverySuggestion: String? {

View File

@@ -98,7 +98,9 @@ final class PatchAppOperation: ResultOperation<Void>
return return
} }
guard let resignedApp = self.context.resignedApp else { return self.finish(.failure(OperationError.invalidParameters)) } guard let resignedApp = self.context.resignedApp else {
return self.finish(.failure(OperationError.invalidParameters("PatchAppOperation.main: self.context.resignedApp is nil")))
}
self.progressHandler?(self.progress, NSLocalizedString("Downloading iOS firmware...", comment: "")) self.progressHandler?(self.progress, NSLocalizedString("Downloading iOS firmware...", comment: ""))

View File

@@ -35,9 +35,15 @@ final class RefreshAppOperation: ResultOperation<InstalledApp>
do do
{ {
if let error = self.context.error { return self.finish(.failure(error)) } if let error = self.context.error {
print("RefreshAppOperation.main: ERROR: self.context.app = \(self.context.app!); self.context.error is \(error)")
return self.finish(.failure(error))
}
guard let profiles = self.context.provisioningProfiles else {
return self.finish(.failure(OperationError.invalidParameters("RefreshAppOperation.main: self.context.provisioningProfiles is nil")))
}
guard let profiles = self.context.provisioningProfiles else { return self.finish(.failure(OperationError.invalidParameters)) }
guard let app = self.context.app else { return self.finish(.failure(OperationError(.appNotFound(name: nil)))) } guard let app = self.context.app else { return self.finish(.failure(OperationError(.appNotFound(name: nil)))) }
for p in profiles { for p in profiles {

View File

@@ -35,7 +35,9 @@ final class RemoveAppBackupOperation: ResultOperation<Void>
return return
} }
guard let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) } guard let installedApp = self.context.installedApp else {
return self.finish(.failure(OperationError.invalidParameters("RemoveAppBackupOperation.main: self.context.installedApp is nil")))
}
installedApp.managedObjectContext?.perform { installedApp.managedObjectContext?.perform {
guard let backupDirectoryURL = FileManager.default.backupDirectoryURL(for: installedApp) else { return self.finish(.failure(OperationError.missingAppGroup)) } guard let backupDirectoryURL = FileManager.default.backupDirectoryURL(for: installedApp) else { return self.finish(.failure(OperationError.missingAppGroup)) }

View File

@@ -33,7 +33,9 @@ final class RemoveAppOperation: ResultOperation<InstalledApp>
return return
} }
guard let installedApp = self.context.installedApp else { return self.finish(.failure(OperationError.invalidParameters)) } guard let installedApp = self.context.installedApp else {
return self.finish(.failure(OperationError.invalidParameters("RemoveAppOperation.main: self.context.installedApp is nil")))
}
installedApp.managedObjectContext?.perform { installedApp.managedObjectContext?.perform {
let resignedBundleIdentifier = installedApp.resignedBundleIdentifier let resignedBundleIdentifier = installedApp.resignedBundleIdentifier

View File

@@ -42,7 +42,12 @@ final class ResignAppOperation: ResultOperation<ALTApplication>
let profiles = self.context.provisioningProfiles, let profiles = self.context.provisioningProfiles,
let team = self.context.team, let team = self.context.team,
let certificate = self.context.certificate let certificate = self.context.certificate
else { return self.finish(.failure(OperationError.invalidParameters)) } else {
return self.finish(.failure(OperationError.invalidParameters("ResignAppOperation.main: " +
"self.context.team or " +
"self.context.provisioningProfiles or" +
"self.context.certificate is nil")))
}
// Prepare app bundle // Prepare app bundle
let prepareAppProgress = Progress.discreteProgress(totalUnitCount: 2) let prepareAppProgress = Progress.discreteProgress(totalUnitCount: 2)

View File

@@ -36,7 +36,9 @@ final class SendAppOperation: ResultOperation<()>
return self.finish(.failure(error)) return self.finish(.failure(error))
} }
guard let resignedApp = self.context.resignedApp else { return self.finish(.failure(OperationError.invalidParameters)) } guard let resignedApp = self.context.resignedApp else {
return self.finish(.failure(OperationError.invalidParameters("SendAppOperation.main: self.resignedApp is nil")))
}
// self.context.resignedApp.fileURL points to the app bundle, but we want the .ipa. // self.context.resignedApp.fileURL points to the app bundle, but we want the .ipa.
let app = AnyApp(name: resignedApp.name, bundleIdentifier: self.context.bundleIdentifier, url: resignedApp.fileURL) let app = AnyApp(name: resignedApp.name, bundleIdentifier: self.context.bundleIdentifier, url: resignedApp.fileURL)

View File

@@ -116,8 +116,12 @@ final class VerifyAppOperation: ResultOperation<Void>
{ {
throw error throw error
} }
let appName = self.context.app?.name ?? NSLocalizedString("The app", comment: "")
self.localizedFailure = String(format: NSLocalizedString("%@ could not be installed.", comment: ""), appName)
guard let app = self.context.app else { throw OperationError.invalidParameters } guard let app = self.context.app else {
throw OperationError.invalidParameters("VerifyAppOperation.main: self.context.app is nil")
}
if !["ny.litritt.ignited", "com.litritt.ignited"].contains(where: { $0 == app.bundleIdentifier }) { if !["ny.litritt.ignited", "com.litritt.ignited"].contains(where: { $0 == app.bundleIdentifier }) {
guard app.bundleIdentifier == self.context.bundleIdentifier else { guard app.bundleIdentifier == self.context.bundleIdentifier else {

View File

@@ -29,6 +29,13 @@ class AnisetteViewModel: ObservableObject {
@Published var source: String = "https://servers.sidestore.io/servers.json" @Published var source: String = "https://servers.sidestore.io/servers.json"
@Published var servers: [Server] = [] @Published var servers: [Server] = []
init() {
// using the custom Anisette list
if !UserDefaults.standard.menuAnisetteList.isEmpty {
self.source = UserDefaults.standard.menuAnisetteList
}
}
func getListOfServers() { func getListOfServers() {
guard let url = URL(string: source) else { return } guard let url = URL(string: source) else { return }
URLSession.shared.dataTask(with: url) { data, response, error in URLSession.shared.dataTask(with: url) { data, response, error in
@@ -55,6 +62,7 @@ struct AnisetteServers: View {
@Environment(\.presentationMode) var presentationMode @Environment(\.presentationMode) var presentationMode
@StateObject var viewModel: AnisetteViewModel = AnisetteViewModel() @StateObject var viewModel: AnisetteViewModel = AnisetteViewModel()
@State var selected: String? = nil @State var selected: String? = nil
@State private var showingConfirmation = false
var errorCallback: () -> () var errorCallback: () -> ()
var body: some View { var body: some View {
@@ -136,9 +144,13 @@ struct AnisetteServers: View {
SUIButton(action: { SUIButton(action: {
presentationMode.wrappedValue.dismiss() presentationMode.wrappedValue.dismiss()
}) { }) {
HStack{
Spacer()
Text("Back") Text("Back")
.fontWeight(.semibold) .fontWeight(.semibold)
.frame(maxWidth: .infinity) Spacer()
}
.contentShape(Rectangle())
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
.padding() .padding()
@@ -149,26 +161,25 @@ struct AnisetteServers: View {
SUIButton(action: { SUIButton(action: {
viewModel.getListOfServers() viewModel.getListOfServers()
}) { }) {
HStack{
Spacer()
Text("Refresh Servers") Text("Refresh Servers")
.fontWeight(.semibold) .fontWeight(.semibold)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
Spacer()
}
.contentShape(Rectangle())
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
.padding() .padding()
.background(RoundedRectangle(cornerRadius: 10).fill(Color.accentColor)) .background(RoundedRectangle(cornerRadius: 10).fill(Color.accentColor))
.foregroundColor(.white) .foregroundColor(.white)
.shadow(color: Color.accentColor.opacity(0.4), radius: 10, x: 0, y: 5) .shadow(color: Color.accentColor.opacity(0.4), radius: 10, x: 0, y: 5)
} }
SUIButton(action: { SUIButton(action: {
#if !DEBUG showingConfirmation = true
if Keychain.shared.adiPb != nil {
Keychain.shared.adiPb = nil
}
#endif
print("Cleared adi.pb from keychain")
errorCallback()
presentationMode.wrappedValue.dismiss()
}) { }) {
Text("Reset adi.pb") Text("Reset adi.pb")
.fontWeight(.semibold) .fontWeight(.semibold)
@@ -179,6 +190,25 @@ struct AnisetteServers: View {
.background(RoundedRectangle(cornerRadius: 10).fill(Color.red)) .background(RoundedRectangle(cornerRadius: 10).fill(Color.red))
.foregroundColor(.white) .foregroundColor(.white)
.shadow(color: Color.red.opacity(0.4), radius: 10, x: 0, y: 5) .shadow(color: Color.red.opacity(0.4), radius: 10, x: 0, y: 5)
.alert(isPresented: $showingConfirmation) {
Alert(
title: Text("Reset adi.pb"),
message: Text("are you sure to clear the adi.pb from keychain"),
primaryButton: .default(Text("do it")) {
#if !DEBUG
if Keychain.shared.adiPb != nil {
Keychain.shared.adiPb = nil
}
#endif
print("Cleared adi.pb from keychain")
errorCallback()
presentationMode.wrappedValue.dismiss()
},
secondaryButton: .cancel(Text("cancel")) {
print("canceled")
}
)
}
} }
.padding(.horizontal) .padding(.horizontal)
.padding(.bottom) .padding(.bottom)

View File

@@ -105,8 +105,8 @@ private extension ErrorLogViewController
self?.searchFAQ(for: loggedError) self?.searchFAQ(for: loggedError)
}, },
UIAction(title: NSLocalizedString("View More Details", comment: ""), image: UIImage(systemName: "ellipsis.circle")) { [weak self] _ in UIAction(title: NSLocalizedString("View More Details", comment: ""), image: UIImage(systemName: "ellipsis.circle")) { [weak self] _ in
self?.viewMoreDetails(for: loggedError)
} },
]) ])
cell.menuButton.menu = menu cell.menuButton.menu = menu

View File

@@ -128,7 +128,7 @@ private extension PatreonViewController
let isPatronText = NSLocalizedString(""" let isPatronText = NSLocalizedString("""
Hey , Hey ,
Youre the best. Your account was linked successfully, so you now have access to the beta versions of all of our apps. You can find them all in the Browse tab. Youre the best. Your account was linked successfully, so you now have access to any beta versions of our apps. You can find them all in the Browse tab.
Thanks for all of your support. Enjoy! Thanks for all of your support. Enjoy!
- SideTeam - SideTeam
@@ -175,7 +175,7 @@ private extension PatreonViewController
@objc func openPatreonURL(_ sender: UIButton) @objc func openPatreonURL(_ sender: UIButton)
{ {
let patreonURL = URL(string: "https://www.patreon.com/SideStore")! let patreonURL = URL(string: "https://www.patreon.com/SideStoreIO")!
let safariViewController = SFSafariViewController(url: patreonURL) let safariViewController = SFSafariViewController(url: patreonURL)
safariViewController.preferredControlTintColor = self.view.tintColor safariViewController.preferredControlTintColor = self.view.tintColor
@@ -348,7 +348,7 @@ extension PatreonViewController: UICollectionViewDelegateFlowLayout
switch section switch section
{ {
case .about: return .zero case .about: return .zero
case .patrons: return CGSize(width: 0, height: 0) case .patrons: return CGSize(width: 320, height: 44)
} }
} }
} }

View File

@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23090" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="5Rz-4h-jJ8"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="5Rz-4h-jJ8">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23079"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Named colors" minToolsVersion="9.0"/> <capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/>
@@ -21,7 +21,7 @@
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="separatorColor" white="1" alpha="0.25" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore 1.0" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="bUR-rp-Nw2"> <label key="tableFooterView" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore 1.0" textAlignment="center" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="bUR-rp-Nw2">
<rect key="frame" x="0.0" y="1296" width="375" height="25"/> <rect key="frame" x="0.0" y="1347" width="375" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="0.69999999999999996" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -272,9 +272,45 @@
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/> <userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/>
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="amC-sE-8O0" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="7PQ-AW-GcV" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="495" width="375" height="51"/> <rect key="frame" x="0.0" y="495" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="7PQ-AW-GcV" id="wQ8-9w-iiw">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Disable App Limit" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="W95-fD-NAd" userLabel="Disable App Limit">
<rect key="frame" x="30" y="15.5" width="142.5" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1aa-og-ZXD">
<rect key="frame" x="296" y="10" width="51" height="31"/>
<connections>
<action selector="toggleDisableAppLimit:" destination="aMk-Xp-UL8" eventType="valueChanged" id="zYc-B2-JPg"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="1aa-og-ZXD" secondAttribute="trailing" id="1Xa-t6-jAC"/>
<constraint firstItem="W95-fD-NAd" firstAttribute="leading" secondItem="wQ8-9w-iiw" secondAttribute="leadingMargin" id="J49-tg-KMa"/>
<constraint firstItem="1aa-og-ZXD" firstAttribute="centerY" secondItem="wQ8-9w-iiw" secondAttribute="centerY" id="UMz-ax-ln4"/>
<constraint firstItem="W95-fD-NAd" firstAttribute="centerY" secondItem="wQ8-9w-iiw" secondAttribute="centerY" id="bFd-lr-0xw"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="0.14999999999999999" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<edgeInsets key="layoutMargins" top="8" left="30" bottom="8" right="30"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="style">
<integer key="value" value="2"/>
</userDefinedRuntimeAttribute>
<userDefinedRuntimeAttribute type="boolean" keyPath="isSelectable" value="YES"/>
</userDefinedRuntimeAttributes>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="amC-sE-8O0" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="546" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="amC-sE-8O0" id="GEO-2e-E4k"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="amC-sE-8O0" id="GEO-2e-E4k">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
@@ -305,19 +341,19 @@
<tableViewSection headerTitle="" id="eHy-qI-w5w"> <tableViewSection headerTitle="" id="eHy-qI-w5w">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="30h-59-88f" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="30h-59-88f" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="586" width="375" height="51"/> <rect key="frame" x="0.0" y="637" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="30h-59-88f" id="7qD-DW-Jls"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="30h-59-88f" id="7qD-DW-Jls">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="How it works" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2CC-iw-3bd"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="How it works" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2CC-iw-3bd">
<rect key="frame" x="30" y="15.5" width="105" height="20.5"/> <rect key="frame" x="30" y="15.5" width="105" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Next" translatesAutoresizingMaskIntoConstraints="NO" id="WtV-Dt-sDn"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" image="Next" translatesAutoresizingMaskIntoConstraints="NO" id="WtV-Dt-sDn">
<rect key="frame" x="327" y="16.5" width="18" height="18"/> <rect key="frame" x="327" y="16.5" width="18" height="18"/>
</imageView> </imageView>
</subviews> </subviews>
@@ -345,28 +381,28 @@
<tableViewSection headerTitle="" id="J90-vn-u2O"> <tableViewSection headerTitle="" id="J90-vn-u2O">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="i4T-2q-jF3" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="i4T-2q-jF3" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="677" width="375" height="51"/> <rect key="frame" x="0.0" y="728" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="i4T-2q-jF3" id="VTQ-H4-aCM"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="i4T-2q-jF3" id="VTQ-H4-aCM">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Developers" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hRA-OK-Vjw"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Developers" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hRA-OK-Vjw">
<rect key="frame" x="30" y="15.5" width="86" height="20.5"/> <rect key="frame" x="30" y="15.5" width="86" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<stackView opaque="NO" contentMode="scaleToFill" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="lx9-35-OSk"> <stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="lx9-35-OSk">
<rect key="frame" x="187.5" y="15.5" width="157.5" height="20.5"/> <rect key="frame" x="187.5" y="15.5" width="157.5" height="20.5"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SideStore Team" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JAA-iZ-VGb"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="SideStore Team" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JAA-iZ-VGb">
<rect key="frame" x="0.0" y="0.0" width="125.5" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="125.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Next" translatesAutoresizingMaskIntoConstraints="NO" id="Mmj-3V-fTb"> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" image="Next" translatesAutoresizingMaskIntoConstraints="NO" id="Mmj-3V-fTb">
<rect key="frame" x="139.5" y="0.0" width="18" height="20.5"/> <rect key="frame" x="139.5" y="0.0" width="18" height="20.5"/>
</imageView> </imageView>
</subviews> </subviews>
@@ -389,7 +425,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="oHX-oR-nwJ" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="oHX-oR-nwJ" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="728" width="375" height="51"/> <rect key="frame" x="0.0" y="774.5" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="oHX-oR-nwJ" id="hN4-i5-igu"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="oHX-oR-nwJ" id="hN4-i5-igu">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -433,7 +469,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="0MT-ht-Sit" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="0MT-ht-Sit" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="779" width="375" height="51"/> <rect key="frame" x="0.0" y="830" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0MT-ht-Sit" id="OZp-WM-5H7"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0MT-ht-Sit" id="OZp-WM-5H7">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -477,7 +513,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="O5R-Al-lGj" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="O5R-Al-lGj" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="830" width="375" height="51"/> <rect key="frame" x="0.0" y="881" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="O5R-Al-lGj" id="CrG-Mr-xQq"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="O5R-Al-lGj" id="CrG-Mr-xQq">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -517,7 +553,7 @@
<tableViewSection headerTitle="" id="OMa-EK-hRI"> <tableViewSection headerTitle="" id="OMa-EK-hRI">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="FMZ-as-Ljo" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="FMZ-as-Ljo" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="921" width="375" height="51"/> <rect key="frame" x="0.0" y="972" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FMZ-as-Ljo" id="JzL-Of-A3T"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FMZ-as-Ljo" id="JzL-Of-A3T">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -550,7 +586,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="Qca-pU-sJh" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="Qca-pU-sJh" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="972" width="375" height="51"/> <rect key="frame" x="0.0" y="1023" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qca-pU-sJh" id="QtU-8J-VQN"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Qca-pU-sJh" id="QtU-8J-VQN">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -586,7 +622,7 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="rE2-P4-OaE" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="rE2-P4-OaE" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1023" width="375" height="51"/> <rect key="frame" x="0.0" y="1074" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="rE2-P4-OaE" id="qIT-rz-ZUb"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="rE2-P4-OaE" id="qIT-rz-ZUb">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -622,7 +658,7 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VrV-qI-zXF" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VrV-qI-zXF" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1074" width="375" height="51"/> <rect key="frame" x="0.0" y="1125" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VrV-qI-zXF" id="w1r-uY-4pD"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VrV-qI-zXF" id="w1r-uY-4pD">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -655,7 +691,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="eZ3-BT-q4D" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="eZ3-BT-q4D" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1125" width="375" height="51"/> <rect key="frame" x="0.0" y="1176" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="eZ3-BT-q4D" id="17m-VV-hzf"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="eZ3-BT-q4D" id="17m-VV-hzf">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -688,7 +724,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VNn-u4-cN8" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="VNn-u4-cN8" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1176" width="375" height="51"/> <rect key="frame" x="0.0" y="1227" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VNn-u4-cN8" id="4bh-qe-l2N"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VNn-u4-cN8" id="4bh-qe-l2N">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -721,7 +757,7 @@
</userDefinedRuntimeAttributes> </userDefinedRuntimeAttributes>
</tableViewCell> </tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="e7s-hL-kv9" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="51" id="e7s-hL-kv9" customClass="InsetGroupTableViewCell" customModule="SideStore" customModuleProvider="target">
<rect key="frame" x="0.0" y="1227" width="375" height="51"/> <rect key="frame" x="0.0" y="1278" width="375" height="51"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="e7s-hL-kv9" id="yjL-Mu-HTk"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="e7s-hL-kv9" id="yjL-Mu-HTk">
<rect key="frame" x="0.0" y="0.0" width="375" height="51"/> <rect key="frame" x="0.0" y="0.0" width="375" height="51"/>
@@ -766,6 +802,7 @@
<outlet property="accountNameLabel" destination="CnN-M1-AYK" id="Ldc-Py-Bix"/> <outlet property="accountNameLabel" destination="CnN-M1-AYK" id="Ldc-Py-Bix"/>
<outlet property="accountTypeLabel" destination="434-MW-Den" id="mNB-QE-4Jg"/> <outlet property="accountTypeLabel" destination="434-MW-Den" id="mNB-QE-4Jg"/>
<outlet property="backgroundRefreshSwitch" destination="DPu-zD-Als" id="eiG-Hv-Vko"/> <outlet property="backgroundRefreshSwitch" destination="DPu-zD-Als" id="eiG-Hv-Vko"/>
<outlet property="disableAppLimitSwitch" destination="1aa-og-ZXD" id="oVL-Md-yZ8"/>
<outlet property="noIdleTimeoutSwitch" destination="iQA-wm-5ag" id="jHC-js-q0Y"/> <outlet property="noIdleTimeoutSwitch" destination="iQA-wm-5ag" id="jHC-js-q0Y"/>
<outlet property="versionLabel" destination="bUR-rp-Nw2" id="85I-5R-hqz"/> <outlet property="versionLabel" destination="bUR-rp-Nw2" id="85I-5R-hqz"/>
</connections> </connections>

View File

@@ -32,13 +32,17 @@ extension SettingsViewController
{ {
case backgroundRefresh case backgroundRefresh
case noIdleTimeout case noIdleTimeout
case disableAppLimit
@available(iOS 14, *) @available(iOS 14, *)
case addToSiri case addToSiri
static var allCases: [AppRefreshRow] { static var allCases: [AppRefreshRow] {
guard #available(iOS 14, *) else { return [.backgroundRefresh, .noIdleTimeout] } var c: [AppRefreshRow] = [.backgroundRefresh, .noIdleTimeout]
return [.backgroundRefresh, .noIdleTimeout, .addToSiri] guard #available(iOS 14, *) else { return c }
if !ProcessInfo().sparseRestorePatched { c.append(.disableAppLimit) }
c.append(.addToSiri)
return c
} }
} }
@@ -79,6 +83,7 @@ final class SettingsViewController: UITableViewController
@IBOutlet private var backgroundRefreshSwitch: UISwitch! @IBOutlet private var backgroundRefreshSwitch: UISwitch!
@IBOutlet private var noIdleTimeoutSwitch: UISwitch! @IBOutlet private var noIdleTimeoutSwitch: UISwitch!
@IBOutlet private var disableAppLimitSwitch: UISwitch!
@IBOutlet private var refreshSideJITServer: UILabel! @IBOutlet private var refreshSideJITServer: UILabel!
@@ -190,6 +195,7 @@ private extension SettingsViewController
self.backgroundRefreshSwitch.isOn = UserDefaults.standard.isBackgroundRefreshEnabled self.backgroundRefreshSwitch.isOn = UserDefaults.standard.isBackgroundRefreshEnabled
self.noIdleTimeoutSwitch.isOn = UserDefaults.standard.isIdleTimeoutDisableEnabled self.noIdleTimeoutSwitch.isOn = UserDefaults.standard.isIdleTimeoutDisableEnabled
self.disableAppLimitSwitch.isOn = UserDefaults.standard.isAppLimitDisabled
if self.isViewLoaded if self.isViewLoaded
{ {
@@ -241,7 +247,7 @@ private extension SettingsViewController
} }
else else
{ {
settingsHeaderFooterView.secondaryLabel.text = NSLocalizedString("Enable Background Refresh to automatically refresh apps in the background when connected to Wi-Fi. \n\nDisable the Idle Timeout toggle to allow SideStore to not let your device go to sleep during a refresh or install of any apps.", comment: "") settingsHeaderFooterView.secondaryLabel.text = NSLocalizedString("Enable Background Refresh to automatically refresh apps in the background when connected to Wi-Fi. \n\nEnable Disable Idle Timeout to allow SideStore to keep your device awake during a refresh or install of any apps.", comment: "")
} }
case .instructions: case .instructions:
@@ -317,6 +323,10 @@ private extension SettingsViewController
self.present(alertController, animated: true, completion: nil) self.present(alertController, animated: true, completion: nil)
} }
@IBAction func toggleDisableAppLimit(_ sender: UISwitch) {
UserDefaults.standard.isAppLimitDisabled = sender.isOn
}
@IBAction func toggleIsBackgroundRefreshEnabled(_ sender: UISwitch) @IBAction func toggleIsBackgroundRefreshEnabled(_ sender: UISwitch)
{ {
UserDefaults.standard.isBackgroundRefreshEnabled = sender.isOn UserDefaults.standard.isBackgroundRefreshEnabled = sender.isOn
@@ -566,6 +576,7 @@ extension SettingsViewController
{ {
case .backgroundRefresh: break case .backgroundRefresh: break
case .noIdleTimeout: break case .noIdleTimeout: break
case .disableAppLimit: break
case .addToSiri: case .addToSiri:
guard #available(iOS 14, *) else { return } guard #available(iOS 14, *) else { return }
self.addRefreshAppsShortcut() self.addRefreshAppsShortcut()

View File

@@ -32,6 +32,7 @@ public extension UserDefaults
@NSManaged var isBackgroundRefreshEnabled: Bool @NSManaged var isBackgroundRefreshEnabled: Bool
@NSManaged var isIdleTimeoutDisableEnabled: Bool @NSManaged var isIdleTimeoutDisableEnabled: Bool
@NSManaged var isAppLimitDisabled: Bool
@NSManaged var isPairingReset: Bool @NSManaged var isPairingReset: Bool
@NSManaged var isDebugModeEnabled: Bool @NSManaged var isDebugModeEnabled: Bool
@NSManaged var presentedLaunchReminderNotification: Bool @NSManaged var presentedLaunchReminderNotification: Bool
@@ -77,6 +78,7 @@ public extension UserDefaults
let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14) let localServerSupportsRefreshing = !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14)
let defaults = [ let defaults = [
#keyPath(UserDefaults.isAppLimitDisabled): false,
#keyPath(UserDefaults.isBackgroundRefreshEnabled): true, #keyPath(UserDefaults.isBackgroundRefreshEnabled): true,
#keyPath(UserDefaults.isIdleTimeoutDisableEnabled): true, #keyPath(UserDefaults.isIdleTimeoutDisableEnabled): true,
#keyPath(UserDefaults.isPairingReset): true, #keyPath(UserDefaults.isPairingReset): true,
@@ -84,6 +86,7 @@ public extension UserDefaults
#keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions, #keyPath(UserDefaults.activeAppLimitIncludesExtensions): activeAppLimitIncludesExtensions,
#keyPath(UserDefaults.localServerSupportsRefreshing): localServerSupportsRefreshing, #keyPath(UserDefaults.localServerSupportsRefreshing): localServerSupportsRefreshing,
#keyPath(UserDefaults.requiresAppGroupMigration): true, #keyPath(UserDefaults.requiresAppGroupMigration): true,
#keyPath(UserDefaults.menuAnisetteList): "https://servers.sidestore.io/servers.json",
#keyPath(UserDefaults.menuAnisetteURL): "https://ani.sidestore.io" #keyPath(UserDefaults.menuAnisetteURL): "https://ani.sidestore.io"
] as [String : Any] ] as [String : Any]

View File

@@ -163,8 +163,9 @@ public extension InstalledApp
class func updatesFetchRequest() -> NSFetchRequest<InstalledApp> class func updatesFetchRequest() -> NSFetchRequest<InstalledApp>
{ {
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp> let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K != nil AND %K != %K", fetchRequest.predicate = NSPredicate(format: "%K == YES AND %K == YES",
#keyPath(InstalledApp.isActive), #keyPath(InstalledApp.storeApp), #keyPath(InstalledApp.version), #keyPath(InstalledApp.storeApp.latestSupportedVersion.version)) #keyPath(InstalledApp.isActive), #keyPath(InstalledApp.hasUpdate))
return fetchRequest return fetchRequest
} }

View File

@@ -169,6 +169,31 @@ public class StoreApp: NSManagedObject, Decodable, Fetchable
return self._versions.array as! [AppVersion] return self._versions.array as! [AppVersion]
} }
@nonobjc public var size: Int64? {
guard let version = self.latestSupportedVersion else { return nil }
return version.size
}
@nonobjc public var version: String? {
guard let version = self.latestSupportedVersion else { return nil }
return version.version
}
@nonobjc public var versionDescription: String? {
guard let version = self.latestSupportedVersion else { return nil }
return version.localizedDescription
}
@nonobjc public var versionDate: Date? {
guard let version = self.latestSupportedVersion else { return nil }
return version.date
}
@nonobjc public var downloadURL: URL? {
guard let version = self.latestSupportedVersion else { return nil }
return version.downloadURL
}
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
{ {
super.init(entity: entity, insertInto: context) super.init(entity: entity, insertInto: context)

View File

@@ -10,20 +10,19 @@ import Foundation
import AuthenticationServices import AuthenticationServices
import CoreData import CoreData
private let clientID = "ZMx0EGUWe4TVWYXNZZwK_fbIK5jHFVWoUf1Qb-sqNXmT-YzAGwDPxxq7ak3_W5Q2" private let clientID = "my4hpHHG4iVRme6QALnQGlhSBQiKdB_AinrVgPpIpiC-xiHstTYiLKO5vfariFo1"
private let clientSecret = "1hktsZB89QyN69cB4R0tu55R4TCPQGXxvebYUUh7Y-5TLSnRswuxs6OUjdJ74IJt" private let clientSecret = "Zow0ggt9YgwIyd4DVLoO9Z02KuuIXW44xhx4lfL27x2u-_u4FE4rYR48bEKREPS5"
private let campaignID = "2863968" private let campaignID = "12794837"
extension PatreonAPI typealias PatreonAPIError = PatreonAPIErrorCode.Error
enum PatreonAPIErrorCode: Int, ALTErrorEnum, CaseIterable
{ {
enum Error: LocalizedError
{
case unknown case unknown
case notAuthenticated case notAuthenticated
case invalidAccessToken case invalidAccessToken
var errorDescription: String? { var errorFailureReason: String {
switch self switch self
{ {
case .unknown: return NSLocalizedString("An unknown error occurred.", comment: "") case .unknown: return NSLocalizedString("An unknown error occurred.", comment: "")
@@ -31,8 +30,10 @@ extension PatreonAPI
case .invalidAccessToken: return NSLocalizedString("Invalid access token.", comment: "") case .invalidAccessToken: return NSLocalizedString("Invalid access token.", comment: "")
} }
} }
} }
extension PatreonAPI
{
enum AuthorizationType enum AuthorizationType
{ {
case none case none
@@ -110,7 +111,7 @@ public extension PatreonAPI
let components = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false), let components = URLComponents(url: callbackURL, resolvingAgainstBaseURL: false),
let codeQueryItem = components.queryItems?.first(where: { $0.name == "code" }), let codeQueryItem = components.queryItems?.first(where: { $0.name == "code" }),
let code = codeQueryItem.value let code = codeQueryItem.value
else { throw Error.unknown } else { throw PatreonAPIError(.unknown) }
self.fetchAccessToken(oauthCode: code) { (result) in self.fetchAccessToken(oauthCode: code) { (result) in
switch result switch result
@@ -151,9 +152,9 @@ public extension PatreonAPI
self.send(request, authorizationType: .user) { (result: Result<AccountResponse, Swift.Error>) in self.send(request, authorizationType: .user) { (result: Result<AccountResponse, Swift.Error>) in
switch result switch result
{ {
case .failure(Error.notAuthenticated): case .failure(~PatreonAPIErrorCode.notAuthenticated):
self.signOut() { (result) in self.signOut() { (result) in
completion(.failure(Error.notAuthenticated)) completion(.failure(PatreonAPIError(.notAuthenticated)))
} }
case .failure(let error): completion(.failure(error)) case .failure(let error): completion(.failure(error))
@@ -357,11 +358,11 @@ private extension PatreonAPI
{ {
case .none: break case .none: break
case .creator: case .creator:
guard let creatorAccessToken = Keychain.shared.patreonCreatorAccessToken else { return completion(.failure(Error.invalidAccessToken)) } guard let creatorAccessToken = Keychain.shared.patreonCreatorAccessToken else { return completion(.failure(PatreonAPIError(.invalidAccessToken))) }
request.setValue("Bearer " + creatorAccessToken, forHTTPHeaderField: "Authorization") request.setValue("Bearer " + creatorAccessToken, forHTTPHeaderField: "Authorization")
case .user: case .user:
guard let accessToken = Keychain.shared.patreonAccessToken else { return completion(.failure(Error.notAuthenticated)) } guard let accessToken = Keychain.shared.patreonAccessToken else { return completion(.failure(PatreonAPIError(.notAuthenticated))) }
request.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization") request.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
} }
@@ -374,8 +375,8 @@ private extension PatreonAPI
{ {
switch authorizationType switch authorizationType
{ {
case .creator: completion(.failure(Error.invalidAccessToken)) case .creator: completion(.failure(PatreonAPIError(.invalidAccessToken)))
case .none: completion(.failure(Error.notAuthenticated)) case .none: completion(.failure(PatreonAPIError(.notAuthenticated)))
case .user: case .user:
self.refreshAccessToken() { (result) in self.refreshAccessToken() { (result) in
switch result switch result

View File

@@ -82,6 +82,7 @@ struct ComplicationView: View
} }
.gaugeStyle(.accessoryCircularCapacity) .gaugeStyle(.accessoryCircularCapacity)
.unredacted() .unredacted()
.widgetBackground(Color.clear)
} }
} }

View File

@@ -0,0 +1,27 @@
//
// View+AltWidget.swift
// AltStore
//
// Created by Riley Testut on 8/18/23.
// Copyright © 2023 Riley Testut. All rights reserved.
//
import SwiftUI
extension View
{
@ViewBuilder
func widgetBackground(_ backgroundView: some View) -> some View
{
if #available(iOSApplicationExtension 17, *)
{
containerBackground(for: .widget) {
backgroundView
}
}
else
{
background(backgroundView)
}
}
}

View File

@@ -104,7 +104,8 @@ struct WidgetView : View
.frame(maxWidth: .infinity, maxHeight: .infinity) .frame(maxWidth: .infinity, maxHeight: .infinity)
} }
} }
.background(backgroundView(icon: entry.app?.icon, tintColor: entry.app?.tintColor)) .widgetBackground(backgroundView(icon: entry.app?.icon, tintColor: entry.app?.tintColor))
} }
} }

View File

@@ -1,8 +1,8 @@
// Configuration settings file format documentation can be found at: // Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974 // https://help.apple.com/xcode/#/dev745c5c974
MARKETING_VERSION = 0.5.8 MARKETING_VERSION = 0.5.9
CURRENT_PROJECT_VERSION = 5080 CURRENT_PROJECT_VERSION = 5090
// Vars to be overwritten by `CodeSigning.xcconfig` if exists // Vars to be overwritten by `CodeSigning.xcconfig` if exists
DEVELOPMENT_TEAM = S32Z3HMYVQ DEVELOPMENT_TEAM = S32Z3HMYVQ

View File

@@ -3,6 +3,34 @@
# Ensure we are in Dependencies directory # Ensure we are in Dependencies directory
cd "$(dirname "$0")" cd "$(dirname "$0")"
# Detect if Homebrew is in /opt/homebrew (Apple Silicon) or /usr/local (Intel)
if [[ -d "/opt/homebrew" ]]; then
export PATH="/opt/homebrew/bin:$PATH"
elif [[ -d "/usr/local" ]]; then
export PATH="/usr/local/bin:$PATH"
fi
# Check if wget and curl are installed; if not, install them via Homebrew
if ! command -v wget &> /dev/null; then
echo "wget not found, attempting to install via Homebrew..."
if command -v brew &> /dev/null; then
brew install wget
else
echo "Homebrew is not installed. Please install Homebrew and rerun the script."
exit 1
fi
fi
if ! command -v curl &> /dev/null; then
echo "curl not found, attempting to install via Homebrew..."
if command -v brew &> /dev/null; then
brew install curl
else
echo "Homebrew is not installed. Please install Homebrew and rerun the script."
exit 1
fi
fi
check_for_update() { check_for_update() {
if [ -f ".skip-prebuilt-fetch-$1" ]; then if [ -f ".skip-prebuilt-fetch-$1" ]; then
echo "Skipping prebuilt fetch for $1 since .skip-prebuilt-fetch-$1 exists. If you are developing $1 alongside SideStore, don't remove this file, or this script will replace your locally built binaries with the ones built by GitHub Actions." echo "Skipping prebuilt fetch for $1 since .skip-prebuilt-fetch-$1 exists. If you are developing $1 alongside SideStore, don't remove this file, or this script will replace your locally built binaries with the ones built by GitHub Actions."

View File

@@ -73,6 +73,7 @@ public extension NSError
{ {
var userInfo = self.userInfo var userInfo = self.userInfo
userInfo[NSLocalizedDescriptionKey] = self.localizedDescription userInfo[NSLocalizedDescriptionKey] = self.localizedDescription
userInfo[NSLocalizedFailureErrorKey] = self.localizedFailure
userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedFailureReason userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedFailureReason
userInfo[NSLocalizedRecoverySuggestionErrorKey] = self.localizedRecoverySuggestion userInfo[NSLocalizedRecoverySuggestionErrorKey] = self.localizedRecoverySuggestion
userInfo[NSDebugDescriptionErrorKey] = self.localizedDebugDescription userInfo[NSDebugDescriptionErrorKey] = self.localizedDebugDescription
@@ -124,6 +125,7 @@ public extension NSError
var userInfo = self.userInfo var userInfo = self.userInfo
userInfo[NSDebugDescriptionErrorKey] = self.localizedDebugDescription userInfo[NSDebugDescriptionErrorKey] = self.localizedDebugDescription
userInfo[NSLocalizedDescriptionKey] = self.localizedDescription
userInfo[NSLocalizedFailureErrorKey] = self.localizedFailure userInfo[NSLocalizedFailureErrorKey] = self.localizedFailure
userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedFailureReason userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedFailureReason
userInfo[NSLocalizedRecoverySuggestionErrorKey] = self.localizedRecoverySuggestion userInfo[NSLocalizedRecoverySuggestionErrorKey] = self.localizedRecoverySuggestion