Create swift package

This commit is contained in:
Joe Mattiello
2023-03-01 00:48:36 -05:00
parent 4669227567
commit 493b3783f0
409 changed files with 13707 additions and 16923 deletions

21
.codecov.yml Normal file
View 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

View File

@@ -1,39 +1,35 @@
# EditorConfig is awesome: https://EditorConfig.org
# http://editorconfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
indent_style = space
charset = utf-8
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8# 4 space indentation
[*.{md,markdown}]
trim_trailing_whitespace = false
# Swift files
[*.swift]
[*.{c,h,m,mm}]
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
charset = utf-8# 4 space indentation
indent_size = 2
# 4 space indentation
[*.py]
indent_style = space
indent_size = 4
[*.js]
indent_size = 2
# Tab indentation (no size specified)
[Makefile]
[*.{swift}]
trim_trailing_whitespace = true
indent_style = tab
indent_size = 4
# Indentation override for all JS under lib directory
[lib/**.js]
indent_style = space
indent_size = 2
[Makefile]
trim_trailing_whitespace = true
indent_style = tab
indent_size = 8
# Matches the exact files either package.json or .travis.yml
[{package.json,.travis.yml}]
indent_style = space
[*.{yaml|yml}]
indent_size = 2

13
.gitmodules vendored
View File

@@ -7,12 +7,15 @@
[submodule "Dependencies/libplist"]
path = Dependencies/libplist
url = https://github.com/libimobiledevice/libplist.git
[submodule "Dependencies/MarkdownAttributedString"]
path = Dependencies/MarkdownAttributedString
url = https://github.com/chockenberry/MarkdownAttributedString.git
[submodule "Dependencies/libimobiledevice-glue"]
path = Dependencies/libimobiledevice-glue
url = https://github.com/libimobiledevice/libimobiledevice-glue
[submodule "Dependencies/libfragmentzip"]
path = Dependencies/libfragmentzip
[submodule "Sources/libfragmentzip"]
path = Sources/libfragmentzip/libfragmentzip-source
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
View 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
View 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
View 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

View 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 */;
}

View File

@@ -14,8 +14,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/microsoft/appcenter-sdk-apple.git",
"state" : {
"revision" : "8354a50fe01a7e54e196d3b5493b5ab53dd5866a",
"version" : "4.4.2"
"revision" : "b2dc99cfedead0bad4e6573d86c5228c89cff332",
"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",
"location" : "https://github.com/krzyzanowskim/OpenSSL",
"state" : {
"revision" : "033fcb41dac96b1b6effa945ca1f9ade002370b2",
"version" : "1.1.1501"
"revision" : "87f41bf9488e7dd2b0de2ab97cb3eafc7304e0e6",
"version" : "1.1.2000"
}
},
{
@@ -59,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/microsoft/PLCrashReporter.git",
"state" : {
"revision" : "6b27393cad517c067dceea85fadf050e70c4ceaa",
"version" : "1.10.1"
"revision" : "81cdec2b3827feb03286cb297f4c501a8eb98df1",
"version" : "1.10.2"
}
},
{
@@ -84,10 +93,10 @@
{
"identity" : "sidekit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/SideStore/SideKit.git",
"location" : "https://github.com/SideStore/SideKit",
"state" : {
"revision" : "7ea34a09b52c104077dea8e0b90f8dc55d43b36b",
"version" : "0.1.0"
"branch" : "main",
"revision" : "7ea34a09b52c104077dea8e0b90f8dc55d43b36b"
}
},
{
@@ -95,8 +104,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/sparkle-project/Sparkle.git",
"state" : {
"revision" : "286edd1fa22505a9e54d170e9fd07d775ea233f2",
"version" : "2.1.0"
"revision" : "dda155c7d3ef38c53d29f8584cb2aad2a1a54dba",
"version" : "2.3.2"
}
},
{

File diff suppressed because it is too large Load Diff

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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()
}
}

View File

@@ -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()
}
}

View File

@@ -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?()
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}
}

View File

@@ -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")
}
}

View File

@@ -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()
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}
}

View File

@@ -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
}
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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)
}
}

View File

@@ -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)])
}
}

View File

@@ -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>

View File

@@ -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")
}
}

View File

@@ -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)
}
}

View File

@@ -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:))
}
}

View File

@@ -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;

View File

@@ -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";

View File

@@ -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;

View File

@@ -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";

View File

@@ -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;

View File

@@ -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";

View File

@@ -0,0 +1 @@
#include "Shared.xcconfig"

View File

@@ -0,0 +1 @@
#include "Shared.xcconfig"

View File

@@ -0,0 +1 @@
#include "Shared.xcconfig"

View File

@@ -0,0 +1 @@
#include "../Build.xcconfig"

1
Dependencies/em_proxy vendored Submodule

Submodule Dependencies/em_proxy added at fd120e5ff5

View File

@@ -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 */;
}

View File

@@ -1 +0,0 @@
Use ../fetch-prebuilt.sh to fetch prebuilt Rust dependencies

View File

@@ -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

Binary file not shown.

1
Dependencies/minimuxer vendored Submodule

Submodule Dependencies/minimuxer added at bc6988e098

View File

@@ -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 */;
}

View File

@@ -1 +0,0 @@
Use ../fetch-prebuilt.sh to fetch prebuilt Rust dependencies

104
Package.resolved Normal file
View 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
View 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())
}

View 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

View 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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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");

View File

@@ -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 }
}

View File

@@ -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

View File

@@ -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
}
}

View 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>

View File

@@ -0,0 +1 @@
../../ALTAppPatcher.h

View 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)
}
}
}

View 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
)
}()
}

View File

@@ -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
}
}

View 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
}

View 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()
}
}
}

View 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)
}
}
}

View File

@@ -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
}
}

View 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
}

View 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
}

View 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()
}
}
}

View 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)
}
}
}

View 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)
}
}

View 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
)
}()
}

View 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()
}
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View 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())
}()

View 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
}
}
}

View 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
}
}

View 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)'."
}

View 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
}

View 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)"
}
}
}

View 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)
}

View 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