mirror of
https://github.com/SideStore/SideStore.git
synced 2026-03-31 15:55:39 +02:00
Compare commits
36 Commits
0.5.8
...
users/june
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62faddf954 | ||
|
|
6a1181b21f | ||
|
|
c25ae10873 | ||
|
|
2842c8f669 | ||
|
|
3161892585 | ||
|
|
489843f987 | ||
|
|
dc0b30ab67 | ||
|
|
c3235cc554 | ||
|
|
6568e5918a | ||
|
|
91fba6db99 | ||
|
|
6b7e9a66f1 | ||
|
|
3682b65a4a | ||
|
|
117412645b | ||
|
|
c784ff6925 | ||
|
|
cf477024fc | ||
|
|
d595b7037f | ||
|
|
0c5007c8d8 | ||
|
|
8a87445d1f | ||
|
|
76a693fae4 | ||
|
|
9c150d5f4a | ||
|
|
e5febcdc6c | ||
|
|
1e969a0888 | ||
|
|
72bb549ea3 | ||
|
|
3c7cfdd91f | ||
|
|
b21f80cdd7 | ||
|
|
867a9c77e6 | ||
|
|
bc2d2c18fc | ||
|
|
ab923d245d | ||
|
|
fcf1b9ae03 | ||
|
|
59896e4f89 | ||
|
|
2a9f88c810 | ||
|
|
e98b0a3758 | ||
|
|
0cb6da7be4 | ||
|
|
fc3ff41fc4 | ||
|
|
719ddc8263 | ||
|
|
9f6b1284bb |
12
.github/workflows/beta.yml
vendored
12
.github/workflows/beta.yml
vendored
@@ -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/
|
||||||
|
|||||||
14
.github/workflows/nightly.yml
vendored
14
.github/workflows/nightly.yml
vendored
@@ -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/
|
||||||
|
|||||||
12
.github/workflows/pr.yml
vendored
12
.github/workflows/pr.yml
vendored
@@ -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/
|
||||||
|
|||||||
12
.github/workflows/stable.yml
vendored
12
.github/workflows/stable.yml
vendored
@@ -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
1
.gitignore
vendored
@@ -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
|
||||||
|
|||||||
48
AltServer/ErrorDetailsViewController.swift
Normal file
48
AltServer/ErrorDetailsViewController.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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)",
|
||||||
|
|||||||
96
AltStore/Extensions/ProcessInfo+SideStore.swift
Normal file
96
AltStore/Extensions/ProcessInfo+SideStore.swift
Normal 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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -118,8 +118,9 @@ 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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,7 +1993,43 @@ 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)
|
||||||
{
|
{
|
||||||
// Find "Install AltStore" operation if it already exists in `context`
|
// Find "Install AltStore" operation if it already exists in `context`
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 ?? ""
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
@@ -112,6 +114,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)
|
||||||
|
|
||||||
@@ -222,8 +240,10 @@ final class InstallAppOperation: ResultOperation<InstalledApp>
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try FileManager.default.removeItem(at: fileURL)
|
if(FileManager.default.fileExists(atPath: fileURL.path)){
|
||||||
print("Removed refreshed IPA")
|
try FileManager.default.removeItem(at: fileURL)
|
||||||
|
print("Removed refreshed IPA")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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? {
|
||||||
|
|||||||
@@ -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: ""))
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)) }
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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()
|
||||||
}) {
|
}) {
|
||||||
Text("Back")
|
HStack{
|
||||||
.fontWeight(.semibold)
|
Spacer()
|
||||||
.frame(maxWidth: .infinity)
|
Text("Back")
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
.contentShape(Rectangle())
|
||||||
}
|
}
|
||||||
.buttonStyle(PlainButtonStyle())
|
.buttonStyle(PlainButtonStyle())
|
||||||
.padding()
|
.padding()
|
||||||
@@ -149,26 +161,25 @@ struct AnisetteServers: View {
|
|||||||
SUIButton(action: {
|
SUIButton(action: {
|
||||||
viewModel.getListOfServers()
|
viewModel.getListOfServers()
|
||||||
}) {
|
}) {
|
||||||
Text("Refresh Servers")
|
HStack{
|
||||||
.fontWeight(.semibold)
|
Spacer()
|
||||||
.frame(maxWidth: .infinity)
|
Text("Refresh Servers")
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ private extension PatreonViewController
|
|||||||
let isPatronText = NSLocalizedString("""
|
let isPatronText = NSLocalizedString("""
|
||||||
Hey ,
|
Hey ,
|
||||||
|
|
||||||
You’re 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.
|
You’re 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -10,29 +10,30 @@ 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"
|
||||||
|
|
||||||
|
typealias PatreonAPIError = PatreonAPIErrorCode.Error
|
||||||
|
enum PatreonAPIErrorCode: Int, ALTErrorEnum, CaseIterable
|
||||||
|
{
|
||||||
|
case unknown
|
||||||
|
case notAuthenticated
|
||||||
|
case invalidAccessToken
|
||||||
|
|
||||||
|
var errorFailureReason: String {
|
||||||
|
switch self
|
||||||
|
{
|
||||||
|
case .unknown: return NSLocalizedString("An unknown error occurred.", comment: "")
|
||||||
|
case .notAuthenticated: return NSLocalizedString("No connected Patreon account.", comment: "")
|
||||||
|
case .invalidAccessToken: return NSLocalizedString("Invalid access token.", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension PatreonAPI
|
extension PatreonAPI
|
||||||
{
|
{
|
||||||
enum Error: LocalizedError
|
|
||||||
{
|
|
||||||
case unknown
|
|
||||||
case notAuthenticated
|
|
||||||
case invalidAccessToken
|
|
||||||
|
|
||||||
var errorDescription: String? {
|
|
||||||
switch self
|
|
||||||
{
|
|
||||||
case .unknown: return NSLocalizedString("An unknown error occurred.", comment: "")
|
|
||||||
case .notAuthenticated: return NSLocalizedString("No connected Patreon account.", comment: "")
|
|
||||||
case .invalidAccessToken: return NSLocalizedString("Invalid access token.", comment: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ struct ComplicationView: View
|
|||||||
}
|
}
|
||||||
.gaugeStyle(.accessoryCircularCapacity)
|
.gaugeStyle(.accessoryCircularCapacity)
|
||||||
.unredacted()
|
.unredacted()
|
||||||
|
.widgetBackground(Color.clear)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
27
AltWidget/Extensions/View+AltWidget.swift
Normal file
27
AltWidget/Extensions/View+AltWidget.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
28
Dependencies/fetch-prebuilt.sh
vendored
28
Dependencies/fetch-prebuilt.sh
vendored
@@ -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."
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user