mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-22 05:03:26 +01:00
Create swift package
This commit is contained in:
21
.codecov.yml
Normal file
21
.codecov.yml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# https://docs.codecov.io/docs/codecov-yaml
|
||||||
|
|
||||||
|
codecov:
|
||||||
|
require_ci_to_pass: true
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
precision: 2
|
||||||
|
round: down
|
||||||
|
range: "70...100"
|
||||||
|
ignore:
|
||||||
|
- Dependencies
|
||||||
|
status:
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
if_no_uploads: error
|
||||||
|
changes: true
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
target: auto
|
||||||
|
if_no_uploads: error
|
||||||
|
comment: false
|
||||||
@@ -1,39 +1,35 @@
|
|||||||
# EditorConfig is awesome: https://EditorConfig.org
|
# http://editorconfig.org
|
||||||
|
|
||||||
# top-most EditorConfig file
|
|
||||||
root = true
|
root = true
|
||||||
|
|
||||||
# Unix-style newlines with a newline ending every file
|
|
||||||
[*]
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
charset = utf-8
|
||||||
|
indent_size = 4
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
# Matches multiple files with brace expansion notation
|
[*.{md,markdown}]
|
||||||
# Set default charset
|
trim_trailing_whitespace = false
|
||||||
[*.{js,py}]
|
|
||||||
charset = utf-8# 4 space indentation
|
|
||||||
|
|
||||||
# Swift files
|
[*.{c,h,m,mm}]
|
||||||
[*.swift]
|
trim_trailing_whitespace = true
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
charset = utf-8# 4 space indentation
|
|
||||||
|
|
||||||
# 4 space indentation
|
[*.js]
|
||||||
[*.py]
|
indent_size = 2
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
# Tab indentation (no size specified)
|
[*.{swift}]
|
||||||
[Makefile]
|
trim_trailing_whitespace = true
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
# Indentation override for all JS under lib directory
|
[Makefile]
|
||||||
[lib/**.js]
|
trim_trailing_whitespace = true
|
||||||
indent_style = space
|
indent_style = tab
|
||||||
indent_size = 2
|
indent_size = 8
|
||||||
|
|
||||||
# Matches the exact files either package.json or .travis.yml
|
[*.{yaml|yml}]
|
||||||
[{package.json,.travis.yml}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|||||||
13
.gitmodules
vendored
13
.gitmodules
vendored
@@ -7,12 +7,15 @@
|
|||||||
[submodule "Dependencies/libplist"]
|
[submodule "Dependencies/libplist"]
|
||||||
path = Dependencies/libplist
|
path = Dependencies/libplist
|
||||||
url = https://github.com/libimobiledevice/libplist.git
|
url = https://github.com/libimobiledevice/libplist.git
|
||||||
[submodule "Dependencies/MarkdownAttributedString"]
|
|
||||||
path = Dependencies/MarkdownAttributedString
|
|
||||||
url = https://github.com/chockenberry/MarkdownAttributedString.git
|
|
||||||
[submodule "Dependencies/libimobiledevice-glue"]
|
[submodule "Dependencies/libimobiledevice-glue"]
|
||||||
path = Dependencies/libimobiledevice-glue
|
path = Dependencies/libimobiledevice-glue
|
||||||
url = https://github.com/libimobiledevice/libimobiledevice-glue
|
url = https://github.com/libimobiledevice/libimobiledevice-glue
|
||||||
[submodule "Dependencies/libfragmentzip"]
|
[submodule "Sources/libfragmentzip"]
|
||||||
path = Dependencies/libfragmentzip
|
path = Sources/libfragmentzip/libfragmentzip-source
|
||||||
url = https://github.com/SideStore/libfragmentzip.git
|
url = https://github.com/SideStore/libfragmentzip.git
|
||||||
|
[submodule "Dependencies/em_proxy"]
|
||||||
|
path = Dependencies/em_proxy
|
||||||
|
url = https://github.com/SideStore/em_proxy.git
|
||||||
|
[submodule "Dependencies/minimuxer"]
|
||||||
|
path = Dependencies/minimuxer
|
||||||
|
url = https://github.com/SideStore/minimuxer.git
|
||||||
|
|||||||
28
.jazzy.yaml
Normal file
28
.jazzy.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# ---- About ----
|
||||||
|
module: SideStore
|
||||||
|
module_version: 1.0,0
|
||||||
|
author: SideStore
|
||||||
|
readme: README.md
|
||||||
|
copyright: 'See [license](https://github.com/SideStore/SideStore/blob/develop/LICENSE) for more details.'
|
||||||
|
|
||||||
|
# ---- URLs ----
|
||||||
|
author_url: https://sidestore.io
|
||||||
|
dash_url: https://sidestore.io/docsets/SideStore.xml
|
||||||
|
github_url: https://github.com/SideStore/SideStore/
|
||||||
|
github_file_prefix: https://github.com/SideStore/SideStore/tree/1.0.2/
|
||||||
|
|
||||||
|
# ---- Sources ----
|
||||||
|
source_directory: Sources
|
||||||
|
documentation: .build/x86_64-apple-macosx/debug/SideStore.docc
|
||||||
|
|
||||||
|
# ---- Generation ----
|
||||||
|
clean: true
|
||||||
|
output: docs
|
||||||
|
min_acl: public
|
||||||
|
hide_documentation_coverage: false
|
||||||
|
skip_undocumented: false
|
||||||
|
objc: false
|
||||||
|
swift_version: 5.1.0
|
||||||
|
|
||||||
|
# ---- Formatting ----
|
||||||
|
theme: fullwidth
|
||||||
42
.swiftformat
Normal file
42
.swiftformat
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# .swiftformat
|
||||||
|
|
||||||
|
## file options
|
||||||
|
|
||||||
|
--exclude .build,.github,.swiftpm,.vscode,Configurations,Dependencies
|
||||||
|
|
||||||
|
## format options
|
||||||
|
|
||||||
|
--allman false
|
||||||
|
--binarygrouping 4,8
|
||||||
|
--commas always
|
||||||
|
--comments indent
|
||||||
|
--decimalgrouping 3,6
|
||||||
|
--elseposition same-line
|
||||||
|
--empty void
|
||||||
|
--exponentcase lowercase
|
||||||
|
--exponentgrouping disabled
|
||||||
|
--fractiongrouping disabled
|
||||||
|
--header ignore
|
||||||
|
--hexgrouping 4,8
|
||||||
|
--hexliteralcase uppercase
|
||||||
|
--ifdef indent
|
||||||
|
--importgrouping testable-bottom
|
||||||
|
--indent 4
|
||||||
|
--indentcase false
|
||||||
|
--linebreaks lf
|
||||||
|
--maxwidth none
|
||||||
|
--octalgrouping 4,8
|
||||||
|
--operatorfunc spaced
|
||||||
|
--patternlet hoist
|
||||||
|
--ranges spaced
|
||||||
|
--self remove
|
||||||
|
--semicolons inline
|
||||||
|
--stripunusedargs always
|
||||||
|
--swiftversion 5.1
|
||||||
|
--trimwhitespace always
|
||||||
|
--wraparguments preserve
|
||||||
|
--wrapcollections preserve
|
||||||
|
|
||||||
|
## rules
|
||||||
|
|
||||||
|
--enable isEmpty,andOperator,assertionFailures
|
||||||
76
.swiftlint.yml
Normal file
76
.swiftlint.yml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
disabled_rules:
|
||||||
|
- block_based_kvo
|
||||||
|
- colon
|
||||||
|
- control_statement
|
||||||
|
- cyclomatic_complexity
|
||||||
|
- discarded_notification_center_observer
|
||||||
|
- file_length
|
||||||
|
- function_parameter_count
|
||||||
|
- generic_type_name
|
||||||
|
- identifier_name
|
||||||
|
- multiple_closures_with_trailing_closure
|
||||||
|
- nesting
|
||||||
|
- switch_case_alignment
|
||||||
|
- todo
|
||||||
|
- type_name
|
||||||
|
- type_body_length
|
||||||
|
- function_body_length
|
||||||
|
- unused_closure_parameter
|
||||||
|
|
||||||
|
# parameterized rules can be customized from this configuration file
|
||||||
|
line_length: 200
|
||||||
|
# parameterized rules are first parameterized as a warning level, then error level.
|
||||||
|
type_body_length:
|
||||||
|
- 300 # warning
|
||||||
|
- 600 # error
|
||||||
|
# parameterized rules are first parameterized as a warning level, then error level.
|
||||||
|
# identifier_name_max_length:
|
||||||
|
# - 40 # warning
|
||||||
|
# - 60 # error
|
||||||
|
# # parameterized rules are first parameterized as a warning level, then error level.
|
||||||
|
# identifier_name_min_length:
|
||||||
|
# - 3 # warning
|
||||||
|
# - 2 # error
|
||||||
|
function_body_length:
|
||||||
|
- 200 # warning
|
||||||
|
- 500 # error
|
||||||
|
large_tuple:
|
||||||
|
- 4 # warning
|
||||||
|
- 6 # error
|
||||||
|
|
||||||
|
opt_in_rules:
|
||||||
|
- empty_count
|
||||||
|
- force_unwrapping
|
||||||
|
|
||||||
|
excluded: # paths to ignore during linting. overridden byincluded.
|
||||||
|
- .build
|
||||||
|
- .github
|
||||||
|
- .swiftpm
|
||||||
|
- .vscode
|
||||||
|
- Dependencies
|
||||||
|
|
||||||
|
analyzer_rules: # Rules run by `swiftlint analyze` (experimental)
|
||||||
|
- explicit_self
|
||||||
|
|
||||||
|
# Override these rules to be warnings for now
|
||||||
|
force_cast: warning
|
||||||
|
force_try: warning
|
||||||
|
empty_count: warning
|
||||||
|
|
||||||
|
reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, junit)
|
||||||
|
|
||||||
|
custom_rules:
|
||||||
|
placeholders_in_comments:
|
||||||
|
included: ".*\\.swift"
|
||||||
|
name: "No Placeholders in Comments"
|
||||||
|
regex: "<#([^#]+)#>"
|
||||||
|
match_kinds:
|
||||||
|
- comment
|
||||||
|
- doccomment
|
||||||
|
message: "Placeholder left in comment."
|
||||||
|
tiles_deprecated:
|
||||||
|
included: ".*\\.swift"
|
||||||
|
name: "Tiles are deprecated in favor of Frame"
|
||||||
|
regex: "([T,t]ile$|^[T,t]il[e,es])"
|
||||||
|
message: "Tiles are deprecated in favor of Frame"
|
||||||
|
severity: warning
|
||||||
720
AltStore-SPM.xcodeproj/project.pbxproj
Normal file
720
AltStore-SPM.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,720 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 52;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
191E6087290C7B50001A3B7C /* libminimuxer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 191E5FB5290A5E1F001A3B7C /* libminimuxer.a */; };
|
||||||
|
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */ = {isa = PBXBuildFile; productRef = 4879A9612861049C00FC1BBD /* OpenSSL */; };
|
||||||
|
B34AFD0A29A9CEDD00E637B4 /* SideKit in Frameworks */ = {isa = PBXBuildFile; productRef = B34AFD0929A9CEDD00E637B4 /* SideKit */; };
|
||||||
|
B34AFD0D29A9CF4000E637B4 /* Roxas in Frameworks */ = {isa = PBXBuildFile; productRef = B34AFD0C29A9CF4000E637B4 /* Roxas */; };
|
||||||
|
B34AFD0F29A9CF4000E637B4 /* RoxasUI in Frameworks */ = {isa = PBXBuildFile; productRef = B34AFD0E29A9CF4000E637B4 /* RoxasUI */; };
|
||||||
|
B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F3284F35DD00DA9E2F /* Nuke */; };
|
||||||
|
B3C395F7284F362400DA9E2F /* AppCenterAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F6284F362400DA9E2F /* AppCenterAnalytics */; };
|
||||||
|
B3C395F9284F362400DA9E2F /* AppCenterCrashes in Frameworks */ = {isa = PBXBuildFile; productRef = B3C395F8284F362400DA9E2F /* AppCenterCrashes */; };
|
||||||
|
BF989177250AABF4002ACF50 /* SideWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BF989167250AABF3002ACF50 /* SideWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
|
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = D533E8B62727841800A9B5DD /* libAppleArchive.tbd */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
BF989175250AABF4002ACF50 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = BFD247622284B9A500981D42 /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = BF989166250AABF3002ACF50;
|
||||||
|
remoteInfo = AltWidgetExtension;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
BF088D2B2501A087008082D9 /* Embed Frameworks */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed Frameworks";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
BF98917B250AABF4002ACF50 /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
BF989177250AABF4002ACF50 /* SideWidgetExtension.appex in Embed App Extensions */,
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
191E5FB5290A5E1F001A3B7C /* libminimuxer.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libminimuxer.a; path = "Dependencies/minimuxer/target/aarch64-apple-ios/debug/libminimuxer.a"; sourceTree = "<group>"; };
|
||||||
|
B32C055D29AF6867004789CF /* SideStore */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = SideStore; sourceTree = "<group>"; };
|
||||||
|
B343F86C295F759E002B1159 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.1.sdk/usr/lib/libresolv.tbd; sourceTree = DEVELOPER_DIR; };
|
||||||
|
B39575F4284F29E20080B4FF /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
B3C39606284F4C8400DA9E2F /* CodeSigning.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = CodeSigning.xcconfig; sourceTree = "<group>"; };
|
||||||
|
B3C39607284F4C8400DA9E2F /* Build.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Build.xcconfig; sourceTree = "<group>"; };
|
||||||
|
B3C39608284F4C8400DA9E2F /* CodeSigning.xcconfig.sample */ = {isa = PBXFileReference; lastKnownFileType = text; path = CodeSigning.xcconfig.sample; sourceTree = "<group>"; };
|
||||||
|
B3C3960B284F4C9800DA9E2F /* AltStore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStore.xcconfig; sourceTree = "<group>"; };
|
||||||
|
B3C3960D284F4E4B00DA9E2F /* AltWidgetExtension.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltWidgetExtension.xcconfig; sourceTree = "<group>"; };
|
||||||
|
B3C3960E284F4F9100DA9E2F /* AltStoreCore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltStoreCore.xcconfig; sourceTree = "<group>"; };
|
||||||
|
B3C3960F284F53E900DA9E2F /* AltBackup.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AltBackup.xcconfig; sourceTree = "<group>"; };
|
||||||
|
BF4588872298DD3F00BD7491 /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; };
|
||||||
|
BF580497246A3D19008AE704 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||||
|
BF989167250AABF3002ACF50 /* SideWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SideWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
BFD2476A2284B9A500981D42 /* SideStore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SideStore.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
BFD247862284BB3B00981D42 /* Roxas.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
D533E8B62727841800A9B5DD /* libAppleArchive.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libAppleArchive.tbd; path = usr/lib/libAppleArchive.tbd; sourceTree = SDKROOT; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
BF989164250AABF3002ACF50 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
BFD247672284B9A500981D42 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
191E6087290C7B50001A3B7C /* libminimuxer.a in Frameworks */,
|
||||||
|
B34AFD0F29A9CF4000E637B4 /* RoxasUI in Frameworks */,
|
||||||
|
D533E8B72727841800A9B5DD /* libAppleArchive.tbd in Frameworks */,
|
||||||
|
B3C395F9284F362400DA9E2F /* AppCenterCrashes in Frameworks */,
|
||||||
|
B34AFD0A29A9CEDD00E637B4 /* SideKit in Frameworks */,
|
||||||
|
4879A9622861049C00FC1BBD /* OpenSSL in Frameworks */,
|
||||||
|
B3C395F4284F35DD00DA9E2F /* Nuke in Frameworks */,
|
||||||
|
B34AFD0D29A9CF4000E637B4 /* Roxas in Frameworks */,
|
||||||
|
B3C395F7284F362400DA9E2F /* AppCenterAnalytics in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
B3E8749729AF639C00745374 /* Packages */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
B32C055D29AF6867004789CF /* SideStore */,
|
||||||
|
);
|
||||||
|
name = Packages;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BFD247612284B9A500981D42 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
B3E8749729AF639C00745374 /* Packages */,
|
||||||
|
B3C39607284F4C8400DA9E2F /* Build.xcconfig */,
|
||||||
|
B3C39606284F4C8400DA9E2F /* CodeSigning.xcconfig */,
|
||||||
|
B3C39608284F4C8400DA9E2F /* CodeSigning.xcconfig.sample */,
|
||||||
|
B3C3960B284F4C9800DA9E2F /* AltStore.xcconfig */,
|
||||||
|
B3C3960F284F53E900DA9E2F /* AltBackup.xcconfig */,
|
||||||
|
B3C3960D284F4E4B00DA9E2F /* AltWidgetExtension.xcconfig */,
|
||||||
|
B3C3960E284F4F9100DA9E2F /* AltStoreCore.xcconfig */,
|
||||||
|
BFD247852284BB3300981D42 /* Frameworks */,
|
||||||
|
BFD2476B2284B9A500981D42 /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BFD2476B2284B9A500981D42 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BFD2476A2284B9A500981D42 /* SideStore.app */,
|
||||||
|
BF989167250AABF3002ACF50 /* SideWidgetExtension.appex */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BFD247852284BB3300981D42 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
B343F86C295F759E002B1159 /* libresolv.tbd */,
|
||||||
|
191E5FB5290A5E1F001A3B7C /* libminimuxer.a */,
|
||||||
|
B39575F4284F29E20080B4FF /* Roxas.framework */,
|
||||||
|
D533E8B62727841800A9B5DD /* libAppleArchive.tbd */,
|
||||||
|
BF580497246A3D19008AE704 /* UIKit.framework */,
|
||||||
|
BF4588872298DD3F00BD7491 /* libxml2.tbd */,
|
||||||
|
BFD247862284BB3B00981D42 /* Roxas.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
BF989166250AABF3002ACF50 /* SideWidgetExtension */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "SideWidgetExtension" */;
|
||||||
|
buildPhases = (
|
||||||
|
BF989163250AABF3002ACF50 /* Sources */,
|
||||||
|
BF989164250AABF3002ACF50 /* Frameworks */,
|
||||||
|
BF989165250AABF3002ACF50 /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = SideWidgetExtension;
|
||||||
|
productName = AltWidgetExtension;
|
||||||
|
productReference = BF989167250AABF3002ACF50 /* SideWidgetExtension.appex */;
|
||||||
|
productType = "com.apple.product-type.app-extension";
|
||||||
|
};
|
||||||
|
BFD247692284B9A500981D42 /* SideStore */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = BFD2477E2284B9A700981D42 /* Build configuration list for PBXNativeTarget "SideStore" */;
|
||||||
|
buildPhases = (
|
||||||
|
BFD247662284B9A500981D42 /* Sources */,
|
||||||
|
BFD247672284B9A500981D42 /* Frameworks */,
|
||||||
|
BFD247682284B9A500981D42 /* Resources */,
|
||||||
|
BF088D2B2501A087008082D9 /* Embed Frameworks */,
|
||||||
|
BF98917B250AABF4002ACF50 /* Embed App Extensions */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
BF989176250AABF4002ACF50 /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
name = SideStore;
|
||||||
|
packageProductDependencies = (
|
||||||
|
B3C395F3284F35DD00DA9E2F /* Nuke */,
|
||||||
|
B3C395F6284F362400DA9E2F /* AppCenterAnalytics */,
|
||||||
|
B3C395F8284F362400DA9E2F /* AppCenterCrashes */,
|
||||||
|
4879A9612861049C00FC1BBD /* OpenSSL */,
|
||||||
|
B34AFD0929A9CEDD00E637B4 /* SideKit */,
|
||||||
|
B34AFD0C29A9CF4000E637B4 /* Roxas */,
|
||||||
|
B34AFD0E29A9CF4000E637B4 /* RoxasUI */,
|
||||||
|
);
|
||||||
|
productName = AltStore;
|
||||||
|
productReference = BFD2476A2284B9A500981D42 /* SideStore.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
BFD247622284B9A500981D42 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 1400;
|
||||||
|
LastUpgradeCheck = 1020;
|
||||||
|
ORGANIZATIONNAME = SideStore;
|
||||||
|
TargetAttributes = {
|
||||||
|
BF989166250AABF3002ACF50 = {
|
||||||
|
CreatedOnToolsVersion = 12.0;
|
||||||
|
LastSwiftMigration = 1200;
|
||||||
|
};
|
||||||
|
BFD247692284B9A500981D42 = {
|
||||||
|
CreatedOnToolsVersion = 10.2.1;
|
||||||
|
LastSwiftMigration = 1020;
|
||||||
|
SystemCapabilities = {
|
||||||
|
com.apple.BackgroundModes = {
|
||||||
|
enabled = 1;
|
||||||
|
};
|
||||||
|
com.apple.Push = {
|
||||||
|
enabled = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = BFD247652284B9A500981D42 /* Build configuration list for PBXProject "AltStore" */;
|
||||||
|
compatibilityVersion = "Xcode 9.3";
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = BFD247612284B9A500981D42;
|
||||||
|
packageReferences = (
|
||||||
|
D58D5F2C26DFE68E00E55E38 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */,
|
||||||
|
B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */,
|
||||||
|
B3C395F2284F35DD00DA9E2F /* XCRemoteSwiftPackageReference "Nuke" */,
|
||||||
|
B3C395F5284F362400DA9E2F /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */,
|
||||||
|
B3C395FA284F3B2400DA9E2F /* XCRemoteSwiftPackageReference "Sparkle" */,
|
||||||
|
B3C395FD284F3C0900DA9E2F /* XCRemoteSwiftPackageReference "STPrivilegedTask" */,
|
||||||
|
4879A95D2861046500FC1BBD /* XCRemoteSwiftPackageReference "AltSign" */,
|
||||||
|
4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */,
|
||||||
|
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */,
|
||||||
|
B34AFD0829A9CEDD00E637B4 /* XCRemoteSwiftPackageReference "SideKit" */,
|
||||||
|
B34AFD0B29A9CF4000E637B4 /* XCRemoteSwiftPackageReference "Roxas" */,
|
||||||
|
);
|
||||||
|
productRefGroup = BFD2476B2284B9A500981D42 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
BFD247692284B9A500981D42 /* SideStore */,
|
||||||
|
BF989166250AABF3002ACF50 /* SideWidgetExtension */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
BF989165250AABF3002ACF50 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
BFD247682284B9A500981D42 /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
BF989163250AABF3002ACF50 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
BFD247662284B9A500981D42 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
BF989176250AABF4002ACF50 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = BF989166250AABF3002ACF50 /* SideWidgetExtension */;
|
||||||
|
targetProxy = BF989175250AABF4002ACF50 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
BF989179250AABF4002ACF50 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = B3C3960D284F4E4B00DA9E2F /* AltWidgetExtension.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
|
||||||
|
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||||
|
INFOPLIST_FILE = AltWidget/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
BF98917A250AABF4002ACF50 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = B3C3960D284F4E4B00DA9E2F /* AltWidgetExtension.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = AltWidget/AltWidgetExtension.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
|
||||||
|
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||||
|
INFOPLIST_FILE = AltWidget/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
"@executable_path/../../Frameworks",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
BFD2477C2284B9A700981D42 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = B3C39607284F4C8400DA9E2F /* Build.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
|
"$(OTHER_CFLAGS)",
|
||||||
|
"-Wno-module-import-in-extern-c",
|
||||||
|
);
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG BETA";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SYSTEM_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Dependencies/AltSign/Dependencies\"";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
BFD2477D2284B9A700981D42 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = B3C39607284F4C8400DA9E2F /* Build.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||||
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 12.2;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
|
"$(OTHER_CFLAGS)",
|
||||||
|
"-Wno-module-import-in-extern-c",
|
||||||
|
);
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos macosx";
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG BETA";
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
|
SYSTEM_HEADER_SEARCH_PATHS = "\"$(SRCROOT)/Dependencies/AltSign/Dependencies\"";
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
BFD2477F2284B9A700981D42 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = B3C3960B284F4C9800DA9E2F /* AltStore.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = AltStore/AltStore.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = AltStore/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Dependencies/fragmentzip",
|
||||||
|
"$(PROJECT_DIR)/Dependencies/libcurl",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "AltStore/AltStore-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
BFD247802284B9A700981D42 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = B3C3960B284F4C9800DA9E2F /* AltStore.xcconfig */;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CODE_SIGN_ENTITLEMENTS = AltStore/AltStore.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)";
|
||||||
|
ENABLE_BITCODE = NO;
|
||||||
|
INFOPLIST_FILE = AltStore/Info.plist;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/Dependencies/fragmentzip",
|
||||||
|
"$(PROJECT_DIR)/Dependencies/libcurl",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "$(PRODUCT_BUNDLE_IDENTIFIER)";
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "AltStore/AltStore-Bridging-Header.h";
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
BF989178250AABF4002ACF50 /* Build configuration list for PBXNativeTarget "SideWidgetExtension" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
BF989179250AABF4002ACF50 /* Debug */,
|
||||||
|
BF98917A250AABF4002ACF50 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
BFD247652284B9A500981D42 /* Build configuration list for PBXProject "AltStore" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
BFD2477C2284B9A700981D42 /* Debug */,
|
||||||
|
BFD2477D2284B9A700981D42 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
BFD2477E2284B9A700981D42 /* Build configuration list for PBXNativeTarget "SideStore" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
BFD2477F2284B9A700981D42 /* Debug */,
|
||||||
|
BFD247802284B9A700981D42 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
4879A95D2861046500FC1BBD /* XCRemoteSwiftPackageReference "AltSign" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/SideStore/AltSign";
|
||||||
|
requirement = {
|
||||||
|
branch = master;
|
||||||
|
kind = branch;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/krzyzanowskim/OpenSSL";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMinorVersion;
|
||||||
|
minimumVersion = 1.1.180;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
99C4EF472978D52400CB538D /* XCRemoteSwiftPackageReference "SemanticVersion" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/SwiftPackageIndex/SemanticVersion.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 0.3.5;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
B34AFD0829A9CEDD00E637B4 /* XCRemoteSwiftPackageReference "SideKit" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/SideStore/SideKit.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMinorVersion;
|
||||||
|
minimumVersion = 0.1.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
B34AFD0B29A9CF4000E637B4 /* XCRemoteSwiftPackageReference "Roxas" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/JoeMatt/Roxas.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 1.2.2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
B3C395EF284F2DE700DA9E2F /* XCRemoteSwiftPackageReference "KeychainAccess" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/kishikawakatsumi/KeychainAccess.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 4.2.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
B3C395F2284F35DD00DA9E2F /* XCRemoteSwiftPackageReference "Nuke" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/kean/Nuke.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 7.0.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
B3C395F5284F362400DA9E2F /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/microsoft/appcenter-sdk-apple.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 4.2.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
B3C395FA284F3B2400DA9E2F /* XCRemoteSwiftPackageReference "Sparkle" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/sparkle-project/Sparkle.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 2.1.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
B3C395FD284F3C0900DA9E2F /* XCRemoteSwiftPackageReference "STPrivilegedTask" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/JoeMatt/STPrivilegedTask.git";
|
||||||
|
requirement = {
|
||||||
|
branch = master;
|
||||||
|
kind = branch;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
D58D5F2C26DFE68E00E55E38 /* XCRemoteSwiftPackageReference "LaunchAtLogin" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 4.1.0;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
4879A9612861049C00FC1BBD /* OpenSSL */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 4879A9602861049C00FC1BBD /* XCRemoteSwiftPackageReference "OpenSSL" */;
|
||||||
|
productName = OpenSSL;
|
||||||
|
};
|
||||||
|
B34AFD0929A9CEDD00E637B4 /* SideKit */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = B34AFD0829A9CEDD00E637B4 /* XCRemoteSwiftPackageReference "SideKit" */;
|
||||||
|
productName = SideKit;
|
||||||
|
};
|
||||||
|
B34AFD0C29A9CF4000E637B4 /* Roxas */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = B34AFD0B29A9CF4000E637B4 /* XCRemoteSwiftPackageReference "Roxas" */;
|
||||||
|
productName = Roxas;
|
||||||
|
};
|
||||||
|
B34AFD0E29A9CF4000E637B4 /* RoxasUI */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = B34AFD0B29A9CF4000E637B4 /* XCRemoteSwiftPackageReference "Roxas" */;
|
||||||
|
productName = RoxasUI;
|
||||||
|
};
|
||||||
|
B3C395F3284F35DD00DA9E2F /* Nuke */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = B3C395F2284F35DD00DA9E2F /* XCRemoteSwiftPackageReference "Nuke" */;
|
||||||
|
productName = Nuke;
|
||||||
|
};
|
||||||
|
B3C395F6284F362400DA9E2F /* AppCenterAnalytics */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = B3C395F5284F362400DA9E2F /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */;
|
||||||
|
productName = AppCenterAnalytics;
|
||||||
|
};
|
||||||
|
B3C395F8284F362400DA9E2F /* AppCenterCrashes */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = B3C395F5284F362400DA9E2F /* XCRemoteSwiftPackageReference "appcenter-sdk-apple" */;
|
||||||
|
productName = AppCenterCrashes;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
};
|
||||||
|
rootObject = BFD247622284B9A500981D42 /* Project object */;
|
||||||
|
}
|
||||||
@@ -14,8 +14,17 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/microsoft/appcenter-sdk-apple.git",
|
"location" : "https://github.com/microsoft/appcenter-sdk-apple.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "8354a50fe01a7e54e196d3b5493b5ab53dd5866a",
|
"revision" : "b2dc99cfedead0bad4e6573d86c5228c89cff332",
|
||||||
"version" : "4.4.2"
|
"version" : "4.4.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "down",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/johnxnguyen/Down",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "master",
|
||||||
|
"revision" : "e754ab1c80920dd51a8e08290c912ac1c2ac8b58"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -50,8 +59,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/krzyzanowskim/OpenSSL",
|
"location" : "https://github.com/krzyzanowskim/OpenSSL",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "033fcb41dac96b1b6effa945ca1f9ade002370b2",
|
"revision" : "87f41bf9488e7dd2b0de2ab97cb3eafc7304e0e6",
|
||||||
"version" : "1.1.1501"
|
"version" : "1.1.2000"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -59,8 +68,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/microsoft/PLCrashReporter.git",
|
"location" : "https://github.com/microsoft/PLCrashReporter.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "6b27393cad517c067dceea85fadf050e70c4ceaa",
|
"revision" : "81cdec2b3827feb03286cb297f4c501a8eb98df1",
|
||||||
"version" : "1.10.1"
|
"version" : "1.10.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -84,10 +93,10 @@
|
|||||||
{
|
{
|
||||||
"identity" : "sidekit",
|
"identity" : "sidekit",
|
||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/SideStore/SideKit.git",
|
"location" : "https://github.com/SideStore/SideKit",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "7ea34a09b52c104077dea8e0b90f8dc55d43b36b",
|
"branch" : "main",
|
||||||
"version" : "0.1.0"
|
"revision" : "7ea34a09b52c104077dea8e0b90f8dc55d43b36b"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -95,8 +104,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/sparkle-project/Sparkle.git",
|
"location" : "https://github.com/sparkle-project/Sparkle.git",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "286edd1fa22505a9e54d170e9fd07d775ea233f2",
|
"revision" : "dda155c7d3ef38c53d29f8584cb2aad2a1a54dba",
|
||||||
"version" : "2.1.0"
|
"version" : "2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,111 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1150"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "NO"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "A7A6DC28A6D60809855FE404C6A3EA29"
|
|
||||||
BuildableName = "libPods-AltDaemon.a"
|
|
||||||
BlueprintName = "Pods-AltDaemon"
|
|
||||||
ReferencedContainer = "container:Pods/Pods.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF1E314F22A0616100370A3C"
|
|
||||||
BuildableName = "libAltKit.a"
|
|
||||||
BlueprintName = "AltKit"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF18BFE624857D7900DD5981"
|
|
||||||
BuildableName = "AltDaemon"
|
|
||||||
BlueprintName = "AltDaemon"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF18BFE624857D7900DD5981"
|
|
||||||
BuildableName = "AltDaemon"
|
|
||||||
BlueprintName = "AltDaemon"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<EnvironmentVariables>
|
|
||||||
<EnvironmentVariable
|
|
||||||
key = "THEOS"
|
|
||||||
value = "~/theos"
|
|
||||||
isEnabled = "YES">
|
|
||||||
</EnvironmentVariable>
|
|
||||||
</EnvironmentVariables>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF18BFE624857D7900DD5981"
|
|
||||||
BuildableName = "AltDaemon"
|
|
||||||
BlueprintName = "AltDaemon"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1120"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF5C5FC4237DF5AE00EDD0C6"
|
|
||||||
BuildableName = "AltPlugin.mailbundle"
|
|
||||||
BlueprintName = "AltPlugin"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "1"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF5C5FC4237DF5AE00EDD0C6"
|
|
||||||
BuildableName = "AltPlugin.mailbundle"
|
|
||||||
BlueprintName = "AltPlugin"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1020"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF45868C229872EA00BD7491"
|
|
||||||
BuildableName = "AltServer.app"
|
|
||||||
BlueprintName = "AltServer"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF45868C229872EA00BD7491"
|
|
||||||
BuildableName = "AltServer.app"
|
|
||||||
BlueprintName = "AltServer"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF45868C229872EA00BD7491"
|
|
||||||
BuildableName = "AltServer.app"
|
|
||||||
BlueprintName = "AltServer"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF45868C229872EA00BD7491"
|
|
||||||
BuildableName = "AltServer.app"
|
|
||||||
BlueprintName = "AltServer"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1020"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFD247692284B9A500981D42"
|
|
||||||
BuildableName = "SideStore.app"
|
|
||||||
BlueprintName = "SideStore"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFD247692284B9A500981D42"
|
|
||||||
BuildableName = "SideStore.app"
|
|
||||||
BlueprintName = "SideStore"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
<CommandLineArguments>
|
|
||||||
<CommandLineArgument
|
|
||||||
argument = "-com.apple.CoreData.ConcurrencyDebug 1"
|
|
||||||
isEnabled = "YES">
|
|
||||||
</CommandLineArgument>
|
|
||||||
</CommandLineArguments>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFD247692284B9A500981D42"
|
|
||||||
BuildableName = "SideStore.app"
|
|
||||||
BlueprintName = "SideStore"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Release">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1020"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFD247692284B9A500981D42"
|
|
||||||
BuildableName = "SideStore.app"
|
|
||||||
BlueprintName = "SideStore"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFD247692284B9A500981D42"
|
|
||||||
BuildableName = "SideStore.app"
|
|
||||||
BlueprintName = "SideStore"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
<CommandLineArguments>
|
|
||||||
<CommandLineArgument
|
|
||||||
argument = "-com.apple.CoreData.ConcurrencyDebug 1"
|
|
||||||
isEnabled = "YES">
|
|
||||||
</CommandLineArgument>
|
|
||||||
</CommandLineArguments>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFD247692284B9A500981D42"
|
|
||||||
BuildableName = "SideStore.app"
|
|
||||||
BlueprintName = "SideStore"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1230"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFF7C903257844C900E55F36"
|
|
||||||
BuildableName = "AltXPC.xpc"
|
|
||||||
BlueprintName = "AltXPC"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BF45868C229872EA00BD7491"
|
|
||||||
BuildableName = "AltServer.app"
|
|
||||||
BlueprintName = "AltServer"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "BFF7C903257844C900E55F36"
|
|
||||||
BuildableName = "AltXPC.xpc"
|
|
||||||
BlueprintName = "AltXPC"
|
|
||||||
ReferencedContainer = "container:AltStore.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
//
|
|
||||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "NSAttributedString+Markdown.h"
|
|
||||||
#import "ALTAppPatcher.h"
|
|
||||||
|
|
||||||
#include "fragmentzip.h"
|
|
||||||
@@ -1,570 +0,0 @@
|
|||||||
//
|
|
||||||
// AppViewController.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 7/22/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import AltStoreCore
|
|
||||||
import RoxasUI
|
|
||||||
|
|
||||||
import Nuke
|
|
||||||
|
|
||||||
final class AppViewController: UIViewController
|
|
||||||
{
|
|
||||||
var app: StoreApp!
|
|
||||||
|
|
||||||
private var contentViewController: AppContentViewController!
|
|
||||||
private var contentViewControllerShadowView: UIView!
|
|
||||||
|
|
||||||
private var blurAnimator: UIViewPropertyAnimator?
|
|
||||||
private var navigationBarAnimator: UIViewPropertyAnimator?
|
|
||||||
|
|
||||||
private var contentSizeObservation: NSKeyValueObservation?
|
|
||||||
|
|
||||||
@IBOutlet private var scrollView: UIScrollView!
|
|
||||||
@IBOutlet private var contentView: UIView!
|
|
||||||
|
|
||||||
@IBOutlet private var bannerView: AppBannerView!
|
|
||||||
|
|
||||||
@IBOutlet private var backButton: UIButton!
|
|
||||||
@IBOutlet private var backButtonContainerView: UIVisualEffectView!
|
|
||||||
|
|
||||||
@IBOutlet private var backgroundAppIconImageView: UIImageView!
|
|
||||||
@IBOutlet private var backgroundBlurView: UIVisualEffectView!
|
|
||||||
|
|
||||||
@IBOutlet private var navigationBarTitleView: UIView!
|
|
||||||
@IBOutlet private var navigationBarDownloadButton: PillButton!
|
|
||||||
@IBOutlet private var navigationBarAppIconImageView: UIImageView!
|
|
||||||
@IBOutlet private var navigationBarAppNameLabel: UILabel!
|
|
||||||
|
|
||||||
private var _shouldResetLayout = false
|
|
||||||
private var _backgroundBlurEffect: UIBlurEffect?
|
|
||||||
private var _backgroundBlurTintColor: UIColor?
|
|
||||||
|
|
||||||
private var _preferredStatusBarStyle: UIStatusBarStyle = .default
|
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
||||||
return _preferredStatusBarStyle
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.navigationBarTitleView.sizeToFit()
|
|
||||||
self.navigationItem.titleView = self.navigationBarTitleView
|
|
||||||
|
|
||||||
self.contentViewControllerShadowView = UIView()
|
|
||||||
self.contentViewControllerShadowView.backgroundColor = .white
|
|
||||||
self.contentViewControllerShadowView.layer.cornerRadius = 38
|
|
||||||
self.contentViewControllerShadowView.layer.shadowColor = UIColor.black.cgColor
|
|
||||||
self.contentViewControllerShadowView.layer.shadowOffset = CGSize(width: 0, height: -1)
|
|
||||||
self.contentViewControllerShadowView.layer.shadowRadius = 10
|
|
||||||
self.contentViewControllerShadowView.layer.shadowOpacity = 0.3
|
|
||||||
self.contentViewController.view.superview?.insertSubview(self.contentViewControllerShadowView, at: 0)
|
|
||||||
|
|
||||||
self.contentView.addGestureRecognizer(self.scrollView.panGestureRecognizer)
|
|
||||||
|
|
||||||
self.contentViewController.view.layer.cornerRadius = 38
|
|
||||||
self.contentViewController.view.layer.masksToBounds = true
|
|
||||||
|
|
||||||
self.contentViewController.tableView.panGestureRecognizer.require(toFail: self.scrollView.panGestureRecognizer)
|
|
||||||
self.contentViewController.tableView.showsVerticalScrollIndicator = false
|
|
||||||
|
|
||||||
// Bring to front so the scroll indicators are visible.
|
|
||||||
self.view.bringSubviewToFront(self.scrollView)
|
|
||||||
self.scrollView.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
self.bannerView.frame = CGRect(x: 0, y: 0, width: 300, height: 93)
|
|
||||||
self.bannerView.backgroundEffectView.effect = UIBlurEffect(style: .regular)
|
|
||||||
self.bannerView.backgroundEffectView.backgroundColor = .clear
|
|
||||||
self.bannerView.iconImageView.image = nil
|
|
||||||
self.bannerView.iconImageView.tintColor = self.app.tintColor
|
|
||||||
self.bannerView.button.tintColor = self.app.tintColor
|
|
||||||
self.bannerView.tintColor = self.app.tintColor
|
|
||||||
|
|
||||||
self.bannerView.configure(for: self.app)
|
|
||||||
self.bannerView.accessibilityTraits.remove(.button)
|
|
||||||
|
|
||||||
self.bannerView.button.addTarget(self, action: #selector(AppViewController.performAppAction(_:)), for: .primaryActionTriggered)
|
|
||||||
|
|
||||||
self.backButtonContainerView.tintColor = self.app.tintColor
|
|
||||||
|
|
||||||
self.navigationController?.navigationBar.tintColor = self.app.tintColor
|
|
||||||
self.navigationBarDownloadButton.tintColor = self.app.tintColor
|
|
||||||
self.navigationBarAppNameLabel.text = self.app.name
|
|
||||||
self.navigationBarAppIconImageView.tintColor = self.app.tintColor
|
|
||||||
|
|
||||||
self.contentSizeObservation = self.contentViewController.tableView.observe(\.contentSize) { [weak self] (tableView, change) in
|
|
||||||
self?.view.setNeedsLayout()
|
|
||||||
self?.view.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(AppViewController.didChangeApp(_:)), name: .NSManagedObjectContextObjectsDidChange, object: DatabaseManager.shared.viewContext)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(AppViewController.willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(AppViewController.didBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
|
|
||||||
|
|
||||||
self._backgroundBlurEffect = self.backgroundBlurView.effect as? UIBlurEffect
|
|
||||||
self._backgroundBlurTintColor = self.backgroundBlurView.contentView.backgroundColor
|
|
||||||
|
|
||||||
// Load Images
|
|
||||||
for imageView in [self.bannerView.iconImageView!, self.backgroundAppIconImageView!, self.navigationBarAppIconImageView!]
|
|
||||||
{
|
|
||||||
imageView.isIndicatingActivity = true
|
|
||||||
|
|
||||||
Nuke.loadImage(with: self.app.iconURL, options: .shared, into: imageView, progress: nil) { [weak imageView] (response, error) in
|
|
||||||
if response?.image != nil
|
|
||||||
{
|
|
||||||
imageView?.isIndicatingActivity = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
self.prepareBlur()
|
|
||||||
|
|
||||||
// Update blur immediately.
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
self.view.layoutIfNeeded()
|
|
||||||
|
|
||||||
self.transitionCoordinator?.animate(alongsideTransition: { (context) in
|
|
||||||
self.hideNavigationBar()
|
|
||||||
}, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
self._shouldResetLayout = true
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
self.view.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewWillDisappear(animated)
|
|
||||||
|
|
||||||
// Guard against "dismissing" when presenting via 3D Touch pop.
|
|
||||||
guard self.navigationController != nil else { return }
|
|
||||||
|
|
||||||
// Store reference since self.navigationController will be nil after disappearing.
|
|
||||||
let navigationController = self.navigationController
|
|
||||||
navigationController?.navigationBar.barStyle = .default // Don't animate, or else status bar might appear messed-up.
|
|
||||||
|
|
||||||
self.transitionCoordinator?.animate(alongsideTransition: { (context) in
|
|
||||||
self.showNavigationBar(for: navigationController)
|
|
||||||
}, completion: { (context) in
|
|
||||||
if !context.isCancelled
|
|
||||||
{
|
|
||||||
self.showNavigationBar(for: navigationController)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidDisappear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewDidDisappear(animated)
|
|
||||||
|
|
||||||
if self.navigationController == nil
|
|
||||||
{
|
|
||||||
self.resetNavigationBarAnimation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
|
||||||
{
|
|
||||||
guard segue.identifier == "embedAppContentViewController" else { return }
|
|
||||||
|
|
||||||
self.contentViewController = segue.destination as? AppContentViewController
|
|
||||||
self.contentViewController.app = self.app
|
|
||||||
|
|
||||||
if #available(iOS 15, *)
|
|
||||||
{
|
|
||||||
// Fix navigation bar + tab bar appearance on iOS 15.
|
|
||||||
self.setContentScrollView(self.scrollView)
|
|
||||||
self.navigationItem.scrollEdgeAppearance = self.navigationController?.navigationBar.standardAppearance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLayoutSubviews()
|
|
||||||
{
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
|
|
||||||
if self._shouldResetLayout
|
|
||||||
{
|
|
||||||
// Various events can cause UI to mess up, so reset affected components now.
|
|
||||||
|
|
||||||
if self.navigationController?.topViewController == self
|
|
||||||
{
|
|
||||||
self.hideNavigationBar()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.prepareBlur()
|
|
||||||
|
|
||||||
// Reset navigation bar animation, and create a new one later in this method if necessary.
|
|
||||||
self.resetNavigationBarAnimation()
|
|
||||||
|
|
||||||
self._shouldResetLayout = false
|
|
||||||
}
|
|
||||||
|
|
||||||
let statusBarHeight = UIApplication.shared.statusBarFrame.height
|
|
||||||
let cornerRadius = self.contentViewControllerShadowView.layer.cornerRadius
|
|
||||||
|
|
||||||
let inset = 12 as CGFloat
|
|
||||||
let padding = 20 as CGFloat
|
|
||||||
|
|
||||||
let backButtonSize = self.backButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
|
||||||
var backButtonFrame = CGRect(x: inset, y: statusBarHeight,
|
|
||||||
width: backButtonSize.width + 20, height: backButtonSize.height + 20)
|
|
||||||
|
|
||||||
var headerFrame = CGRect(x: inset, y: 0, width: self.view.bounds.width - inset * 2, height: self.bannerView.bounds.height)
|
|
||||||
var contentFrame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)
|
|
||||||
var backgroundIconFrame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.width)
|
|
||||||
|
|
||||||
let minimumHeaderY = backButtonFrame.maxY + 8
|
|
||||||
|
|
||||||
let minimumContentY = minimumHeaderY + headerFrame.height + padding
|
|
||||||
let maximumContentY = self.view.bounds.width * 0.667
|
|
||||||
|
|
||||||
// A full blur is too much, so we reduce the visible blur by 0.3, resulting in 70% blur.
|
|
||||||
let minimumBlurFraction = 0.3 as CGFloat
|
|
||||||
|
|
||||||
contentFrame.origin.y = maximumContentY - self.scrollView.contentOffset.y
|
|
||||||
headerFrame.origin.y = contentFrame.origin.y - padding - headerFrame.height
|
|
||||||
|
|
||||||
// Stretch the app icon image to fill additional vertical space if necessary.
|
|
||||||
let height = max(contentFrame.origin.y + cornerRadius * 2, backgroundIconFrame.height)
|
|
||||||
backgroundIconFrame.size.height = height
|
|
||||||
|
|
||||||
let blurThreshold = 0 as CGFloat
|
|
||||||
if self.scrollView.contentOffset.y < blurThreshold
|
|
||||||
{
|
|
||||||
// Determine how much to lessen blur by.
|
|
||||||
|
|
||||||
let range = 75 as CGFloat
|
|
||||||
let difference = -self.scrollView.contentOffset.y
|
|
||||||
|
|
||||||
let fraction = min(difference, range) / range
|
|
||||||
|
|
||||||
let fractionComplete = (fraction * (1.0 - minimumBlurFraction)) + minimumBlurFraction
|
|
||||||
self.blurAnimator?.fractionComplete = fractionComplete
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Set blur to default.
|
|
||||||
|
|
||||||
self.blurAnimator?.fractionComplete = minimumBlurFraction
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animate navigation bar.
|
|
||||||
let showNavigationBarThreshold = (maximumContentY - minimumContentY) + backButtonFrame.origin.y
|
|
||||||
if self.scrollView.contentOffset.y > showNavigationBarThreshold
|
|
||||||
{
|
|
||||||
if self.navigationBarAnimator == nil
|
|
||||||
{
|
|
||||||
self.prepareNavigationBarAnimation()
|
|
||||||
}
|
|
||||||
|
|
||||||
let difference = self.scrollView.contentOffset.y - showNavigationBarThreshold
|
|
||||||
let range = (headerFrame.height + padding) - (self.navigationController?.navigationBar.bounds.height ?? self.view.safeAreaInsets.top)
|
|
||||||
|
|
||||||
let fractionComplete = min(difference, range) / range
|
|
||||||
self.navigationBarAnimator?.fractionComplete = fractionComplete
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.resetNavigationBarAnimation()
|
|
||||||
}
|
|
||||||
|
|
||||||
let beginMovingBackButtonThreshold = (maximumContentY - minimumContentY)
|
|
||||||
if self.scrollView.contentOffset.y > beginMovingBackButtonThreshold
|
|
||||||
{
|
|
||||||
let difference = self.scrollView.contentOffset.y - beginMovingBackButtonThreshold
|
|
||||||
backButtonFrame.origin.y -= difference
|
|
||||||
}
|
|
||||||
|
|
||||||
let pinContentToTopThreshold = maximumContentY
|
|
||||||
if self.scrollView.contentOffset.y > pinContentToTopThreshold
|
|
||||||
{
|
|
||||||
contentFrame.origin.y = 0
|
|
||||||
backgroundIconFrame.origin.y = 0
|
|
||||||
|
|
||||||
let difference = self.scrollView.contentOffset.y - pinContentToTopThreshold
|
|
||||||
self.contentViewController.tableView.contentOffset.y = difference
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Keep content table view's content offset at the top.
|
|
||||||
self.contentViewController.tableView.contentOffset.y = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep background app icon centered in gap between top of content and top of screen.
|
|
||||||
backgroundIconFrame.origin.y = (contentFrame.origin.y / 2) - backgroundIconFrame.height / 2
|
|
||||||
|
|
||||||
// Set frames.
|
|
||||||
self.contentViewController.view.superview?.frame = contentFrame
|
|
||||||
self.bannerView.frame = headerFrame
|
|
||||||
self.backgroundAppIconImageView.frame = backgroundIconFrame
|
|
||||||
self.backgroundBlurView.frame = backgroundIconFrame
|
|
||||||
self.backButtonContainerView.frame = backButtonFrame
|
|
||||||
|
|
||||||
self.contentViewControllerShadowView.frame = self.contentViewController.view.frame
|
|
||||||
|
|
||||||
self.backButtonContainerView.layer.cornerRadius = self.backButtonContainerView.bounds.midY
|
|
||||||
|
|
||||||
self.scrollView.scrollIndicatorInsets.top = statusBarHeight
|
|
||||||
|
|
||||||
// Adjust content offset + size.
|
|
||||||
let contentOffset = self.scrollView.contentOffset
|
|
||||||
|
|
||||||
var contentSize = self.contentViewController.tableView.contentSize
|
|
||||||
contentSize.height += maximumContentY
|
|
||||||
|
|
||||||
self.scrollView.contentSize = contentSize
|
|
||||||
self.scrollView.contentOffset = contentOffset
|
|
||||||
|
|
||||||
self.bannerView.backgroundEffectView.backgroundColor = .clear
|
|
||||||
}
|
|
||||||
|
|
||||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
|
|
||||||
{
|
|
||||||
super.traitCollectionDidChange(previousTraitCollection)
|
|
||||||
self._shouldResetLayout = true
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit
|
|
||||||
{
|
|
||||||
self.blurAnimator?.stopAnimation(true)
|
|
||||||
self.navigationBarAnimator?.stopAnimation(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AppViewController
|
|
||||||
{
|
|
||||||
final class func makeAppViewController(app: StoreApp) -> AppViewController
|
|
||||||
{
|
|
||||||
let storyboard = UIStoryboard(name: "Main", bundle: nil)
|
|
||||||
|
|
||||||
let appViewController = storyboard.instantiateViewController(withIdentifier: "appViewController") as! AppViewController
|
|
||||||
appViewController.app = app
|
|
||||||
return appViewController
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AppViewController
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
for button in [self.bannerView.button!, self.navigationBarDownloadButton!]
|
|
||||||
{
|
|
||||||
button.tintColor = self.app.tintColor
|
|
||||||
button.isIndicatingActivity = false
|
|
||||||
|
|
||||||
if self.app.installedApp == nil
|
|
||||||
{
|
|
||||||
button.setTitle(NSLocalizedString("FREE", comment: ""), for: .normal)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
button.setTitle(NSLocalizedString("OPEN", comment: ""), for: .normal)
|
|
||||||
}
|
|
||||||
|
|
||||||
let progress = AppManager.shared.installationProgress(for: self.app)
|
|
||||||
button.progress = progress
|
|
||||||
}
|
|
||||||
|
|
||||||
if let versionDate = self.app.latestVersion?.date, versionDate > Date()
|
|
||||||
{
|
|
||||||
self.bannerView.button.countdownDate = versionDate
|
|
||||||
self.navigationBarDownloadButton.countdownDate = versionDate
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.bannerView.button.countdownDate = nil
|
|
||||||
self.navigationBarDownloadButton.countdownDate = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
let barButtonItem = self.navigationItem.rightBarButtonItem
|
|
||||||
self.navigationItem.rightBarButtonItem = nil
|
|
||||||
self.navigationItem.rightBarButtonItem = barButtonItem
|
|
||||||
}
|
|
||||||
|
|
||||||
func showNavigationBar(for navigationController: UINavigationController? = nil)
|
|
||||||
{
|
|
||||||
let navigationController = navigationController ?? self.navigationController
|
|
||||||
navigationController?.navigationBar.alpha = 1.0
|
|
||||||
navigationController?.navigationBar.tintColor = .altPrimary
|
|
||||||
navigationController?.navigationBar.setNeedsLayout()
|
|
||||||
|
|
||||||
if self.traitCollection.userInterfaceStyle == .dark
|
|
||||||
{
|
|
||||||
self._preferredStatusBarStyle = .lightContent
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self._preferredStatusBarStyle = .default
|
|
||||||
}
|
|
||||||
|
|
||||||
navigationController?.setNeedsStatusBarAppearanceUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func hideNavigationBar(for navigationController: UINavigationController? = nil)
|
|
||||||
{
|
|
||||||
let navigationController = navigationController ?? self.navigationController
|
|
||||||
navigationController?.navigationBar.alpha = 0.0
|
|
||||||
|
|
||||||
self._preferredStatusBarStyle = .lightContent
|
|
||||||
navigationController?.setNeedsStatusBarAppearanceUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareBlur()
|
|
||||||
{
|
|
||||||
if let animator = self.blurAnimator
|
|
||||||
{
|
|
||||||
animator.stopAnimation(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.backgroundBlurView.effect = self._backgroundBlurEffect
|
|
||||||
self.backgroundBlurView.contentView.backgroundColor = self._backgroundBlurTintColor
|
|
||||||
|
|
||||||
self.blurAnimator = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { [weak self] in
|
|
||||||
self?.backgroundBlurView.effect = nil
|
|
||||||
self?.backgroundBlurView.contentView.backgroundColor = .clear
|
|
||||||
}
|
|
||||||
|
|
||||||
self.blurAnimator?.startAnimation()
|
|
||||||
self.blurAnimator?.pauseAnimation()
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepareNavigationBarAnimation()
|
|
||||||
{
|
|
||||||
self.resetNavigationBarAnimation()
|
|
||||||
|
|
||||||
self.navigationBarAnimator = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { [weak self] in
|
|
||||||
self?.showNavigationBar()
|
|
||||||
self?.navigationController?.navigationBar.tintColor = self?.app.tintColor
|
|
||||||
self?.navigationController?.navigationBar.barTintColor = nil
|
|
||||||
self?.contentViewController.view.layer.cornerRadius = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
self.navigationBarAnimator?.startAnimation()
|
|
||||||
self.navigationBarAnimator?.pauseAnimation()
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetNavigationBarAnimation()
|
|
||||||
{
|
|
||||||
self.navigationBarAnimator?.stopAnimation(true)
|
|
||||||
self.navigationBarAnimator = nil
|
|
||||||
|
|
||||||
self.hideNavigationBar()
|
|
||||||
|
|
||||||
self.contentViewController.view.layer.cornerRadius = self.contentViewControllerShadowView.layer.cornerRadius
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AppViewController
|
|
||||||
{
|
|
||||||
@IBAction func popViewController(_ sender: UIButton)
|
|
||||||
{
|
|
||||||
self.navigationController?.popViewController(animated: true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func performAppAction(_ sender: PillButton)
|
|
||||||
{
|
|
||||||
if let installedApp = self.app.installedApp
|
|
||||||
{
|
|
||||||
self.open(installedApp)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.downloadApp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadApp()
|
|
||||||
{
|
|
||||||
guard self.app.installedApp == nil else { return }
|
|
||||||
|
|
||||||
let group = AppManager.shared.install(self.app, presentingViewController: self) { (result) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
_ = try result.get()
|
|
||||||
}
|
|
||||||
catch OperationError.cancelled
|
|
||||||
{
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let toastView = ToastView(error: error)
|
|
||||||
toastView.show(in: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.bannerView.button.progress = nil
|
|
||||||
self.navigationBarDownloadButton.progress = nil
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.bannerView.button.progress = group.progress
|
|
||||||
self.navigationBarDownloadButton.progress = group.progress
|
|
||||||
}
|
|
||||||
|
|
||||||
func open(_ installedApp: InstalledApp)
|
|
||||||
{
|
|
||||||
UIApplication.shared.open(installedApp.openAppURL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AppViewController
|
|
||||||
{
|
|
||||||
@objc func didChangeApp(_ notification: Notification)
|
|
||||||
{
|
|
||||||
// Async so that AppManager.installationProgress(for:) is nil when we update.
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func willEnterForeground(_ notification: Notification)
|
|
||||||
{
|
|
||||||
guard let navigationController = self.navigationController, navigationController.topViewController == self else { return }
|
|
||||||
|
|
||||||
self._shouldResetLayout = true
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func didBecomeActive(_ notification: Notification)
|
|
||||||
{
|
|
||||||
guard let navigationController = self.navigationController, navigationController.topViewController == self else { return }
|
|
||||||
|
|
||||||
// Fixes Navigation Bar appearing after app becomes inactive -> active again.
|
|
||||||
self._shouldResetLayout = true
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AppViewController: UIScrollViewDelegate
|
|
||||||
{
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView)
|
|
||||||
{
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
self.view.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
//
|
|
||||||
// AuthenticationViewController.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 9/5/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import AltSign
|
|
||||||
|
|
||||||
final class AuthenticationViewController: UIViewController
|
|
||||||
{
|
|
||||||
var authenticationHandler: ((String, String, @escaping (Result<(ALTAccount, ALTAppleAPISession), Error>) -> Void) -> Void)?
|
|
||||||
var completionHandler: (((ALTAccount, ALTAppleAPISession, String)?) -> Void)?
|
|
||||||
|
|
||||||
private weak var toastView: ToastView?
|
|
||||||
|
|
||||||
@IBOutlet private var appleIDTextField: UITextField!
|
|
||||||
@IBOutlet private var passwordTextField: UITextField!
|
|
||||||
@IBOutlet private var signInButton: UIButton!
|
|
||||||
|
|
||||||
@IBOutlet private var appleIDBackgroundView: UIView!
|
|
||||||
@IBOutlet private var passwordBackgroundView: UIView!
|
|
||||||
|
|
||||||
@IBOutlet private var scrollView: UIScrollView!
|
|
||||||
@IBOutlet private var contentStackView: UIStackView!
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.signInButton.activityIndicatorView.style = .medium
|
|
||||||
|
|
||||||
for view in [self.appleIDBackgroundView!, self.passwordBackgroundView!, self.signInButton!]
|
|
||||||
{
|
|
||||||
view.clipsToBounds = true
|
|
||||||
view.layer.cornerRadius = 16
|
|
||||||
}
|
|
||||||
|
|
||||||
if UIScreen.main.isExtraCompactHeight
|
|
||||||
{
|
|
||||||
self.contentStackView.spacing = 20
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(AuthenticationViewController.textFieldDidChangeText(_:)), name: UITextField.textDidChangeNotification, object: self.appleIDTextField)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(AuthenticationViewController.textFieldDidChangeText(_:)), name: UITextField.textDidChangeNotification, object: self.passwordTextField)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidDisappear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewDidDisappear(animated)
|
|
||||||
|
|
||||||
self.signInButton.isIndicatingActivity = false
|
|
||||||
self.toastView?.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AuthenticationViewController
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
if let _ = self.validate()
|
|
||||||
{
|
|
||||||
self.signInButton.isEnabled = true
|
|
||||||
self.signInButton.alpha = 1.0
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.signInButton.isEnabled = false
|
|
||||||
self.signInButton.alpha = 0.6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func validate() -> (String, String)?
|
|
||||||
{
|
|
||||||
guard
|
|
||||||
let emailAddress = self.appleIDTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !emailAddress.isEmpty,
|
|
||||||
let password = self.passwordTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines), !password.isEmpty
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
return (emailAddress, password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AuthenticationViewController
|
|
||||||
{
|
|
||||||
@IBAction func authenticate()
|
|
||||||
{
|
|
||||||
guard let (emailAddress, password) = self.validate() else { return }
|
|
||||||
|
|
||||||
self.appleIDTextField.resignFirstResponder()
|
|
||||||
self.passwordTextField.resignFirstResponder()
|
|
||||||
|
|
||||||
self.signInButton.isIndicatingActivity = true
|
|
||||||
|
|
||||||
self.authenticationHandler?(emailAddress, password) { (result) in
|
|
||||||
switch result
|
|
||||||
{
|
|
||||||
case .failure(ALTAppleAPIError.requiresTwoFactorAuthentication):
|
|
||||||
// Ignore
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.signInButton.isIndicatingActivity = false
|
|
||||||
}
|
|
||||||
|
|
||||||
case .failure(let error as NSError):
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let error = error.withLocalizedFailure(NSLocalizedString("Failed to Log In", comment: ""))
|
|
||||||
|
|
||||||
let toastView = ToastView(error: error)
|
|
||||||
toastView.textLabel.textColor = .altPink
|
|
||||||
toastView.detailTextLabel.textColor = .altPink
|
|
||||||
toastView.show(in: self)
|
|
||||||
self.toastView = toastView
|
|
||||||
|
|
||||||
self.signInButton.isIndicatingActivity = false
|
|
||||||
}
|
|
||||||
|
|
||||||
case .success((let account, let session)):
|
|
||||||
self.completionHandler?((account, session, password))
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.scrollView.setContentOffset(CGPoint(x: 0, y: -self.view.safeAreaInsets.top), animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func cancel(_ sender: UIBarButtonItem)
|
|
||||||
{
|
|
||||||
self.completionHandler?(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AuthenticationViewController: UITextFieldDelegate
|
|
||||||
{
|
|
||||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool
|
|
||||||
{
|
|
||||||
switch textField
|
|
||||||
{
|
|
||||||
case self.appleIDTextField: self.passwordTextField.becomeFirstResponder()
|
|
||||||
case self.passwordTextField: self.authenticate()
|
|
||||||
default: break
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func textFieldDidBeginEditing(_ textField: UITextField)
|
|
||||||
{
|
|
||||||
guard UIScreen.main.isExtraCompactHeight else { return }
|
|
||||||
|
|
||||||
// Position all the controls within visible frame.
|
|
||||||
var contentOffset = self.scrollView.contentOffset
|
|
||||||
contentOffset.y = 44
|
|
||||||
self.scrollView.setContentOffset(contentOffset, animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AuthenticationViewController
|
|
||||||
{
|
|
||||||
@objc func textFieldDidChangeText(_ notification: Notification)
|
|
||||||
{
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// InstructionsViewController.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 9/6/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class InstructionsViewController: UIViewController
|
|
||||||
{
|
|
||||||
var completionHandler: (() -> Void)?
|
|
||||||
|
|
||||||
var showsBottomButton: Bool = false
|
|
||||||
|
|
||||||
@IBOutlet private var contentStackView: UIStackView!
|
|
||||||
@IBOutlet private var dismissButton: UIButton!
|
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
||||||
return .lightContent
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
if UIScreen.main.isExtraCompactHeight
|
|
||||||
{
|
|
||||||
self.contentStackView.layoutMargins.top = 0
|
|
||||||
self.contentStackView.layoutMargins.bottom = self.contentStackView.layoutMargins.left
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dismissButton.clipsToBounds = true
|
|
||||||
self.dismissButton.layer.cornerRadius = 16
|
|
||||||
|
|
||||||
if self.showsBottomButton
|
|
||||||
{
|
|
||||||
self.navigationItem.hidesBackButton = true
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.dismissButton.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension InstructionsViewController
|
|
||||||
{
|
|
||||||
@IBAction func dismiss()
|
|
||||||
{
|
|
||||||
self.completionHandler?()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
//
|
|
||||||
// ScreenshotCollectionViewCell.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 7/15/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import RoxasUI
|
|
||||||
|
|
||||||
@objc(ScreenshotCollectionViewCell)
|
|
||||||
class ScreenshotCollectionViewCell: UICollectionViewCell
|
|
||||||
{
|
|
||||||
let imageView = UIImageView(image: nil)
|
|
||||||
|
|
||||||
override init(frame: CGRect)
|
|
||||||
{
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
self.initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
|
||||||
{
|
|
||||||
super.init(coder: aDecoder)
|
|
||||||
|
|
||||||
self.initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func initialize()
|
|
||||||
{
|
|
||||||
self.imageView.layer.masksToBounds = true
|
|
||||||
self.addSubview(self.imageView, pinningEdgesWith: .zero)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutSubviews()
|
|
||||||
{
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
self.imageView.layer.cornerRadius = 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
//
|
|
||||||
// AppBannerView.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/29/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import AltStoreCore
|
|
||||||
import RoxasUI
|
|
||||||
|
|
||||||
class AppBannerView: RSTNibView
|
|
||||||
{
|
|
||||||
override var accessibilityLabel: String? {
|
|
||||||
get { return self.accessibilityView?.accessibilityLabel }
|
|
||||||
set { self.accessibilityView?.accessibilityLabel = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
override open var accessibilityAttributedLabel: NSAttributedString? {
|
|
||||||
get { return self.accessibilityView?.accessibilityAttributedLabel }
|
|
||||||
set { self.accessibilityView?.accessibilityAttributedLabel = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
override var accessibilityValue: String? {
|
|
||||||
get { return self.accessibilityView?.accessibilityValue }
|
|
||||||
set { self.accessibilityView?.accessibilityValue = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
override open var accessibilityAttributedValue: NSAttributedString? {
|
|
||||||
get { return self.accessibilityView?.accessibilityAttributedValue }
|
|
||||||
set { self.accessibilityView?.accessibilityAttributedValue = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
override open var accessibilityTraits: UIAccessibilityTraits {
|
|
||||||
get { return self.accessibilityView?.accessibilityTraits ?? [] }
|
|
||||||
set { self.accessibilityView?.accessibilityTraits = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
private var originalTintColor: UIColor?
|
|
||||||
|
|
||||||
@IBOutlet var titleLabel: UILabel!
|
|
||||||
@IBOutlet var subtitleLabel: UILabel!
|
|
||||||
@IBOutlet var iconImageView: AppIconImageView!
|
|
||||||
@IBOutlet var button: PillButton!
|
|
||||||
@IBOutlet var buttonLabel: UILabel!
|
|
||||||
@IBOutlet var betaBadgeView: UIView!
|
|
||||||
|
|
||||||
@IBOutlet var backgroundEffectView: UIVisualEffectView!
|
|
||||||
|
|
||||||
@IBOutlet private var vibrancyView: UIVisualEffectView!
|
|
||||||
@IBOutlet private var accessibilityView: UIView!
|
|
||||||
|
|
||||||
override init(frame: CGRect)
|
|
||||||
{
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
self.initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder)
|
|
||||||
{
|
|
||||||
super.init(coder: coder)
|
|
||||||
|
|
||||||
self.initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func initialize()
|
|
||||||
{
|
|
||||||
self.accessibilityView.accessibilityTraits.formUnion(.button)
|
|
||||||
|
|
||||||
self.isAccessibilityElement = false
|
|
||||||
self.accessibilityElements = [self.accessibilityView, self.button].compactMap { $0 }
|
|
||||||
|
|
||||||
self.betaBadgeView.isHidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tintColorDidChange()
|
|
||||||
{
|
|
||||||
super.tintColorDidChange()
|
|
||||||
|
|
||||||
if self.tintAdjustmentMode != .dimmed
|
|
||||||
{
|
|
||||||
self.originalTintColor = self.tintColor
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AppBannerView
|
|
||||||
{
|
|
||||||
func configure(for app: AppProtocol)
|
|
||||||
{
|
|
||||||
struct AppValues
|
|
||||||
{
|
|
||||||
var name: String
|
|
||||||
var developerName: String? = nil
|
|
||||||
var isBeta: Bool = false
|
|
||||||
|
|
||||||
init(app: AppProtocol)
|
|
||||||
{
|
|
||||||
self.name = app.name
|
|
||||||
|
|
||||||
guard let storeApp = (app as? StoreApp) ?? (app as? InstalledApp)?.storeApp else { return }
|
|
||||||
self.developerName = storeApp.developerName
|
|
||||||
|
|
||||||
if storeApp.isBeta
|
|
||||||
{
|
|
||||||
self.name = String(format: NSLocalizedString("%@ beta", comment: ""), app.name)
|
|
||||||
self.isBeta = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let values = AppValues(app: app)
|
|
||||||
self.titleLabel.text = app.name // Don't use values.name since that already includes "beta".
|
|
||||||
self.betaBadgeView.isHidden = !values.isBeta
|
|
||||||
|
|
||||||
if let developerName = values.developerName
|
|
||||||
{
|
|
||||||
self.subtitleLabel.text = developerName
|
|
||||||
self.accessibilityLabel = String(format: NSLocalizedString("%@ by %@", comment: ""), values.name, developerName)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.subtitleLabel.text = NSLocalizedString("Sideloaded", comment: "")
|
|
||||||
self.accessibilityLabel = values.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension AppBannerView
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
self.clipsToBounds = true
|
|
||||||
self.layer.cornerRadius = 22
|
|
||||||
|
|
||||||
self.subtitleLabel.textColor = self.originalTintColor ?? self.tintColor
|
|
||||||
self.backgroundEffectView.backgroundColor = self.originalTintColor ?? self.tintColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
//
|
|
||||||
// AppIconImageView.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 5/9/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class AppIconImageView: UIImageView
|
|
||||||
{
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.contentMode = .scaleAspectFill
|
|
||||||
self.clipsToBounds = true
|
|
||||||
|
|
||||||
self.backgroundColor = .white
|
|
||||||
|
|
||||||
if #available(iOS 13, *)
|
|
||||||
{
|
|
||||||
self.layer.cornerCurve = .continuous
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if self.layer.responds(to: Selector(("continuousCorners")))
|
|
||||||
{
|
|
||||||
self.layer.setValue(true, forKey: "continuousCorners")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutSubviews()
|
|
||||||
{
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
// Based off of 60pt icon having 12pt radius.
|
|
||||||
let radius = self.bounds.height / 5
|
|
||||||
self.layer.cornerRadius = radius
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
//
|
|
||||||
// CollapsingTextView.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 7/23/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class CollapsingTextView: UITextView
|
|
||||||
{
|
|
||||||
var isCollapsed = true {
|
|
||||||
didSet {
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var maximumNumberOfLines = 2 {
|
|
||||||
didSet {
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lineSpacing: CGFloat = 2 {
|
|
||||||
didSet {
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let moreButton = UIButton(type: .system)
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.layoutManager.delegate = self
|
|
||||||
|
|
||||||
self.textContainerInset = .zero
|
|
||||||
self.textContainer.lineFragmentPadding = 0
|
|
||||||
self.textContainer.lineBreakMode = .byTruncatingTail
|
|
||||||
self.textContainer.heightTracksTextView = true
|
|
||||||
self.textContainer.widthTracksTextView = true
|
|
||||||
|
|
||||||
self.moreButton.setTitle(NSLocalizedString("More", comment: ""), for: .normal)
|
|
||||||
self.moreButton.addTarget(self, action: #selector(CollapsingTextView.toggleCollapsed(_:)), for: .primaryActionTriggered)
|
|
||||||
self.addSubview(self.moreButton)
|
|
||||||
|
|
||||||
self.setNeedsLayout()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutSubviews()
|
|
||||||
{
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
guard let font = self.font else { return }
|
|
||||||
|
|
||||||
let buttonFont = UIFont.systemFont(ofSize: font.pointSize, weight: .medium)
|
|
||||||
self.moreButton.titleLabel?.font = buttonFont
|
|
||||||
|
|
||||||
let buttonY = (font.lineHeight + self.lineSpacing) * CGFloat(self.maximumNumberOfLines - 1)
|
|
||||||
let size = self.moreButton.sizeThatFits(CGSize(width: 1000, height: 1000))
|
|
||||||
|
|
||||||
let moreButtonFrame = CGRect(x: self.bounds.width - self.moreButton.bounds.width,
|
|
||||||
y: buttonY,
|
|
||||||
width: size.width,
|
|
||||||
height: font.lineHeight)
|
|
||||||
self.moreButton.frame = moreButtonFrame
|
|
||||||
|
|
||||||
if self.isCollapsed
|
|
||||||
{
|
|
||||||
self.textContainer.maximumNumberOfLines = self.maximumNumberOfLines
|
|
||||||
|
|
||||||
let boundingSize = self.attributedText.boundingRect(with: CGSize(width: self.textContainer.size.width, height: .infinity), options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil)
|
|
||||||
let maximumCollapsedHeight = font.lineHeight * Double(self.maximumNumberOfLines)
|
|
||||||
|
|
||||||
if boundingSize.height.rounded() > maximumCollapsedHeight.rounded()
|
|
||||||
{
|
|
||||||
var exclusionFrame = moreButtonFrame
|
|
||||||
exclusionFrame.origin.y += self.moreButton.bounds.midY
|
|
||||||
exclusionFrame.size.width = self.bounds.width // Extra wide to make sure it wraps to next line.
|
|
||||||
self.textContainer.exclusionPaths = [UIBezierPath(rect: exclusionFrame)]
|
|
||||||
|
|
||||||
self.moreButton.isHidden = false
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.textContainer.exclusionPaths = []
|
|
||||||
|
|
||||||
self.moreButton.isHidden = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.textContainer.maximumNumberOfLines = 0
|
|
||||||
self.textContainer.exclusionPaths = []
|
|
||||||
|
|
||||||
self.moreButton.isHidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
self.invalidateIntrinsicContentSize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension CollapsingTextView
|
|
||||||
{
|
|
||||||
@objc func toggleCollapsed(_ sender: UIButton)
|
|
||||||
{
|
|
||||||
self.isCollapsed.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CollapsingTextView: NSLayoutManagerDelegate
|
|
||||||
{
|
|
||||||
func layoutManager(_ layoutManager: NSLayoutManager, lineSpacingAfterGlyphAt glyphIndex: Int, withProposedLineFragmentRect rect: CGRect) -> CGFloat
|
|
||||||
{
|
|
||||||
return self.lineSpacing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
//
|
|
||||||
// PillButton.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 7/15/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class PillButton: UIButton
|
|
||||||
{
|
|
||||||
override var accessibilityValue: String? {
|
|
||||||
get {
|
|
||||||
guard self.progress != nil else { return super.accessibilityValue }
|
|
||||||
return self.progressView.accessibilityValue
|
|
||||||
}
|
|
||||||
set { super.accessibilityValue = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
var progress: Progress? {
|
|
||||||
didSet {
|
|
||||||
self.progressView.progress = Float(self.progress?.fractionCompleted ?? 0)
|
|
||||||
self.progressView.observedProgress = self.progress
|
|
||||||
|
|
||||||
let isUserInteractionEnabled = self.isUserInteractionEnabled
|
|
||||||
self.isIndicatingActivity = (self.progress != nil)
|
|
||||||
self.isUserInteractionEnabled = isUserInteractionEnabled
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var progressTintColor: UIColor? {
|
|
||||||
get {
|
|
||||||
return self.progressView.progressTintColor
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
self.progressView.progressTintColor = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var countdownDate: Date? {
|
|
||||||
didSet {
|
|
||||||
self.isEnabled = (self.countdownDate == nil)
|
|
||||||
self.displayLink.isPaused = (self.countdownDate == nil)
|
|
||||||
|
|
||||||
if self.countdownDate == nil
|
|
||||||
{
|
|
||||||
self.setTitle(nil, for: .disabled)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private let progressView = UIProgressView(progressViewStyle: .default)
|
|
||||||
|
|
||||||
private lazy var displayLink: CADisplayLink = {
|
|
||||||
let displayLink = CADisplayLink(target: self, selector: #selector(PillButton.updateCountdown))
|
|
||||||
displayLink.preferredFramesPerSecond = 15
|
|
||||||
displayLink.isPaused = true
|
|
||||||
displayLink.add(to: .main, forMode: .common)
|
|
||||||
return displayLink
|
|
||||||
}()
|
|
||||||
|
|
||||||
private let dateComponentsFormatter: DateComponentsFormatter = {
|
|
||||||
let dateComponentsFormatter = DateComponentsFormatter()
|
|
||||||
dateComponentsFormatter.zeroFormattingBehavior = [.pad]
|
|
||||||
dateComponentsFormatter.collapsesLargestUnit = false
|
|
||||||
return dateComponentsFormatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
override var intrinsicContentSize: CGSize {
|
|
||||||
var size = super.intrinsicContentSize
|
|
||||||
size.width += 26
|
|
||||||
size.height += 3
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit
|
|
||||||
{
|
|
||||||
self.displayLink.remove(from: .main, forMode: RunLoop.Mode.default)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.layer.masksToBounds = true
|
|
||||||
self.accessibilityTraits.formUnion([.updatesFrequently, .button])
|
|
||||||
|
|
||||||
self.activityIndicatorView.style = .medium
|
|
||||||
self.activityIndicatorView.isUserInteractionEnabled = false
|
|
||||||
|
|
||||||
self.progressView.progress = 0
|
|
||||||
self.progressView.trackImage = UIImage()
|
|
||||||
self.progressView.isUserInteractionEnabled = false
|
|
||||||
self.addSubview(self.progressView)
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutSubviews()
|
|
||||||
{
|
|
||||||
super.layoutSubviews()
|
|
||||||
|
|
||||||
self.progressView.bounds.size.width = self.bounds.width
|
|
||||||
|
|
||||||
let scale = self.bounds.height / self.progressView.bounds.height
|
|
||||||
|
|
||||||
self.progressView.transform = CGAffineTransform.identity.scaledBy(x: 1, y: scale)
|
|
||||||
self.progressView.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
|
|
||||||
|
|
||||||
self.layer.cornerRadius = self.bounds.midY
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tintColorDidChange()
|
|
||||||
{
|
|
||||||
super.tintColorDidChange()
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension PillButton
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
if self.progress == nil
|
|
||||||
{
|
|
||||||
self.setTitleColor(.white, for: .normal)
|
|
||||||
self.backgroundColor = self.tintColor
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.setTitleColor(self.tintColor, for: .normal)
|
|
||||||
self.backgroundColor = self.tintColor.withAlphaComponent(0.15)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.progressView.progressTintColor = self.tintColor
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func updateCountdown()
|
|
||||||
{
|
|
||||||
guard let endDate = self.countdownDate else { return }
|
|
||||||
|
|
||||||
let startDate = Date()
|
|
||||||
|
|
||||||
let interval = endDate.timeIntervalSince(startDate)
|
|
||||||
guard interval > 0 else {
|
|
||||||
self.isEnabled = true
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let text: String?
|
|
||||||
|
|
||||||
if interval < (1 * 60 * 60)
|
|
||||||
{
|
|
||||||
self.dateComponentsFormatter.unitsStyle = .positional
|
|
||||||
self.dateComponentsFormatter.allowedUnits = [.minute, .second]
|
|
||||||
|
|
||||||
text = self.dateComponentsFormatter.string(from: startDate, to: endDate)
|
|
||||||
}
|
|
||||||
else if interval < (2 * 24 * 60 * 60)
|
|
||||||
{
|
|
||||||
self.dateComponentsFormatter.unitsStyle = .positional
|
|
||||||
self.dateComponentsFormatter.allowedUnits = [.hour, .minute, .second]
|
|
||||||
|
|
||||||
text = self.dateComponentsFormatter.string(from: startDate, to: endDate)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.dateComponentsFormatter.unitsStyle = .full
|
|
||||||
self.dateComponentsFormatter.allowedUnits = [.day]
|
|
||||||
|
|
||||||
let numberOfDays = endDate.numberOfCalendarDays(since: startDate)
|
|
||||||
text = String(format: NSLocalizedString("%@ DAYS", comment: ""), NSNumber(value: numberOfDays))
|
|
||||||
}
|
|
||||||
|
|
||||||
if let text = text
|
|
||||||
{
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
self.isEnabled = false
|
|
||||||
self.setTitle(text, for: .disabled)
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.isEnabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
//
|
|
||||||
// InstalledAppsCollectionHeaderView.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 3/9/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class InstalledAppsCollectionHeaderView: UICollectionReusableView
|
|
||||||
{
|
|
||||||
let textLabel: UILabel
|
|
||||||
let button: UIButton
|
|
||||||
|
|
||||||
override init(frame: CGRect)
|
|
||||||
{
|
|
||||||
self.textLabel = UILabel()
|
|
||||||
self.textLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
self.textLabel.font = UIFont.systemFont(ofSize: 24, weight: .bold)
|
|
||||||
self.textLabel.accessibilityTraits.insert(.header)
|
|
||||||
|
|
||||||
self.button = UIButton(type: .system)
|
|
||||||
self.button.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
self.button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
|
|
||||||
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
self.addSubview(self.textLabel)
|
|
||||||
self.addSubview(self.button)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.textLabel.leadingAnchor.constraint(equalTo: self.layoutMarginsGuide.leadingAnchor),
|
|
||||||
self.textLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor)])
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.button.trailingAnchor.constraint(equalTo: self.layoutMarginsGuide.trailingAnchor),
|
|
||||||
self.button.firstBaselineAnchor.constraint(equalTo: self.textLabel.firstBaselineAnchor)])
|
|
||||||
|
|
||||||
self.preservesSuperviewLayoutMargins = true
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
//
|
|
||||||
// UpdateCollectionViewCell.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 7/16/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UpdateCollectionViewCell
|
|
||||||
{
|
|
||||||
enum Mode
|
|
||||||
{
|
|
||||||
case collapsed
|
|
||||||
case expanded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc final class UpdateCollectionViewCell: UICollectionViewCell
|
|
||||||
{
|
|
||||||
var mode: Mode = .expanded {
|
|
||||||
didSet {
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBOutlet var bannerView: AppBannerView!
|
|
||||||
@IBOutlet var versionDescriptionTitleLabel: UILabel!
|
|
||||||
@IBOutlet var versionDescriptionTextView: CollapsingTextView!
|
|
||||||
|
|
||||||
@IBOutlet private var blurView: UIVisualEffectView!
|
|
||||||
|
|
||||||
private var originalTintColor: UIColor?
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
// Prevent temporary unsatisfiable constraint errors due to UIView-Encapsulated-Layout constraints.
|
|
||||||
self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
||||||
self.contentView.preservesSuperviewLayoutMargins = true
|
|
||||||
|
|
||||||
self.bannerView.backgroundEffectView.isHidden = true
|
|
||||||
self.bannerView.button.setTitle(NSLocalizedString("UPDATE", comment: ""), for: .normal)
|
|
||||||
|
|
||||||
self.blurView.layer.cornerRadius = 20
|
|
||||||
self.blurView.layer.masksToBounds = true
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tintColorDidChange()
|
|
||||||
{
|
|
||||||
super.tintColorDidChange()
|
|
||||||
|
|
||||||
if self.tintAdjustmentMode != .dimmed
|
|
||||||
{
|
|
||||||
self.originalTintColor = self.tintColor
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func apply(_ layoutAttributes: UICollectionViewLayoutAttributes)
|
|
||||||
{
|
|
||||||
// Animates transition to new attributes.
|
|
||||||
let animator = UIViewPropertyAnimator(springTimingParameters: UISpringTimingParameters()) {
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
animator.startAnimation()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
|
|
||||||
{
|
|
||||||
let view = super.hitTest(point, with: event)
|
|
||||||
|
|
||||||
if view == self.versionDescriptionTextView
|
|
||||||
{
|
|
||||||
// Forward touches on the text view (but not on the nested "more" button)
|
|
||||||
// so cell selection works as expected.
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return view
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension UpdateCollectionViewCell
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
switch self.mode
|
|
||||||
{
|
|
||||||
case .collapsed: self.versionDescriptionTextView.isCollapsed = true
|
|
||||||
case .expanded: self.versionDescriptionTextView.isCollapsed = false
|
|
||||||
}
|
|
||||||
|
|
||||||
self.versionDescriptionTitleLabel.textColor = self.originalTintColor ?? self.tintColor
|
|
||||||
self.blurView.backgroundColor = self.originalTintColor ?? self.tintColor
|
|
||||||
self.bannerView.button.progressTintColor = self.originalTintColor ?? self.tintColor
|
|
||||||
|
|
||||||
self.setNeedsLayout()
|
|
||||||
self.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
//
|
|
||||||
// NewsCollectionViewCell.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/29/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class NewsCollectionViewCell: UICollectionViewCell
|
|
||||||
{
|
|
||||||
@IBOutlet var titleLabel: UILabel!
|
|
||||||
@IBOutlet var captionLabel: UILabel!
|
|
||||||
@IBOutlet var imageView: UIImageView!
|
|
||||||
@IBOutlet var contentBackgroundView: UIView!
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.contentView.preservesSuperviewLayoutMargins = true
|
|
||||||
|
|
||||||
self.contentBackgroundView.layer.cornerRadius = 30
|
|
||||||
self.contentBackgroundView.clipsToBounds = true
|
|
||||||
|
|
||||||
self.imageView.layer.cornerRadius = 30
|
|
||||||
self.imageView.clipsToBounds = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
//
|
|
||||||
// Operation.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 6/7/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import RoxasUI
|
|
||||||
|
|
||||||
class ResultOperation<ResultType>: Operation
|
|
||||||
{
|
|
||||||
var resultHandler: ((Result<ResultType, Error>) -> Void)?
|
|
||||||
|
|
||||||
@available(*, unavailable)
|
|
||||||
override func finish()
|
|
||||||
{
|
|
||||||
super.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
func finish(_ result: Result<ResultType, Error>)
|
|
||||||
{
|
|
||||||
guard !self.isFinished else { return }
|
|
||||||
|
|
||||||
if self.isCancelled
|
|
||||||
{
|
|
||||||
self.resultHandler?(.failure(OperationError.cancelled))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.resultHandler?(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
super.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Operation: RSTOperation, ProgressReporting
|
|
||||||
{
|
|
||||||
let progress = Progress.discreteProgress(totalUnitCount: 1)
|
|
||||||
|
|
||||||
private var backgroundTaskID: UIBackgroundTaskIdentifier?
|
|
||||||
|
|
||||||
override var isAsynchronous: Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override init()
|
|
||||||
{
|
|
||||||
super.init()
|
|
||||||
|
|
||||||
self.progress.cancellationHandler = { [weak self] in self?.cancel() }
|
|
||||||
}
|
|
||||||
|
|
||||||
override func cancel()
|
|
||||||
{
|
|
||||||
super.cancel()
|
|
||||||
|
|
||||||
if !self.progress.isCancelled
|
|
||||||
{
|
|
||||||
self.progress.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func main()
|
|
||||||
{
|
|
||||||
super.main()
|
|
||||||
|
|
||||||
let name = "com.altstore." + NSStringFromClass(type(of: self))
|
|
||||||
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask(withName: name) { [weak self] in
|
|
||||||
guard let backgroundTask = self?.backgroundTaskID else { return }
|
|
||||||
|
|
||||||
self?.cancel()
|
|
||||||
|
|
||||||
UIApplication.shared.endBackgroundTask(backgroundTask)
|
|
||||||
self?.backgroundTaskID = .invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func finish()
|
|
||||||
{
|
|
||||||
guard !self.isFinished else { return }
|
|
||||||
|
|
||||||
super.finish()
|
|
||||||
|
|
||||||
if let backgroundTaskID = self.backgroundTaskID
|
|
||||||
{
|
|
||||||
UIApplication.shared.endBackgroundTask(backgroundTaskID)
|
|
||||||
self.backgroundTaskID = .invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,499 +0,0 @@
|
|||||||
//
|
|
||||||
// PatchViewController.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 10/20/21.
|
|
||||||
// Copyright © 2021 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
import AltStoreCore
|
|
||||||
import AltSign
|
|
||||||
import RoxasUI
|
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
|
||||||
extension PatchViewController
|
|
||||||
{
|
|
||||||
enum Step
|
|
||||||
{
|
|
||||||
case confirm
|
|
||||||
case install
|
|
||||||
case openApp
|
|
||||||
case patchApp
|
|
||||||
case reboot
|
|
||||||
case refresh
|
|
||||||
case finish
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
|
||||||
final class PatchViewController: UIViewController
|
|
||||||
{
|
|
||||||
var patchApp: AnyApp?
|
|
||||||
var installedApp: InstalledApp?
|
|
||||||
|
|
||||||
var completionHandler: ((Result<Void, Error>) -> Void)?
|
|
||||||
|
|
||||||
private let context = AuthenticatedOperationContext()
|
|
||||||
|
|
||||||
private var currentStep: Step = .confirm {
|
|
||||||
didSet {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var buttonHandler: (() -> Void)?
|
|
||||||
private var resignedApp: ALTApplication?
|
|
||||||
|
|
||||||
private lazy var temporaryDirectory: URL = FileManager.default.uniqueTemporaryURL()
|
|
||||||
|
|
||||||
private var didEnterBackgroundObservation: NSObjectProtocol?
|
|
||||||
private weak var cancellableProgress: Progress?
|
|
||||||
|
|
||||||
@IBOutlet private var placeholderView: RSTPlaceholderView!
|
|
||||||
@IBOutlet private var taskDescriptionLabel: UILabel!
|
|
||||||
@IBOutlet private var pillButton: PillButton!
|
|
||||||
@IBOutlet private var cancelBarButtonItem: UIBarButtonItem!
|
|
||||||
@IBOutlet private var cancelButton: UIButton!
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.isModalInPresentation = true
|
|
||||||
|
|
||||||
self.placeholderView.stackView.spacing = 20
|
|
||||||
self.placeholderView.textLabel.textColor = .white
|
|
||||||
|
|
||||||
self.placeholderView.detailTextLabel.textAlignment = .left
|
|
||||||
self.placeholderView.detailTextLabel.textColor = UIColor.white.withAlphaComponent(0.6)
|
|
||||||
|
|
||||||
self.buttonHandler = { [weak self] in
|
|
||||||
self?.startProcess()
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try FileManager.default.createDirectory(at: self.temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
print("Failed to create temporary directory:", error)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
if self.installedApp != nil
|
|
||||||
{
|
|
||||||
self.refreshApp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
|
||||||
private extension PatchViewController
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
self.cancelButton.alpha = 0.0
|
|
||||||
|
|
||||||
switch self.currentStep
|
|
||||||
{
|
|
||||||
case .confirm:
|
|
||||||
guard let app = self.patchApp else { break }
|
|
||||||
|
|
||||||
if UIDevice.current.isUntetheredJailbreakRequired
|
|
||||||
{
|
|
||||||
self.placeholderView.textLabel.text = NSLocalizedString("Jailbreak Requires Untethering", comment: "")
|
|
||||||
self.placeholderView.detailTextLabel.text = String(format: NSLocalizedString("This jailbreak is untethered, which means %@ will never expire — even after 7 days or rebooting the device.\n\nInstalling an untethered jailbreak requires a few extra steps, but SideStore will walk you through the process.\n\nWould you like to continue? ", comment: ""), app.name)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.placeholderView.textLabel.text = NSLocalizedString("Jailbreak Supports Untethering", comment: "")
|
|
||||||
self.placeholderView.detailTextLabel.text = String(format: NSLocalizedString("This jailbreak has an untethered version, which means %@ will never expire — even after 7 days or rebooting the device.\n\nInstalling an untethered jailbreak requires a few extra steps, but SideStore will walk you through the process.\n\nWould you like to continue? ", comment: ""), app.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.pillButton.setTitle(NSLocalizedString("Install Untethered Jailbreak", comment: ""), for: .normal)
|
|
||||||
|
|
||||||
self.cancelButton.alpha = 1.0
|
|
||||||
|
|
||||||
case .install:
|
|
||||||
guard let app = self.patchApp else { break }
|
|
||||||
|
|
||||||
self.placeholderView.textLabel.text = String(format: NSLocalizedString("Installing %@ placeholder…", comment: ""), app.name)
|
|
||||||
self.placeholderView.detailTextLabel.text = NSLocalizedString("A placeholder app needs to be installed in order to prepare your device for untethering.\n\nThis may take a few moments.", comment: "")
|
|
||||||
|
|
||||||
case .openApp:
|
|
||||||
self.placeholderView.textLabel.text = NSLocalizedString("Continue in App", comment: "")
|
|
||||||
self.placeholderView.detailTextLabel.text = NSLocalizedString("Please open the placeholder app and follow the instructions to continue jailbreaking your device.", comment: "")
|
|
||||||
|
|
||||||
self.pillButton.setTitle(NSLocalizedString("Open Placeholder", comment: ""), for: .normal)
|
|
||||||
|
|
||||||
case .patchApp:
|
|
||||||
guard let app = self.patchApp else { break }
|
|
||||||
|
|
||||||
self.placeholderView.textLabel.text = String(format: NSLocalizedString("Patching %@ placeholder…", comment: ""), app.name)
|
|
||||||
self.placeholderView.detailTextLabel.text = NSLocalizedString("This will take a few moments. Please do not turn off the screen or leave the app until patching is complete.", comment: "")
|
|
||||||
|
|
||||||
self.pillButton.setTitle(NSLocalizedString("Patch Placeholder", comment: ""), for: .normal)
|
|
||||||
|
|
||||||
case .reboot:
|
|
||||||
self.placeholderView.textLabel.text = NSLocalizedString("Continue in App", comment: "")
|
|
||||||
self.placeholderView.detailTextLabel.text = NSLocalizedString("Please open the placeholder app and follow the instructions to continue jailbreaking your device.", comment: "")
|
|
||||||
|
|
||||||
self.pillButton.setTitle(NSLocalizedString("Open Placeholder", comment: ""), for: .normal)
|
|
||||||
|
|
||||||
case .refresh:
|
|
||||||
guard let installedApp = self.installedApp else { break }
|
|
||||||
|
|
||||||
self.placeholderView.textLabel.text = String(format: NSLocalizedString("Finish installing %@?", comment: ""), installedApp.name)
|
|
||||||
self.placeholderView.detailTextLabel.text = String(format: NSLocalizedString("In order to finish jailbreaking this device, you need to install %@ then follow the instructions in the app.", comment: ""), installedApp.name)
|
|
||||||
|
|
||||||
self.pillButton.setTitle(String(format: NSLocalizedString("Install %@", comment: ""), installedApp.name), for: .normal)
|
|
||||||
|
|
||||||
case .finish:
|
|
||||||
guard let installedApp = self.installedApp else { break }
|
|
||||||
|
|
||||||
self.placeholderView.textLabel.text = String(format: NSLocalizedString("Finish in %@", comment: ""), installedApp.name)
|
|
||||||
self.placeholderView.detailTextLabel.text = String(format: NSLocalizedString("Follow the instructions in %@ to finish jailbreaking this device.", comment: ""), installedApp.name)
|
|
||||||
|
|
||||||
self.pillButton.setTitle(String(format: NSLocalizedString("Open %@", comment: ""), installedApp.name), for: .normal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func present(_ error: Error, title: String)
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
let nsError = error as NSError
|
|
||||||
|
|
||||||
let alertController = UIAlertController(title: nsError.localizedFailure ?? title, message: error.localizedDescription, preferredStyle: .alert)
|
|
||||||
alertController.addAction(.ok)
|
|
||||||
self.present(alertController, animated: true, completion: nil)
|
|
||||||
|
|
||||||
self.setProgress(nil, description: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setProgress(_ progress: Progress?, description: String?)
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.pillButton.progress = progress
|
|
||||||
self.taskDescriptionLabel.text = description ?? " " // Use non-empty string to prevent label resizing itself.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func finish(with result: Result<Void, Error>)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try FileManager.default.removeItem(at: self.temporaryDirectory)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
print("Failed to remove temporary directory:", error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let observation = self.didEnterBackgroundObservation
|
|
||||||
{
|
|
||||||
NotificationCenter.default.removeObserver(observation)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.completionHandler?(result)
|
|
||||||
self.completionHandler = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
|
||||||
private extension PatchViewController
|
|
||||||
{
|
|
||||||
@IBAction func performButtonAction()
|
|
||||||
{
|
|
||||||
self.buttonHandler?()
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func cancel()
|
|
||||||
{
|
|
||||||
self.finish(with: .success(()))
|
|
||||||
|
|
||||||
self.cancellableProgress?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func installRegularJailbreak()
|
|
||||||
{
|
|
||||||
guard let app = self.patchApp else { return }
|
|
||||||
|
|
||||||
let title: String
|
|
||||||
let message: String
|
|
||||||
|
|
||||||
if UIDevice.current.isUntetheredJailbreakRequired
|
|
||||||
{
|
|
||||||
title = NSLocalizedString("Untethering Required", comment: "")
|
|
||||||
message = String(format: NSLocalizedString("%@ can not jailbreak this device unless you untether it first. Are you sure you want to install without untethering?", comment: ""), app.name)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
title = NSLocalizedString("Untethering Recommended", comment: "")
|
|
||||||
message = String(format: NSLocalizedString("Untethering this jailbreak will prevent %@ from expiring, even after 7 days or rebooting the device. Are you sure you want to install without untethering?", comment: ""), app.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
|
||||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Install Without Untethering", comment: ""), style: .default) { _ in
|
|
||||||
self.finish(with: .failure(OperationError.cancelled))
|
|
||||||
})
|
|
||||||
alertController.addAction(.cancel)
|
|
||||||
self.present(alertController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
|
||||||
private extension PatchViewController
|
|
||||||
{
|
|
||||||
func startProcess()
|
|
||||||
{
|
|
||||||
guard let patchApp = self.patchApp else { return }
|
|
||||||
|
|
||||||
self.currentStep = .install
|
|
||||||
|
|
||||||
if let progress = AppManager.shared.installationProgress(for: patchApp)
|
|
||||||
{
|
|
||||||
// Cancel pending jailbreak app installation so we can start a new one.
|
|
||||||
progress.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
let appURL = InstalledApp.fileURL(for: patchApp)
|
|
||||||
let cachedAppURL = self.temporaryDirectory.appendingPathComponent("Cached.app")
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Make copy of original app, so we can replace the cached patch app with it later.
|
|
||||||
try FileManager.default.copyItem(at: appURL, to: cachedAppURL, shouldReplace: true)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
self.present(error, title: NSLocalizedString("Could not back up jailbreak app.", comment: ""))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var unzippingError: Error?
|
|
||||||
let refreshGroup = AppManager.shared.install(patchApp, presentingViewController: self, context: self.context) { result in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
_ = try result.get()
|
|
||||||
|
|
||||||
if let unzippingError = unzippingError
|
|
||||||
{
|
|
||||||
throw unzippingError
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace cached patch app with original app so we can resume installing it post-reboot.
|
|
||||||
try FileManager.default.copyItem(at: cachedAppURL, to: appURL, shouldReplace: true)
|
|
||||||
|
|
||||||
self.openApp()
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
self.present(error, title: String(format: NSLocalizedString("Could not install %@ placeholder.", comment: ""), patchApp.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refreshGroup.beginInstallationHandler = { (installedApp) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// Replace patch app name with correct name.
|
|
||||||
installedApp.name = patchApp.name
|
|
||||||
|
|
||||||
let ipaURL = installedApp.refreshedIPAURL
|
|
||||||
let resignedAppURL = try FileManager.default.unzipAppBundle(at: ipaURL, toDirectory: self.temporaryDirectory)
|
|
||||||
|
|
||||||
self.resignedApp = ALTApplication(fileURL: resignedAppURL)
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
print("Error unzipping app bundle:", error)
|
|
||||||
unzippingError = error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.setProgress(refreshGroup.progress, description: nil)
|
|
||||||
|
|
||||||
self.cancellableProgress = refreshGroup.progress
|
|
||||||
}
|
|
||||||
|
|
||||||
func openApp()
|
|
||||||
{
|
|
||||||
guard let patchApp = self.patchApp else { return }
|
|
||||||
|
|
||||||
self.setProgress(nil, description: nil)
|
|
||||||
self.currentStep = .openApp
|
|
||||||
|
|
||||||
// This observation is willEnterForeground because patching starts immediately upon return.
|
|
||||||
self.didEnterBackgroundObservation = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { (notification) in
|
|
||||||
self.didEnterBackgroundObservation.map { NotificationCenter.default.removeObserver($0) }
|
|
||||||
self.patchApplication()
|
|
||||||
}
|
|
||||||
|
|
||||||
self.buttonHandler = { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
|
|
||||||
let openURL = InstalledApp.openAppURL(for: patchApp)
|
|
||||||
UIApplication.shared.open(openURL) { success in
|
|
||||||
guard !success else { return }
|
|
||||||
self.present(OperationError.openAppFailed(name: patchApp.name), title: String(format: NSLocalizedString("Could not open %@ placeholder.", comment: ""), patchApp.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func patchApplication()
|
|
||||||
{
|
|
||||||
guard let resignedApp = self.resignedApp else { return }
|
|
||||||
|
|
||||||
self.currentStep = .patchApp
|
|
||||||
|
|
||||||
self.buttonHandler = { [weak self] in
|
|
||||||
self?.patchApplication()
|
|
||||||
}
|
|
||||||
|
|
||||||
let patchAppOperation = AppManager.shared.patch(resignedApp: resignedApp, presentingViewController: self, context: self.context) { result in
|
|
||||||
switch result
|
|
||||||
{
|
|
||||||
case .failure(let error): self.present(error, title: String(format: NSLocalizedString("Could not patch %@ placeholder.", comment: ""), resignedApp.name))
|
|
||||||
case .success: self.rebootDevice()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
patchAppOperation.progressHandler = { (progress, description) in
|
|
||||||
self.setProgress(progress, description: description)
|
|
||||||
}
|
|
||||||
self.cancellableProgress = patchAppOperation.progress
|
|
||||||
}
|
|
||||||
|
|
||||||
func rebootDevice()
|
|
||||||
{
|
|
||||||
guard let patchApp = self.patchApp else { return }
|
|
||||||
|
|
||||||
self.setProgress(nil, description: nil)
|
|
||||||
self.currentStep = .reboot
|
|
||||||
|
|
||||||
self.didEnterBackgroundObservation = NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: .main) { (notification) in
|
|
||||||
self.didEnterBackgroundObservation.map { NotificationCenter.default.removeObserver($0) }
|
|
||||||
|
|
||||||
var patchedApps = UserDefaults.standard.patchedApps ?? []
|
|
||||||
if !patchedApps.contains(patchApp.bundleIdentifier)
|
|
||||||
{
|
|
||||||
patchedApps.append(patchApp.bundleIdentifier)
|
|
||||||
UserDefaults.standard.patchedApps = patchedApps
|
|
||||||
}
|
|
||||||
|
|
||||||
self.finish(with: .success(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
self.buttonHandler = { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
|
|
||||||
let openURL = InstalledApp.openAppURL(for: patchApp)
|
|
||||||
UIApplication.shared.open(openURL) { success in
|
|
||||||
guard !success else { return }
|
|
||||||
self.present(OperationError.openAppFailed(name: patchApp.name), title: String(format: NSLocalizedString("Could not open %@ placeholder.", comment: ""), patchApp.name))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func refreshApp()
|
|
||||||
{
|
|
||||||
guard let installedApp = self.installedApp else { return }
|
|
||||||
|
|
||||||
self.currentStep = .refresh
|
|
||||||
|
|
||||||
self.buttonHandler = { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { context in
|
|
||||||
let tempApp = context.object(with: installedApp.objectID) as! InstalledApp
|
|
||||||
tempApp.needsResign = true
|
|
||||||
|
|
||||||
let errorTitle = String(format: NSLocalizedString("Could not install %@.", comment: ""), tempApp.name)
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try context.save()
|
|
||||||
|
|
||||||
installedApp.managedObjectContext?.perform {
|
|
||||||
// Refreshing ensures we don't attempt to patch the app again,
|
|
||||||
// since that is only checked when installing a new app.
|
|
||||||
let refreshGroup = AppManager.shared.refresh([installedApp], presentingViewController: self, group: nil)
|
|
||||||
refreshGroup.completionHandler = { [weak refreshGroup, weak self] (results) in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
guard let (bundleIdentifier, result) = results.first else { throw refreshGroup?.context.error ?? OperationError.unknown }
|
|
||||||
_ = try result.get()
|
|
||||||
|
|
||||||
if var patchedApps = UserDefaults.standard.patchedApps, let index = patchedApps.firstIndex(of: bundleIdentifier)
|
|
||||||
{
|
|
||||||
patchedApps.remove(at: index)
|
|
||||||
UserDefaults.standard.patchedApps = patchedApps
|
|
||||||
}
|
|
||||||
|
|
||||||
self.finish()
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
self.present(error, title: errorTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.setProgress(refreshGroup.progress, description: String(format: NSLocalizedString("Installing %@...", comment: ""), installedApp.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
self.present(error, title: errorTitle)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func finish()
|
|
||||||
{
|
|
||||||
guard let installedApp = self.installedApp else { return }
|
|
||||||
|
|
||||||
self.setProgress(nil, description: nil)
|
|
||||||
self.currentStep = .finish
|
|
||||||
|
|
||||||
self.didEnterBackgroundObservation = NotificationCenter.default.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: .main) { (notification) in
|
|
||||||
self.didEnterBackgroundObservation.map { NotificationCenter.default.removeObserver($0) }
|
|
||||||
self.finish(with: .success(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
installedApp.managedObjectContext?.perform {
|
|
||||||
let appName = installedApp.name
|
|
||||||
let openURL = installedApp.openAppURL
|
|
||||||
|
|
||||||
self.buttonHandler = { [weak self] in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
#if !targetEnvironment(simulator)
|
|
||||||
|
|
||||||
UIApplication.shared.open(openURL) { success in
|
|
||||||
guard !success else { return }
|
|
||||||
self.present(OperationError.openAppFailed(name: appName), title: String(format: NSLocalizedString("Could not open %@.", comment: ""), appName))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
//
|
|
||||||
// InsetGroupTableViewCell.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/31/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension InsetGroupTableViewCell
|
|
||||||
{
|
|
||||||
@objc enum Style: Int
|
|
||||||
{
|
|
||||||
case single
|
|
||||||
case top
|
|
||||||
case middle
|
|
||||||
case bottom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class InsetGroupTableViewCell: UITableViewCell
|
|
||||||
{
|
|
||||||
#if !TARGET_INTERFACE_BUILDER
|
|
||||||
@IBInspectable var style: Style = .single {
|
|
||||||
didSet {
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
@IBInspectable var style: Int = 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@IBInspectable var isSelectable: Bool = false
|
|
||||||
|
|
||||||
private let separatorView = UIView()
|
|
||||||
private let insetView = UIView()
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.selectionStyle = .none
|
|
||||||
|
|
||||||
self.separatorView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
self.separatorView.backgroundColor = UIColor.white.withAlphaComponent(0.25)
|
|
||||||
self.addSubview(self.separatorView)
|
|
||||||
|
|
||||||
self.insetView.layer.masksToBounds = true
|
|
||||||
self.insetView.layer.cornerRadius = 16
|
|
||||||
|
|
||||||
// Get the preferred background color from Interface Builder.
|
|
||||||
self.insetView.backgroundColor = self.backgroundColor
|
|
||||||
self.backgroundColor = nil
|
|
||||||
|
|
||||||
self.addSubview(self.insetView, pinningEdgesWith: UIEdgeInsets(top: 0, left: 15, bottom: 0, right: 15))
|
|
||||||
self.sendSubviewToBack(self.insetView)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.separatorView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 30),
|
|
||||||
self.separatorView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -30),
|
|
||||||
self.separatorView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
|
|
||||||
self.separatorView.heightAnchor.constraint(equalToConstant: 1)])
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func setSelected(_ selected: Bool, animated: Bool)
|
|
||||||
{
|
|
||||||
super.setSelected(selected, animated: animated)
|
|
||||||
|
|
||||||
if animated
|
|
||||||
{
|
|
||||||
UIView.animate(withDuration: 0.4) {
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func setHighlighted(_ highlighted: Bool, animated: Bool)
|
|
||||||
{
|
|
||||||
super.setHighlighted(highlighted, animated: animated)
|
|
||||||
|
|
||||||
if animated
|
|
||||||
{
|
|
||||||
UIView.animate(withDuration: 0.4) {
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension InsetGroupTableViewCell
|
|
||||||
{
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
switch self.style
|
|
||||||
{
|
|
||||||
case .single:
|
|
||||||
self.insetView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
|
||||||
self.separatorView.isHidden = true
|
|
||||||
|
|
||||||
case .top:
|
|
||||||
self.insetView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
|
||||||
self.separatorView.isHidden = false
|
|
||||||
|
|
||||||
case .middle:
|
|
||||||
self.insetView.layer.maskedCorners = []
|
|
||||||
self.separatorView.isHidden = false
|
|
||||||
|
|
||||||
case .bottom:
|
|
||||||
self.insetView.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner]
|
|
||||||
self.separatorView.isHidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.isSelectable && (self.isHighlighted || self.isSelected)
|
|
||||||
{
|
|
||||||
self.insetView.backgroundColor = UIColor.white.withAlphaComponent(0.55)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.insetView.backgroundColor = UIColor.white.withAlphaComponent(0.25)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
//
|
|
||||||
// LicensesViewController.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 9/6/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class LicensesViewController: UIViewController
|
|
||||||
{
|
|
||||||
private var _didAppear = false
|
|
||||||
|
|
||||||
@IBOutlet private var textView: UITextView!
|
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
||||||
return .lightContent
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewWillAppear(animated)
|
|
||||||
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
self.view.layoutIfNeeded()
|
|
||||||
|
|
||||||
// Fix incorrect initial offset on iPhone SE.
|
|
||||||
self.textView.contentOffset.y = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
_didAppear = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLayoutSubviews()
|
|
||||||
{
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
|
|
||||||
self.textView.textContainerInset.left = self.view.layoutMargins.left
|
|
||||||
self.textView.textContainerInset.right = self.view.layoutMargins.right
|
|
||||||
self.textView.textContainer.lineFragmentPadding = 0
|
|
||||||
|
|
||||||
if !_didAppear
|
|
||||||
{
|
|
||||||
// Fix incorrect initial offset on iPhone SE.
|
|
||||||
self.textView.contentOffset.y = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
//
|
|
||||||
// PatreonComponents.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 9/5/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
final class PatronCollectionViewCell: UICollectionViewCell
|
|
||||||
{
|
|
||||||
@IBOutlet var textLabel: UILabel!
|
|
||||||
}
|
|
||||||
|
|
||||||
final class PatronsHeaderView: UICollectionReusableView
|
|
||||||
{
|
|
||||||
let textLabel = UILabel()
|
|
||||||
|
|
||||||
override init(frame: CGRect)
|
|
||||||
{
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
self.textLabel.font = UIFont.boldSystemFont(ofSize: 17)
|
|
||||||
self.textLabel.textColor = .white
|
|
||||||
self.addSubview(self.textLabel, pinningEdgesWith: UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20))
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class PatronsFooterView: UICollectionReusableView
|
|
||||||
{
|
|
||||||
let button = UIButton(type: .system)
|
|
||||||
|
|
||||||
override init(frame: CGRect)
|
|
||||||
{
|
|
||||||
super.init(frame: frame)
|
|
||||||
|
|
||||||
self.button.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
self.button.activityIndicatorView.style = .medium
|
|
||||||
self.button.titleLabel?.textColor = .white
|
|
||||||
self.addSubview(self.button)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.button.centerXAnchor.constraint(equalTo: self.centerXAnchor),
|
|
||||||
self.button.centerYAnchor.constraint(equalTo: self.centerYAnchor)])
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
|
||||||
fatalError("init(coder:) has not been implemented")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class AboutPatreonHeaderView: UICollectionReusableView
|
|
||||||
{
|
|
||||||
@IBOutlet var supportButton: UIButton!
|
|
||||||
@IBOutlet var accountButton: UIButton!
|
|
||||||
@IBOutlet var textView: UITextView!
|
|
||||||
|
|
||||||
@IBOutlet private var rileyLabel: UILabel!
|
|
||||||
@IBOutlet private var shaneLabel: UILabel!
|
|
||||||
|
|
||||||
@IBOutlet private var rileyImageView: UIImageView!
|
|
||||||
@IBOutlet private var shaneImageView: UIImageView!
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.textView.clipsToBounds = true
|
|
||||||
self.textView.layer.cornerRadius = 20
|
|
||||||
self.textView.textContainer.lineFragmentPadding = 0
|
|
||||||
|
|
||||||
for imageView in [self.rileyImageView, self.shaneImageView].compactMap({ $0 })
|
|
||||||
{
|
|
||||||
imageView.clipsToBounds = true
|
|
||||||
imageView.layer.cornerRadius = imageView.bounds.midY
|
|
||||||
}
|
|
||||||
|
|
||||||
for button in [self.supportButton, self.accountButton].compactMap({ $0 })
|
|
||||||
{
|
|
||||||
button.clipsToBounds = true
|
|
||||||
button.layer.cornerRadius = 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func layoutMarginsDidChange()
|
|
||||||
{
|
|
||||||
super.layoutMarginsDidChange()
|
|
||||||
|
|
||||||
self.textView.textContainerInset = UIEdgeInsets(top: self.layoutMargins.left, left: self.layoutMargins.left, bottom: self.layoutMargins.right, right: self.layoutMargins.right)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
//
|
|
||||||
// SettingsHeaderFooterView.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/31/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import RoxasUI
|
|
||||||
|
|
||||||
final class SettingsHeaderFooterView: UITableViewHeaderFooterView
|
|
||||||
{
|
|
||||||
@IBOutlet var primaryLabel: UILabel!
|
|
||||||
@IBOutlet var secondaryLabel: UILabel!
|
|
||||||
@IBOutlet var button: UIButton!
|
|
||||||
|
|
||||||
@IBOutlet private var stackView: UIStackView!
|
|
||||||
|
|
||||||
override func awakeFromNib()
|
|
||||||
{
|
|
||||||
super.awakeFromNib()
|
|
||||||
|
|
||||||
self.contentView.layoutMargins = .zero
|
|
||||||
self.contentView.preservesSuperviewLayoutMargins = true
|
|
||||||
|
|
||||||
self.stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
self.contentView.addSubview(self.stackView)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([self.stackView.leadingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.leadingAnchor),
|
|
||||||
self.stackView.trailingAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.trailingAnchor),
|
|
||||||
self.stackView.topAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.topAnchor),
|
|
||||||
self.stackView.bottomAnchor.constraint(equalTo: self.contentView.layoutMarginsGuide.bottomAnchor)])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// AltStoreCore.h
|
|
||||||
// AltStoreCore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 9/3/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
//! Project version number for AltStoreCore.
|
|
||||||
FOUNDATION_EXPORT double AltStoreCoreVersionNumber;
|
|
||||||
|
|
||||||
//! Project version string for AltStoreCore.
|
|
||||||
FOUNDATION_EXPORT const unsigned char AltStoreCoreVersionString[];
|
|
||||||
|
|
||||||
// In this header, you should import all the public headers of your framework using statements like #import <AltStoreCore/PublicHeader.h>
|
|
||||||
|
|
||||||
#import <AltStoreCore/ALTAppPermission.h>
|
|
||||||
#import <AltStoreCore/ALTSourceUserInfoKey.h>
|
|
||||||
#import <AltStoreCore/ALTPatreonBenefitType.h>
|
|
||||||
|
|
||||||
// Shared
|
|
||||||
#import <AltStoreCore/ALTConstants.h>
|
|
||||||
#import <AltStoreCore/CFNotificationName+AltStore.h>
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
//
|
|
||||||
// InstalledExtension.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 1/7/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import CoreData
|
|
||||||
|
|
||||||
import AltSign
|
|
||||||
|
|
||||||
@objc(InstalledExtension)
|
|
||||||
public class InstalledExtension: NSManagedObject, InstalledAppProtocol
|
|
||||||
{
|
|
||||||
/* Properties */
|
|
||||||
@NSManaged public var name: String
|
|
||||||
@NSManaged public var bundleIdentifier: String
|
|
||||||
@NSManaged public var resignedBundleIdentifier: String
|
|
||||||
@NSManaged public var version: String
|
|
||||||
|
|
||||||
@NSManaged public var refreshedDate: Date
|
|
||||||
@NSManaged public var expirationDate: Date
|
|
||||||
@NSManaged public var installedDate: Date
|
|
||||||
|
|
||||||
/* Relationships */
|
|
||||||
@NSManaged public var parentApp: InstalledApp?
|
|
||||||
|
|
||||||
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
|
||||||
{
|
|
||||||
super.init(entity: entity, insertInto: context)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(resignedAppExtension: ALTApplication, originalBundleIdentifier: String, context: NSManagedObjectContext)
|
|
||||||
{
|
|
||||||
super.init(entity: InstalledExtension.entity(), insertInto: context)
|
|
||||||
|
|
||||||
self.bundleIdentifier = originalBundleIdentifier
|
|
||||||
|
|
||||||
self.refreshedDate = Date()
|
|
||||||
self.installedDate = Date()
|
|
||||||
|
|
||||||
self.expirationDate = self.refreshedDate.addingTimeInterval(60 * 60 * 24 * 7) // Rough estimate until we get real values from provisioning profile.
|
|
||||||
|
|
||||||
self.update(resignedAppExtension: resignedAppExtension)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func update(resignedAppExtension: ALTApplication)
|
|
||||||
{
|
|
||||||
self.name = resignedAppExtension.name
|
|
||||||
|
|
||||||
self.resignedBundleIdentifier = resignedAppExtension.bundleIdentifier
|
|
||||||
self.version = resignedAppExtension.version
|
|
||||||
|
|
||||||
if let provisioningProfile = resignedAppExtension.provisioningProfile
|
|
||||||
{
|
|
||||||
self.update(provisioningProfile: provisioningProfile)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func update(provisioningProfile: ALTProvisioningProfile)
|
|
||||||
{
|
|
||||||
self.refreshedDate = provisioningProfile.creationDate
|
|
||||||
self.expirationDate = provisioningProfile.expirationDate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension InstalledExtension
|
|
||||||
{
|
|
||||||
@nonobjc class func fetchRequest() -> NSFetchRequest<InstalledExtension>
|
|
||||||
{
|
|
||||||
return NSFetchRequest<InstalledExtension>(entityName: "InstalledExtension")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// Benefit.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/21/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension PatreonAPI
|
|
||||||
{
|
|
||||||
struct BenefitResponse: Decodable
|
|
||||||
{
|
|
||||||
var id: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Benefit: Hashable
|
|
||||||
{
|
|
||||||
public var type: ALTPatreonBenefitType
|
|
||||||
|
|
||||||
init(response: PatreonAPI.BenefitResponse)
|
|
||||||
{
|
|
||||||
self.type = ALTPatreonBenefitType(response.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//
|
|
||||||
// Tier.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/21/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension PatreonAPI
|
|
||||||
{
|
|
||||||
struct TierResponse: Decodable
|
|
||||||
{
|
|
||||||
struct Attributes: Decodable
|
|
||||||
{
|
|
||||||
var title: String
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Relationships: Decodable
|
|
||||||
{
|
|
||||||
struct Benefits: Decodable
|
|
||||||
{
|
|
||||||
var data: [BenefitResponse]
|
|
||||||
}
|
|
||||||
|
|
||||||
var benefits: Benefits
|
|
||||||
}
|
|
||||||
|
|
||||||
var id: String
|
|
||||||
var attributes: Attributes
|
|
||||||
|
|
||||||
var relationships: Relationships
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Tier
|
|
||||||
{
|
|
||||||
public var name: String
|
|
||||||
public var identifier: String
|
|
||||||
|
|
||||||
public var benefits: [Benefit] = []
|
|
||||||
|
|
||||||
init(response: PatreonAPI.TierResponse)
|
|
||||||
{
|
|
||||||
self.name = response.attributes.title
|
|
||||||
self.identifier = response.id
|
|
||||||
self.benefits = response.relationships.benefits.data.map(Benefit.init(response:))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTAppPermission.h
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 7/23/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
typedef NSString *ALTAppPermissionType NS_TYPED_EXTENSIBLE_ENUM;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypePhotos;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeCamera;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeLocation;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeContacts;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeReminders;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeAppleMusic;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeMicrophone;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeSpeechRecognition;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeBackgroundAudio;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeBackgroundFetch;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeBluetooth;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeNetwork;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeCalendars;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeTouchID;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeFaceID;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeSiri;
|
|
||||||
extern ALTAppPermissionType const ALTAppPermissionTypeMotion;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTAppPermission.m
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 7/23/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "ALTAppPermission.h"
|
|
||||||
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypePhotos = @"photos";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeCamera = @"camera";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeLocation = @"location";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeContacts = @"contacts";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeReminders = @"reminders";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeAppleMusic = @"music";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeMicrophone = @"microphone";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeSpeechRecognition = @"speech-recognition";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeBackgroundAudio = @"background-audio";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeBackgroundFetch = @"background-fetch";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeBluetooth = @"bluetooth";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeNetwork = @"network";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeCalendars = @"calendars";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeTouchID = @"touchid";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeFaceID = @"faceid";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeSiri = @"siri";
|
|
||||||
ALTAppPermissionType const ALTAppPermissionTypeMotion = @"motion";
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTPatreonBenefitType.h
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/27/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
typedef NSString *ALTPatreonBenefitType NS_TYPED_EXTENSIBLE_ENUM;
|
|
||||||
extern ALTPatreonBenefitType const ALTPatreonBenefitTypeBetaAccess;
|
|
||||||
extern ALTPatreonBenefitType const ALTPatreonBenefitTypeCredits;
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTPatreonBenefitType.m
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 8/27/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "ALTPatreonBenefitType.h"
|
|
||||||
|
|
||||||
ALTPatreonBenefitType const ALTPatreonBenefitTypeBetaAccess = @"1186336";
|
|
||||||
ALTPatreonBenefitType const ALTPatreonBenefitTypeCredits = @"1186340";
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTSourceUserInfoKey.h
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 11/4/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
typedef NSString *ALTSourceUserInfoKey NS_TYPED_EXTENSIBLE_ENUM;
|
|
||||||
extern ALTSourceUserInfoKey const ALTSourceUserInfoKeyPatreonAccessToken;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTSourceUserInfoKey.m
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 11/4/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "ALTSourceUserInfoKey.h"
|
|
||||||
|
|
||||||
ALTSourceUserInfoKey const ALTSourceUserInfoKeyPatreonAccessToken = @"patreonAccessToken";
|
|
||||||
1
Configurations/Archive.xcconfig
Normal file
1
Configurations/Archive.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "Shared.xcconfig"
|
||||||
1
Configurations/Debug.xcconfig
Normal file
1
Configurations/Debug.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "Shared.xcconfig"
|
||||||
1
Configurations/Release.xcconfig
Normal file
1
Configurations/Release.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "Shared.xcconfig"
|
||||||
1
Configurations/Shared.xcconfig
Normal file
1
Configurations/Shared.xcconfig
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#include "../Build.xcconfig"
|
||||||
1
Dependencies/MarkdownAttributedString
vendored
1
Dependencies/MarkdownAttributedString
vendored
Submodule Dependencies/MarkdownAttributedString deleted from 750e8d5cb4
1
Dependencies/em_proxy
vendored
Submodule
1
Dependencies/em_proxy
vendored
Submodule
Submodule Dependencies/em_proxy added at fd120e5ff5
343
Dependencies/em_proxy.xcodeproj/project.pbxproj
vendored
343
Dependencies/em_proxy.xcodeproj/project.pbxproj
vendored
@@ -1,343 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 53;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
9987603429A4555300818586 /* em_proxy.h in Sources */ = {isa = PBXBuildFile; fileRef = 9999259129A45319005CF020 /* em_proxy.h */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXBuildRule section */
|
|
||||||
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */ = {
|
|
||||||
isa = PBXBuildRule;
|
|
||||||
compilerSpec = com.apple.compilers.proxy.script;
|
|
||||||
filePatterns = "*/em_proxy.h";
|
|
||||||
fileType = pattern.proxy;
|
|
||||||
inputFiles = (
|
|
||||||
);
|
|
||||||
isEditable = 0;
|
|
||||||
name = "Cargo project build";
|
|
||||||
outputFiles = (
|
|
||||||
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
|
|
||||||
);
|
|
||||||
script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./em_proxy/$LIB_FILE_NAME.a\"\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
|
|
||||||
};
|
|
||||||
/* End PBXBuildRule section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
9999259129A45319005CF020 /* em_proxy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = em_proxy.h; path = em_proxy/em_proxy.h; sourceTree = "<group>"; };
|
|
||||||
ADDEDBA66A6E1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
|
||||||
CA60058A9FBE4D17AF51A7D5 /* run */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = run; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libem_proxy_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
ADDEDBA66A6E2 /* Required for static linking */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
ADDEDBA66A6E1 /* libresolv.tbd */,
|
|
||||||
);
|
|
||||||
name = "Required for static linking";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CA6094FFF69222869D176AE5 /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */,
|
|
||||||
CA60058A9FBE4D17AF51A7D5 /* run */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CA6094FFF69298AF0B5890DB /* Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
ADDEDBA66A6E2 /* Required for static linking */,
|
|
||||||
);
|
|
||||||
name = Frameworks;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CA6094FFF692D65BC3C892A8 = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
9999259129A45319005CF020 /* em_proxy.h */,
|
|
||||||
CA6094FFF69222869D176AE5 /* Products */,
|
|
||||||
CA6094FFF69298AF0B5890DB /* Frameworks */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
CA60058A9FBE37FC563E4BCC /* run-bin */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = CA603DD75FB437FC563E4BCC /* Build configuration list for PBXNativeTarget "run-bin" */;
|
|
||||||
buildPhases = (
|
|
||||||
CA60445C303637FC563E4BCC /* Sources */,
|
|
||||||
CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */,
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = "run-bin";
|
|
||||||
productName = run;
|
|
||||||
productReference = CA60058A9FBE4D17AF51A7D5 /* run */;
|
|
||||||
productType = "com.apple.product-type.tool";
|
|
||||||
};
|
|
||||||
CA60C44C93D7A30E3695DD59 /* em_proxy-staticlib */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = CA603DD75FB4A30E3695DD59 /* Build configuration list for PBXNativeTarget "em_proxy-staticlib" */;
|
|
||||||
buildPhases = (
|
|
||||||
9987603529A4610700818586 /* ShellScript */,
|
|
||||||
CA60445C3036A30E3695DD59 /* Sources */,
|
|
||||||
CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
CA6094FFF692AC6C1400ACA8 /* PBXBuildRule */,
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = "em_proxy-staticlib";
|
|
||||||
productName = libem_proxy_static.a;
|
|
||||||
productReference = CA60C44C93D7916DE57E6EBD /* libem_proxy_static.a */;
|
|
||||||
productType = "com.apple.product-type.library.static";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
CA6094FFF692E04653AD465F /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
LastUpgradeCheck = 1300;
|
|
||||||
TargetAttributes = {
|
|
||||||
CA60058A9FBE37FC563E4BCC = {
|
|
||||||
CreatedOnToolsVersion = 9.2;
|
|
||||||
ProvisioningStyle = Automatic;
|
|
||||||
};
|
|
||||||
CA60C44C93D7A30E3695DD59 = {
|
|
||||||
CreatedOnToolsVersion = 9.2;
|
|
||||||
ProvisioningStyle = Automatic;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = CA6094FFF69280E02D6C7F57 /* Build configuration list for PBXProject "em_proxy" */;
|
|
||||||
compatibilityVersion = "Xcode 11.4";
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
Base,
|
|
||||||
);
|
|
||||||
mainGroup = CA6094FFF692D65BC3C892A8;
|
|
||||||
productRefGroup = CA6094FFF69222869D176AE5 /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
CA60C44C93D7A30E3695DD59 /* em_proxy-staticlib */,
|
|
||||||
CA60058A9FBE37FC563E4BCC /* run-bin */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
|
||||||
9987603529A4610700818586 /* ShellScript */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
./em_proxy/em_proxy.h,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "bash ./fetch-prebuilt.sh em_proxy\n";
|
|
||||||
};
|
|
||||||
CA6094FFF692AF6EBB7F357C /* Universal Binary lipo */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).xcfilelist",
|
|
||||||
);
|
|
||||||
name = "Universal Binary lipo";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "# generated with cargo-xcode 1.5.0\n\n#set -eux; cat \"$DERIVED_FILE_DIR/$ARCHS-$EXECUTABLE_NAME.xcfilelist\" | tr '\\n' '\\0' | xargs -0 lipo -create -output \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#if [ ${LD_DYLIB_INSTALL_NAME:+1} ]; then\n# install_name_tool -id \"$LD_DYLIB_INSTALL_NAME\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#fi\n";
|
|
||||||
};
|
|
||||||
/* End PBXShellScriptBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
CA60445C303637FC563E4BCC /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
CA60445C3036A30E3695DD59 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
9987603429A4555300818586 /* em_proxy.h in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
CA604DFE779B37FC563E4BCC /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = run.d;
|
|
||||||
CARGO_XCODE_CARGO_FILE_NAME = run;
|
|
||||||
PRODUCT_NAME = run;
|
|
||||||
SUPPORTED_PLATFORMS = macosx;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
CA604DFE779BA30E3695DD59 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libem_proxy.d;
|
|
||||||
CARGO_XCODE_CARGO_FILE_NAME = libem_proxy.a;
|
|
||||||
INSTALL_GROUP = "";
|
|
||||||
INSTALL_MODE_FLAG = "";
|
|
||||||
INSTALL_OWNER = "";
|
|
||||||
LIB_FILE_NAME = "";
|
|
||||||
"LIB_FILE_NAME[sdk=iphoneos*]" = libem_proxy;
|
|
||||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libem_proxy-sim";
|
|
||||||
PRODUCT_NAME = em_proxy_static;
|
|
||||||
SKIP_INSTALL = YES;
|
|
||||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
CA609A517351228BE02872F8 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
|
||||||
CARGO_XCODE_BUILD_MODE = debug;
|
|
||||||
CARGO_XCODE_FEATURES = "";
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
|
||||||
CURRENT_PROJECT_VERSION = 0.1;
|
|
||||||
MARKETING_VERSION = 0.1.0;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
PRODUCT_NAME = em_proxy;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SUPPORTS_MACCATALYST = YES;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
CA609A5173513CC16B37690B /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
|
||||||
CARGO_XCODE_BUILD_MODE = release;
|
|
||||||
CARGO_XCODE_FEATURES = "";
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
|
||||||
CURRENT_PROJECT_VERSION = 0.1;
|
|
||||||
MARKETING_VERSION = 0.1.0;
|
|
||||||
PRODUCT_NAME = em_proxy;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SUPPORTS_MACCATALYST = YES;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
CA60DE07A83F37FC563E4BCC /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = run.d;
|
|
||||||
CARGO_XCODE_CARGO_FILE_NAME = run;
|
|
||||||
PRODUCT_NAME = run;
|
|
||||||
SUPPORTED_PLATFORMS = macosx;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
CA60DE07A83FA30E3695DD59 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libem_proxy.d;
|
|
||||||
CARGO_XCODE_CARGO_FILE_NAME = libem_proxy.a;
|
|
||||||
INSTALL_GROUP = "";
|
|
||||||
INSTALL_MODE_FLAG = "";
|
|
||||||
INSTALL_OWNER = "";
|
|
||||||
LIB_FILE_NAME = "";
|
|
||||||
"LIB_FILE_NAME[sdk=iphoneos*]" = libem_proxy;
|
|
||||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libem_proxy-sim";
|
|
||||||
PRODUCT_NAME = em_proxy_static;
|
|
||||||
SKIP_INSTALL = YES;
|
|
||||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
CA603DD75FB437FC563E4BCC /* Build configuration list for PBXNativeTarget "run-bin" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
CA604DFE779B37FC563E4BCC /* Release */,
|
|
||||||
CA60DE07A83F37FC563E4BCC /* Debug */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
CA603DD75FB4A30E3695DD59 /* Build configuration list for PBXNativeTarget "em_proxy-staticlib" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
CA604DFE779BA30E3695DD59 /* Release */,
|
|
||||||
CA60DE07A83FA30E3695DD59 /* Debug */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
CA6094FFF69280E02D6C7F57 /* Build configuration list for PBXProject "em_proxy" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
CA609A5173513CC16B37690B /* Release */,
|
|
||||||
CA609A517351228BE02872F8 /* Debug */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = CA6094FFF692E04653AD465F /* Project object */;
|
|
||||||
}
|
|
||||||
1
Dependencies/em_proxy/.gitkeep
vendored
1
Dependencies/em_proxy/.gitkeep
vendored
@@ -1 +0,0 @@
|
|||||||
Use ../fetch-prebuilt.sh to fetch prebuilt Rust dependencies
|
|
||||||
52
Dependencies/fetch-prebuilt.sh
vendored
52
Dependencies/fetch-prebuilt.sh
vendored
@@ -1,52 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Ensure we are in Dependencies directory
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
|
|
||||||
check_for_update() {
|
|
||||||
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."
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f ".last-prebuilt-fetch-$1" ]; then
|
|
||||||
echo "0,none" > ".last-prebuilt-fetch-$1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
LAST_FETCH=`cat .last-prebuilt-fetch-$1 | perl -n -e '/([0-9]*),([^ ]*)$/ && print $1'`
|
|
||||||
LAST_COMMIT=`cat .last-prebuilt-fetch-$1 | perl -n -e '/([0-9]*),([^ ]*)$/ && print $2'`
|
|
||||||
|
|
||||||
# fetch if last fetch was over 6 hours ago
|
|
||||||
if [[ $LAST_FETCH -lt $(expr $(date +%s) - 21600) ]] || [[ "$2" == "force" ]]; then
|
|
||||||
echo "Checking $1 for update"
|
|
||||||
echo
|
|
||||||
LATEST_COMMIT=`curl https://api.github.com/repos/SideStore/$1/releases/latest | perl -n -e '/Commit: https:\\/\\/github\\.com\\/[^\\/]*\\/[^\\/]*\\/commit\\/([^"]*)/ && print $1'`
|
|
||||||
echo
|
|
||||||
echo "Last commit: $LAST_COMMIT"
|
|
||||||
echo "Latest commit: $LATEST_COMMIT"
|
|
||||||
if [[ "$LAST_COMMIT" != "$LATEST_COMMIT" ]]; then
|
|
||||||
echo "Found update, downloading binaries"
|
|
||||||
echo
|
|
||||||
wget -O "$1/lib$1.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1.a"
|
|
||||||
wget -O "$1/lib$1-sim.a" "https://github.com/SideStore/$1/releases/latest/download/lib$1-sim.a"
|
|
||||||
wget -O "$1/$1.h" "https://github.com/SideStore/$1/releases/latest/download/$1.h"
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo "Up-to-date"
|
|
||||||
fi
|
|
||||||
echo "$(date +%s),$LATEST_COMMIT" > ".last-prebuilt-fetch-$1"
|
|
||||||
else
|
|
||||||
echo "It hasn't been 6 hours and force was not specified, skipping update check for $1"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Allow for Xcode to check minimuxer and em_proxy separately by skipping the update check if the other one is specified as an argument
|
|
||||||
if [[ "$1" != "em_proxy" ]]; then
|
|
||||||
check_for_update minimuxer "$1"
|
|
||||||
if [[ "$1" != "minimuxer" ]]; then
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [[ "$1" != "minimuxer" ]]; then
|
|
||||||
check_for_update em_proxy "$1"
|
|
||||||
fi
|
|
||||||
BIN
Dependencies/libcurl/libcurl.a
vendored
BIN
Dependencies/libcurl/libcurl.a
vendored
Binary file not shown.
2
Dependencies/libimobiledevice
vendored
2
Dependencies/libimobiledevice
vendored
Submodule Dependencies/libimobiledevice updated: b314f04bd7...7a8e432e9b
2
Dependencies/libplist
vendored
2
Dependencies/libplist
vendored
Submodule Dependencies/libplist updated: c3af449543...17546f53ac
2
Dependencies/libusbmuxd
vendored
2
Dependencies/libusbmuxd
vendored
Submodule Dependencies/libusbmuxd updated: 6426362e5c...8b82ef166e
1
Dependencies/minimuxer
vendored
Submodule
1
Dependencies/minimuxer
vendored
Submodule
Submodule Dependencies/minimuxer added at bc6988e098
283
Dependencies/minimuxer.xcodeproj/project.pbxproj
vendored
283
Dependencies/minimuxer.xcodeproj/project.pbxproj
vendored
@@ -1,283 +0,0 @@
|
|||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 53;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
9987603329A454B500818586 /* minimuxer.h in Sources */ = {isa = PBXBuildFile; fileRef = 9987603229A454B500818586 /* minimuxer.h */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXBuildRule section */
|
|
||||||
CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */ = {
|
|
||||||
isa = PBXBuildRule;
|
|
||||||
compilerSpec = com.apple.compilers.proxy.script;
|
|
||||||
filePatterns = "*/minimuxer.h";
|
|
||||||
fileType = pattern.proxy;
|
|
||||||
inputFiles = (
|
|
||||||
);
|
|
||||||
isEditable = 0;
|
|
||||||
name = "Cargo project build";
|
|
||||||
outputFiles = (
|
|
||||||
"$(OBJECT_FILE_DIR)/$(CARGO_XCODE_TARGET_ARCH)-$(EXECUTABLE_NAME)",
|
|
||||||
);
|
|
||||||
script = "# generated with cargo-xcode 1.5.0\n# modified to use prebuilt binaries\n\nset -eu;\n\nBUILT_SRC=\"./minimuxer/$LIB_FILE_NAME.a\"\nln -f -- \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\" || cp \"$BUILT_SRC\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\necho \"$BUILT_SRC -> $TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n\n# xcode generates dep file, but for its own path, so append our rename to it\n#DEP_FILE_SRC=\"minimuxer/target/${CARGO_XCODE_TARGET_TRIPLE}/release/${CARGO_XCODE_CARGO_DEP_FILE_NAME}\"\n#if [ -f \"$DEP_FILE_SRC\" ]; then\n# DEP_FILE_DST=\"${DERIVED_FILE_DIR}/${CARGO_XCODE_TARGET_ARCH}-${EXECUTABLE_NAME}.d\"\n# cp -f \"$DEP_FILE_SRC\" \"$DEP_FILE_DST\"\n# echo >> \"$DEP_FILE_DST\" \"$SCRIPT_OUTPUT_FILE_0: $BUILT_SRC\"\n#fi\n\n# lipo script needs to know all the platform-specific files that have been built\n# archs is in the file name, so that paths don't stay around after archs change\n# must match input for LipoScript\n#FILE_LIST=\"${DERIVED_FILE_DIR}/${ARCHS}-${EXECUTABLE_NAME}.xcfilelist\"\n#touch \"$FILE_LIST\"\n#if ! egrep -q \"$SCRIPT_OUTPUT_FILE_0\" \"$FILE_LIST\" ; then\n# echo >> \"$FILE_LIST\" \"$SCRIPT_OUTPUT_FILE_0\"\n#fi\n";
|
|
||||||
};
|
|
||||||
/* End PBXBuildRule section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
9987603229A454B500818586 /* minimuxer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = minimuxer.h; path = minimuxer/minimuxer.h; sourceTree = "<group>"; };
|
|
||||||
ADDEDBA66A6E1 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
|
|
||||||
CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libminimuxer_static.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
ADDEDBA66A6E2 /* Required for static linking */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
ADDEDBA66A6E1 /* libresolv.tbd */,
|
|
||||||
);
|
|
||||||
name = "Required for static linking";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CA6012A875F922869D176AE5 /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CA6012A875F998AF0B5890DB /* Frameworks */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
ADDEDBA66A6E2 /* Required for static linking */,
|
|
||||||
);
|
|
||||||
name = Frameworks;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CA6012A875F9D65BC3C892A8 = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
9987603229A454B500818586 /* minimuxer.h */,
|
|
||||||
CA6012A875F922869D176AE5 /* Products */,
|
|
||||||
CA6012A875F998AF0B5890DB /* Frameworks */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
CA609C732349A560B9642892 /* minimuxer-staticlib */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = CA600589A243A560B9642892 /* Build configuration list for PBXNativeTarget "minimuxer-staticlib" */;
|
|
||||||
buildPhases = (
|
|
||||||
9987603629A4611D00818586 /* ShellScript */,
|
|
||||||
CA600F638141A560B9642892 /* Sources */,
|
|
||||||
CA6012A875F9AF6EBB7F357C /* Universal Binary lipo */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
CA6012A875F9AC6C1400ACA8 /* PBXBuildRule */,
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = "minimuxer-staticlib";
|
|
||||||
productName = libminimuxer_static.a;
|
|
||||||
productReference = CA609C732349C7AAD9FA67C4 /* libminimuxer_static.a */;
|
|
||||||
productType = "com.apple.product-type.library.static";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
CA6012A875F9E04653AD465F /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
LastUpgradeCheck = 1300;
|
|
||||||
TargetAttributes = {
|
|
||||||
CA609C732349A560B9642892 = {
|
|
||||||
CreatedOnToolsVersion = 9.2;
|
|
||||||
ProvisioningStyle = Automatic;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = CA6012A875F980E02D6C7F57 /* Build configuration list for PBXProject "minimuxer" */;
|
|
||||||
compatibilityVersion = "Xcode 11.4";
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
Base,
|
|
||||||
);
|
|
||||||
mainGroup = CA6012A875F9D65BC3C892A8;
|
|
||||||
productRefGroup = CA6012A875F922869D176AE5 /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
CA609C732349A560B9642892 /* minimuxer-staticlib */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXShellScriptBuildPhase section */
|
|
||||||
9987603629A4611D00818586 /* ShellScript */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
);
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
./minimuxer/minimuxer.h,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "bash ./fetch-prebuilt.sh minimuxer\n";
|
|
||||||
};
|
|
||||||
CA6012A875F9AF6EBB7F357C /* Universal Binary lipo */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"$(DERIVED_FILE_DIR)/$(ARCHS)-$(EXECUTABLE_NAME).xcfilelist",
|
|
||||||
);
|
|
||||||
name = "Universal Binary lipo";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
"$(TARGET_BUILD_DIR)/$(EXECUTABLE_PATH)",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "# generated with cargo-xcode 1.5.0\n\n#set -eux; cat \"$DERIVED_FILE_DIR/$ARCHS-$EXECUTABLE_NAME.xcfilelist\" | tr '\\n' '\\0' | xargs -0 lipo -create -output \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#if [ ${LD_DYLIB_INSTALL_NAME:+1} ]; then\n# install_name_tool -id \"$LD_DYLIB_INSTALL_NAME\" \"$TARGET_BUILD_DIR/$EXECUTABLE_PATH\"\n#fi\n";
|
|
||||||
};
|
|
||||||
/* End PBXShellScriptBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
CA600F638141A560B9642892 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
9987603329A454B500818586 /* minimuxer.h in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
CA6008D36272A560B9642892 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libminimuxer.d;
|
|
||||||
CARGO_XCODE_CARGO_FILE_NAME = libminimuxer.a;
|
|
||||||
INSTALL_GROUP = "";
|
|
||||||
INSTALL_MODE_FLAG = "";
|
|
||||||
INSTALL_OWNER = "";
|
|
||||||
LIB_FILE_NAME = "";
|
|
||||||
"LIB_FILE_NAME[sdk=iphoneos*]" = libminimuxer;
|
|
||||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libminimuxer-sim";
|
|
||||||
PRODUCT_NAME = minimuxer_static;
|
|
||||||
SKIP_INSTALL = YES;
|
|
||||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
CA602DE9FCEDA560B9642892 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CARGO_XCODE_CARGO_DEP_FILE_NAME = libminimuxer.d;
|
|
||||||
CARGO_XCODE_CARGO_FILE_NAME = libminimuxer.a;
|
|
||||||
INSTALL_GROUP = "";
|
|
||||||
INSTALL_MODE_FLAG = "";
|
|
||||||
INSTALL_OWNER = "";
|
|
||||||
LIB_FILE_NAME = "";
|
|
||||||
"LIB_FILE_NAME[sdk=iphoneos*]" = libminimuxer;
|
|
||||||
"LIB_FILE_NAME[sdk=iphonesimulator*]" = "libminimuxer-sim";
|
|
||||||
PRODUCT_NAME = minimuxer_static;
|
|
||||||
SKIP_INSTALL = YES;
|
|
||||||
SUPPORTED_PLATFORMS = "macosx iphonesimulator iphoneos appletvsimulator appletvos";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
CA60A20F8EA6228BE02872F8 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
|
||||||
CARGO_XCODE_BUILD_MODE = debug;
|
|
||||||
CARGO_XCODE_FEATURES = "";
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
|
||||||
CURRENT_PROJECT_VERSION = 0.1;
|
|
||||||
MARKETING_VERSION = 0.1.0;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
PRODUCT_NAME = minimuxer;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SUPPORTS_MACCATALYST = YES;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
CA60A20F8EA63CC16B37690B /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CARGO_TARGET_DIR = "$(PROJECT_TEMP_DIR)/cargo_target";
|
|
||||||
CARGO_XCODE_BUILD_MODE = release;
|
|
||||||
CARGO_XCODE_FEATURES = "";
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=arm64*]" = aarch64;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=i386]" = i686;
|
|
||||||
"CARGO_XCODE_TARGET_ARCH[arch=x86_64*]" = x86_64;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvos*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=appletvsimulator*]" = tvos;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphoneos*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*]" = "ios-sim";
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=iphonesimulator*][arch=x86_64*]" = ios;
|
|
||||||
"CARGO_XCODE_TARGET_OS[sdk=macosx*]" = darwin;
|
|
||||||
CURRENT_PROJECT_VERSION = 0.1;
|
|
||||||
MARKETING_VERSION = 0.1.0;
|
|
||||||
PRODUCT_NAME = minimuxer;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
SUPPORTS_MACCATALYST = YES;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
CA600589A243A560B9642892 /* Build configuration list for PBXNativeTarget "minimuxer-staticlib" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
CA602DE9FCEDA560B9642892 /* Release */,
|
|
||||||
CA6008D36272A560B9642892 /* Debug */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
CA6012A875F980E02D6C7F57 /* Build configuration list for PBXProject "minimuxer" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
CA60A20F8EA63CC16B37690B /* Release */,
|
|
||||||
CA60A20F8EA6228BE02872F8 /* Debug */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = CA6012A875F9E04653AD465F /* Project object */;
|
|
||||||
}
|
|
||||||
1
Dependencies/minimuxer/.gitkeep
vendored
1
Dependencies/minimuxer/.gitkeep
vendored
@@ -1 +0,0 @@
|
|||||||
Use ../fetch-prebuilt.sh to fetch prebuilt Rust dependencies
|
|
||||||
104
Package.resolved
Normal file
104
Package.resolved
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
{
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "altsign",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/SideStore/AltSign.git",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "master",
|
||||||
|
"revision" : "7e0e7edcf8fbc44ac1e35da3e9030a297aa18b84"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "appcenter-sdk-apple",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/microsoft/appcenter-sdk-apple",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "b2dc99cfedead0bad4e6573d86c5228c89cff332",
|
||||||
|
"version" : "4.4.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "down",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/johnxnguyen/Down.git",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "master",
|
||||||
|
"revision" : "e754ab1c80920dd51a8e08290c912ac1c2ac8b58"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "keychainaccess",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/kishikawakatsumi/KeychainAccess",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "84e546727d66f1adc5439debad16270d0fdd04e7",
|
||||||
|
"version" : "4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "launchatlogin",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/sindresorhus/LaunchAtLogin",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "e8171b3e38a2816f579f58f3dac1522aa39efe41",
|
||||||
|
"version" : "4.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "nuke",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/kean/Nuke",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "9318d02a8a6d20af56505c9673261c1fd3b3aebe",
|
||||||
|
"version" : "7.6.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "openssl",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/krzyzanowskim/OpenSSL.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "87f41bf9488e7dd2b0de2ab97cb3eafc7304e0e6",
|
||||||
|
"version" : "1.1.2000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "plcrashreporter",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/microsoft/PLCrashReporter.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "81cdec2b3827feb03286cb297f4c501a8eb98df1",
|
||||||
|
"version" : "1.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "roxas",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/JoeMatt/Roxas",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "17338c09ec0ffeea4c68135f17c1f801a3d6d10d",
|
||||||
|
"version" : "1.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "semanticversion",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/SwiftPackageIndex/SemanticVersion",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "fc670910dc0903cc269b3d0b776cda5703979c4e",
|
||||||
|
"version" : "0.3.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "sidekit",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/SideStore/SideKit.git",
|
||||||
|
"state" : {
|
||||||
|
"branch" : "main",
|
||||||
|
"revision" : "7ea34a09b52c104077dea8e0b90f8dc55d43b36b"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 2
|
||||||
|
}
|
||||||
289
Package.swift
Normal file
289
Package.swift
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
// swift-tools-version: 5.7
|
||||||
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import os.log
|
||||||
|
import PackageDescription
|
||||||
|
|
||||||
|
let env: [String: Bool] = [
|
||||||
|
"USE_CARGO": false,
|
||||||
|
"USE_CXX_INTEROP": false,
|
||||||
|
"USE_CXX_MODULES": false,
|
||||||
|
"INHIBIT_UPSTREAM_WARNINGS": false,
|
||||||
|
"STATIC_LIBRARY": false,
|
||||||
|
]
|
||||||
|
|
||||||
|
let USE_CARGO = envBool("USE_CARGO")
|
||||||
|
let USE_CXX_INTEROP = envBool("USE_CXX_INTEROP")
|
||||||
|
let USE_CXX_MODULES = envBool("USE_CXX_MODULES")
|
||||||
|
let INHIBIT_UPSTREAM_WARNINGS = envBool("INHIBIT_UPSTREAM_WARNINGS")
|
||||||
|
let STATIC_LIBRARY = envBool("STATIC_LIBRARY")
|
||||||
|
|
||||||
|
// let dependencies_cargo: [Package.Dependency] = {
|
||||||
|
// USE_CARGO ? [
|
||||||
|
// // CargoPlugin
|
||||||
|
// .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"),
|
||||||
|
// .package(url: "https://github.com/apple/swift-package-manager.git", branch: "release/5.7"),
|
||||||
|
// .package(url: "https://github.com/apple/swift-tools-support-core.git", branch: "release/5.7"),
|
||||||
|
// ] : []
|
||||||
|
// }()
|
||||||
|
|
||||||
|
// let cargo_targets: [Target] = [
|
||||||
|
// .executableTarget(
|
||||||
|
// name: "Cargo",
|
||||||
|
// dependencies: [
|
||||||
|
// .product(name: "ArgumentParser", package: "swift-argument-parser"),
|
||||||
|
// .product(name: "SwiftPM-auto", package: "swift-package-manager"),
|
||||||
|
// .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core")
|
||||||
|
// ]
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// .testTarget(
|
||||||
|
// name: "CargoTests",
|
||||||
|
// dependencies: ["Cargo"],
|
||||||
|
// exclude: [
|
||||||
|
// "swiftlint",
|
||||||
|
// "xcframework"
|
||||||
|
// ]
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// .plugin(
|
||||||
|
// name: "CargoPlugin",
|
||||||
|
// capability: .buildTool(),
|
||||||
|
// dependencies: [
|
||||||
|
// "Cargo"
|
||||||
|
// ]
|
||||||
|
// ),
|
||||||
|
//
|
||||||
|
// .plugin(
|
||||||
|
// name: "CargoPlugin-Generate",
|
||||||
|
// capability: .command(
|
||||||
|
// intent: .custom(
|
||||||
|
// verb: "generate-code-from-rust",
|
||||||
|
// description: "Creates .c code from your `rust` code"
|
||||||
|
// ),
|
||||||
|
// permissions: [
|
||||||
|
// .writeToPackageDirectory(reason: "This command generates source code")
|
||||||
|
// ]
|
||||||
|
// ),
|
||||||
|
// dependencies: ["Cargo"]
|
||||||
|
// )
|
||||||
|
// ]
|
||||||
|
|
||||||
|
let dependencies: [Package.Dependency] = [
|
||||||
|
.package(url: "https://github.com/JoeMatt/Roxas", from: "1.2.2"),
|
||||||
|
.package(url: "https://github.com/johnxnguyen/Down", branch: "master"),
|
||||||
|
.package(url: "https://github.com/kean/Nuke", from: "7.0.0"),
|
||||||
|
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess", from: "4.2.0"),
|
||||||
|
// .package(url: "https://github.com/krzyzanowskim/OpenSSL", from: "1.1.180"),
|
||||||
|
.package(url: "https://github.com/microsoft/appcenter-sdk-apple", from: "4.2.0"),
|
||||||
|
.package(url: "https://github.com/SideStore/AltSign", branch: "master"),
|
||||||
|
.package(url: "https://github.com/SideStore/SideKit", branch: "main"),
|
||||||
|
.package(url: "https://github.com/sindresorhus/LaunchAtLogin", from: "4.1.0"),
|
||||||
|
.package(url: "https://github.com/SwiftPackageIndex/SemanticVersion", from: "0.3.5"),
|
||||||
|
] // + dependencies_cargo
|
||||||
|
|
||||||
|
let package = Package(
|
||||||
|
name: "SideStoreCore",
|
||||||
|
defaultLocalization: "en",
|
||||||
|
platforms: [
|
||||||
|
.iOS(.v12),
|
||||||
|
// .tvOS(.v12),
|
||||||
|
// .macOS(.v12),
|
||||||
|
],
|
||||||
|
|
||||||
|
products: [
|
||||||
|
.executable(
|
||||||
|
name: "SideStore",
|
||||||
|
targets: ["SideStore"]),
|
||||||
|
|
||||||
|
.executable(
|
||||||
|
name: "SideWidget",
|
||||||
|
targets: ["SideWidget"]),
|
||||||
|
|
||||||
|
.executable(
|
||||||
|
name: "SideDaemon",
|
||||||
|
targets: ["SideDaemon"]),
|
||||||
|
|
||||||
|
.library(name: "EmotionalDamage", targets: ["EmotionalDamage"]),
|
||||||
|
.library(name: "MiniMuxerSwift", targets: ["MiniMuxerSwift"]),
|
||||||
|
.library(name: "SideStoreCore", targets: ["SideStoreCore"]),
|
||||||
|
],
|
||||||
|
|
||||||
|
dependencies: dependencies,
|
||||||
|
targets: [
|
||||||
|
|
||||||
|
// MARK: - SideStore
|
||||||
|
.executableTarget(
|
||||||
|
name: "SideStore",
|
||||||
|
dependencies: [
|
||||||
|
"EmotionalDamage",
|
||||||
|
"MiniMuxerSwift",
|
||||||
|
"SideStoreCore",
|
||||||
|
"Shared",
|
||||||
|
.product(name: "Down", package: "Down"),
|
||||||
|
.product(name: "AltSign", package: "AltSign"),
|
||||||
|
.product(name: "Nuke", package: "Nuke"),
|
||||||
|
.product(name: "Roxas", package: "Roxas"),
|
||||||
|
.product(name: "RoxasUI", package: "Roxas"),
|
||||||
|
.product(name: "AppCenterAnalytics", package: "appcenter-sdk-apple"),
|
||||||
|
.product(name: "AppCenterCrashes", package: "appcenter-sdk-apple")
|
||||||
|
],
|
||||||
|
linkerSettings: [
|
||||||
|
.linkedFramework("UIKit"),
|
||||||
|
.linkedFramework("Avfoundation"),
|
||||||
|
.linkedFramework("Combine"),
|
||||||
|
.linkedFramework("AppleArchive"),
|
||||||
|
.linkedFramework("Network"),
|
||||||
|
.linkedFramework("CoreData"),
|
||||||
|
.linkedFramework("UniformTypeIdentifiers"),
|
||||||
|
.linkedFramework("QuickLook", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("AuthenticationServices", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("SafariServices", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("Intents", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("IntentsUI", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("MessageUI", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("ARKit", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("CoreHaptics", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("AudioToolbox", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("WidgetKit", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("UserNotifications", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
.linkedFramework("MobileCoreServices", .when(platforms: [.iOS, .macCatalyst])),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - SideWidget
|
||||||
|
.executableTarget(
|
||||||
|
name: "SideWidget"
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - EmotionalDamage
|
||||||
|
|
||||||
|
.target(
|
||||||
|
name: "EmotionalDamage",
|
||||||
|
dependencies: ["em_proxy"]
|
||||||
|
),
|
||||||
|
|
||||||
|
.binaryTarget(
|
||||||
|
name: "em_proxy",
|
||||||
|
path: "Dependencies/em_proxy/em_proxy.xcframework.zip"
|
||||||
|
),
|
||||||
|
|
||||||
|
.testTarget(
|
||||||
|
name: "EmotionalDamageTests",
|
||||||
|
dependencies: ["EmotionalDamage"]
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - MiniMuxer
|
||||||
|
|
||||||
|
.target(
|
||||||
|
name: "MiniMuxerSwift",
|
||||||
|
dependencies: ["minimuxer"],
|
||||||
|
cSettings: [
|
||||||
|
// .headerSearchPath("Dependencies/minimuxer/include"),
|
||||||
|
],
|
||||||
|
cxxSettings: [
|
||||||
|
],
|
||||||
|
swiftSettings: [
|
||||||
|
],
|
||||||
|
linkerSettings: [
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
.binaryTarget(
|
||||||
|
name: "minimuxer",
|
||||||
|
path: "Dependencies/minimuxer/minimuxer.xcframework.zip"
|
||||||
|
),
|
||||||
|
|
||||||
|
.testTarget(
|
||||||
|
name: "MiniMuxerTests",
|
||||||
|
dependencies: ["MiniMuxerSwift"]
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - Shared
|
||||||
|
|
||||||
|
.target(
|
||||||
|
name: "Shared",
|
||||||
|
dependencies: ["SideKit"]
|
||||||
|
),
|
||||||
|
|
||||||
|
.testTarget(
|
||||||
|
name: "SharedTests",
|
||||||
|
dependencies: ["Shared"]
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - SideBackup
|
||||||
|
|
||||||
|
.executableTarget(
|
||||||
|
name: "SideBackup",
|
||||||
|
dependencies: []
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - SideDaemon
|
||||||
|
|
||||||
|
.executableTarget(
|
||||||
|
name: "SideDaemon",
|
||||||
|
dependencies: [
|
||||||
|
"Shared",
|
||||||
|
.product(name: "AltSign", package: "AltSign"),
|
||||||
|
.product(name: "LaunchAtLogin", package: "LaunchAtLogin"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
.testTarget(
|
||||||
|
name: "SideDaemonTests",
|
||||||
|
dependencies: ["SideDaemon"]
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - SideStoreCore
|
||||||
|
|
||||||
|
.target(
|
||||||
|
name: "SideStoreCore",
|
||||||
|
dependencies: [
|
||||||
|
"Shared",
|
||||||
|
.product(name: "Roxas", package: "Roxas"),
|
||||||
|
.product(name: "AltSign", package: "AltSign"),
|
||||||
|
.product(name: "KeychainAccess", package: "KeychainAccess"),
|
||||||
|
.product(name: "SemanticVersion", package: "SemanticVersion"),
|
||||||
|
],
|
||||||
|
swiftSettings: [
|
||||||
|
.unsafeFlags([
|
||||||
|
// "--xcconfig-overrides", "AltStoreCore.xconfig"
|
||||||
|
])
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
.testTarget(
|
||||||
|
name: "SideStoreCoreTests",
|
||||||
|
dependencies: ["SideStoreCore"]
|
||||||
|
),
|
||||||
|
|
||||||
|
// MARK: - libfragmentzip
|
||||||
|
.target(
|
||||||
|
name: "libfragmentzip",
|
||||||
|
dependencies: [],
|
||||||
|
sources: [
|
||||||
|
"libfragmentzip-source/libfragmentzip/libfragmentzip.c"
|
||||||
|
],
|
||||||
|
cSettings: [
|
||||||
|
.headerSearchPath("libfragmentzip-source/libfragmentzip/include")
|
||||||
|
]
|
||||||
|
),
|
||||||
|
|
||||||
|
.testTarget(
|
||||||
|
name: "libfragmentzipTests",
|
||||||
|
dependencies: ["libfragmentzip"]
|
||||||
|
),
|
||||||
|
],
|
||||||
|
swiftLanguageVersions: [.v5],
|
||||||
|
cLanguageStandard: .c2x,
|
||||||
|
cxxLanguageStandard: .cxx20
|
||||||
|
)
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
|
||||||
|
func envBool(_ key: String) -> Bool {
|
||||||
|
guard let value = ProcessInfo.processInfo.environment[key] else { return env[key, default: true] }
|
||||||
|
let trueValues = ["1", "on", "true", "yes"]
|
||||||
|
return trueValues.contains(value.lowercased())
|
||||||
|
}
|
||||||
70
Plugins/CargoPlugin-Generate/Plugin.swift
Normal file
70
Plugins/CargoPlugin-Generate/Plugin.swift
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import Foundation
|
||||||
|
import PackagePlugin
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct CargoPlugin: CommandPlugin {
|
||||||
|
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
|
||||||
|
guard let sourceTarget = target as? SourceModuleTarget else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return createBuildCommands(
|
||||||
|
inputFiles: sourceTarget.sourceFiles(withSuffix: "toml").map(\.path),
|
||||||
|
packageDirectory: context.package.directory,
|
||||||
|
workingDirectory: context.pluginWorkDirectory,
|
||||||
|
tool: try context.tool(named: "cargo")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createBuildCommands(
|
||||||
|
inputFiles: [Path],
|
||||||
|
packageDirectory: Path,
|
||||||
|
workingDirectory: Path,
|
||||||
|
tool: PluginContext.Tool
|
||||||
|
) -> [Command] {
|
||||||
|
if inputFiles.isEmpty {
|
||||||
|
// Don't lint anything if there are no Swift source files in this target
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
var arguments = [
|
||||||
|
"build", "\(workingDirectory)"
|
||||||
|
]
|
||||||
|
|
||||||
|
// Manually look for configuration files, to avoid issues when the plugin does not execute our tool from the
|
||||||
|
// package source directory.
|
||||||
|
if let configuration = packageDirectory.firstConfigurationFileInParentDirectories() {
|
||||||
|
arguments.append(contentsOf: ["--config", "\(configuration.string)"])
|
||||||
|
}
|
||||||
|
arguments += inputFiles.map(\.string)
|
||||||
|
|
||||||
|
// We are not producing output files and this is needed only to not include cache files into bundle
|
||||||
|
let outputFilesDirectory = workingDirectory.appending("Output")
|
||||||
|
|
||||||
|
return [
|
||||||
|
.prebuildCommand(
|
||||||
|
displayName: "Cargo",
|
||||||
|
executable: tool.path,
|
||||||
|
arguments: arguments,
|
||||||
|
outputFilesDirectory: outputFilesDirectory
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if canImport(XcodeProjectPlugin)
|
||||||
|
import XcodeProjectPlugin
|
||||||
|
|
||||||
|
extension SwiftLintPlugin: XcodeBuildToolPlugin {
|
||||||
|
func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] {
|
||||||
|
let inputFilePaths = target.inputFiles
|
||||||
|
.filter { $0.type == .source && $0.path.extension == "swift" }
|
||||||
|
.map(\.path)
|
||||||
|
return createBuildCommands(
|
||||||
|
inputFiles: inputFilePaths,
|
||||||
|
packageDirectory: context.xcodeProject.directory,
|
||||||
|
workingDirectory: context.pluginWorkDirectory,
|
||||||
|
tool: try context.tool(named: "swiftlint")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
70
Plugins/CargoPlugin/Plugin.swift
Normal file
70
Plugins/CargoPlugin/Plugin.swift
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import Foundation
|
||||||
|
import PackagePlugin
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct CargoPlugin: BuildToolPlugin {
|
||||||
|
func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] {
|
||||||
|
guard let sourceTarget = target as? SourceModuleTarget else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
return createBuildCommands(
|
||||||
|
inputFiles: sourceTarget.sourceFiles(withSuffix: "toml").map(\.path),
|
||||||
|
packageDirectory: context.package.directory,
|
||||||
|
workingDirectory: context.pluginWorkDirectory,
|
||||||
|
tool: try context.tool(named: "cargo")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createBuildCommands(
|
||||||
|
inputFiles: [Path],
|
||||||
|
packageDirectory: Path,
|
||||||
|
workingDirectory: Path,
|
||||||
|
tool: PluginContext.Tool
|
||||||
|
) -> [Command] {
|
||||||
|
if inputFiles.isEmpty {
|
||||||
|
// Don't lint anything if there are no Swift source files in this target
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
var arguments = [
|
||||||
|
"build", "\(workingDirectory)"
|
||||||
|
]
|
||||||
|
|
||||||
|
// Manually look for configuration files, to avoid issues when the plugin does not execute our tool from the
|
||||||
|
// package source directory.
|
||||||
|
if let configuration = packageDirectory.firstConfigurationFileInParentDirectories() {
|
||||||
|
arguments.append(contentsOf: ["--config", "\(configuration.string)"])
|
||||||
|
}
|
||||||
|
arguments += inputFiles.map(\.string)
|
||||||
|
|
||||||
|
// We are not producing output files and this is needed only to not include cache files into bundle
|
||||||
|
let outputFilesDirectory = workingDirectory.appending("Output")
|
||||||
|
|
||||||
|
return [
|
||||||
|
.prebuildCommand(
|
||||||
|
displayName: "Cargo",
|
||||||
|
executable: tool.path,
|
||||||
|
arguments: arguments,
|
||||||
|
outputFilesDirectory: outputFilesDirectory
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if canImport(XcodeProjectPlugin)
|
||||||
|
import XcodeProjectPlugin
|
||||||
|
|
||||||
|
extension SwiftLintPlugin: XcodeBuildToolPlugin {
|
||||||
|
func createBuildCommands(context: XcodePluginContext, target: XcodeTarget) throws -> [Command] {
|
||||||
|
let inputFilePaths = target.inputFiles
|
||||||
|
.filter { $0.type == .source && $0.path.extension == "swift" }
|
||||||
|
.map(\.path)
|
||||||
|
return createBuildCommands(
|
||||||
|
inputFiles: inputFilePaths,
|
||||||
|
packageDirectory: context.xcodeProject.directory,
|
||||||
|
workingDirectory: context.pluginWorkDirectory,
|
||||||
|
tool: try context.tool(named: "swiftlint")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTConstants.h
|
|
||||||
// AltKit
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 5/30/19.
|
|
||||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
extern uint16_t ALTDeviceListeningSocket;
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
//
|
|
||||||
// ALTConstants.m
|
|
||||||
// AltKit
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 1/10/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
uint16_t ALTDeviceListeningSocket = 28151;
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// CFNotificationName+AltStore.h
|
|
||||||
// AltKit
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 1/10/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
extern CFNotificationName const ALTWiredServerConnectionAvailableRequest NS_SWIFT_NAME(wiredServerConnectionAvailableRequest);
|
|
||||||
extern CFNotificationName const ALTWiredServerConnectionAvailableResponse NS_SWIFT_NAME(wiredServerConnectionAvailableResponse);
|
|
||||||
extern CFNotificationName const ALTWiredServerConnectionStartRequest NS_SWIFT_NAME(wiredServerConnectionStartRequest);
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// CFNotificationName+AltStore.m
|
|
||||||
// AltKit
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 1/10/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "CFNotificationName+AltStore.h"
|
|
||||||
|
|
||||||
CFNotificationName const ALTWiredServerConnectionAvailableRequest = CFSTR("io.altstore.Request.WiredServerConnectionAvailable");
|
|
||||||
CFNotificationName const ALTWiredServerConnectionAvailableResponse = CFSTR("io.altstore.Response.WiredServerConnectionAvailable");
|
|
||||||
CFNotificationName const ALTWiredServerConnectionStartRequest = CFSTR("io.altstore.Request.WiredServerConnectionStart");
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
//
|
|
||||||
// NSError+AltStore.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 3/11/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension NSError
|
|
||||||
{
|
|
||||||
@objc(alt_localizedFailure)
|
|
||||||
var localizedFailure: String? {
|
|
||||||
let localizedFailure = (self.userInfo[NSLocalizedFailureErrorKey] as? String) ?? (NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSLocalizedFailureErrorKey) as? String)
|
|
||||||
return localizedFailure
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc(alt_localizedDebugDescription)
|
|
||||||
var localizedDebugDescription: String? {
|
|
||||||
let debugDescription = (self.userInfo[NSDebugDescriptionErrorKey] as? String) ?? (NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSDebugDescriptionErrorKey) as? String)
|
|
||||||
return debugDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc(alt_errorWithLocalizedFailure:)
|
|
||||||
func withLocalizedFailure(_ failure: String) -> NSError
|
|
||||||
{
|
|
||||||
var userInfo = self.userInfo
|
|
||||||
userInfo[NSLocalizedFailureErrorKey] = failure
|
|
||||||
|
|
||||||
if let failureReason = self.localizedFailureReason
|
|
||||||
{
|
|
||||||
userInfo[NSLocalizedFailureReasonErrorKey] = failureReason
|
|
||||||
}
|
|
||||||
else if self.localizedFailure == nil && self.localizedFailureReason == nil && self.localizedDescription.contains(self.localizedErrorCode)
|
|
||||||
{
|
|
||||||
// Default localizedDescription, so replace with just the localized error code portion.
|
|
||||||
userInfo[NSLocalizedFailureReasonErrorKey] = "(\(self.localizedErrorCode).)"
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
if let localizedDescription = NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSLocalizedDescriptionKey) as? String
|
|
||||||
{
|
|
||||||
userInfo[NSLocalizedDescriptionKey] = localizedDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't accidentally remove localizedDescription from dictionary
|
|
||||||
// userInfo[NSLocalizedDescriptionKey] = NSError.userInfoValueProvider(forDomain: self.domain)?(self, NSLocalizedDescriptionKey) as? String
|
|
||||||
|
|
||||||
if let recoverySuggestion = self.localizedRecoverySuggestion
|
|
||||||
{
|
|
||||||
userInfo[NSLocalizedRecoverySuggestionErrorKey] = recoverySuggestion
|
|
||||||
}
|
|
||||||
|
|
||||||
let error = NSError(domain: self.domain, code: self.code, userInfo: userInfo)
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
|
|
||||||
func sanitizedForCoreData() -> NSError
|
|
||||||
{
|
|
||||||
var userInfo = self.userInfo
|
|
||||||
userInfo[NSLocalizedFailureErrorKey] = self.localizedFailure
|
|
||||||
userInfo[NSLocalizedDescriptionKey] = self.localizedDescription
|
|
||||||
userInfo[NSLocalizedFailureReasonErrorKey] = self.localizedFailureReason
|
|
||||||
userInfo[NSLocalizedRecoverySuggestionErrorKey] = self.localizedRecoverySuggestion
|
|
||||||
|
|
||||||
// Remove userInfo values that don't conform to NSSecureEncoding.
|
|
||||||
userInfo = userInfo.filter { (key, value) in
|
|
||||||
return (value as AnyObject) is NSSecureCoding
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitize underlying errors.
|
|
||||||
if let underlyingError = userInfo[NSUnderlyingErrorKey] as? Error
|
|
||||||
{
|
|
||||||
let sanitizedError = (underlyingError as NSError).sanitizedForCoreData()
|
|
||||||
userInfo[NSUnderlyingErrorKey] = sanitizedError
|
|
||||||
}
|
|
||||||
|
|
||||||
if #available(iOS 14.5, macOS 11.3, *), let underlyingErrors = userInfo[NSMultipleUnderlyingErrorsKey] as? [Error]
|
|
||||||
{
|
|
||||||
let sanitizedErrors = underlyingErrors.map { ($0 as NSError).sanitizedForCoreData() }
|
|
||||||
userInfo[NSMultipleUnderlyingErrorsKey] = sanitizedErrors
|
|
||||||
}
|
|
||||||
|
|
||||||
let error = NSError(domain: self.domain, code: self.code, userInfo: userInfo)
|
|
||||||
return error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Error
|
|
||||||
{
|
|
||||||
var underlyingError: Error? {
|
|
||||||
let underlyingError = (self as NSError).userInfo[NSUnderlyingErrorKey] as? Error
|
|
||||||
return underlyingError
|
|
||||||
}
|
|
||||||
|
|
||||||
var localizedErrorCode: String {
|
|
||||||
let localizedErrorCode = String(format: NSLocalizedString("%@ error %@", comment: ""), (self as NSError).domain, (self as NSError).code as NSNumber)
|
|
||||||
return localizedErrorCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protocol ALTLocalizedError: LocalizedError, CustomNSError
|
|
||||||
{
|
|
||||||
var failure: String? { get }
|
|
||||||
|
|
||||||
var underlyingError: Error? { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ALTLocalizedError
|
|
||||||
{
|
|
||||||
var errorUserInfo: [String : Any] {
|
|
||||||
let userInfo = ([
|
|
||||||
NSLocalizedDescriptionKey: self.errorDescription,
|
|
||||||
NSLocalizedFailureReasonErrorKey: self.failureReason,
|
|
||||||
NSLocalizedFailureErrorKey: self.failure,
|
|
||||||
NSUnderlyingErrorKey: self.underlyingError
|
|
||||||
] as [String: Any?]).compactMapValues { $0 }
|
|
||||||
return userInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
var underlyingError: Error? {
|
|
||||||
// Error's default implementation calls errorUserInfo,
|
|
||||||
// but ALTLocalizedError.errorUserInfo calls underlyingError.
|
|
||||||
// Return nil to prevent infinite recursion.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var errorDescription: String? {
|
|
||||||
guard let errorFailure = self.failure else { return (self.underlyingError as NSError?)?.localizedDescription }
|
|
||||||
guard let failureReason = self.failureReason else { return errorFailure }
|
|
||||||
|
|
||||||
let errorDescription = errorFailure + " " + failureReason
|
|
||||||
return errorDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
var failureReason: String? { (self.underlyingError as NSError?)?.localizedDescription }
|
|
||||||
var recoverySuggestion: String? { (self.underlyingError as NSError?)?.localizedRecoverySuggestion }
|
|
||||||
var helpAnchor: String? { (self.underlyingError as NSError?)?.helpAnchor }
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// AltXPCProtocol.h
|
|
||||||
// AltXPC
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 12/2/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@class ALTAnisetteData;
|
|
||||||
|
|
||||||
@protocol AltXPCProtocol
|
|
||||||
|
|
||||||
- (void)ping:(void (^_Nonnull)(void))completionHandler;
|
|
||||||
- (void)requestAnisetteDataWithCompletionHandler:(void (^_Nonnull)(ALTAnisetteData *_Nullable anisetteData, NSError *_Nullable error))completionHandler;
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
//
|
|
||||||
// ViewController.swift
|
|
||||||
// AltBackup
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 5/11/20.
|
|
||||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension Bundle
|
|
||||||
{
|
|
||||||
var appName: String? {
|
|
||||||
let appName =
|
|
||||||
Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ??
|
|
||||||
Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as? String
|
|
||||||
return appName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ViewController
|
|
||||||
{
|
|
||||||
enum BackupOperation
|
|
||||||
{
|
|
||||||
case backup
|
|
||||||
case restore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ViewController: UIViewController
|
|
||||||
{
|
|
||||||
private let backupController = BackupController()
|
|
||||||
|
|
||||||
private var currentOperation: BackupOperation? {
|
|
||||||
didSet {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var textLabel: UILabel!
|
|
||||||
private var detailTextLabel: UILabel!
|
|
||||||
private var activityIndicatorView: UIActivityIndicatorView!
|
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
||||||
return .lightContent
|
|
||||||
}
|
|
||||||
|
|
||||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
|
|
||||||
{
|
|
||||||
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.backup), name: AppDelegate.startBackupNotification, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.restore), name: AppDelegate.startRestoreNotification, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.view.backgroundColor = .altstoreBackground
|
|
||||||
|
|
||||||
self.textLabel = UILabel(frame: .zero)
|
|
||||||
self.textLabel.font = UIFont.preferredFont(forTextStyle: .title2)
|
|
||||||
self.textLabel.textColor = .altstoreText
|
|
||||||
self.textLabel.textAlignment = .center
|
|
||||||
self.textLabel.numberOfLines = 0
|
|
||||||
|
|
||||||
self.detailTextLabel = UILabel(frame: .zero)
|
|
||||||
self.detailTextLabel.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
self.detailTextLabel.textColor = .altstoreText
|
|
||||||
self.detailTextLabel.textAlignment = .center
|
|
||||||
self.detailTextLabel.numberOfLines = 0
|
|
||||||
|
|
||||||
self.activityIndicatorView = UIActivityIndicatorView(style: .whiteLarge)
|
|
||||||
self.activityIndicatorView.color = .altstoreText
|
|
||||||
self.activityIndicatorView.startAnimating()
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
let button1 = UIButton(type: .system)
|
|
||||||
button1.setTitle("Backup", for: .normal)
|
|
||||||
button1.setTitleColor(.white, for: .normal)
|
|
||||||
button1.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
button1.addTarget(self, action: #selector(ViewController.backup), for: .primaryActionTriggered)
|
|
||||||
|
|
||||||
let button2 = UIButton(type: .system)
|
|
||||||
button2.setTitle("Restore", for: .normal)
|
|
||||||
button2.setTitleColor(.white, for: .normal)
|
|
||||||
button2.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
|
|
||||||
button2.addTarget(self, action: #selector(ViewController.restore), for: .primaryActionTriggered)
|
|
||||||
|
|
||||||
let arrangedSubviews = [self.textLabel!, self.detailTextLabel!, self.activityIndicatorView!, button1, button2]
|
|
||||||
#else
|
|
||||||
let arrangedSubviews = [self.textLabel!, self.detailTextLabel!, self.activityIndicatorView!]
|
|
||||||
#endif
|
|
||||||
|
|
||||||
let stackView = UIStackView(arrangedSubviews: arrangedSubviews)
|
|
||||||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
stackView.spacing = 22
|
|
||||||
stackView.axis = .vertical
|
|
||||||
stackView.alignment = .center
|
|
||||||
self.view.addSubview(stackView)
|
|
||||||
|
|
||||||
NSLayoutConstraint.activate([stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
|
|
||||||
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
|
|
||||||
stackView.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: self.view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1.0),
|
|
||||||
self.view.safeAreaLayoutGuide.trailingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: stackView.trailingAnchor, multiplier: 1.0)])
|
|
||||||
|
|
||||||
self.update()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension ViewController
|
|
||||||
{
|
|
||||||
@objc func backup()
|
|
||||||
{
|
|
||||||
self.currentOperation = .backup
|
|
||||||
|
|
||||||
self.backupController.performBackup { (result) in
|
|
||||||
let appName = Bundle.main.appName ?? NSLocalizedString("App", comment: "")
|
|
||||||
|
|
||||||
let title = String(format: NSLocalizedString("%@ could not be backed up.", comment: ""), appName)
|
|
||||||
self.process(result, errorTitle: title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func restore()
|
|
||||||
{
|
|
||||||
self.currentOperation = .restore
|
|
||||||
|
|
||||||
self.backupController.restoreBackup { (result) in
|
|
||||||
let appName = Bundle.main.appName ?? NSLocalizedString("App", comment: "")
|
|
||||||
|
|
||||||
let title = String(format: NSLocalizedString("%@ could not be restored.", comment: ""), appName)
|
|
||||||
self.process(result, errorTitle: title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func update()
|
|
||||||
{
|
|
||||||
switch self.currentOperation
|
|
||||||
{
|
|
||||||
case .backup:
|
|
||||||
self.textLabel.text = NSLocalizedString("Backing up app data…", comment: "")
|
|
||||||
self.detailTextLabel.isHidden = true
|
|
||||||
self.activityIndicatorView.startAnimating()
|
|
||||||
|
|
||||||
case .restore:
|
|
||||||
self.textLabel.text = NSLocalizedString("Restoring app data…", comment: "")
|
|
||||||
self.detailTextLabel.isHidden = true
|
|
||||||
self.activityIndicatorView.startAnimating()
|
|
||||||
|
|
||||||
case .none:
|
|
||||||
self.textLabel.text = String(format: NSLocalizedString("%@ is inactive.", comment: ""),
|
|
||||||
Bundle.main.appName ?? NSLocalizedString("App", comment: ""))
|
|
||||||
|
|
||||||
self.detailTextLabel.text = String(format: NSLocalizedString("Refresh %@ in SideStore to continue using it.", comment: ""),
|
|
||||||
Bundle.main.appName ?? NSLocalizedString("this app", comment: ""))
|
|
||||||
|
|
||||||
self.detailTextLabel.isHidden = false
|
|
||||||
self.activityIndicatorView.stopAnimating()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension ViewController
|
|
||||||
{
|
|
||||||
func process(_ result: Result<Void, Error>, errorTitle: String)
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
switch result
|
|
||||||
{
|
|
||||||
case .success: break
|
|
||||||
case .failure(let error as NSError):
|
|
||||||
let message: String
|
|
||||||
|
|
||||||
if let sourceDescription = error.sourceDescription
|
|
||||||
{
|
|
||||||
message = error.localizedDescription + "\n\n" + sourceDescription
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
message = error.localizedDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
let alertController = UIAlertController(title: errorTitle, message: message, preferredStyle: .alert)
|
|
||||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default, handler: nil))
|
|
||||||
self.present(alertController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationCenter.default.post(name: AppDelegate.operationDidFinishNotification, object: nil, userInfo: [AppDelegate.operationResultKey: result])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func didEnterBackground(_ notification: Notification)
|
|
||||||
{
|
|
||||||
// Reset UI once we've left app (but not before).
|
|
||||||
self.currentOperation = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
Sources/AltPatcher/include/ALTAppPatcher/ALTAppPatcher.h
Normal file
19
Sources/AltPatcher/include/ALTAppPatcher/ALTAppPatcher.h
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
//
|
||||||
|
// ALTAppPatcher.h
|
||||||
|
// ALTAppPatcher
|
||||||
|
//
|
||||||
|
// Created by Joseph Mattiello on 03/01/23.
|
||||||
|
// Copyright © 2023 Provenance Emu. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
//! Project version number for ALTAppPatcher.
|
||||||
|
FOUNDATION_EXPORT double ALTAppPatcherVersionNumber;
|
||||||
|
|
||||||
|
//! Project version string for ALTAppPatcher.
|
||||||
|
FOUNDATION_EXPORT const unsigned char ALTAppPatcherVersionString[];
|
||||||
|
|
||||||
|
|
||||||
|
# pragma mark - ALTAppPatcher
|
||||||
|
#import <ALTAppPatcher/_ALTAppPatcher.h>
|
||||||
1
Sources/AltPatcher/include/ALTAppPatcher/_ALTAppPatcher.h
Symbolic link
1
Sources/AltPatcher/include/ALTAppPatcher/_ALTAppPatcher.h
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../ALTAppPatcher.h
|
||||||
61
Sources/Cargo/Commands/Build.swift
Normal file
61
Sources/Cargo/Commands/Build.swift
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension SwiftLint {
|
||||||
|
struct Build: AsyncParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(abstract: "Print lint warnings and errors")
|
||||||
|
|
||||||
|
@OptionGroup
|
||||||
|
var common: LintOrAnalyzeArguments
|
||||||
|
@Option(help: pathOptionDescription(for: .build))
|
||||||
|
var path: String?
|
||||||
|
@Flag(help: quietOptionDescription(for: .build))
|
||||||
|
var quiet = false
|
||||||
|
@Option(help: "The directory of the cache used when linting.")
|
||||||
|
var cachePath: String?
|
||||||
|
@Flag(help: "Ignore cache when linting.")
|
||||||
|
var noCache = false
|
||||||
|
@Flag(help: "Run all rules, even opt-in and disabled ones, ignoring `only_rules`.")
|
||||||
|
var enableAllRules = false
|
||||||
|
@Argument(help: pathsArgumentDescription(for: .build))
|
||||||
|
var paths = [String]()
|
||||||
|
|
||||||
|
func run() async throws {
|
||||||
|
let allPaths: [String]
|
||||||
|
if let path {
|
||||||
|
queuedPrintError("""
|
||||||
|
warning: The --path option is deprecated. Pass the path(s) to lint last to the swiftlint command.
|
||||||
|
""")
|
||||||
|
allPaths = [path] + paths
|
||||||
|
} else if !paths.isEmpty {
|
||||||
|
allPaths = paths
|
||||||
|
} else {
|
||||||
|
allPaths = [""] // Lint files in current working directory if no paths were specified.
|
||||||
|
}
|
||||||
|
let options = LintOrAnalyzeOptions(
|
||||||
|
mode: .build,
|
||||||
|
paths: allPaths,
|
||||||
|
configurationFiles: common.config,
|
||||||
|
strict: common.leniency == .strict,
|
||||||
|
lenient: common.leniency == .lenient,
|
||||||
|
forceExclude: common.forceExclude,
|
||||||
|
useExcludingByPrefix: common.useAlternativeExcluding,
|
||||||
|
useScriptInputFiles: common.useScriptInputFiles,
|
||||||
|
benchmark: common.benchmark,
|
||||||
|
reporter: common.reporter,
|
||||||
|
quiet: quiet,
|
||||||
|
output: common.output,
|
||||||
|
progress: common.progress,
|
||||||
|
cachePath: cachePath,
|
||||||
|
ignoreCache: noCache,
|
||||||
|
enableAllRules: enableAllRules,
|
||||||
|
autocorrect: common.fix,
|
||||||
|
format: common.format,
|
||||||
|
compilerLogPath: nil,
|
||||||
|
compileCommands: nil,
|
||||||
|
inProcessSourcekit: common.inProcessSourcekit
|
||||||
|
)
|
||||||
|
try await LintOrAnalyzeCommand.run(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
Sources/Cargo/Commands/Cargo.swift
Normal file
22
Sources/Cargo/Commands/Cargo.swift
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct Cargo: AsyncParsableCommand {
|
||||||
|
static let configuration: CommandConfiguration = {
|
||||||
|
if let directory = ProcessInfo.processInfo.environment["BUILD_WORKSPACE_DIRECTORY"] {
|
||||||
|
FileManager.default.changeCurrentDirectoryPath(directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandConfiguration(
|
||||||
|
commandName: "cargo",
|
||||||
|
abstract: "A tool to build `rust` projects with `cargo`.",
|
||||||
|
version: Version.value,
|
||||||
|
subcommands: [
|
||||||
|
Build.self,
|
||||||
|
Version.self
|
||||||
|
],
|
||||||
|
defaultSubcommand: Build.self
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
extension RulesFilter.ExcludingOptions {
|
||||||
|
static func excludingOptions(byCommandLineOptions rulesFilterOptions: RulesFilterOptions) -> Self {
|
||||||
|
var excludingOptions: Self = []
|
||||||
|
|
||||||
|
switch rulesFilterOptions.ruleEnablement {
|
||||||
|
case .enabled:
|
||||||
|
excludingOptions.insert(.disabled)
|
||||||
|
case .disabled:
|
||||||
|
excludingOptions.insert(.enabled)
|
||||||
|
case .none:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if rulesFilterOptions.correctable {
|
||||||
|
excludingOptions.insert(.uncorrectable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return excludingOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Sources/Cargo/Commands/Common/RulesFilterOptions.swift
Normal file
20
Sources/Cargo/Commands/Common/RulesFilterOptions.swift
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
enum RuleEnablementOptions: String, EnumerableFlag {
|
||||||
|
case enabled, disabled
|
||||||
|
|
||||||
|
static func name(for value: RuleEnablementOptions) -> NameSpecification {
|
||||||
|
return .shortAndLong
|
||||||
|
}
|
||||||
|
|
||||||
|
static func help(for value: RuleEnablementOptions) -> ArgumentHelp? {
|
||||||
|
return "Only show \(value.rawValue) rules"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RulesFilterOptions: ParsableArguments {
|
||||||
|
@Flag(exclusivity: .exclusive)
|
||||||
|
var ruleEnablement: RuleEnablementOptions?
|
||||||
|
@Flag(name: .shortAndLong, help: "Only display correctable rules")
|
||||||
|
var correctable = false
|
||||||
|
}
|
||||||
23
Sources/Cargo/Commands/Version.swift
Normal file
23
Sources/Cargo/Commands/Version.swift
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension Cargo {
|
||||||
|
struct Version: ParsableCommand {
|
||||||
|
@Flag(help: "Display full version info")
|
||||||
|
var verbose = false
|
||||||
|
|
||||||
|
static let configuration = CommandConfiguration(abstract: "Display the current version of Cargo")
|
||||||
|
|
||||||
|
static var value: String { "TODO" }
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
if verbose, let buildID = ExecutableInfo.buildID {
|
||||||
|
print("Version:", Self.value)
|
||||||
|
print("Build ID:", buildID)
|
||||||
|
} else {
|
||||||
|
print(Self.value)
|
||||||
|
}
|
||||||
|
ExitHelper.successfullyExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
61
Sources/Cargo/swiftlint/Commands/Analyze.swift
Normal file
61
Sources/Cargo/swiftlint/Commands/Analyze.swift
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension SwiftLint {
|
||||||
|
struct Analyze: AsyncParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(abstract: "Run analysis rules")
|
||||||
|
|
||||||
|
@OptionGroup
|
||||||
|
var common: LintOrAnalyzeArguments
|
||||||
|
@Option(help: pathOptionDescription(for: .analyze))
|
||||||
|
var path: String?
|
||||||
|
@Flag(help: quietOptionDescription(for: .analyze))
|
||||||
|
var quiet = false
|
||||||
|
@Option(help: "The path of the full xcodebuild log to use when running AnalyzerRules.")
|
||||||
|
var compilerLogPath: String?
|
||||||
|
@Option(help: "The path of a compilation database to use when running AnalyzerRules.")
|
||||||
|
var compileCommands: String?
|
||||||
|
@Argument(help: pathsArgumentDescription(for: .analyze))
|
||||||
|
var paths = [String]()
|
||||||
|
|
||||||
|
func run() async throws {
|
||||||
|
let allPaths: [String]
|
||||||
|
if let path {
|
||||||
|
queuedPrintError("""
|
||||||
|
warning: The --path option is deprecated. Pass the path(s) to analyze last to the swiftlint command.
|
||||||
|
""")
|
||||||
|
allPaths = [path] + paths
|
||||||
|
} else if !paths.isEmpty {
|
||||||
|
allPaths = paths
|
||||||
|
} else {
|
||||||
|
allPaths = [""] // Analyze files in current working directory if no paths were specified.
|
||||||
|
}
|
||||||
|
let options = LintOrAnalyzeOptions(
|
||||||
|
mode: .analyze,
|
||||||
|
paths: allPaths,
|
||||||
|
useSTDIN: false,
|
||||||
|
configurationFiles: common.config,
|
||||||
|
strict: common.leniency == .strict,
|
||||||
|
lenient: common.leniency == .lenient,
|
||||||
|
forceExclude: common.forceExclude,
|
||||||
|
useExcludingByPrefix: common.useAlternativeExcluding,
|
||||||
|
useScriptInputFiles: common.useScriptInputFiles,
|
||||||
|
benchmark: common.benchmark,
|
||||||
|
reporter: common.reporter,
|
||||||
|
quiet: quiet,
|
||||||
|
output: common.output,
|
||||||
|
progress: common.progress,
|
||||||
|
cachePath: nil,
|
||||||
|
ignoreCache: true,
|
||||||
|
enableAllRules: false,
|
||||||
|
autocorrect: common.fix,
|
||||||
|
format: common.format,
|
||||||
|
compilerLogPath: compilerLogPath,
|
||||||
|
compileCommands: compileCommands,
|
||||||
|
inProcessSourcekit: common.inProcessSourcekit
|
||||||
|
)
|
||||||
|
|
||||||
|
try await LintOrAnalyzeCommand.run(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
extension RulesFilter.ExcludingOptions {
|
||||||
|
static func excludingOptions(byCommandLineOptions rulesFilterOptions: RulesFilterOptions) -> Self {
|
||||||
|
var excludingOptions: Self = []
|
||||||
|
|
||||||
|
switch rulesFilterOptions.ruleEnablement {
|
||||||
|
case .enabled:
|
||||||
|
excludingOptions.insert(.disabled)
|
||||||
|
case .disabled:
|
||||||
|
excludingOptions.insert(.enabled)
|
||||||
|
case .none:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if rulesFilterOptions.correctable {
|
||||||
|
excludingOptions.insert(.uncorrectable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return excludingOptions
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
enum RuleEnablementOptions: String, EnumerableFlag {
|
||||||
|
case enabled, disabled
|
||||||
|
|
||||||
|
static func name(for value: RuleEnablementOptions) -> NameSpecification {
|
||||||
|
return .shortAndLong
|
||||||
|
}
|
||||||
|
|
||||||
|
static func help(for value: RuleEnablementOptions) -> ArgumentHelp? {
|
||||||
|
return "Only show \(value.rawValue) rules"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RulesFilterOptions: ParsableArguments {
|
||||||
|
@Flag(exclusivity: .exclusive)
|
||||||
|
var ruleEnablement: RuleEnablementOptions?
|
||||||
|
@Flag(name: .shortAndLong, help: "Only display correctable rules")
|
||||||
|
var correctable = false
|
||||||
|
}
|
||||||
43
Sources/Cargo/swiftlint/Commands/Docs.swift
Normal file
43
Sources/Cargo/swiftlint/Commands/Docs.swift
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension SwiftLint {
|
||||||
|
struct Docs: ParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(
|
||||||
|
abstract: "Open SwiftLint documentation website in the default web browser"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Argument(help: "The identifier of the rule to open the documentation for")
|
||||||
|
var ruleID: String?
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
var subPage = ""
|
||||||
|
if let ruleID {
|
||||||
|
if primaryRuleList.list[ruleID] == nil {
|
||||||
|
queuedPrintError("There is no rule named '\(ruleID)'. Opening rule directory instead.")
|
||||||
|
subPage = "rule-directory.html"
|
||||||
|
} else {
|
||||||
|
subPage = ruleID + ".html"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
open(URL(string: "https://realm.github.io/SwiftLint/\(subPage)")!)
|
||||||
|
ExitHelper.successfullyExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func open(_ url: URL) {
|
||||||
|
let process = Process()
|
||||||
|
#if os(Linux)
|
||||||
|
process.executableURL = URL(fileURLWithPath: "/usr/bin/env", isDirectory: false)
|
||||||
|
let command = "xdg-open"
|
||||||
|
process.arguments = [command, url.absoluteString]
|
||||||
|
try? process.run()
|
||||||
|
#else
|
||||||
|
process.launchPath = "/usr/bin/env"
|
||||||
|
let command = "open"
|
||||||
|
process.arguments = [command, url.absoluteString]
|
||||||
|
process.launch()
|
||||||
|
#endif
|
||||||
|
}
|
||||||
28
Sources/Cargo/swiftlint/Commands/GenerateDocs.swift
Normal file
28
Sources/Cargo/swiftlint/Commands/GenerateDocs.swift
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension SwiftLint {
|
||||||
|
struct GenerateDocs: ParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(
|
||||||
|
abstract: "Generates markdown documentation for selected group of rules"
|
||||||
|
)
|
||||||
|
|
||||||
|
@Option(help: "The directory where the documentation should be saved")
|
||||||
|
var path = "rule_docs"
|
||||||
|
@Option(help: "The path to a SwiftLint configuration file")
|
||||||
|
var config: String?
|
||||||
|
@OptionGroup
|
||||||
|
var rulesFilterOptions: RulesFilterOptions
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
let configuration = Configuration(configurationFiles: [config].compactMap({ $0 }))
|
||||||
|
let rulesFilter = RulesFilter(enabledRules: configuration.rules)
|
||||||
|
let rules = rulesFilter.getRules(excluding: .excludingOptions(byCommandLineOptions: rulesFilterOptions))
|
||||||
|
|
||||||
|
try RuleListDocumentation(rules)
|
||||||
|
.write(to: URL(fileURLWithPath: path, isDirectory: true))
|
||||||
|
ExitHelper.successfullyExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Sources/Cargo/swiftlint/Commands/Lint.swift
Normal file
64
Sources/Cargo/swiftlint/Commands/Lint.swift
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension SwiftLint {
|
||||||
|
struct Lint: AsyncParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(abstract: "Print lint warnings and errors")
|
||||||
|
|
||||||
|
@OptionGroup
|
||||||
|
var common: LintOrAnalyzeArguments
|
||||||
|
@Option(help: pathOptionDescription(for: .lint))
|
||||||
|
var path: String?
|
||||||
|
@Flag(help: "Lint standard input.")
|
||||||
|
var useSTDIN = false
|
||||||
|
@Flag(help: quietOptionDescription(for: .lint))
|
||||||
|
var quiet = false
|
||||||
|
@Option(help: "The directory of the cache used when linting.")
|
||||||
|
var cachePath: String?
|
||||||
|
@Flag(help: "Ignore cache when linting.")
|
||||||
|
var noCache = false
|
||||||
|
@Flag(help: "Run all rules, even opt-in and disabled ones, ignoring `only_rules`.")
|
||||||
|
var enableAllRules = false
|
||||||
|
@Argument(help: pathsArgumentDescription(for: .lint))
|
||||||
|
var paths = [String]()
|
||||||
|
|
||||||
|
func run() async throws {
|
||||||
|
let allPaths: [String]
|
||||||
|
if let path {
|
||||||
|
queuedPrintError("""
|
||||||
|
warning: The --path option is deprecated. Pass the path(s) to lint last to the swiftlint command.
|
||||||
|
""")
|
||||||
|
allPaths = [path] + paths
|
||||||
|
} else if !paths.isEmpty {
|
||||||
|
allPaths = paths
|
||||||
|
} else {
|
||||||
|
allPaths = [""] // Lint files in current working directory if no paths were specified.
|
||||||
|
}
|
||||||
|
let options = LintOrAnalyzeOptions(
|
||||||
|
mode: .lint,
|
||||||
|
paths: allPaths,
|
||||||
|
useSTDIN: useSTDIN,
|
||||||
|
configurationFiles: common.config,
|
||||||
|
strict: common.leniency == .strict,
|
||||||
|
lenient: common.leniency == .lenient,
|
||||||
|
forceExclude: common.forceExclude,
|
||||||
|
useExcludingByPrefix: common.useAlternativeExcluding,
|
||||||
|
useScriptInputFiles: common.useScriptInputFiles,
|
||||||
|
benchmark: common.benchmark,
|
||||||
|
reporter: common.reporter,
|
||||||
|
quiet: quiet,
|
||||||
|
output: common.output,
|
||||||
|
progress: common.progress,
|
||||||
|
cachePath: cachePath,
|
||||||
|
ignoreCache: noCache,
|
||||||
|
enableAllRules: enableAllRules,
|
||||||
|
autocorrect: common.fix,
|
||||||
|
format: common.format,
|
||||||
|
compilerLogPath: nil,
|
||||||
|
compileCommands: nil,
|
||||||
|
inProcessSourcekit: common.inProcessSourcekit
|
||||||
|
)
|
||||||
|
try await LintOrAnalyzeCommand.run(options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
123
Sources/Cargo/swiftlint/Commands/Rules.swift
Normal file
123
Sources/Cargo/swiftlint/Commands/Rules.swift
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
#if canImport(Darwin)
|
||||||
|
import Darwin
|
||||||
|
#elseif canImport(Glibc)
|
||||||
|
import Glibc
|
||||||
|
#else
|
||||||
|
#error("Unsupported platform")
|
||||||
|
#endif
|
||||||
|
import Foundation
|
||||||
|
@_spi(TestHelper)
|
||||||
|
import SwiftLintFramework
|
||||||
|
import SwiftyTextTable
|
||||||
|
|
||||||
|
extension SwiftLint {
|
||||||
|
struct Rules: ParsableCommand {
|
||||||
|
static let configuration = CommandConfiguration(abstract: "Display the list of rules and their identifiers")
|
||||||
|
|
||||||
|
@Option(help: "The path to a SwiftLint configuration file")
|
||||||
|
var config: String?
|
||||||
|
@OptionGroup
|
||||||
|
var rulesFilterOptions: RulesFilterOptions
|
||||||
|
@Flag(name: .shortAndLong, help: "Display full configuration details")
|
||||||
|
var verbose = false
|
||||||
|
@Argument(help: "The rule identifier to display description for")
|
||||||
|
var ruleID: String?
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
if let ruleID {
|
||||||
|
guard let rule = primaryRuleList.list[ruleID] else {
|
||||||
|
throw SwiftLintError.usageError(description: "No rule with identifier: \(ruleID)")
|
||||||
|
}
|
||||||
|
|
||||||
|
rule.description.printDescription()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let configuration = Configuration(configurationFiles: [config].compactMap({ $0 }))
|
||||||
|
let rulesFilter = RulesFilter(enabledRules: configuration.rules)
|
||||||
|
let rules = rulesFilter.getRules(excluding: .excludingOptions(byCommandLineOptions: rulesFilterOptions))
|
||||||
|
let table = TextTable(ruleList: rules, configuration: configuration, verbose: verbose)
|
||||||
|
print(table.render())
|
||||||
|
ExitHelper.successfullyExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension RuleDescription {
|
||||||
|
func printDescription() {
|
||||||
|
print("\(consoleDescription)")
|
||||||
|
|
||||||
|
guard !triggeringExamples.isEmpty else { return }
|
||||||
|
|
||||||
|
func indent(_ string: String) -> String {
|
||||||
|
return string.components(separatedBy: "\n")
|
||||||
|
.map { " \($0)" }
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
print("\nTriggering Examples (violation is marked with '↓'):")
|
||||||
|
for (index, example) in triggeringExamples.enumerated() {
|
||||||
|
print("\nExample #\(index + 1)\n\n\(indent(example.code))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - SwiftyTextTable
|
||||||
|
|
||||||
|
private extension TextTable {
|
||||||
|
init(ruleList: RuleList, configuration: Configuration, verbose: Bool) {
|
||||||
|
let columns = [
|
||||||
|
TextTableColumn(header: "identifier"),
|
||||||
|
TextTableColumn(header: "opt-in"),
|
||||||
|
TextTableColumn(header: "correctable"),
|
||||||
|
TextTableColumn(header: "enabled in your config"),
|
||||||
|
TextTableColumn(header: "kind"),
|
||||||
|
TextTableColumn(header: "analyzer"),
|
||||||
|
TextTableColumn(header: "uses sourcekit"),
|
||||||
|
TextTableColumn(header: "configuration")
|
||||||
|
]
|
||||||
|
self.init(columns: columns)
|
||||||
|
let sortedRules = ruleList.list.sorted { $0.0 < $1.0 }
|
||||||
|
func truncate(_ string: String) -> String {
|
||||||
|
let stringWithNoNewlines = string.replacingOccurrences(of: "\n", with: "\\n")
|
||||||
|
let minWidth = "configuration".count - "...".count
|
||||||
|
let configurationStartColumn = 140
|
||||||
|
let maxWidth = verbose ? Int.max : Terminal.currentWidth()
|
||||||
|
let truncatedEndIndex = stringWithNoNewlines.index(
|
||||||
|
stringWithNoNewlines.startIndex,
|
||||||
|
offsetBy: max(minWidth, maxWidth - configurationStartColumn),
|
||||||
|
limitedBy: stringWithNoNewlines.endIndex
|
||||||
|
)
|
||||||
|
if let truncatedEndIndex {
|
||||||
|
return stringWithNoNewlines[..<truncatedEndIndex] + "..."
|
||||||
|
}
|
||||||
|
return stringWithNoNewlines
|
||||||
|
}
|
||||||
|
for (ruleID, ruleType) in sortedRules {
|
||||||
|
let rule = ruleType.init()
|
||||||
|
let configuredRule = configuration.configuredRule(forID: ruleID)
|
||||||
|
addRow(values: [
|
||||||
|
ruleID,
|
||||||
|
(rule is OptInRule) ? "yes" : "no",
|
||||||
|
(rule is CorrectableRule) ? "yes" : "no",
|
||||||
|
configuredRule != nil ? "yes" : "no",
|
||||||
|
ruleType.description.kind.rawValue,
|
||||||
|
(rule is AnalyzerRule) ? "yes" : "no",
|
||||||
|
(rule is SourceKitFreeRule) ? "no" : "yes",
|
||||||
|
truncate((configuredRule ?? rule).configurationDescription)
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Terminal {
|
||||||
|
static func currentWidth() -> Int {
|
||||||
|
var size = winsize()
|
||||||
|
#if os(Linux)
|
||||||
|
_ = ioctl(CInt(STDOUT_FILENO), UInt(TIOCGWINSZ), &size)
|
||||||
|
#else
|
||||||
|
_ = ioctl(STDOUT_FILENO, TIOCGWINSZ, &size)
|
||||||
|
#endif
|
||||||
|
return Int(size.ws_col)
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Sources/Cargo/swiftlint/Commands/SwiftLint.swift
Normal file
26
Sources/Cargo/swiftlint/Commands/SwiftLint.swift
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct SwiftLint: AsyncParsableCommand {
|
||||||
|
static let configuration: CommandConfiguration = {
|
||||||
|
if let directory = ProcessInfo.processInfo.environment["BUILD_WORKSPACE_DIRECTORY"] {
|
||||||
|
FileManager.default.changeCurrentDirectoryPath(directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandConfiguration(
|
||||||
|
commandName: "swiftlint",
|
||||||
|
abstract: "A tool to enforce Swift style and conventions.",
|
||||||
|
version: Version.value,
|
||||||
|
subcommands: [
|
||||||
|
Analyze.self,
|
||||||
|
Docs.self,
|
||||||
|
GenerateDocs.self,
|
||||||
|
Lint.self,
|
||||||
|
Rules.self,
|
||||||
|
Version.self
|
||||||
|
],
|
||||||
|
defaultSubcommand: Lint.self
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
}
|
||||||
23
Sources/Cargo/swiftlint/Commands/Version.swift
Normal file
23
Sources/Cargo/swiftlint/Commands/Version.swift
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension SwiftLint {
|
||||||
|
struct Version: ParsableCommand {
|
||||||
|
@Flag(help: "Display full version info")
|
||||||
|
var verbose = false
|
||||||
|
|
||||||
|
static let configuration = CommandConfiguration(abstract: "Display the current version of SwiftLint")
|
||||||
|
|
||||||
|
static var value: String { SwiftLintFramework.Version.current.value }
|
||||||
|
|
||||||
|
func run() throws {
|
||||||
|
if verbose, let buildID = ExecutableInfo.buildID {
|
||||||
|
print("Version:", Self.value)
|
||||||
|
print("Build ID:", buildID)
|
||||||
|
} else {
|
||||||
|
print(Self.value)
|
||||||
|
}
|
||||||
|
ExitHelper.successfullyExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
import CollectionConcurrencyKit
|
||||||
|
import Foundation
|
||||||
|
import SourceKittenFramework
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
private actor CounterActor {
|
||||||
|
private var count = 0
|
||||||
|
|
||||||
|
func next() -> Int {
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func scriptInputFiles() throws -> [SwiftLintFile] {
|
||||||
|
let inputFileKey = "SCRIPT_INPUT_FILE_COUNT"
|
||||||
|
guard let countString = ProcessInfo.processInfo.environment[inputFileKey] else {
|
||||||
|
throw SwiftLintError.usageError(description: "\(inputFileKey) variable not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let count = Int(countString) else {
|
||||||
|
throw SwiftLintError.usageError(description: "\(inputFileKey) did not specify a number")
|
||||||
|
}
|
||||||
|
|
||||||
|
return (0..<count).compactMap { fileNumber in
|
||||||
|
do {
|
||||||
|
let environment = ProcessInfo.processInfo.environment
|
||||||
|
let variable = "SCRIPT_INPUT_FILE_\(fileNumber)"
|
||||||
|
guard let path = environment[variable] else {
|
||||||
|
throw SwiftLintError.usageError(description: "Environment variable not set: \(variable)")
|
||||||
|
}
|
||||||
|
if path.bridge().isSwiftFile() {
|
||||||
|
return SwiftLintFile(pathDeferringReading: path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} catch {
|
||||||
|
queuedPrintError(String(describing: error))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(Linux)
|
||||||
|
private func autoreleasepool<T>(block: () -> T) -> T { return block() }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extension Configuration {
|
||||||
|
func visitLintableFiles(with visitor: LintableFilesVisitor, storage: RuleStorage) async throws -> [SwiftLintFile] {
|
||||||
|
let files = try await Signposts.record(name: "Configuration.VisitLintableFiles.GetFiles") {
|
||||||
|
try await getFiles(with: visitor)
|
||||||
|
}
|
||||||
|
let groupedFiles = try Signposts.record(name: "Configuration.VisitLintableFiles.GroupFiles") {
|
||||||
|
try groupFiles(files, visitor: visitor)
|
||||||
|
}
|
||||||
|
let lintersForFile = Signposts.record(name: "Configuration.VisitLintableFiles.LintersForFile") {
|
||||||
|
groupedFiles.map { file in
|
||||||
|
linters(for: [file.key: file.value], visitor: visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let duplicateFileNames = Signposts.record(name: "Configuration.VisitLintableFiles.DuplicateFileNames") {
|
||||||
|
lintersForFile.map(\.duplicateFileNames)
|
||||||
|
}
|
||||||
|
let collected = await Signposts.record(name: "Configuration.VisitLintableFiles.Collect") {
|
||||||
|
await zip(lintersForFile, duplicateFileNames).asyncMap { linters, duplicateFileNames in
|
||||||
|
await collect(linters: linters, visitor: visitor, storage: storage,
|
||||||
|
duplicateFileNames: duplicateFileNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result = await Signposts.record(name: "Configuration.VisitLintableFiles.Visit") {
|
||||||
|
await collected.asyncMap { linters, duplicateFileNames in
|
||||||
|
await visit(linters: linters, visitor: visitor, duplicateFileNames: duplicateFileNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.flatMap { $0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
private func groupFiles(_ files: [SwiftLintFile], visitor: LintableFilesVisitor) throws
|
||||||
|
-> [Configuration: [SwiftLintFile]] {
|
||||||
|
if files.isEmpty && !visitor.allowZeroLintableFiles {
|
||||||
|
throw SwiftLintError.usageError(
|
||||||
|
description: "No lintable files found at paths: '\(visitor.paths.joined(separator: ", "))'"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var groupedFiles = [Configuration: [SwiftLintFile]]()
|
||||||
|
for file in files {
|
||||||
|
let fileConfiguration = configuration(for: file)
|
||||||
|
let fileConfigurationRootPath = fileConfiguration.rootDirectory.bridge()
|
||||||
|
|
||||||
|
// Files whose configuration specifies they should be excluded will be skipped
|
||||||
|
let shouldSkip = fileConfiguration.excludedPaths.contains { excludedRelativePath in
|
||||||
|
let excludedPath = fileConfigurationRootPath.appendingPathComponent(excludedRelativePath)
|
||||||
|
let filePathComponents = file.path?.bridge().pathComponents ?? []
|
||||||
|
let excludedPathComponents = excludedPath.bridge().pathComponents
|
||||||
|
return filePathComponents.starts(with: excludedPathComponents)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !shouldSkip {
|
||||||
|
groupedFiles[fileConfiguration, default: []].append(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupedFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
private func outputFilename(for path: String, duplicateFileNames: Set<String>) -> String {
|
||||||
|
let basename = path.bridge().lastPathComponent
|
||||||
|
if !duplicateFileNames.contains(basename) {
|
||||||
|
return basename
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathComponents = path.bridge().pathComponents
|
||||||
|
for component in rootDirectory.bridge().pathComponents where pathComponents.first == component {
|
||||||
|
pathComponents.removeFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathComponents.joined(separator: "/")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func linters(for filesPerConfiguration: [Configuration: [SwiftLintFile]],
|
||||||
|
visitor: LintableFilesVisitor) -> [Linter] {
|
||||||
|
let fileCount = filesPerConfiguration.reduce(0) { $0 + $1.value.count }
|
||||||
|
|
||||||
|
var linters = [Linter]()
|
||||||
|
linters.reserveCapacity(fileCount)
|
||||||
|
for (config, files) in filesPerConfiguration {
|
||||||
|
let newConfig: Configuration
|
||||||
|
if visitor.cache != nil {
|
||||||
|
newConfig = config.withPrecomputedCacheDescription()
|
||||||
|
} else {
|
||||||
|
newConfig = config
|
||||||
|
}
|
||||||
|
linters += files.map { visitor.linter(forFile: $0, configuration: newConfig) }
|
||||||
|
}
|
||||||
|
return linters
|
||||||
|
}
|
||||||
|
|
||||||
|
private func collect(linters: [Linter],
|
||||||
|
visitor: LintableFilesVisitor,
|
||||||
|
storage: RuleStorage,
|
||||||
|
duplicateFileNames: Set<String>) async -> ([CollectedLinter], Set<String>) {
|
||||||
|
let counter = CounterActor()
|
||||||
|
let total = linters.filter(\.isCollecting).count
|
||||||
|
let progress = ProgressBar(count: total)
|
||||||
|
if visitor.showProgressBar && total > 0 {
|
||||||
|
await progress.initialize()
|
||||||
|
}
|
||||||
|
let collect = { (linter: Linter) -> CollectedLinter? in
|
||||||
|
let skipFile = visitor.shouldSkipFile(atPath: linter.file.path)
|
||||||
|
if !visitor.quiet && linter.isCollecting {
|
||||||
|
if visitor.showProgressBar {
|
||||||
|
await progress.printNext()
|
||||||
|
} else if let filePath = linter.file.path {
|
||||||
|
let outputFilename = self.outputFilename(for: filePath, duplicateFileNames: duplicateFileNames)
|
||||||
|
let collected = await counter.next()
|
||||||
|
if skipFile {
|
||||||
|
queuedPrintError("""
|
||||||
|
Skipping '\(outputFilename)' (\(collected)/\(total)) \
|
||||||
|
because its compiler arguments could not be found
|
||||||
|
""")
|
||||||
|
} else {
|
||||||
|
queuedPrintError("Collecting '\(outputFilename)' (\(collected)/\(total))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard !skipFile else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return autoreleasepool {
|
||||||
|
linter.collect(into: storage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let collectedLinters = await visitor.parallel ?
|
||||||
|
linters.concurrentCompactMap(collect) :
|
||||||
|
linters.asyncCompactMap(collect)
|
||||||
|
return (collectedLinters, duplicateFileNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func visit(linters: [CollectedLinter],
|
||||||
|
visitor: LintableFilesVisitor,
|
||||||
|
duplicateFileNames: Set<String>) async -> [SwiftLintFile] {
|
||||||
|
let counter = CounterActor()
|
||||||
|
let progress = ProgressBar(count: linters.count)
|
||||||
|
if visitor.showProgressBar {
|
||||||
|
await progress.initialize()
|
||||||
|
}
|
||||||
|
let visit = { (linter: CollectedLinter) -> SwiftLintFile in
|
||||||
|
if !visitor.quiet {
|
||||||
|
if visitor.showProgressBar {
|
||||||
|
await progress.printNext()
|
||||||
|
} else if let filePath = linter.file.path {
|
||||||
|
let outputFilename = self.outputFilename(for: filePath, duplicateFileNames: duplicateFileNames)
|
||||||
|
let visited = await counter.next()
|
||||||
|
queuedPrintError("\(visitor.action) '\(outputFilename)' (\(visited)/\(linters.count))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Signposts.record(name: "Configuration.Visit", span: .file(linter.file.path ?? "")) {
|
||||||
|
await visitor.block(linter)
|
||||||
|
}
|
||||||
|
return linter.file
|
||||||
|
}
|
||||||
|
return await visitor.parallel ?
|
||||||
|
linters.concurrentMap(visit) :
|
||||||
|
linters.asyncMap(visit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func getFiles(with visitor: LintableFilesVisitor) async throws -> [SwiftLintFile] {
|
||||||
|
if visitor.useSTDIN {
|
||||||
|
let stdinData = FileHandle.standardInput.readDataToEndOfFile()
|
||||||
|
if let stdinString = String(data: stdinData, encoding: .utf8) {
|
||||||
|
return [SwiftLintFile(contents: stdinString)]
|
||||||
|
}
|
||||||
|
throw SwiftLintError.usageError(description: "stdin isn't a UTF8-encoded string")
|
||||||
|
} else if visitor.useScriptInputFiles {
|
||||||
|
let files = try scriptInputFiles()
|
||||||
|
guard visitor.forceExclude else {
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
let scriptInputPaths = files.compactMap { $0.path }
|
||||||
|
let filesToLint = visitor.useExcludingByPrefix ?
|
||||||
|
filterExcludedPathsByPrefix(in: scriptInputPaths) :
|
||||||
|
filterExcludedPaths(in: scriptInputPaths)
|
||||||
|
return filesToLint.map(SwiftLintFile.init(pathDeferringReading:))
|
||||||
|
}
|
||||||
|
if !visitor.quiet {
|
||||||
|
let filesInfo: String
|
||||||
|
if visitor.paths.isEmpty || visitor.paths == [""] {
|
||||||
|
filesInfo = "in current working directory"
|
||||||
|
} else {
|
||||||
|
filesInfo = "at paths \(visitor.paths.joined(separator: ", "))"
|
||||||
|
}
|
||||||
|
|
||||||
|
queuedPrintError("\(visitor.action) Swift files \(filesInfo)")
|
||||||
|
}
|
||||||
|
return visitor.paths.flatMap {
|
||||||
|
self.lintableFiles(inPath: $0, forceExclude: visitor.forceExclude,
|
||||||
|
excludeByPrefix: visitor.useExcludingByPrefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func visitLintableFiles(options: LintOrAnalyzeOptions, cache: LinterCache? = nil, storage: RuleStorage,
|
||||||
|
visitorBlock: @escaping (CollectedLinter) async -> Void) async throws -> [SwiftLintFile] {
|
||||||
|
let visitor = try LintableFilesVisitor.create(options, cache: cache,
|
||||||
|
allowZeroLintableFiles: allowZeroLintableFiles,
|
||||||
|
block: visitorBlock)
|
||||||
|
return try await visitLintableFiles(with: visitor, storage: storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: LintOrAnalyze Command
|
||||||
|
|
||||||
|
init(options: LintOrAnalyzeOptions) {
|
||||||
|
self.init(
|
||||||
|
configurationFiles: options.configurationFiles,
|
||||||
|
enableAllRules: options.enableAllRules,
|
||||||
|
cachePath: options.cachePath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct DuplicateCollector {
|
||||||
|
var all = Set<String>()
|
||||||
|
var duplicates = Set<String>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension Collection where Element == Linter {
|
||||||
|
var duplicateFileNames: Set<String> {
|
||||||
|
let collector = reduce(into: DuplicateCollector()) { result, linter in
|
||||||
|
if let filename = linter.file.path?.bridge().lastPathComponent {
|
||||||
|
if result.all.contains(filename) {
|
||||||
|
result.duplicates.insert(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
result.all.insert(filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collector.duplicates
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension ProcessInfo {
|
||||||
|
var isLikelyXcodeCloudEnvironment: Bool {
|
||||||
|
// https://developer.apple.com/documentation/xcode/environment-variable-reference
|
||||||
|
let requiredKeys: Set = [
|
||||||
|
"CI",
|
||||||
|
"CI_BUILD_ID",
|
||||||
|
"CI_BUILD_NUMBER",
|
||||||
|
"CI_BUNDLE_ID",
|
||||||
|
"CI_COMMIT",
|
||||||
|
"CI_DERIVED_DATA_PATH",
|
||||||
|
"CI_PRODUCT",
|
||||||
|
"CI_PRODUCT_ID",
|
||||||
|
"CI_PRODUCT_PLATFORM",
|
||||||
|
"CI_PROJECT_FILE_PATH",
|
||||||
|
"CI_START_CONDITION",
|
||||||
|
"CI_TEAM_ID",
|
||||||
|
"CI_WORKFLOW",
|
||||||
|
"CI_WORKSPACE",
|
||||||
|
"CI_XCODE_PROJECT",
|
||||||
|
"CI_XCODE_SCHEME",
|
||||||
|
"CI_XCODEBUILD_ACTION"
|
||||||
|
]
|
||||||
|
|
||||||
|
return requiredKeys.isSubset(of: environment.keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
52
Sources/Cargo/swiftlint/Helpers/Benchmark.swift
Normal file
52
Sources/Cargo/swiftlint/Helpers/Benchmark.swift
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
struct BenchmarkEntry {
|
||||||
|
let id: String
|
||||||
|
let time: Double
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Benchmark {
|
||||||
|
private let name: String
|
||||||
|
private var entries = [BenchmarkEntry]()
|
||||||
|
|
||||||
|
init(name: String) {
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func record(id: String, time: Double) {
|
||||||
|
guard id != "custom_rules" else { return }
|
||||||
|
entries.append(BenchmarkEntry(id: id, time: time))
|
||||||
|
}
|
||||||
|
|
||||||
|
mutating func record(file: SwiftLintFile, from start: Date) {
|
||||||
|
record(id: file.path ?? "<nopath>", time: -start.timeIntervalSinceNow)
|
||||||
|
}
|
||||||
|
|
||||||
|
func save() {
|
||||||
|
// Decomposed to improve compile times
|
||||||
|
let entriesDict: [String: Double] = entries.reduce(into: [String: Double]()) { accu, idAndTime in
|
||||||
|
accu[idAndTime.id] = (accu[idAndTime.id] ?? 0) + idAndTime.time
|
||||||
|
}
|
||||||
|
let entriesKeyValues: [(String, Double)] = entriesDict.sorted { $0.1 < $1.1 }
|
||||||
|
let lines: [String] = entriesKeyValues.map { id, time -> String in
|
||||||
|
return "\(numberFormatter.string(from: NSNumber(value: time))!): \(id)"
|
||||||
|
}
|
||||||
|
let string: String = lines.joined(separator: "\n") + "\n"
|
||||||
|
let url = URL(fileURLWithPath: "benchmark_\(name)_\(timestamp).txt", isDirectory: false)
|
||||||
|
try? string.data(using: .utf8)?.write(to: url, options: [.atomic])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let numberFormatter: NumberFormatter = {
|
||||||
|
let formatter = NumberFormatter()
|
||||||
|
formatter.numberStyle = .decimal
|
||||||
|
formatter.minimumFractionDigits = 3
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
private let timestamp: String = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateFormat = "yyyy_MM_dd_HH_mm_ss"
|
||||||
|
return formatter.string(from: Date())
|
||||||
|
}()
|
||||||
108
Sources/Cargo/swiftlint/Helpers/CompilerArgumentsExtractor.swift
Normal file
108
Sources/Cargo/swiftlint/Helpers/CompilerArgumentsExtractor.swift
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct CompilerArgumentsExtractor {
|
||||||
|
static func allCompilerInvocations(compilerLogs: String) -> [[String]] {
|
||||||
|
var compilerInvocations = [[String]]()
|
||||||
|
compilerLogs.enumerateLines { line, _ in
|
||||||
|
if let swiftcIndex = line.range(of: "swiftc ")?.upperBound, line.contains(" -module-name ") {
|
||||||
|
let invocation = parseCLIArguments(String(line[swiftcIndex...]))
|
||||||
|
.expandingResponseFiles
|
||||||
|
.filteringCompilerArguments
|
||||||
|
compilerInvocations.append(invocation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return compilerInvocations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func parseCLIArguments(_ string: String) -> [String] {
|
||||||
|
let escapedSpacePlaceholder = "\u{0}"
|
||||||
|
let scanner = Scanner(string: string)
|
||||||
|
var str = ""
|
||||||
|
var didStart = false
|
||||||
|
while let result = scanner.scanUpToString("\"") {
|
||||||
|
if didStart {
|
||||||
|
str += result.replacingOccurrences(of: " ", with: escapedSpacePlaceholder)
|
||||||
|
str += " "
|
||||||
|
} else {
|
||||||
|
str += result
|
||||||
|
}
|
||||||
|
_ = scanner.scanString("\"")
|
||||||
|
didStart.toggle()
|
||||||
|
}
|
||||||
|
return str.trimmingCharacters(in: .whitespaces)
|
||||||
|
.replacingOccurrences(of: "\\ ", with: escapedSpacePlaceholder)
|
||||||
|
.components(separatedBy: " ")
|
||||||
|
.map { $0.replacingOccurrences(of: escapedSpacePlaceholder, with: " ") }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Partially filters compiler arguments from `xcodebuild` to something that SourceKit/Clang will accept.
|
||||||
|
|
||||||
|
- parameter args: Compiler arguments, as parsed from `xcodebuild`.
|
||||||
|
|
||||||
|
- returns: A tuple of partially filtered compiler arguments in `.0`, and whether or not there are
|
||||||
|
more flags to remove in `.1`.
|
||||||
|
*/
|
||||||
|
private func partiallyFilter(arguments args: [String]) -> ([String], Bool) {
|
||||||
|
guard let indexOfFlagToRemove = args.firstIndex(of: "-output-file-map") else {
|
||||||
|
return (args, false)
|
||||||
|
}
|
||||||
|
var args = args
|
||||||
|
args.remove(at: args.index(after: indexOfFlagToRemove))
|
||||||
|
args.remove(at: indexOfFlagToRemove)
|
||||||
|
return (args, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Array where Element == String {
|
||||||
|
/// Return the full list of compiler arguments, replacing any response files with their contents.
|
||||||
|
fileprivate var expandingResponseFiles: [String] {
|
||||||
|
return flatMap { arg -> [String] in
|
||||||
|
guard arg.starts(with: "@") else {
|
||||||
|
return [arg]
|
||||||
|
}
|
||||||
|
let responseFile = String(arg.dropFirst())
|
||||||
|
return (try? String(contentsOf: URL(fileURLWithPath: responseFile, isDirectory: false))).flatMap {
|
||||||
|
$0.trimmingCharacters(in: .newlines)
|
||||||
|
.components(separatedBy: "\n")
|
||||||
|
.expandingResponseFiles
|
||||||
|
} ?? [arg]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns filtered compiler arguments from `xcodebuild` to something that SourceKit/Clang will accept.
|
||||||
|
var filteringCompilerArguments: [String] {
|
||||||
|
var args = self
|
||||||
|
if args.first == "swiftc" {
|
||||||
|
args.removeFirst()
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/realm/SwiftLint/issues/3365
|
||||||
|
args = args.map { $0.replacingOccurrences(of: "\\=", with: "=") }
|
||||||
|
args = args.map { $0.replacingOccurrences(of: "\\ ", with: " ") }
|
||||||
|
args.append(contentsOf: ["-D", "DEBUG"])
|
||||||
|
var shouldContinueToFilterArguments = true
|
||||||
|
while shouldContinueToFilterArguments {
|
||||||
|
(args, shouldContinueToFilterArguments) = partiallyFilter(arguments: args)
|
||||||
|
}
|
||||||
|
|
||||||
|
return args.filter {
|
||||||
|
![
|
||||||
|
"-parseable-output",
|
||||||
|
"-incremental",
|
||||||
|
"-serialize-diagnostics",
|
||||||
|
"-emit-dependencies",
|
||||||
|
"-use-frontend-parseable-output"
|
||||||
|
].contains($0)
|
||||||
|
}.map {
|
||||||
|
if $0 == "-O" {
|
||||||
|
return "-Onone"
|
||||||
|
} else if $0 == "-DNDEBUG=1" {
|
||||||
|
return "-DDEBUG=1"
|
||||||
|
}
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Sources/Cargo/swiftlint/Helpers/ExitHelper.swift
Normal file
12
Sources/Cargo/swiftlint/Helpers/ExitHelper.swift
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#if os(Linux)
|
||||||
|
import Glibc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum ExitHelper {
|
||||||
|
static func successfullyExit() {
|
||||||
|
#if os(Linux)
|
||||||
|
// Workaround for https://github.com/apple/swift/issues/59961
|
||||||
|
Glibc.exit(0)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Sources/Cargo/swiftlint/Helpers/LintOrAnalyzeArguments.swift
Normal file
63
Sources/Cargo/swiftlint/Helpers/LintOrAnalyzeArguments.swift
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import ArgumentParser
|
||||||
|
|
||||||
|
enum LeniencyOptions: String, EnumerableFlag {
|
||||||
|
case strict, lenient
|
||||||
|
|
||||||
|
static func help(for value: LeniencyOptions) -> ArgumentHelp? {
|
||||||
|
switch value {
|
||||||
|
case .strict:
|
||||||
|
return "Upgrades warnings to serious violations (errors)."
|
||||||
|
case .lenient:
|
||||||
|
return "Downgrades serious violations to warnings, warning threshold is disabled."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Common Arguments
|
||||||
|
|
||||||
|
struct LintOrAnalyzeArguments: ParsableArguments {
|
||||||
|
@Option(help: "The path to one or more SwiftLint configuration files, evaluated as a parent-child hierarchy.")
|
||||||
|
var config = [String]()
|
||||||
|
@Flag(name: [.long, .customLong("autocorrect")], help: "Correct violations whenever possible.")
|
||||||
|
var fix = false
|
||||||
|
@Flag(help: """
|
||||||
|
Should reformat the Swift files using the same mechanism used by Xcode (via SourceKit).
|
||||||
|
Only applied with `--fix`/`--autocorrect`.
|
||||||
|
""")
|
||||||
|
var format = false
|
||||||
|
@Flag(help: "Use an alternative algorithm to exclude paths for `excluded`, which may be faster in some cases.")
|
||||||
|
var useAlternativeExcluding = false
|
||||||
|
@Flag(help: "Read SCRIPT_INPUT_FILE* environment variables as files.")
|
||||||
|
var useScriptInputFiles = false
|
||||||
|
@Flag(exclusivity: .exclusive)
|
||||||
|
var leniency: LeniencyOptions?
|
||||||
|
@Flag(help: "Exclude files in config `excluded` even if their paths are explicitly specified.")
|
||||||
|
var forceExclude = false
|
||||||
|
@Flag(help: "Save benchmarks to `benchmark_files.txt` and `benchmark_rules.txt`.")
|
||||||
|
var benchmark = false
|
||||||
|
@Option(help: "The reporter used to log errors and warnings.")
|
||||||
|
var reporter: String?
|
||||||
|
@Flag(help: "Use the in-process version of SourceKit.")
|
||||||
|
var inProcessSourcekit = false
|
||||||
|
@Option(help: "The file where violations should be saved. Prints to stdout by default.")
|
||||||
|
var output: String?
|
||||||
|
@Flag(help: "Show a live-updating progress bar instead of each file being processed.")
|
||||||
|
var progress = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Common Argument Help
|
||||||
|
|
||||||
|
// It'd be great to be able to parameterize an `@OptionGroup` so we could move these options into
|
||||||
|
// `LintOrAnalyzeArguments`.
|
||||||
|
|
||||||
|
func pathOptionDescription(for mode: LintOrAnalyzeMode) -> ArgumentHelp {
|
||||||
|
ArgumentHelp(visibility: .hidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathsArgumentDescription(for mode: LintOrAnalyzeMode) -> ArgumentHelp {
|
||||||
|
"List of paths to the files or directories to \(mode.imperative)."
|
||||||
|
}
|
||||||
|
|
||||||
|
func quietOptionDescription(for mode: LintOrAnalyzeMode) -> ArgumentHelp {
|
||||||
|
"Don't print status logs like '\(mode.verb.capitalized) <file>' & 'Done \(mode.verb)'."
|
||||||
|
}
|
||||||
342
Sources/Cargo/swiftlint/Helpers/LintOrAnalyzeCommand.swift
Normal file
342
Sources/Cargo/swiftlint/Helpers/LintOrAnalyzeCommand.swift
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
import Dispatch
|
||||||
|
import Foundation
|
||||||
|
@_spi(TestHelper)
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
enum LintOrAnalyzeMode {
|
||||||
|
case lint, analyze
|
||||||
|
|
||||||
|
var imperative: String {
|
||||||
|
switch self {
|
||||||
|
case .lint:
|
||||||
|
return "lint"
|
||||||
|
case .analyze:
|
||||||
|
return "analyze"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var verb: String {
|
||||||
|
switch self {
|
||||||
|
case .lint:
|
||||||
|
return "linting"
|
||||||
|
case .analyze:
|
||||||
|
return "analyzing"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LintOrAnalyzeCommand {
|
||||||
|
static func run(_ options: LintOrAnalyzeOptions) async throws {
|
||||||
|
if options.inProcessSourcekit {
|
||||||
|
queuedPrintError(
|
||||||
|
"""
|
||||||
|
warning: The --in-process-sourcekit option is deprecated. \
|
||||||
|
SwiftLint now always uses an in-process SourceKit.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
try await Signposts.record(name: "LintOrAnalyzeCommand.run") {
|
||||||
|
try await options.autocorrect ? autocorrect(options) : lintOrAnalyze(options)
|
||||||
|
}
|
||||||
|
ExitHelper.successfullyExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func lintOrAnalyze(_ options: LintOrAnalyzeOptions) async throws {
|
||||||
|
let builder = LintOrAnalyzeResultBuilder(options)
|
||||||
|
let files = try await collectViolations(builder: builder)
|
||||||
|
try Signposts.record(name: "LintOrAnalyzeCommand.PostProcessViolations") {
|
||||||
|
try postProcessViolations(files: files, builder: builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func collectViolations(builder: LintOrAnalyzeResultBuilder) async throws -> [SwiftLintFile] {
|
||||||
|
let options = builder.options
|
||||||
|
let visitorMutationQueue = DispatchQueue(label: "io.realm.swiftlint.lintVisitorMutation")
|
||||||
|
return try await builder.configuration.visitLintableFiles(options: options, cache: builder.cache,
|
||||||
|
storage: builder.storage) { linter in
|
||||||
|
let currentViolations: [StyleViolation]
|
||||||
|
if options.benchmark {
|
||||||
|
CustomRuleTimer.shared.activate()
|
||||||
|
let start = Date()
|
||||||
|
let (violationsBeforeLeniency, currentRuleTimes) = linter
|
||||||
|
.styleViolationsAndRuleTimes(using: builder.storage)
|
||||||
|
currentViolations = applyLeniency(options: options, violations: violationsBeforeLeniency)
|
||||||
|
visitorMutationQueue.sync {
|
||||||
|
builder.fileBenchmark.record(file: linter.file, from: start)
|
||||||
|
currentRuleTimes.forEach { builder.ruleBenchmark.record(id: $0, time: $1) }
|
||||||
|
builder.violations += currentViolations
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentViolations = applyLeniency(options: options,
|
||||||
|
violations: linter.styleViolations(using: builder.storage))
|
||||||
|
visitorMutationQueue.sync {
|
||||||
|
builder.violations += currentViolations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
linter.file.invalidateCache()
|
||||||
|
builder.report(violations: currentViolations, realtimeCondition: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func postProcessViolations(files: [SwiftLintFile], builder: LintOrAnalyzeResultBuilder) throws {
|
||||||
|
let options = builder.options
|
||||||
|
let configuration = builder.configuration
|
||||||
|
if isWarningThresholdBroken(configuration: configuration, violations: builder.violations)
|
||||||
|
&& !options.lenient {
|
||||||
|
builder.violations.append(
|
||||||
|
createThresholdViolation(threshold: configuration.warningThreshold!)
|
||||||
|
)
|
||||||
|
builder.report(violations: [builder.violations.last!], realtimeCondition: true)
|
||||||
|
}
|
||||||
|
builder.report(violations: builder.violations, realtimeCondition: false)
|
||||||
|
let numberOfSeriousViolations = builder.violations.filter({ $0.severity == .error }).count
|
||||||
|
if !options.quiet {
|
||||||
|
printStatus(violations: builder.violations, files: files, serious: numberOfSeriousViolations,
|
||||||
|
verb: options.verb)
|
||||||
|
}
|
||||||
|
if options.benchmark {
|
||||||
|
builder.fileBenchmark.save()
|
||||||
|
for (id, time) in CustomRuleTimer.shared.dump() {
|
||||||
|
builder.ruleBenchmark.record(id: id, time: time)
|
||||||
|
}
|
||||||
|
builder.ruleBenchmark.save()
|
||||||
|
if !options.quiet, let memoryUsage = memoryUsage() {
|
||||||
|
queuedPrintError(memoryUsage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try builder.cache?.save()
|
||||||
|
guard numberOfSeriousViolations == 0 else { exit(2) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func printStatus(violations: [StyleViolation], files: [SwiftLintFile], serious: Int, verb: String) {
|
||||||
|
let pluralSuffix = { (collection: [Any]) -> String in
|
||||||
|
return collection.count != 1 ? "s" : ""
|
||||||
|
}
|
||||||
|
queuedPrintError(
|
||||||
|
"Done \(verb)! Found \(violations.count) violation\(pluralSuffix(violations)), " +
|
||||||
|
"\(serious) serious in \(files.count) file\(pluralSuffix(files))."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func isWarningThresholdBroken(configuration: Configuration,
|
||||||
|
violations: [StyleViolation]) -> Bool {
|
||||||
|
guard let warningThreshold = configuration.warningThreshold else { return false }
|
||||||
|
let numberOfWarningViolations = violations.filter({ $0.severity == .warning }).count
|
||||||
|
return numberOfWarningViolations >= warningThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func createThresholdViolation(threshold: Int) -> StyleViolation {
|
||||||
|
let description = RuleDescription(
|
||||||
|
identifier: "warning_threshold",
|
||||||
|
name: "Warning Threshold",
|
||||||
|
description: "Number of warnings thrown is above the threshold",
|
||||||
|
kind: .lint
|
||||||
|
)
|
||||||
|
return StyleViolation(
|
||||||
|
ruleDescription: description,
|
||||||
|
severity: .error,
|
||||||
|
location: Location(file: "", line: 0, character: 0),
|
||||||
|
reason: "Number of warnings exceeded threshold of \(threshold).")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func applyLeniency(options: LintOrAnalyzeOptions, violations: [StyleViolation]) -> [StyleViolation] {
|
||||||
|
switch (options.lenient, options.strict) {
|
||||||
|
case (false, false):
|
||||||
|
return violations
|
||||||
|
|
||||||
|
case (true, false):
|
||||||
|
return violations.map {
|
||||||
|
if $0.severity == .error {
|
||||||
|
return $0.with(severity: .warning)
|
||||||
|
} else {
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case (false, true):
|
||||||
|
return violations.map {
|
||||||
|
if $0.severity == .warning {
|
||||||
|
return $0.with(severity: .error)
|
||||||
|
} else {
|
||||||
|
return $0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case (true, true):
|
||||||
|
queuedFatalError("Invalid command line options: 'lenient' and 'strict' are mutually exclusive.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func autocorrect(_ options: LintOrAnalyzeOptions) async throws {
|
||||||
|
let storage = RuleStorage()
|
||||||
|
let configuration = Configuration(options: options)
|
||||||
|
let correctionsBuilder = CorrectionsBuilder()
|
||||||
|
let files = try await configuration
|
||||||
|
.visitLintableFiles(options: options, cache: nil, storage: storage) { linter in
|
||||||
|
if options.format {
|
||||||
|
switch configuration.indentation {
|
||||||
|
case .tabs:
|
||||||
|
linter.format(useTabs: true, indentWidth: 4)
|
||||||
|
case .spaces(let count):
|
||||||
|
linter.format(useTabs: false, indentWidth: count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let corrections = linter.correct(using: storage)
|
||||||
|
if !corrections.isEmpty && !options.quiet {
|
||||||
|
if options.useSTDIN {
|
||||||
|
queuedPrint(linter.file.contents)
|
||||||
|
} else {
|
||||||
|
if options.progress {
|
||||||
|
await correctionsBuilder.append(corrections)
|
||||||
|
} else {
|
||||||
|
let correctionLogs = corrections.map(\.consoleDescription)
|
||||||
|
queuedPrint(correctionLogs.joined(separator: "\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !options.quiet {
|
||||||
|
if options.progress {
|
||||||
|
let corrections = await correctionsBuilder.corrections
|
||||||
|
if !corrections.isEmpty {
|
||||||
|
let correctionLogs = corrections.map(\.consoleDescription)
|
||||||
|
options.writeToOutput(correctionLogs.joined(separator: "\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pluralSuffix = { (collection: [Any]) -> String in
|
||||||
|
return collection.count != 1 ? "s" : ""
|
||||||
|
}
|
||||||
|
queuedPrintError("Done correcting \(files.count) file\(pluralSuffix(files))!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LintOrAnalyzeOptions {
|
||||||
|
let mode: LintOrAnalyzeMode
|
||||||
|
let paths: [String]
|
||||||
|
let useSTDIN: Bool
|
||||||
|
let configurationFiles: [String]
|
||||||
|
let strict: Bool
|
||||||
|
let lenient: Bool
|
||||||
|
let forceExclude: Bool
|
||||||
|
let useExcludingByPrefix: Bool
|
||||||
|
let useScriptInputFiles: Bool
|
||||||
|
let benchmark: Bool
|
||||||
|
let reporter: String?
|
||||||
|
let quiet: Bool
|
||||||
|
let output: String?
|
||||||
|
let progress: Bool
|
||||||
|
let cachePath: String?
|
||||||
|
let ignoreCache: Bool
|
||||||
|
let enableAllRules: Bool
|
||||||
|
let autocorrect: Bool
|
||||||
|
let format: Bool
|
||||||
|
let compilerLogPath: String?
|
||||||
|
let compileCommands: String?
|
||||||
|
let inProcessSourcekit: Bool
|
||||||
|
|
||||||
|
var verb: String {
|
||||||
|
if autocorrect {
|
||||||
|
return "correcting"
|
||||||
|
} else {
|
||||||
|
return mode.verb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LintOrAnalyzeResultBuilder {
|
||||||
|
var fileBenchmark = Benchmark(name: "files")
|
||||||
|
var ruleBenchmark = Benchmark(name: "rules")
|
||||||
|
var violations = [StyleViolation]()
|
||||||
|
let storage = RuleStorage()
|
||||||
|
let configuration: Configuration
|
||||||
|
let reporter: Reporter.Type
|
||||||
|
let cache: LinterCache?
|
||||||
|
let options: LintOrAnalyzeOptions
|
||||||
|
|
||||||
|
init(_ options: LintOrAnalyzeOptions) {
|
||||||
|
let config = Signposts.record(name: "LintOrAnalyzeCommand.ParseConfiguration") {
|
||||||
|
Configuration(options: options)
|
||||||
|
}
|
||||||
|
configuration = config
|
||||||
|
reporter = reporterFrom(identifier: options.reporter ?? config.reporter)
|
||||||
|
if options.ignoreCache || ProcessInfo.processInfo.isLikelyXcodeCloudEnvironment {
|
||||||
|
cache = nil
|
||||||
|
} else {
|
||||||
|
cache = LinterCache(configuration: config)
|
||||||
|
}
|
||||||
|
self.options = options
|
||||||
|
|
||||||
|
if let outFile = options.output {
|
||||||
|
do {
|
||||||
|
try Data().write(to: URL(fileURLWithPath: outFile))
|
||||||
|
} catch {
|
||||||
|
queuedPrintError("Could not write to file at path \(outFile)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func report(violations: [StyleViolation], realtimeCondition: Bool) {
|
||||||
|
if (reporter.isRealtime && (!options.progress || options.output != nil)) == realtimeCondition {
|
||||||
|
let report = reporter.generateReport(violations)
|
||||||
|
if !report.isEmpty {
|
||||||
|
options.writeToOutput(report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension LintOrAnalyzeOptions {
|
||||||
|
func writeToOutput(_ string: String) {
|
||||||
|
guard let outFile = output else {
|
||||||
|
queuedPrint(string)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let outFileURL = URL(fileURLWithPath: outFile)
|
||||||
|
let fileUpdater = try FileHandle(forUpdating: outFileURL)
|
||||||
|
fileUpdater.seekToEndOfFile()
|
||||||
|
fileUpdater.write(Data((string + "\n").utf8))
|
||||||
|
fileUpdater.closeFile()
|
||||||
|
} catch {
|
||||||
|
queuedPrintError("Could not write to file at path \(outFile)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private actor CorrectionsBuilder {
|
||||||
|
private(set) var corrections: [Correction] = []
|
||||||
|
|
||||||
|
func append(_ corrections: [Correction]) {
|
||||||
|
self.corrections.append(contentsOf: corrections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func memoryUsage() -> String? {
|
||||||
|
#if os(Linux)
|
||||||
|
return nil
|
||||||
|
#else
|
||||||
|
var info = mach_task_basic_info()
|
||||||
|
let basicInfoCount = MemoryLayout<mach_task_basic_info>.stride / MemoryLayout<natural_t>.stride
|
||||||
|
var count = mach_msg_type_number_t(basicInfoCount)
|
||||||
|
|
||||||
|
let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
|
||||||
|
$0.withMemoryRebound(to: integer_t.self, capacity: basicInfoCount) {
|
||||||
|
task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if kerr == KERN_SUCCESS {
|
||||||
|
let bytes = Measurement<UnitInformationStorage>(value: Double(info.resident_size), unit: .bytes)
|
||||||
|
let formatted = ByteCountFormatter().string(from: bytes)
|
||||||
|
return "Memory used: \(formatted)"
|
||||||
|
} else {
|
||||||
|
let errorMessage = String(cString: mach_error_string(kerr), encoding: .ascii)
|
||||||
|
return "Error with task_info(): \(errorMessage ?? "unknown")"
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
253
Sources/Cargo/swiftlint/Helpers/LintableFilesVisitor.swift
Normal file
253
Sources/Cargo/swiftlint/Helpers/LintableFilesVisitor.swift
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
import Foundation
|
||||||
|
import SourceKittenFramework
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
typealias File = String
|
||||||
|
typealias Arguments = [String]
|
||||||
|
|
||||||
|
class CompilerInvocations {
|
||||||
|
static func buildLog(compilerInvocations: [[String]]) -> CompilerInvocations {
|
||||||
|
return ArrayCompilerInvocations(invocations: compilerInvocations)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func compilationDatabase(compileCommands: [File: Arguments]) -> CompilerInvocations {
|
||||||
|
return CompilationDatabaseInvocations(compileCommands: compileCommands)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Default implementation
|
||||||
|
func arguments(forFile path: String?) -> Arguments { [] }
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private class ArrayCompilerInvocations: CompilerInvocations {
|
||||||
|
private let invocationsByArgument: [String: [Arguments]]
|
||||||
|
|
||||||
|
init(invocations: [Arguments]) {
|
||||||
|
// Store invocations by the path, so next when we'll be asked for arguments,
|
||||||
|
// we'll be able to return them faster
|
||||||
|
self.invocationsByArgument = invocations.reduce(into: [:]) { result, arguments in
|
||||||
|
arguments.forEach { result[$0, default: []].append(arguments) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func arguments(forFile path: String?) -> Arguments {
|
||||||
|
return path.flatMap { path in
|
||||||
|
return invocationsByArgument[path]?.first
|
||||||
|
} ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CompilationDatabaseInvocations: CompilerInvocations {
|
||||||
|
private let compileCommands: [File: Arguments]
|
||||||
|
|
||||||
|
init(compileCommands: [File: Arguments]) {
|
||||||
|
self.compileCommands = compileCommands
|
||||||
|
}
|
||||||
|
|
||||||
|
override func arguments(forFile path: String?) -> Arguments {
|
||||||
|
return path.flatMap { path in
|
||||||
|
return compileCommands[path] ??
|
||||||
|
compileCommands[path.path(relativeTo: FileManager.default.currentDirectoryPath)]
|
||||||
|
} ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LintOrAnalyzeModeWithCompilerArguments {
|
||||||
|
case lint
|
||||||
|
case analyze(allCompilerInvocations: CompilerInvocations)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func resolveParamsFiles(args: [String]) -> [String] {
|
||||||
|
return args.reduce(into: []) { (allArgs: inout [String], arg: String) -> Void in
|
||||||
|
if arg.hasPrefix("@"), let contents = try? String(contentsOfFile: String(arg.dropFirst())) {
|
||||||
|
allArgs.append(contentsOf: resolveParamsFiles(args: contents.split(separator: "\n").map(String.init)))
|
||||||
|
} else {
|
||||||
|
allArgs.append(arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LintableFilesVisitor {
|
||||||
|
let paths: [String]
|
||||||
|
let action: String
|
||||||
|
let useSTDIN: Bool
|
||||||
|
let quiet: Bool
|
||||||
|
let showProgressBar: Bool
|
||||||
|
let useScriptInputFiles: Bool
|
||||||
|
let forceExclude: Bool
|
||||||
|
let useExcludingByPrefix: Bool
|
||||||
|
let cache: LinterCache?
|
||||||
|
let parallel: Bool
|
||||||
|
let allowZeroLintableFiles: Bool
|
||||||
|
let mode: LintOrAnalyzeModeWithCompilerArguments
|
||||||
|
let block: (CollectedLinter) async -> Void
|
||||||
|
|
||||||
|
private init(paths: [String], action: String, useSTDIN: Bool, quiet: Bool, showProgressBar: Bool,
|
||||||
|
useScriptInputFiles: Bool, forceExclude: Bool, useExcludingByPrefix: Bool,
|
||||||
|
cache: LinterCache?, compilerInvocations: CompilerInvocations?,
|
||||||
|
allowZeroLintableFiles: Bool, block: @escaping (CollectedLinter) async -> Void) {
|
||||||
|
self.paths = resolveParamsFiles(args: paths)
|
||||||
|
self.action = action
|
||||||
|
self.useSTDIN = useSTDIN
|
||||||
|
self.quiet = quiet
|
||||||
|
self.showProgressBar = showProgressBar
|
||||||
|
self.useScriptInputFiles = useScriptInputFiles
|
||||||
|
self.forceExclude = forceExclude
|
||||||
|
self.useExcludingByPrefix = useExcludingByPrefix
|
||||||
|
self.cache = cache
|
||||||
|
if let compilerInvocations {
|
||||||
|
self.mode = .analyze(allCompilerInvocations: compilerInvocations)
|
||||||
|
// SourceKit had some changes in 5.6 that makes it ~100x more expensive
|
||||||
|
// to process files concurrently. By processing files serially, it's
|
||||||
|
// only 2x slower than before.
|
||||||
|
self.parallel = SwiftVersion.current < .fiveDotSix
|
||||||
|
} else {
|
||||||
|
self.mode = .lint
|
||||||
|
self.parallel = true
|
||||||
|
}
|
||||||
|
self.block = block
|
||||||
|
self.allowZeroLintableFiles = allowZeroLintableFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
static func create(_ options: LintOrAnalyzeOptions,
|
||||||
|
cache: LinterCache?,
|
||||||
|
allowZeroLintableFiles: Bool,
|
||||||
|
block: @escaping (CollectedLinter) async -> Void)
|
||||||
|
throws -> LintableFilesVisitor {
|
||||||
|
try Signposts.record(name: "LintableFilesVisitor.Create") {
|
||||||
|
let compilerInvocations: CompilerInvocations?
|
||||||
|
if options.mode == .lint {
|
||||||
|
compilerInvocations = nil
|
||||||
|
} else {
|
||||||
|
compilerInvocations = try loadCompilerInvocations(options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return LintableFilesVisitor(
|
||||||
|
paths: options.paths, action: options.verb.bridge().capitalized,
|
||||||
|
useSTDIN: options.useSTDIN, quiet: options.quiet,
|
||||||
|
showProgressBar: options.progress,
|
||||||
|
useScriptInputFiles: options.useScriptInputFiles,
|
||||||
|
forceExclude: options.forceExclude,
|
||||||
|
useExcludingByPrefix: options.useExcludingByPrefix,
|
||||||
|
cache: cache,
|
||||||
|
compilerInvocations: compilerInvocations,
|
||||||
|
allowZeroLintableFiles: allowZeroLintableFiles, block: block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldSkipFile(atPath path: String?) -> Bool {
|
||||||
|
switch self.mode {
|
||||||
|
case .lint:
|
||||||
|
return false
|
||||||
|
case let .analyze(compilerInvocations):
|
||||||
|
let compilerArguments = compilerInvocations.arguments(forFile: path)
|
||||||
|
return compilerArguments.isEmpty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func linter(forFile file: SwiftLintFile, configuration: Configuration) -> Linter {
|
||||||
|
switch self.mode {
|
||||||
|
case .lint:
|
||||||
|
return Linter(file: file, configuration: configuration, cache: cache)
|
||||||
|
case let .analyze(compilerInvocations):
|
||||||
|
let compilerArguments = compilerInvocations.arguments(forFile: file.path)
|
||||||
|
return Linter(file: file, configuration: configuration, compilerArguments: compilerArguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func loadCompilerInvocations(_ options: LintOrAnalyzeOptions) throws -> CompilerInvocations {
|
||||||
|
if let path = options.compilerLogPath {
|
||||||
|
guard let compilerInvocations = self.loadLogCompilerInvocations(path) else {
|
||||||
|
throw SwiftLintError.usageError(description: "Could not read compiler log at path: '\(path)'")
|
||||||
|
}
|
||||||
|
|
||||||
|
return .buildLog(compilerInvocations: compilerInvocations)
|
||||||
|
} else if let path = options.compileCommands {
|
||||||
|
do {
|
||||||
|
return .compilationDatabase(compileCommands: try self.loadCompileCommands(path))
|
||||||
|
} catch {
|
||||||
|
throw SwiftLintError.usageError(
|
||||||
|
description: "Could not read compilation database at path: '\(path)' \(error.localizedDescription)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw SwiftLintError.usageError(description: "Could not read compiler invocations")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func loadLogCompilerInvocations(_ path: String) -> [[String]]? {
|
||||||
|
if let data = FileManager.default.contents(atPath: path),
|
||||||
|
let logContents = String(data: data, encoding: .utf8) {
|
||||||
|
if logContents.isEmpty {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompilerArgumentsExtractor.allCompilerInvocations(compilerLogs: logContents)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func loadCompileCommands(_ path: String) throws -> [File: Arguments] {
|
||||||
|
guard let fileContents = FileManager.default.contents(atPath: path) else {
|
||||||
|
throw CompileCommandsLoadError.nonExistentFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if path.hasSuffix(".yaml") || path.hasSuffix(".yml") {
|
||||||
|
// Assume this is a SwiftPM yaml file
|
||||||
|
return try SwiftPMCompilationDB.parse(yaml: fileContents)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let object = try? JSONSerialization.jsonObject(with: fileContents),
|
||||||
|
let compileDB = object as? [[String: Any]] else {
|
||||||
|
throw CompileCommandsLoadError.malformedCommands(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the compilation database to a dictionary, with source files as keys and compiler arguments as values.
|
||||||
|
//
|
||||||
|
// Compilation databases are an array of dictionaries. Each dict has "file" and "arguments" keys.
|
||||||
|
var commands = [File: Arguments]()
|
||||||
|
for (index, entry) in compileDB.enumerated() {
|
||||||
|
guard let file = entry["file"] as? String else {
|
||||||
|
throw CompileCommandsLoadError.malformedFile(path, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let arguments = entry["arguments"] as? [String] else {
|
||||||
|
throw CompileCommandsLoadError.malformedArguments(path, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard arguments.contains(file) else {
|
||||||
|
throw CompileCommandsLoadError.missingFileInArguments(path, index, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
commands[file] = arguments.filteringCompilerArguments
|
||||||
|
}
|
||||||
|
|
||||||
|
return commands
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum CompileCommandsLoadError: LocalizedError {
|
||||||
|
case nonExistentFile(String)
|
||||||
|
case malformedCommands(String)
|
||||||
|
case malformedFile(String, Int)
|
||||||
|
case malformedArguments(String, Int)
|
||||||
|
case missingFileInArguments(String, Int, [String])
|
||||||
|
|
||||||
|
var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case let .nonExistentFile(path):
|
||||||
|
return "Could not read compile commands file at '\(path)'"
|
||||||
|
case let .malformedCommands(path):
|
||||||
|
return "Compile commands file at '\(path)' isn't in the correct format"
|
||||||
|
case let .malformedFile(path, index):
|
||||||
|
return "Missing or invalid (must be a string) 'file' key in \(path) at index \(index)"
|
||||||
|
case let .malformedArguments(path, index):
|
||||||
|
return "Missing or invalid (must be an array of strings) 'arguments' key in \(path) at index \(index)"
|
||||||
|
case let .missingFileInArguments(path, index, arguments):
|
||||||
|
return "Entry in \(path) at index \(index) has 'arguments' which do not contain the 'file': \(arguments)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
Sources/Cargo/swiftlint/Helpers/ProgressBar.swift
Normal file
63
Sources/Cargo/swiftlint/Helpers/ProgressBar.swift
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import Dispatch
|
||||||
|
import Foundation
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
// Inspired by https://github.com/jkandzi/Progress.swift
|
||||||
|
actor ProgressBar {
|
||||||
|
private var index = 1
|
||||||
|
private var lastPrintedTime: TimeInterval = 0.0
|
||||||
|
private let startTime = uptime()
|
||||||
|
private let count: Int
|
||||||
|
|
||||||
|
init(count: Int) {
|
||||||
|
self.count = count
|
||||||
|
}
|
||||||
|
|
||||||
|
func initialize() {
|
||||||
|
// When progress is printed, the previous line is reset, so print an empty line before anything else
|
||||||
|
queuedPrintError("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func printNext() {
|
||||||
|
guard index <= count else { return }
|
||||||
|
|
||||||
|
let currentTime = uptime()
|
||||||
|
if currentTime - lastPrintedTime > 0.1 || index == count {
|
||||||
|
let lineReset = "\u{1B}[1A\u{1B}[K"
|
||||||
|
let bar = makeBar()
|
||||||
|
let timeEstimate = makeTimeEstimate(currentTime: currentTime)
|
||||||
|
let lineContents = "\(index) of \(count) \(bar) \(timeEstimate)"
|
||||||
|
queuedPrintError("\(lineReset)\(lineContents)")
|
||||||
|
lastPrintedTime = currentTime
|
||||||
|
}
|
||||||
|
|
||||||
|
index += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func makeBar() -> String {
|
||||||
|
let barLength = 30
|
||||||
|
let completedBarElements = Int(Double(barLength) * (Double(index) / Double(count)))
|
||||||
|
let barArray = Array(repeating: "=", count: completedBarElements) +
|
||||||
|
Array(repeating: " ", count: barLength - completedBarElements)
|
||||||
|
return "[\(barArray.joined())]"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeTimeEstimate(currentTime: TimeInterval) -> String {
|
||||||
|
let totalTime = currentTime - startTime
|
||||||
|
let itemsPerSecond = Double(index) / totalTime
|
||||||
|
let estimatedTimeRemaining = Double(count - index) / itemsPerSecond
|
||||||
|
let estimatedTimeRemainingString = "\(Int(estimatedTimeRemaining))s"
|
||||||
|
return "ETA: \(estimatedTimeRemainingString) (\(Int(itemsPerSecond)) files/s)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(Linux)
|
||||||
|
// swiftlint:disable:next identifier_name
|
||||||
|
private let NSEC_PER_SEC = 1_000_000_000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private func uptime() -> TimeInterval {
|
||||||
|
Double(DispatchTime.now().uptimeNanoseconds) / Double(NSEC_PER_SEC)
|
||||||
|
}
|
||||||
49
Sources/Cargo/swiftlint/Helpers/RulesFilter.swift
Normal file
49
Sources/Cargo/swiftlint/Helpers/RulesFilter.swift
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
@_spi(TestHelper)
|
||||||
|
import SwiftLintFramework
|
||||||
|
|
||||||
|
extension RulesFilter {
|
||||||
|
struct ExcludingOptions: OptionSet {
|
||||||
|
let rawValue: Int
|
||||||
|
|
||||||
|
static let enabled = Self(rawValue: 1 << 0)
|
||||||
|
static let disabled = Self(rawValue: 1 << 1)
|
||||||
|
static let uncorrectable = Self(rawValue: 1 << 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RulesFilter {
|
||||||
|
private let allRules: RuleList
|
||||||
|
private let enabledRules: [Rule]
|
||||||
|
|
||||||
|
init(allRules: RuleList = primaryRuleList, enabledRules: [Rule]) {
|
||||||
|
self.allRules = allRules
|
||||||
|
self.enabledRules = enabledRules
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRules(excluding excludingOptions: ExcludingOptions) -> RuleList {
|
||||||
|
if excludingOptions.isEmpty {
|
||||||
|
return allRules
|
||||||
|
}
|
||||||
|
|
||||||
|
let filtered: [Rule.Type] = allRules.list.compactMap { ruleID, ruleType in
|
||||||
|
let enabledRule = enabledRules.first { rule in
|
||||||
|
type(of: rule).description.identifier == ruleID
|
||||||
|
}
|
||||||
|
let isRuleEnabled = enabledRule != nil
|
||||||
|
|
||||||
|
if excludingOptions.contains(.enabled) && isRuleEnabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if excludingOptions.contains(.disabled) && !isRuleEnabled {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if excludingOptions.contains(.uncorrectable) && !(ruleType is CorrectableRule.Type) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return ruleType
|
||||||
|
}
|
||||||
|
|
||||||
|
return RuleList(rules: filtered)
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user