├── LICENSE
├── README.md
├── README_EN.md
├── SuperIcons.entitlements
├── SuperIcons.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ └── huami.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── huami.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
└── SuperIcons
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ ├── Contents.json
│ └── appstore.png
└── Contents.json
├── AuxiliaryExecute+Spawn.swift
├── AuxiliaryExecute.swift
├── ContentView.swift
├── Execute.swift
├── LSApplicationProxy.h
├── LSApplicationWorkspace.h
├── MyAction.swift
├── Preview Content
└── Preview Assets.xcassets
│ └── Contents.json
├── SuperIconsApp.swift
├── en.lproj
└── Localizable.strings
└── zh-Hans.lproj
└── Localizable.strings
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 huami.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SuperIcons
2 |
3 | [EN_Us README](README_EN.md)
4 |
5 | SuperIcons 是一款用于修改 TrollStore 安装应用图标的工具,它可以替换或还原支持的应用图标。
6 |
7 | ## 许可证
8 |
9 | 本项目使用 [MIT 许可证](https://github.com/huami1314/SuperIcons?tab=MIT-1-ov-file)
10 |
11 | ## 使用的封装库
12 |
13 | 本项目使用了 [AuxiliaryExecute](https://github.com/Lakr233/AuxiliaryExecute) 封装库。
14 |
15 | ## 构建参考
16 |
17 | 请参考 [TrollStore](https://github.com/opa334/TrollStore) 以了解构建指南。
18 |
19 | ## 特别感谢
20 |
21 | 感谢 [TrollFools](https://github.com/Lessica/TrollFools) 提供的代码参考支持。
22 |
23 | ## 免责声明
24 |
25 | 该工具仅供个人使用,不对任何可能的损坏或数据丢失负责。
26 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | # SuperIcons
2 |
3 | [ZH_Hans README](README.md)
4 |
5 | SuperIcons is a tool for modifying icons of apps installed by TrollStore. It can replace or restore icons of supported apps.
6 |
7 | ## License
8 |
9 | This project uses the [MIT License](https://github.com/huami1314/SuperIcons?tab=MIT-1-ov-file)
10 |
11 | ## Libraries Used
12 |
13 | This project uses the [AuxiliaryExecute](https://github.com/Lakr233/AuxiliaryExecute) library.
14 |
15 | ## Build Reference
16 |
17 | Please refer to [TrollStore](https://github.com/opa334/TrollStore) for build instructions.
18 |
19 | ## Special Thanks
20 |
21 | Special thanks to [TrollFools](https://github.com/Lessica/TrollFools) for code reference support.
22 |
23 | ## Disclaimer
24 |
25 | This tool is for personal use only and does not assume any responsibility for potential damage or data loss.
26 |
--------------------------------------------------------------------------------
/SuperIcons.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.private.security.no-sandbox
6 |
7 | com.apple.CommCenter.fine-grained
8 |
9 | spi
10 | identity
11 |
12 | com.apple.private.persona-mgmt
13 |
14 | com.apple.private.security.container-manager
15 |
16 | com.apple.private.security.container-required
17 |
18 | com.apple.private.security.disk-device-access
19 |
20 | com.apple.private.security.no-container
21 |
22 | com.apple.private.security.storage.AppBundles
23 |
24 | com.apple.private.security.storage.AppDataContainers
25 |
26 | com.apple.private.thread-set-state
27 |
28 | com.apple.security.exception.files.absolute-path.read-write
29 |
30 | /
31 |
32 | com.apple.springboard.iconState
33 |
34 | com.apple.springboard.iconState.mutate
35 |
36 | com.apple.springboard.launchapplications
37 |
38 | com.apple.springboard.launchapplicationswithoptions
39 |
40 | com.apple.springboard.opensensitiveurl
41 |
42 | com.apple.springboard.openurlswhenlocked
43 |
44 | com.apple.system-task-ports
45 |
46 | com.apple.system-task-ports.control
47 |
48 | keychain-access-groups
49 |
50 | com.huami.SuperIcons
51 | Q6GZT6G9SB.com.huami.SuperIcons
52 |
53 | platform-application
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/SuperIcons.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0341260D2C5A67B000990BB2 /* SuperIconsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0341260C2C5A67B000990BB2 /* SuperIconsApp.swift */; };
11 | 0341260F2C5A67B000990BB2 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0341260E2C5A67B000990BB2 /* ContentView.swift */; };
12 | 034126112C5A67B400990BB2 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 034126102C5A67B400990BB2 /* Assets.xcassets */; };
13 | 034126142C5A67B400990BB2 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 034126132C5A67B400990BB2 /* Preview Assets.xcassets */; };
14 | 0341261C2C5A68C700990BB2 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0341261E2C5A68C700990BB2 /* Localizable.strings */; };
15 | 034126252C5A698A00990BB2 /* Execute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034126202C5A698A00990BB2 /* Execute.swift */; };
16 | 034126262C5A698A00990BB2 /* AuxiliaryExecute+Spawn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034126222C5A698A00990BB2 /* AuxiliaryExecute+Spawn.swift */; };
17 | 034126272C5A698A00990BB2 /* AuxiliaryExecute.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034126242C5A698A00990BB2 /* AuxiliaryExecute.swift */; };
18 | 0341262F2C5A69AF00990BB2 /* mv-15 in Resources */ = {isa = PBXBuildFile; fileRef = 034126282C5A69AD00990BB2 /* mv-15 */; };
19 | 034126302C5A69AF00990BB2 /* cp in Resources */ = {isa = PBXBuildFile; fileRef = 034126292C5A69AE00990BB2 /* cp */; };
20 | 034126312C5A69AF00990BB2 /* rm in Resources */ = {isa = PBXBuildFile; fileRef = 0341262A2C5A69AE00990BB2 /* rm */; };
21 | 034126332C5A69AF00990BB2 /* cp-15 in Resources */ = {isa = PBXBuildFile; fileRef = 0341262C2C5A69AE00990BB2 /* cp-15 */; };
22 | 034126342C5A69AF00990BB2 /* mv in Resources */ = {isa = PBXBuildFile; fileRef = 0341262D2C5A69AE00990BB2 /* mv */; };
23 | 034126362C5A69C300990BB2 /* libintl.8.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 0341262B2C5A69AE00990BB2 /* libintl.8.dylib */; };
24 | 034126372C5A69C300990BB2 /* libiosexec.1.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 0341262E2C5A69AE00990BB2 /* libiosexec.1.dylib */; };
25 | 034126392C5A6A0900990BB2 /* MyAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 034126382C5A6A0900990BB2 /* MyAction.swift */; };
26 | 0341263C2C5A6A4E00990BB2 /* CocoaLumberjack in Frameworks */ = {isa = PBXBuildFile; productRef = 0341263B2C5A6A4E00990BB2 /* CocoaLumberjack */; };
27 | 0341263E2C5A6A4E00990BB2 /* CocoaLumberjackSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0341263D2C5A6A4E00990BB2 /* CocoaLumberjackSwift */; };
28 | 034126402C5A6A4E00990BB2 /* CocoaLumberjackSwiftLogBackend in Frameworks */ = {isa = PBXBuildFile; productRef = 0341263F2C5A6A4E00990BB2 /* CocoaLumberjackSwiftLogBackend */; };
29 | /* End PBXBuildFile section */
30 |
31 | /* Begin PBXFileReference section */
32 | 034126092C5A67B000990BB2 /* SuperIcons.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SuperIcons.app; sourceTree = BUILT_PRODUCTS_DIR; };
33 | 0341260C2C5A67B000990BB2 /* SuperIconsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuperIconsApp.swift; sourceTree = ""; };
34 | 0341260E2C5A67B000990BB2 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
35 | 034126102C5A67B400990BB2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
36 | 034126132C5A67B400990BB2 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
37 | 0341261D2C5A68C700990BB2 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; };
38 | 0341261F2C5A68CA00990BB2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; };
39 | 034126202C5A698A00990BB2 /* Execute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Execute.swift; sourceTree = ""; };
40 | 034126212C5A698A00990BB2 /* LSApplicationProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSApplicationProxy.h; sourceTree = ""; };
41 | 034126222C5A698A00990BB2 /* AuxiliaryExecute+Spawn.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AuxiliaryExecute+Spawn.swift"; sourceTree = ""; };
42 | 034126232C5A698A00990BB2 /* LSApplicationWorkspace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LSApplicationWorkspace.h; sourceTree = ""; };
43 | 034126242C5A698A00990BB2 /* AuxiliaryExecute.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuxiliaryExecute.swift; sourceTree = ""; };
44 | 034126282C5A69AD00990BB2 /* mv-15 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "mv-15"; sourceTree = ""; };
45 | 034126292C5A69AE00990BB2 /* cp */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = cp; sourceTree = ""; };
46 | 0341262A2C5A69AE00990BB2 /* rm */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = rm; sourceTree = ""; };
47 | 0341262B2C5A69AE00990BB2 /* libintl.8.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libintl.8.dylib; sourceTree = ""; };
48 | 0341262C2C5A69AE00990BB2 /* cp-15 */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = "cp-15"; sourceTree = ""; };
49 | 0341262D2C5A69AE00990BB2 /* mv */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = mv; sourceTree = ""; };
50 | 0341262E2C5A69AE00990BB2 /* libiosexec.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libiosexec.1.dylib; sourceTree = ""; };
51 | 034126382C5A6A0900990BB2 /* MyAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAction.swift; sourceTree = ""; };
52 | /* End PBXFileReference section */
53 |
54 | /* Begin PBXFrameworksBuildPhase section */
55 | 034126062C5A67B000990BB2 /* Frameworks */ = {
56 | isa = PBXFrameworksBuildPhase;
57 | buildActionMask = 2147483647;
58 | files = (
59 | 0341263C2C5A6A4E00990BB2 /* CocoaLumberjack in Frameworks */,
60 | 0341263E2C5A6A4E00990BB2 /* CocoaLumberjackSwift in Frameworks */,
61 | 034126402C5A6A4E00990BB2 /* CocoaLumberjackSwiftLogBackend in Frameworks */,
62 | );
63 | runOnlyForDeploymentPostprocessing = 0;
64 | };
65 | /* End PBXFrameworksBuildPhase section */
66 |
67 | /* Begin PBXGroup section */
68 | 034126002C5A67B000990BB2 = {
69 | isa = PBXGroup;
70 | children = (
71 | 0341260B2C5A67B000990BB2 /* SuperIcons */,
72 | 0341260A2C5A67B000990BB2 /* Products */,
73 | );
74 | sourceTree = "";
75 | };
76 | 0341260A2C5A67B000990BB2 /* Products */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 034126092C5A67B000990BB2 /* SuperIcons.app */,
80 | );
81 | name = Products;
82 | sourceTree = "";
83 | };
84 | 0341260B2C5A67B000990BB2 /* SuperIcons */ = {
85 | isa = PBXGroup;
86 | children = (
87 | 034126292C5A69AE00990BB2 /* cp */,
88 | 0341262C2C5A69AE00990BB2 /* cp-15 */,
89 | 0341262B2C5A69AE00990BB2 /* libintl.8.dylib */,
90 | 0341262E2C5A69AE00990BB2 /* libiosexec.1.dylib */,
91 | 0341262D2C5A69AE00990BB2 /* mv */,
92 | 034126282C5A69AD00990BB2 /* mv-15 */,
93 | 0341262A2C5A69AE00990BB2 /* rm */,
94 | 034126242C5A698A00990BB2 /* AuxiliaryExecute.swift */,
95 | 034126222C5A698A00990BB2 /* AuxiliaryExecute+Spawn.swift */,
96 | 034126202C5A698A00990BB2 /* Execute.swift */,
97 | 034126212C5A698A00990BB2 /* LSApplicationProxy.h */,
98 | 034126232C5A698A00990BB2 /* LSApplicationWorkspace.h */,
99 | 0341260C2C5A67B000990BB2 /* SuperIconsApp.swift */,
100 | 0341260E2C5A67B000990BB2 /* ContentView.swift */,
101 | 034126102C5A67B400990BB2 /* Assets.xcassets */,
102 | 034126122C5A67B400990BB2 /* Preview Content */,
103 | 0341261E2C5A68C700990BB2 /* Localizable.strings */,
104 | 034126382C5A6A0900990BB2 /* MyAction.swift */,
105 | );
106 | path = SuperIcons;
107 | sourceTree = "";
108 | };
109 | 034126122C5A67B400990BB2 /* Preview Content */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 034126132C5A67B400990BB2 /* Preview Assets.xcassets */,
113 | );
114 | path = "Preview Content";
115 | sourceTree = "";
116 | };
117 | /* End PBXGroup section */
118 |
119 | /* Begin PBXNativeTarget section */
120 | 034126082C5A67B000990BB2 /* SuperIcons */ = {
121 | isa = PBXNativeTarget;
122 | buildConfigurationList = 034126172C5A67B400990BB2 /* Build configuration list for PBXNativeTarget "SuperIcons" */;
123 | buildPhases = (
124 | 034126052C5A67B000990BB2 /* Sources */,
125 | 034126062C5A67B000990BB2 /* Frameworks */,
126 | 034126072C5A67B000990BB2 /* Resources */,
127 | );
128 | buildRules = (
129 | );
130 | dependencies = (
131 | );
132 | name = SuperIcons;
133 | packageProductDependencies = (
134 | 0341263B2C5A6A4E00990BB2 /* CocoaLumberjack */,
135 | 0341263D2C5A6A4E00990BB2 /* CocoaLumberjackSwift */,
136 | 0341263F2C5A6A4E00990BB2 /* CocoaLumberjackSwiftLogBackend */,
137 | );
138 | productName = SuperIcons;
139 | productReference = 034126092C5A67B000990BB2 /* SuperIcons.app */;
140 | productType = "com.apple.product-type.application";
141 | };
142 | /* End PBXNativeTarget section */
143 |
144 | /* Begin PBXProject section */
145 | 034126012C5A67B000990BB2 /* Project object */ = {
146 | isa = PBXProject;
147 | attributes = {
148 | BuildIndependentTargetsInParallel = 1;
149 | LastSwiftUpdateCheck = 1520;
150 | LastUpgradeCheck = 1520;
151 | TargetAttributes = {
152 | 034126082C5A67B000990BB2 = {
153 | CreatedOnToolsVersion = 15.2;
154 | };
155 | };
156 | };
157 | buildConfigurationList = 034126042C5A67B000990BB2 /* Build configuration list for PBXProject "SuperIcons" */;
158 | compatibilityVersion = "Xcode 14.0";
159 | developmentRegion = en;
160 | hasScannedForEncodings = 0;
161 | knownRegions = (
162 | en,
163 | Base,
164 | "zh-Hans",
165 | );
166 | mainGroup = 034126002C5A67B000990BB2;
167 | packageReferences = (
168 | 0341263A2C5A6A4E00990BB2 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */,
169 | );
170 | productRefGroup = 0341260A2C5A67B000990BB2 /* Products */;
171 | projectDirPath = "";
172 | projectRoot = "";
173 | targets = (
174 | 034126082C5A67B000990BB2 /* SuperIcons */,
175 | );
176 | };
177 | /* End PBXProject section */
178 |
179 | /* Begin PBXResourcesBuildPhase section */
180 | 034126072C5A67B000990BB2 /* Resources */ = {
181 | isa = PBXResourcesBuildPhase;
182 | buildActionMask = 2147483647;
183 | files = (
184 | 034126362C5A69C300990BB2 /* libintl.8.dylib in Resources */,
185 | 034126372C5A69C300990BB2 /* libiosexec.1.dylib in Resources */,
186 | 034126302C5A69AF00990BB2 /* cp in Resources */,
187 | 034126342C5A69AF00990BB2 /* mv in Resources */,
188 | 034126332C5A69AF00990BB2 /* cp-15 in Resources */,
189 | 034126142C5A67B400990BB2 /* Preview Assets.xcassets in Resources */,
190 | 0341261C2C5A68C700990BB2 /* Localizable.strings in Resources */,
191 | 034126312C5A69AF00990BB2 /* rm in Resources */,
192 | 034126112C5A67B400990BB2 /* Assets.xcassets in Resources */,
193 | 0341262F2C5A69AF00990BB2 /* mv-15 in Resources */,
194 | );
195 | runOnlyForDeploymentPostprocessing = 0;
196 | };
197 | /* End PBXResourcesBuildPhase section */
198 |
199 | /* Begin PBXSourcesBuildPhase section */
200 | 034126052C5A67B000990BB2 /* Sources */ = {
201 | isa = PBXSourcesBuildPhase;
202 | buildActionMask = 2147483647;
203 | files = (
204 | 034126252C5A698A00990BB2 /* Execute.swift in Sources */,
205 | 0341260F2C5A67B000990BB2 /* ContentView.swift in Sources */,
206 | 034126262C5A698A00990BB2 /* AuxiliaryExecute+Spawn.swift in Sources */,
207 | 034126392C5A6A0900990BB2 /* MyAction.swift in Sources */,
208 | 034126272C5A698A00990BB2 /* AuxiliaryExecute.swift in Sources */,
209 | 0341260D2C5A67B000990BB2 /* SuperIconsApp.swift in Sources */,
210 | );
211 | runOnlyForDeploymentPostprocessing = 0;
212 | };
213 | /* End PBXSourcesBuildPhase section */
214 |
215 | /* Begin PBXVariantGroup section */
216 | 0341261E2C5A68C700990BB2 /* Localizable.strings */ = {
217 | isa = PBXVariantGroup;
218 | children = (
219 | 0341261D2C5A68C700990BB2 /* zh-Hans */,
220 | 0341261F2C5A68CA00990BB2 /* en */,
221 | );
222 | name = Localizable.strings;
223 | sourceTree = "";
224 | };
225 | /* End PBXVariantGroup section */
226 |
227 | /* Begin XCBuildConfiguration section */
228 | 034126152C5A67B400990BB2 /* Debug */ = {
229 | isa = XCBuildConfiguration;
230 | buildSettings = {
231 | ALWAYS_SEARCH_USER_PATHS = NO;
232 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
233 | CLANG_ANALYZER_NONNULL = YES;
234 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
235 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
236 | CLANG_ENABLE_MODULES = YES;
237 | CLANG_ENABLE_OBJC_ARC = YES;
238 | CLANG_ENABLE_OBJC_WEAK = YES;
239 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
240 | CLANG_WARN_BOOL_CONVERSION = YES;
241 | CLANG_WARN_COMMA = YES;
242 | CLANG_WARN_CONSTANT_CONVERSION = YES;
243 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
244 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
245 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
246 | CLANG_WARN_EMPTY_BODY = YES;
247 | CLANG_WARN_ENUM_CONVERSION = YES;
248 | CLANG_WARN_INFINITE_RECURSION = YES;
249 | CLANG_WARN_INT_CONVERSION = YES;
250 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
251 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
252 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
253 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
254 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
255 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
256 | CLANG_WARN_STRICT_PROTOTYPES = YES;
257 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
258 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
259 | CLANG_WARN_UNREACHABLE_CODE = YES;
260 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
261 | COPY_PHASE_STRIP = NO;
262 | DEBUG_INFORMATION_FORMAT = dwarf;
263 | ENABLE_STRICT_OBJC_MSGSEND = YES;
264 | ENABLE_TESTABILITY = YES;
265 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
266 | GCC_C_LANGUAGE_STANDARD = gnu17;
267 | GCC_DYNAMIC_NO_PIC = NO;
268 | GCC_NO_COMMON_BLOCKS = YES;
269 | GCC_OPTIMIZATION_LEVEL = 0;
270 | GCC_PREPROCESSOR_DEFINITIONS = (
271 | "DEBUG=1",
272 | "$(inherited)",
273 | );
274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
276 | GCC_WARN_UNDECLARED_SELECTOR = YES;
277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
278 | GCC_WARN_UNUSED_FUNCTION = YES;
279 | GCC_WARN_UNUSED_VARIABLE = YES;
280 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
281 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
282 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
283 | MTL_FAST_MATH = YES;
284 | ONLY_ACTIVE_ARCH = YES;
285 | SDKROOT = iphoneos;
286 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
287 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
288 | };
289 | name = Debug;
290 | };
291 | 034126162C5A67B400990BB2 /* Release */ = {
292 | isa = XCBuildConfiguration;
293 | buildSettings = {
294 | ALWAYS_SEARCH_USER_PATHS = NO;
295 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
296 | CLANG_ANALYZER_NONNULL = YES;
297 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
298 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
299 | CLANG_ENABLE_MODULES = YES;
300 | CLANG_ENABLE_OBJC_ARC = YES;
301 | CLANG_ENABLE_OBJC_WEAK = YES;
302 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
303 | CLANG_WARN_BOOL_CONVERSION = YES;
304 | CLANG_WARN_COMMA = YES;
305 | CLANG_WARN_CONSTANT_CONVERSION = YES;
306 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
307 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
308 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
309 | CLANG_WARN_EMPTY_BODY = YES;
310 | CLANG_WARN_ENUM_CONVERSION = YES;
311 | CLANG_WARN_INFINITE_RECURSION = YES;
312 | CLANG_WARN_INT_CONVERSION = YES;
313 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
314 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
315 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
317 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
318 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
319 | CLANG_WARN_STRICT_PROTOTYPES = YES;
320 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
321 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
322 | CLANG_WARN_UNREACHABLE_CODE = YES;
323 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
324 | COPY_PHASE_STRIP = NO;
325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
326 | ENABLE_NS_ASSERTIONS = NO;
327 | ENABLE_STRICT_OBJC_MSGSEND = YES;
328 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
329 | GCC_C_LANGUAGE_STANDARD = gnu17;
330 | GCC_NO_COMMON_BLOCKS = YES;
331 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
332 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
333 | GCC_WARN_UNDECLARED_SELECTOR = YES;
334 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
335 | GCC_WARN_UNUSED_FUNCTION = YES;
336 | GCC_WARN_UNUSED_VARIABLE = YES;
337 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
338 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
339 | MTL_ENABLE_DEBUG_INFO = NO;
340 | MTL_FAST_MATH = YES;
341 | SDKROOT = iphoneos;
342 | SWIFT_COMPILATION_MODE = wholemodule;
343 | VALIDATE_PRODUCT = YES;
344 | };
345 | name = Release;
346 | };
347 | 034126182C5A67B400990BB2 /* Debug */ = {
348 | isa = XCBuildConfiguration;
349 | buildSettings = {
350 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
351 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
352 | CODE_SIGN_STYLE = Automatic;
353 | CURRENT_PROJECT_VERSION = 2;
354 | DEVELOPMENT_ASSET_PATHS = "\"SuperIcons/Preview Content\"";
355 | DEVELOPMENT_TEAM = Q6GZT6G9SB;
356 | ENABLE_PREVIEWS = YES;
357 | GENERATE_INFOPLIST_FILE = YES;
358 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
359 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
360 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
361 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
362 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
363 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
364 | LD_RUNPATH_SEARCH_PATHS = (
365 | "$(inherited)",
366 | "@executable_path/Frameworks",
367 | );
368 | LIBRARY_SEARCH_PATHS = (
369 | "$(inherited)",
370 | "$(PROJECT_DIR)/SuperIcons",
371 | );
372 | MARKETING_VERSION = 1.0;
373 | PRODUCT_BUNDLE_IDENTIFIER = com.huami.SuperIcons;
374 | PRODUCT_NAME = "$(TARGET_NAME)";
375 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
376 | SUPPORTS_MACCATALYST = NO;
377 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
378 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
379 | SWIFT_EMIT_LOC_STRINGS = YES;
380 | SWIFT_VERSION = 5.0;
381 | TARGETED_DEVICE_FAMILY = "1,2";
382 | };
383 | name = Debug;
384 | };
385 | 034126192C5A67B400990BB2 /* Release */ = {
386 | isa = XCBuildConfiguration;
387 | buildSettings = {
388 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
389 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
390 | CODE_SIGN_STYLE = Automatic;
391 | CURRENT_PROJECT_VERSION = 2;
392 | DEVELOPMENT_ASSET_PATHS = "\"SuperIcons/Preview Content\"";
393 | DEVELOPMENT_TEAM = Q6GZT6G9SB;
394 | ENABLE_PREVIEWS = YES;
395 | GENERATE_INFOPLIST_FILE = YES;
396 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
397 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
398 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
399 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
400 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
401 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
402 | LD_RUNPATH_SEARCH_PATHS = (
403 | "$(inherited)",
404 | "@executable_path/Frameworks",
405 | );
406 | LIBRARY_SEARCH_PATHS = (
407 | "$(inherited)",
408 | "$(PROJECT_DIR)/SuperIcons",
409 | );
410 | MARKETING_VERSION = 1.0;
411 | PRODUCT_BUNDLE_IDENTIFIER = com.huami.SuperIcons;
412 | PRODUCT_NAME = "$(TARGET_NAME)";
413 | SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
414 | SUPPORTS_MACCATALYST = NO;
415 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
416 | SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
417 | SWIFT_EMIT_LOC_STRINGS = YES;
418 | SWIFT_VERSION = 5.0;
419 | TARGETED_DEVICE_FAMILY = "1,2";
420 | };
421 | name = Release;
422 | };
423 | /* End XCBuildConfiguration section */
424 |
425 | /* Begin XCConfigurationList section */
426 | 034126042C5A67B000990BB2 /* Build configuration list for PBXProject "SuperIcons" */ = {
427 | isa = XCConfigurationList;
428 | buildConfigurations = (
429 | 034126152C5A67B400990BB2 /* Debug */,
430 | 034126162C5A67B400990BB2 /* Release */,
431 | );
432 | defaultConfigurationIsVisible = 0;
433 | defaultConfigurationName = Release;
434 | };
435 | 034126172C5A67B400990BB2 /* Build configuration list for PBXNativeTarget "SuperIcons" */ = {
436 | isa = XCConfigurationList;
437 | buildConfigurations = (
438 | 034126182C5A67B400990BB2 /* Debug */,
439 | 034126192C5A67B400990BB2 /* Release */,
440 | );
441 | defaultConfigurationIsVisible = 0;
442 | defaultConfigurationName = Release;
443 | };
444 | /* End XCConfigurationList section */
445 |
446 | /* Begin XCRemoteSwiftPackageReference section */
447 | 0341263A2C5A6A4E00990BB2 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */ = {
448 | isa = XCRemoteSwiftPackageReference;
449 | repositoryURL = "https://github.com/CocoaLumberjack/CocoaLumberjack.git";
450 | requirement = {
451 | kind = upToNextMajorVersion;
452 | minimumVersion = 3.8.5;
453 | };
454 | };
455 | /* End XCRemoteSwiftPackageReference section */
456 |
457 | /* Begin XCSwiftPackageProductDependency section */
458 | 0341263B2C5A6A4E00990BB2 /* CocoaLumberjack */ = {
459 | isa = XCSwiftPackageProductDependency;
460 | package = 0341263A2C5A6A4E00990BB2 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */;
461 | productName = CocoaLumberjack;
462 | };
463 | 0341263D2C5A6A4E00990BB2 /* CocoaLumberjackSwift */ = {
464 | isa = XCSwiftPackageProductDependency;
465 | package = 0341263A2C5A6A4E00990BB2 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */;
466 | productName = CocoaLumberjackSwift;
467 | };
468 | 0341263F2C5A6A4E00990BB2 /* CocoaLumberjackSwiftLogBackend */ = {
469 | isa = XCSwiftPackageProductDependency;
470 | package = 0341263A2C5A6A4E00990BB2 /* XCRemoteSwiftPackageReference "CocoaLumberjack" */;
471 | productName = CocoaLumberjackSwiftLogBackend;
472 | };
473 | /* End XCSwiftPackageProductDependency section */
474 | };
475 | rootObject = 034126012C5A67B000990BB2 /* Project object */;
476 | }
477 |
--------------------------------------------------------------------------------
/SuperIcons.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SuperIcons.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SuperIcons.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "cocoalumberjack",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/CocoaLumberjack/CocoaLumberjack.git",
7 | "state" : {
8 | "revision" : "4b8714a7fb84d42393314ce897127b3939885ec3",
9 | "version" : "3.8.5"
10 | }
11 | },
12 | {
13 | "identity" : "swift-log",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/apple/swift-log",
16 | "state" : {
17 | "revision" : "9cb486020ebf03bfa5b5df985387a14a98744537",
18 | "version" : "1.6.1"
19 | }
20 | }
21 | ],
22 | "version" : 2
23 | }
24 |
--------------------------------------------------------------------------------
/SuperIcons.xcodeproj/project.xcworkspace/xcuserdata/huami.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huami1314/SuperIcons/937a5c0b09287ba94d704a6331cc77e578abe5ca/SuperIcons.xcodeproj/project.xcworkspace/xcuserdata/huami.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/SuperIcons.xcodeproj/xcuserdata/huami.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | SuperIcons.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/SuperIcons/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SuperIcons/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "appstore.png",
5 | "idiom" : "universal",
6 | "platform" : "ios",
7 | "size" : "1024x1024"
8 | }
9 | ],
10 | "info" : {
11 | "author" : "xcode",
12 | "version" : 1
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/SuperIcons/Assets.xcassets/AppIcon.appiconset/appstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huami1314/SuperIcons/937a5c0b09287ba94d704a6331cc77e578abe5ca/SuperIcons/Assets.xcassets/AppIcon.appiconset/appstore.png
--------------------------------------------------------------------------------
/SuperIcons/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SuperIcons/AuxiliaryExecute+Spawn.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuxiliaryExecute+Spawn.swift
3 | // AuxiliaryExecute
4 | //
5 | // Created by Lakr Aream on 2021/12/6.
6 | //
7 |
8 | import CocoaLumberjackSwift
9 | import Foundation
10 |
11 | @discardableResult
12 | @_silgen_name("posix_spawn_file_actions_addchdir_np")
13 | private func posix_spawn_file_actions_addchdir_np(
14 | _ attr: UnsafeMutablePointer,
15 | _ dir: UnsafePointer
16 | ) -> Int32
17 |
18 | @discardableResult
19 | @_silgen_name("posix_spawnattr_set_persona_np")
20 | private func posix_spawnattr_set_persona_np(
21 | _ attr: UnsafeMutablePointer,
22 | _ persona_id: uid_t,
23 | _ flags: UInt32
24 | ) -> Int32
25 |
26 | @discardableResult
27 | @_silgen_name("posix_spawnattr_set_persona_uid_np")
28 | private func posix_spawnattr_set_persona_uid_np(
29 | _ attr: UnsafeMutablePointer,
30 | _ persona_id: uid_t
31 | ) -> Int32
32 |
33 | @discardableResult
34 | @_silgen_name("posix_spawnattr_set_persona_gid_np")
35 | private func posix_spawnattr_set_persona_gid_np(
36 | _ attr: UnsafeMutablePointer,
37 | _ persona_id: gid_t
38 | ) -> Int32
39 |
40 | private func WIFEXITED(_ status: Int32) -> Bool {
41 | _WSTATUS(status) == 0
42 | }
43 |
44 | private func _WSTATUS(_ status: Int32) -> Int32 {
45 | status & 0x7f
46 | }
47 |
48 | private func WIFSIGNALED(_ status: Int32) -> Bool {
49 | (_WSTATUS(status) != 0) && (_WSTATUS(status) != 0x7f)
50 | }
51 |
52 | private func WEXITSTATUS(_ status: Int32) -> Int32 {
53 | (status >> 8) & 0xff
54 | }
55 |
56 | private func WTERMSIG(_ status: Int32) -> Int32 {
57 | status & 0x7f
58 | }
59 |
60 | private let POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE = UInt32(1)
61 |
62 | public extension AuxiliaryExecute {
63 | /// call posix spawn to begin execute
64 | /// - Parameters:
65 | /// - command: full path of the binary file. eg: "/bin/cat"
66 | /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
67 | /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
68 | /// - workingDirectory: chdir
69 | /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6
70 | /// - output: a block call from pipeControlQueue in background when buffer from stdout or stderr available for read
71 | /// - Returns: execution receipt, see it's definition for details
72 | @discardableResult
73 | static func spawn(
74 | command: String,
75 | args: [String] = [],
76 | environment: [String: String] = [:],
77 | workingDirectory: String? = nil,
78 | personaOptions: PersonaOptions? = nil,
79 | timeout: Double = 0,
80 | setPid: ((pid_t) -> Void)? = nil,
81 | output: ((String) -> Void)? = nil
82 | )
83 | -> ExecuteReceipt
84 | {
85 | let outputLock = NSLock()
86 | let result = spawn(
87 | command: command,
88 | args: args,
89 | environment: environment,
90 | workingDirectory: workingDirectory,
91 | personaOptions: personaOptions,
92 | timeout: timeout,
93 | setPid: setPid
94 | ) { str in
95 | outputLock.lock()
96 | output?(str)
97 | outputLock.unlock()
98 | } stderrBlock: { str in
99 | outputLock.lock()
100 | output?(str)
101 | outputLock.unlock()
102 | }
103 | return result
104 | }
105 |
106 | /// call posix spawn to begin execute and block until the process exits
107 | /// - Parameters:
108 | /// - command: full path of the binary file. eg: "/bin/cat"
109 | /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
110 | /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
111 | /// - workingDirectory: chdir
112 | /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6
113 | /// - stdout: a block call from pipeControlQueue in background when buffer from stdout available for read
114 | /// - stderr: a block call from pipeControlQueue in background when buffer from stderr available for read
115 | /// - Returns: execution receipt, see it's definition for details
116 | static func spawn(
117 | command: String,
118 | args: [String] = [],
119 | environment: [String: String] = [:],
120 | workingDirectory: String? = nil,
121 | personaOptions: PersonaOptions? = nil,
122 | timeout: Double = 0,
123 | setPid: ((pid_t) -> Void)? = nil,
124 | stdoutBlock: ((String) -> Void)? = nil,
125 | stderrBlock: ((String) -> Void)? = nil
126 | ) -> ExecuteReceipt {
127 | let sema = DispatchSemaphore(value: 0)
128 | var receipt: ExecuteReceipt!
129 | spawn(
130 | command: command,
131 | args: args,
132 | environment: environment,
133 | workingDirectory: workingDirectory,
134 | personaOptions: personaOptions,
135 | timeout: timeout,
136 | setPid: setPid,
137 | stdoutBlock: stdoutBlock,
138 | stderrBlock: stderrBlock
139 | ) {
140 | receipt = $0
141 | sema.signal()
142 | }
143 | sema.wait()
144 | return receipt
145 | }
146 |
147 | /// call posix spawn to begin execute
148 | /// - Parameters:
149 | /// - command: full path of the binary file. eg: "/bin/cat"
150 | /// - args: arg to pass to the binary, exclude argv[0] which is the path itself. eg: ["nya"]
151 | /// - environment: any environment to be appended/overwrite when calling posix spawn. eg: ["mua" : "nya"]
152 | /// - workingDirectory: chdir file action
153 | /// - personaOptions: persona options
154 | /// - timeout: any wall timeout if lager than 0, in seconds. eg: 6
155 | /// - setPid: called sync when pid available
156 | /// - stdoutBlock: a block call from pipeControlQueue in background when buffer from stdout available for read
157 | /// - stderrBlock: a block call from pipeControlQueue in background when buffer from stderr available for read
158 | /// - completionBlock: a block called from processControlQueue or current queue when the process is finished or an error occurred
159 | static func spawn(
160 | command: String,
161 | args: [String] = [],
162 | environment: [String: String] = [:],
163 | workingDirectory: String? = nil,
164 | personaOptions: PersonaOptions? = nil,
165 | timeout: Double = 0,
166 | setPid: ((pid_t) -> Void)? = nil,
167 | stdoutBlock: ((String) -> Void)? = nil,
168 | stderrBlock: ((String) -> Void)? = nil,
169 | completionBlock: ((ExecuteReceipt) -> Void)? = nil
170 | ) {
171 | // MARK: PREPARE ATTRIBUTE -
172 |
173 | var attrs: posix_spawnattr_t?
174 | posix_spawnattr_init(&attrs)
175 | defer { posix_spawnattr_destroy(&attrs) }
176 |
177 | if let personaOptions {
178 | posix_spawnattr_set_persona_np(&attrs, 99, POSIX_SPAWN_PERSONA_FLAGS_OVERRIDE)
179 | posix_spawnattr_set_persona_uid_np(&attrs, personaOptions.uid)
180 | posix_spawnattr_set_persona_gid_np(&attrs, personaOptions.gid)
181 | }
182 |
183 | // MARK: PREPARE FILE PIPE -
184 |
185 | var pipestdout: [Int32] = [0, 0]
186 | var pipestderr: [Int32] = [0, 0]
187 |
188 | let bufsiz = Int(exactly: BUFSIZ) ?? 65535
189 |
190 | pipe(&pipestdout)
191 | pipe(&pipestderr)
192 |
193 | guard fcntl(pipestdout[0], F_SETFL, O_NONBLOCK) != -1 else {
194 | let receipt = ExecuteReceipt.failure(error: .openFilePipeFailed)
195 | completionBlock?(receipt)
196 | return
197 | }
198 | guard fcntl(pipestderr[0], F_SETFL, O_NONBLOCK) != -1 else {
199 | let receipt = ExecuteReceipt.failure(error: .openFilePipeFailed)
200 | completionBlock?(receipt)
201 | return
202 | }
203 |
204 | // MARK: PREPARE FILE ACTION -
205 |
206 | var fileActions: posix_spawn_file_actions_t?
207 | posix_spawn_file_actions_init(&fileActions)
208 | posix_spawn_file_actions_addclose(&fileActions, pipestdout[0])
209 | posix_spawn_file_actions_addclose(&fileActions, pipestderr[0])
210 | posix_spawn_file_actions_adddup2(&fileActions, pipestdout[1], STDOUT_FILENO)
211 | posix_spawn_file_actions_adddup2(&fileActions, pipestderr[1], STDERR_FILENO)
212 | posix_spawn_file_actions_addclose(&fileActions, pipestdout[1])
213 | posix_spawn_file_actions_addclose(&fileActions, pipestderr[1])
214 |
215 | if let workingDirectory = workingDirectory {
216 | posix_spawn_file_actions_addchdir_np(&fileActions, workingDirectory)
217 | }
218 |
219 | defer { posix_spawn_file_actions_destroy(&fileActions) }
220 |
221 | // MARK: PREPARE ENV -
222 |
223 | var realEnvironmentBuilder: [String] = []
224 | // before building the environment, we need to read from the existing environment
225 | do {
226 | var envBuilder = [String: String]()
227 | var currentEnv = environ
228 | while let rawStr = currentEnv.pointee {
229 | defer { currentEnv += 1 }
230 | // get the env
231 | let str = String(cString: rawStr)
232 | guard let key = str.components(separatedBy: "=").first else {
233 | continue
234 | }
235 | if !(str.count >= "\(key)=".count) {
236 | continue
237 | }
238 | // this is to aviod any problem with mua=nya=nya= that ending with =
239 | let value = String(str.dropFirst("\(key)=".count))
240 | envBuilder[key] = value
241 | }
242 | // now, let's overwrite the environment specified in parameters
243 | for (key, value) in environment {
244 | envBuilder[key] = value
245 | }
246 | // now, package those items
247 | for (key, value) in envBuilder {
248 | realEnvironmentBuilder.append("\(key)=\(value)")
249 | }
250 | }
251 | // making it a c shit
252 | let realEnv: [UnsafeMutablePointer?] = realEnvironmentBuilder.map { $0.withCString(strdup) }
253 | defer { for case let env? in realEnv { free(env) } }
254 |
255 | // MARK: PREPARE ARGS -
256 |
257 | let args = [command] + args
258 | let argv: [UnsafeMutablePointer?] = args.map { $0.withCString(strdup) }
259 | defer { for case let arg? in argv { free(arg) } }
260 |
261 | // MARK: NOW POSIX_SPAWN -
262 | DDLogInfo("Execute \(command) \(args.joined(separator: " "))")
263 |
264 | var pid: pid_t = 0
265 | let spawnStatus = posix_spawn(&pid, command, &fileActions, &attrs, argv + [nil], realEnv + [nil])
266 | if spawnStatus != 0 {
267 | let receipt = ExecuteReceipt.failure(error: .posixSpawnFailed)
268 | completionBlock?(receipt)
269 | return
270 | }
271 |
272 | setPid?(pid)
273 |
274 | close(pipestdout[1])
275 | close(pipestderr[1])
276 |
277 | var stdoutStr = ""
278 | var stderrStr = ""
279 |
280 | // MARK: OUTPUT BRIDGE -
281 |
282 | let stdoutSource = DispatchSource.makeReadSource(fileDescriptor: pipestdout[0], queue: pipeControlQueue)
283 | let stderrSource = DispatchSource.makeReadSource(fileDescriptor: pipestderr[0], queue: pipeControlQueue)
284 |
285 | let stdoutSem = DispatchSemaphore(value: 0)
286 | let stderrSem = DispatchSemaphore(value: 0)
287 |
288 | stdoutSource.setCancelHandler {
289 | close(pipestdout[0])
290 | stdoutSem.signal()
291 | }
292 | stderrSource.setCancelHandler {
293 | close(pipestderr[0])
294 | stderrSem.signal()
295 | }
296 |
297 | stdoutSource.setEventHandler {
298 | let buffer = UnsafeMutablePointer.allocate(capacity: bufsiz)
299 | defer { buffer.deallocate() }
300 | let bytesRead = read(pipestdout[0], buffer, bufsiz)
301 | guard bytesRead > 0 else {
302 | if bytesRead == -1, errno == EAGAIN {
303 | return
304 | }
305 | stdoutSource.cancel()
306 | return
307 | }
308 |
309 | let array = Array(UnsafeBufferPointer(start: buffer, count: bytesRead)) + [UInt8(0)]
310 | array.withUnsafeBufferPointer { ptr in
311 | let str = String(cString: unsafeBitCast(ptr.baseAddress, to: UnsafePointer.self))
312 | stdoutStr += str
313 | stdoutBlock?(str)
314 | }
315 | }
316 | stderrSource.setEventHandler {
317 | let buffer = UnsafeMutablePointer.allocate(capacity: bufsiz)
318 | defer { buffer.deallocate() }
319 |
320 | let bytesRead = read(pipestderr[0], buffer, bufsiz)
321 | guard bytesRead > 0 else {
322 | if bytesRead == -1, errno == EAGAIN {
323 | return
324 | }
325 | stderrSource.cancel()
326 | return
327 | }
328 |
329 | let array = Array(UnsafeBufferPointer(start: buffer, count: bytesRead)) + [UInt8(0)]
330 | array.withUnsafeBufferPointer { ptr in
331 | let str = String(cString: unsafeBitCast(ptr.baseAddress, to: UnsafePointer.self))
332 | stderrStr += str
333 | stderrBlock?(str)
334 | }
335 | }
336 |
337 | stdoutSource.resume()
338 | stderrSource.resume()
339 |
340 | // MARK: WAIT + TIMEOUT CONTROL -
341 |
342 | let realTimeout = timeout > 0 ? timeout : maxTimeoutValue
343 | let wallTimeout = DispatchTime.now() + (
344 | TimeInterval(exactly: realTimeout) ?? maxTimeoutValue
345 | )
346 | var status: Int32 = 0
347 | var wait: pid_t = 0
348 | var isTimeout = false
349 |
350 | let timerSource = DispatchSource.makeTimerSource(flags: [], queue: processControlQueue)
351 | timerSource.setEventHandler {
352 | isTimeout = true
353 | kill(pid, SIGKILL)
354 | }
355 |
356 | let processSource = DispatchSource.makeProcessSource(identifier: pid, eventMask: .exit, queue: processControlQueue)
357 | processSource.setEventHandler {
358 | wait = waitpid(pid, &status, 0)
359 |
360 | processSource.cancel()
361 | timerSource.cancel()
362 |
363 | stdoutSem.wait()
364 | stderrSem.wait()
365 |
366 | let terminationReason: TerminationReason
367 | if WIFSIGNALED(status) {
368 | let signal = WTERMSIG(status)
369 | DDLogError("Process \(pid) terminated with uncaught signal \(signal)")
370 | terminationReason = .uncaughtSignal(signal)
371 | } else {
372 | assert(WIFEXITED(status))
373 |
374 | let exitCode = WEXITSTATUS(status)
375 | if exitCode == 0 {
376 | DDLogInfo("Process \(pid) exited successfully")
377 | } else {
378 | DDLogError("Process \(pid) exited with code \(exitCode)")
379 | }
380 |
381 | terminationReason = .exit(exitCode)
382 | }
383 |
384 | // by using exactly method, we won't crash it!
385 | let receipt = ExecuteReceipt(
386 | terminationReason: terminationReason,
387 | pid: Int(exactly: pid) ?? -1,
388 | wait: Int(exactly: wait) ?? -1,
389 | error: isTimeout ? .timeout : nil,
390 | stdout: stdoutStr,
391 | stderr: stderrStr
392 | )
393 | completionBlock?(receipt)
394 | }
395 | processSource.resume()
396 |
397 | // timeout control
398 | timerSource.schedule(deadline: wallTimeout)
399 | timerSource.resume()
400 | }
401 | }
402 |
--------------------------------------------------------------------------------
/SuperIcons/AuxiliaryExecute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AuxiliaryExecute.swift
3 | // MyYearWithGit
4 | //
5 | // Created by Lakr Aream on 2021/11/27.
6 | //
7 |
8 | import Foundation
9 |
10 | /// Execute command or shell with posix, shared with AuxiliaryExecute.local
11 | public class AuxiliaryExecute {
12 | /// we do not recommend you to subclass this singleton
13 | public static let local = AuxiliaryExecute()
14 |
15 | // if binary not found when you call the shell api
16 | // we will take some time to rebuild the bianry table each time
17 | // -->>> this is a time-heavy-task
18 | // so use binaryLocationFor(command:) to cache it if needed
19 |
20 | // system path
21 | internal var currentPath: [String] = []
22 | // system binary table
23 | internal var binaryTable: [String: String] = [:]
24 |
25 | // for you to put your own search path
26 | internal var extraSearchPath: [String] = []
27 | // for you to set your own binary table and will be used firstly
28 | // if you set nil here
29 | // -> we will return nil even the binary found in system path
30 | internal var overwriteTable: [String: String?] = [:]
31 |
32 | // this value is used when providing 0 or negative timeout paramete
33 | internal static let maxTimeoutValue: Double = 2_147_483_647
34 |
35 | /// when reading from file pipe, must called from async queue
36 | internal static let pipeControlQueue = DispatchQueue(
37 | label: "wiki.qaq.AuxiliaryExecute.pipeRead",
38 | attributes: .concurrent
39 | )
40 |
41 | /// when killing process or monitoring events from process, must called from async queue
42 | /// we are making this queue serial queue so won't called at the same time when timeout
43 | internal static let processControlQueue = DispatchQueue(
44 | label: "wiki.qaq.AuxiliaryExecute.processControl",
45 | attributes: []
46 | )
47 |
48 | /// used for setting binary table, avoid crash
49 | internal let lock = NSLock()
50 |
51 | /// nope!
52 | private init() {
53 | // no need to setup binary table
54 | // we will make call to it when you call the shell api
55 | // if you only use the spawn api
56 | // we don't need to setup the hole table cause it‘s time-heavy-task
57 | }
58 |
59 | /// Execution Error, do the localization your self
60 | public enum ExecuteError: Error, LocalizedError, Codable {
61 | // not found in path
62 | case commandNotFound
63 | // invalid, may be missing, wrong permission or any other reason
64 | case commandInvalid
65 | // fcntl failed
66 | case openFilePipeFailed
67 | // posix failed
68 | case posixSpawnFailed
69 | // waitpid failed
70 | case waitPidFailed
71 | // timeout when execute
72 | case timeout
73 | }
74 |
75 | public enum TerminationReason: Codable {
76 | case exit(Int32)
77 | case uncaughtSignal(Int32)
78 | }
79 |
80 | public struct PersonaOptions: Codable {
81 | let uid: uid_t
82 | let gid: gid_t
83 | }
84 |
85 | /// Execution Receipt
86 | public struct ExecuteReceipt: Codable {
87 | // exit code when process exit,
88 | // or signal code when process terminated by signal
89 | public let terminationReason: TerminationReason
90 | // process pid that was when it is alive
91 | // -1 means spawn failed in some situation
92 | public let pid: Int
93 | // wait result for final waitpid inside block at
94 | // processSource - eventMask.exit, usually is pid
95 | // -1 for other cases
96 | public let wait: Int
97 | // any error from us, not the command it self
98 | // DOES NOT MEAN THAT THE COMMAND DONE WELL
99 | public let error: ExecuteError?
100 | // stdout
101 | public let stdout: String
102 | // stderr
103 | public let stderr: String
104 |
105 | /// General initialization of receipt object
106 | /// - Parameters:
107 | /// - terminationReason: termination reason
108 | /// - pid: pid when process alive
109 | /// - wait: wait result on waitpid
110 | /// - error: error if any
111 | /// - stdout: stdout
112 | /// - stderr: stderr
113 | internal init(
114 | terminationReason: TerminationReason,
115 | pid: Int,
116 | wait: Int,
117 | error: AuxiliaryExecute.ExecuteError?,
118 | stdout: String,
119 | stderr: String
120 | ) {
121 | self.terminationReason = terminationReason
122 | self.pid = pid
123 | self.wait = wait
124 | self.error = error
125 | self.stdout = stdout
126 | self.stderr = stderr
127 | }
128 |
129 | /// Template for making failure receipt
130 | /// - Parameters:
131 | /// - terminationReason: default uncaught signal 0
132 | /// - pid: default -1
133 | /// - wait: default -1
134 | /// - error: error
135 | /// - stdout: default empty
136 | /// - stderr: default empty
137 | internal static func failure(
138 | terminationReason: TerminationReason = .uncaughtSignal(0),
139 | pid: Int = -1,
140 | wait: Int = -1,
141 | error: AuxiliaryExecute.ExecuteError?,
142 | stdout: String = "",
143 | stderr: String = ""
144 | ) -> ExecuteReceipt {
145 | .init(
146 | terminationReason: terminationReason,
147 | pid: pid,
148 | wait: wait,
149 | error: error,
150 | stdout: stdout,
151 | stderr: stderr
152 | )
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/SuperIcons/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 | @State private var searchText = ""
5 | @State private var appDetails: [(name: String, bundleID: String, version: String, icon: UIImage?, infoPlistPath: String)] = []
6 | @State private var selectedIconPath: String?
7 | @State private var showActionSheet = false
8 | @State private var showFilePicker = false
9 | @State private var selectedAppDetail: (name: String, bundleID: String, version: String, icon: UIImage?, infoPlistPath: String)? = nil
10 |
11 | var filteredAppDetails: [(name: String, bundleID: String, version: String, icon: UIImage?, infoPlistPath: String)] {
12 | let isValidApp = { (bundleID: String, appType: String) -> Bool in
13 | return appType != "User" && !bundleID.starts(with: "com.apple")
14 | }
15 |
16 | let filtered = searchText.isEmpty
17 | ? appDetails.filter { isValidApp($0.bundleID, $0.infoPlistPath) }
18 | : appDetails.filter {
19 | isValidApp($0.bundleID, $0.infoPlistPath) && $0.name.localizedCaseInsensitiveContains(searchText)
20 | }
21 |
22 | return filtered.sorted { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
23 | }
24 |
25 | var body: some View {
26 | NavigationView {
27 | VStack {
28 | if #available(iOS 15.0, *) {
29 | List {
30 | ForEach(filteredAppDetails, id: \.bundleID) { appDetail in
31 | Button(action: {
32 | selectedAppDetail = appDetail
33 | showActionSheet = true
34 | }) {
35 | HStack {
36 | if let icon = appDetail.icon {
37 | Image(uiImage: icon)
38 | .resizable()
39 | .scaledToFit()
40 | .frame(width: 40, height: 40)
41 | .clipShape(Circle())
42 | .padding(.leading)
43 | } else {
44 | Image(systemName: "app")
45 | .resizable()
46 | .scaledToFit()
47 | .frame(width: 40, height: 40)
48 | .clipShape(Circle())
49 | .padding(.leading)
50 | }
51 |
52 | VStack(alignment: .leading) {
53 | Text(appDetail.name)
54 | .font(.headline)
55 | Text("Bundle ID: \(appDetail.bundleID)")
56 | .font(.subheadline)
57 | Text("Version: \(appDetail.version)")
58 | .font(.subheadline)
59 | }
60 | .padding(.leading)
61 | Spacer()
62 | }
63 | .padding(.vertical, 10)
64 | .background(Color.clear)
65 | .contentShape(Rectangle())
66 | }
67 | .buttonStyle(PlainButtonStyle())
68 | .listRowInsets(EdgeInsets())
69 | }
70 | FooterView()
71 | }
72 | .searchable(text: $searchText, prompt: NSLocalizedString("Search...", comment: "Search prompt"))
73 | .listStyle(PlainListStyle())
74 | } else {
75 | VStack {
76 | TextField(NSLocalizedString("Search...", comment: "Search placeholder"), text: $searchText)
77 | .textFieldStyle(RoundedBorderTextFieldStyle())
78 | .padding(.horizontal)
79 | .background(Color(.systemGray6))
80 | .cornerRadius(8)
81 | .padding()
82 |
83 | List {
84 | ForEach(filteredAppDetails, id: \.bundleID) { appDetail in
85 | Button(action: {
86 | selectedAppDetail = appDetail
87 | showActionSheet = true
88 | }) {
89 | HStack {
90 | if let icon = appDetail.icon {
91 | Image(uiImage: icon)
92 | .resizable()
93 | .scaledToFit()
94 | .frame(width: 40, height: 40)
95 | .clipShape(Circle())
96 | .padding(.leading)
97 | } else {
98 | Image(systemName: "app")
99 | .resizable()
100 | .scaledToFit()
101 | .frame(width: 40, height: 40)
102 | .clipShape(Circle())
103 | .padding(.leading)
104 | }
105 |
106 | VStack(alignment: .leading) {
107 | Text(appDetail.name)
108 | .font(.headline)
109 | Text("Bundle ID: \(appDetail.bundleID)")
110 | .font(.subheadline)
111 | Text("Version: \(appDetail.version)")
112 | .font(.subheadline)
113 | }
114 | .padding(.leading)
115 | Spacer()
116 | }
117 | .padding(.vertical, 10)
118 | .background(Color.clear)
119 | .contentShape(Rectangle())
120 | }
121 | .buttonStyle(PlainButtonStyle())
122 | .listRowInsets(EdgeInsets())
123 | }
124 | FooterView()
125 | }
126 | }
127 | }
128 | }
129 | .onAppear {
130 | fetchAppDetails()
131 | }
132 | .navigationTitle(NSLocalizedString("SuperIcons", comment: "Navigation title"))
133 | .actionSheet(isPresented: $showActionSheet) {
134 | ActionSheet(
135 | title: Text(NSLocalizedString("Select Action", comment: "Action sheet title")),
136 | buttons: [
137 | .default(Text(NSLocalizedString("Change Icon", comment: "Change icon action"))) {
138 | showFilePicker = true
139 | },
140 | .default(Text(NSLocalizedString("Restore Icon", comment: "Restore icon action"))) {
141 | if let appDetail = selectedAppDetail {
142 | MyAction.restoreIcons(mainpath: appDetail.infoPlistPath)
143 | }
144 | },
145 | .cancel()
146 | ]
147 | )
148 | }
149 | .fileImporter(isPresented: $showFilePicker, allowedContentTypes: [.png, .jpeg]) { result in
150 | switch result {
151 | case .success(let url):
152 | selectedIconPath = url.path
153 | if let appDetail = selectedAppDetail {
154 | MyAction.changeIcons(mainpath: appDetail.infoPlistPath, iconPath: selectedIconPath!, iconName: "AppIcon_AA.png")
155 | }
156 | case .failure(let error):
157 | print(NSLocalizedString("Error selecting file: \(error.localizedDescription)", comment: "File selection error"))
158 | }
159 | }
160 | }
161 | }
162 |
163 | func fetchAppDetails() {
164 | guard let workspaceClass = NSClassFromString("LSApplicationWorkspace") as? NSObject.Type,
165 | let workspace = workspaceClass.perform(NSSelectorFromString("defaultWorkspace")).takeUnretainedValue() as? NSObject,
166 | let applications = workspace.perform(NSSelectorFromString("allApplications")).takeUnretainedValue() as? [NSObject] else {
167 | return
168 | }
169 |
170 | appDetails = applications.compactMap { app in
171 | if let bundleID = app.perform(NSSelectorFromString("applicationIdentifier")).takeUnretainedValue() as? String,
172 | let bundlePath = app.perform(NSSelectorFromString("bundleURL")).takeUnretainedValue() as? URL,
173 | let appType = app.perform(NSSelectorFromString("applicationType")).takeUnretainedValue() as? String {
174 |
175 | if appType != "User" && !bundleID.starts(with: "com.apple") {
176 | let infoPlistPath = bundlePath.appendingPathComponent("Info.plist").path
177 | if FileManager.default.fileExists(atPath: infoPlistPath),
178 | let infoDict = NSDictionary(contentsOfFile: infoPlistPath),
179 | let displayName = infoDict["CFBundleDisplayName"] as? String,
180 | let version = infoDict["CFBundleShortVersionString"] as? String {
181 | let icon = getAppIcon(from: bundlePath)
182 | return (name: displayName, bundleID: bundleID, version: version, icon: icon, infoPlistPath: infoPlistPath)
183 | }
184 | }
185 | }
186 | return nil
187 | }
188 | }
189 |
190 | func getAppIcon(from bundleURL: URL) -> UIImage? {
191 | let possibleIconPaths = [
192 | "AppIcon_AA.png",
193 | "AppIcon60x60@2x.png",
194 | "AppIcon76x76@2x.png",
195 | "AppIcon83.5x83.5@2x.png",
196 | "AppIcon40x40@2x.png",
197 | "AppIcon29x29@2x.png",
198 | "AppIcon.png",
199 | "Icon.png",
200 | "icon.png",
201 | "Icon-60.png",
202 | "icon-60.png"
203 | ]
204 |
205 | for iconName in possibleIconPaths {
206 | let iconPath = bundleURL.appendingPathComponent(iconName)
207 | if let image = UIImage(contentsOfFile: iconPath.path) {
208 | return image
209 | }
210 | }
211 |
212 | return nil
213 | }
214 | }
215 |
216 | struct FooterView: View {
217 | var body: some View {
218 | VStack {
219 | Spacer()
220 | HStack {
221 | VStack(alignment: .leading, spacing: 2) {
222 | Text(NSLocalizedString("SuperIcons v1.0 (1) ©️ 2024", comment: "Footer text"))
223 | .font(.footnote)
224 | .foregroundColor(.gray)
225 | Text(NSLocalizedString("Developed by huami.", comment: "Developer text"))
226 | .font(.footnote)
227 | .foregroundColor(.gray)
228 | }
229 | Spacer()
230 | Link(NSLocalizedString("Get Repository", comment: "Repository link text"), destination: URL(string: "https://github.com/huami1314/SuperIcons")!)
231 | .font(.footnote)
232 | .foregroundColor(.blue)
233 | .padding(.horizontal)
234 | }
235 | .padding()
236 | .background(Color(.systemGray6))
237 | }
238 | }
239 | }
240 |
241 | struct FooterView_Previews: PreviewProvider {
242 | static var previews: some View {
243 | FooterView()
244 | }
245 | }
246 |
247 |
248 | struct ContentView_Previews: PreviewProvider {
249 | static var previews: some View {
250 | ContentView()
251 | }
252 | }
253 |
--------------------------------------------------------------------------------
/SuperIcons/Execute.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Execute.swift
3 | // TrollFools
4 | //
5 | // Created by Lessica on 2024/7/19.
6 | //
7 |
8 | import CocoaLumberjackSwift
9 | import Foundation
10 |
11 | enum Execute {
12 |
13 | @discardableResult
14 | static func rootSpawn(
15 | binary: String,
16 | arguments: [String] = [],
17 | environment: [String: String] = [:]
18 | ) throws -> AuxiliaryExecute.TerminationReason {
19 | let receipt = AuxiliaryExecute.spawn(
20 | command: binary,
21 | args: arguments,
22 | environment: environment,
23 | personaOptions: .init(uid: 0, gid: 0)
24 | )
25 | if !receipt.stdout.isEmpty {
26 | DDLogInfo("Standard output: \(receipt.stdout)")
27 | }
28 | if !receipt.stderr.isEmpty {
29 | DDLogInfo("Standard error: \(receipt.stderr)")
30 | }
31 | return receipt.terminationReason
32 | }
33 |
34 | static func rootSpawnWithOutputs(
35 | binary: String,
36 | arguments: [String] = [],
37 | environment: [String: String] = [:]
38 | ) throws -> AuxiliaryExecute.ExecuteReceipt {
39 | let receipt = AuxiliaryExecute.spawn(
40 | command: binary,
41 | args: arguments,
42 | environment: environment,
43 | personaOptions: .init(uid: 0, gid: 0)
44 | )
45 | if !receipt.stdout.isEmpty {
46 | DDLogInfo("Standard output: \(receipt.stdout)")
47 | }
48 | if !receipt.stderr.isEmpty {
49 | DDLogInfo("Standard error: \(receipt.stderr)")
50 | }
51 | return receipt
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/SuperIcons/LSApplicationProxy.h:
--------------------------------------------------------------------------------
1 | #ifndef LSApplicationProxy_h
2 | #define LSApplicationProxy_h
3 |
4 | #import
5 |
6 | @class LSPlugInKitProxy;
7 |
8 | @interface LSApplicationProxy : NSObject
9 |
10 | + (LSApplicationProxy *)applicationProxyForIdentifier:(NSString *)bundleIdentifier;
11 |
12 | - (BOOL)installed;
13 | - (BOOL)restricted;
14 |
15 | - (NSString *)applicationIdentifier;
16 | - (NSString *)localizedName;
17 | - (NSString *)shortVersionString;
18 | - (NSString *)applicationType;
19 | - (NSString *)teamID;
20 |
21 | - (NSURL *)bundleURL;
22 | - (NSURL *)dataContainerURL;
23 | - (NSURL *)bundleContainerURL;
24 |
25 | - (NSDictionary *)groupContainerURLs;
26 | - (NSDictionary *)entitlements;
27 |
28 | - (NSArray *)plugInKitPlugins;
29 |
30 | - (BOOL)isRemoveableSystemApp;
31 | - (BOOL)isRemovedSystemApp;
32 |
33 | @end
34 |
35 | #endif /* LSApplicationProxy_h */
36 |
--------------------------------------------------------------------------------
/SuperIcons/LSApplicationWorkspace.h:
--------------------------------------------------------------------------------
1 | #ifndef LSApplicationWorkspace_h
2 | #define LSApplicationWorkspace_h
3 |
4 | #import
5 |
6 | @class LSApplicationProxy;
7 |
8 | @interface LSApplicationWorkspace : NSObject
9 |
10 | + (LSApplicationWorkspace *)defaultWorkspace;
11 | - (NSArray *)allApplications;
12 | - (NSArray *)allInstalledApplications;
13 |
14 | - (void)enumerateApplicationsOfType:(NSInteger)type block:(void (^)(id))block;
15 |
16 | - (BOOL)openApplicationWithBundleID:(NSString *)bundleIdentifier;
17 | - (BOOL)installApplication:(NSURL *)ipaPath withOptions:(id)arg2 error:(NSError *__autoreleasing *)error;
18 | - (BOOL)uninstallApplication:(NSString *)bundleIdentifier withOptions:(id)arg2;
19 | - (BOOL)uninstallApplication:(NSString *)arg1
20 | withOptions:(id)arg2
21 | error:(NSError *__autoreleasing *)arg3
22 | usingBlock:(/*^block*/ id)arg4;
23 | - (BOOL)invalidateIconCache:(id)arg1;
24 | - (BOOL)openSensitiveURL:(NSURL *)url withOptions:(id)arg2 error:(NSError *__autoreleasing *)error;
25 |
26 | - (void)removeObserver:(id)arg1;
27 | - (void)addObserver:(id)arg1;
28 |
29 | @end
30 |
31 | #endif /* LSApplicationWorkspace_h */
32 |
--------------------------------------------------------------------------------
/SuperIcons/MyAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MyAction.swift
3 | // SuperIcons
4 | //
5 | // Created by huami on 2024/7/31.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | class MyAction {
12 |
13 | private static var cpBinaryURL: URL = {
14 | if #available(iOS 16.0, *) {
15 | return Bundle.main.url(forResource: "cp", withExtension: nil)!
16 | } else {
17 | return Bundle.main.url(forResource: "cp-15", withExtension: nil)!
18 | }
19 | }()
20 |
21 | private static var mvBinaryURL: URL = {
22 | if #available(iOS 16.0, *) {
23 | return Bundle.main.url(forResource: "mv", withExtension: nil)!
24 | } else {
25 | return Bundle.main.url(forResource: "mv-15", withExtension: nil)!
26 | }
27 | }()
28 |
29 | private static var rmBinaryURL: URL = {
30 | if #available(iOS 16.0, *) {
31 | return Bundle.main.url(forResource: "rm", withExtension: nil)!
32 | } else {
33 | return Bundle.main.url(forResource: "rm", withExtension: nil)!
34 | }
35 | }()
36 |
37 | static func changeIcons(mainpath: String, iconPath: String, iconName: String) {
38 | let fileManager = FileManager.default
39 | let tempDirectoryURL = URL(fileURLWithPath: "/tmp").appendingPathComponent(UUID().uuidString)
40 |
41 | do {
42 | try fileManager.createDirectory(at: tempDirectoryURL, withIntermediateDirectories: true, attributes: nil)
43 | } catch {
44 | showErrorAlert(error: error)
45 | return
46 | }
47 |
48 | let infoPlistURL = URL(fileURLWithPath: mainpath)
49 | let backupPlistURL = infoPlistURL.deletingLastPathComponent().appendingPathComponent("Info.plist.bak")
50 | let tempInfoPlistURL = tempDirectoryURL.appendingPathComponent("Info.plist")
51 | let iconDestinationURL = infoPlistURL.deletingLastPathComponent().appendingPathComponent(iconName)
52 |
53 | var convertIconURL = URL(fileURLWithPath: iconPath)
54 |
55 | if iconPath.lowercased().hasSuffix(".jpeg") || iconPath.lowercased().hasSuffix(".jpg") {
56 | let pngIconPath = tempDirectoryURL.appendingPathComponent("\(UUID().uuidString).png").path
57 | guard let jpegData = FileManager.default.contents(atPath: iconPath),
58 | let image = UIImage(data: jpegData),
59 | let pngData = image.pngData() else {
60 | showErrorAlert(error: NSError(domain: "ConvertJPEGToPNGError", code: 1, userInfo: [NSLocalizedDescriptionKey: "Failed to convert JPEG to PNG"]))
61 | return
62 | }
63 | do {
64 | try pngData.write(to: URL(fileURLWithPath: pngIconPath))
65 | convertIconURL = URL(fileURLWithPath: pngIconPath)
66 | } catch {
67 | showErrorAlert(error: error)
68 | return
69 | }
70 | }
71 |
72 | var success = false
73 |
74 | do {
75 | try copyURL(infoPlistURL, to: backupPlistURL)
76 | try copyURL(infoPlistURL, to: tempInfoPlistURL)
77 | try copyURL(convertIconURL, to: iconDestinationURL)
78 | try updateInfoPlist(at: tempInfoPlistURL, withIconName: iconName)
79 | try copyURL(tempInfoPlistURL, to: infoPlistURL)
80 | success = true
81 | } catch {
82 | showErrorAlert(error: error)
83 | }
84 |
85 | showResultAlert(success: success)
86 | }
87 |
88 | static func restoreIcons(mainpath: String) {
89 | let fileManager = FileManager.default
90 | let infoPlistURL = URL(fileURLWithPath: mainpath)
91 | let backupURL = infoPlistURL.deletingLastPathComponent().appendingPathComponent("Info.plist.bak")
92 |
93 | var success = false
94 |
95 | if !fileManager.fileExists(atPath: backupURL.path) {
96 | showErrorAlert(error: NSError(domain: "MyActionErrorDomain", code: 3, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("No backup found. Please change the icon before attempting to restore.", comment: "")]))
97 | return
98 | }
99 |
100 | do {
101 | try removeURL(infoPlistURL)
102 | try removeURL(infoPlistURL.deletingLastPathComponent().appendingPathComponent("AppIcon_AA.png"))
103 | try moveURL(backupURL, to: infoPlistURL)
104 | success = true
105 | } catch {
106 | showErrorAlert(error: error)
107 | }
108 |
109 | showResultAlert(success: success)
110 | }
111 |
112 | private static func copyURL(_ src: URL, to dst: URL) throws {
113 | let retCode = try Execute.rootSpawnWithOutputs(binary: cpBinaryURL.path, arguments: ["-rfp", src.path, dst.path])
114 | guard case .exit(let code) = retCode.terminationReason, code == 0 else {
115 | throw CommandFailureError(command: "cp", reason: retCode.terminationReason)
116 | }
117 | }
118 |
119 | private static func removeURL(_ url: URL) throws {
120 | let retCode = try Execute.rootSpawnWithOutputs(binary: rmBinaryURL.path, arguments: ["-rf", url.path])
121 | guard case .exit(let code) = retCode.terminationReason, code == 0 else {
122 | throw CommandFailureError(command: "rm", reason: retCode.terminationReason)
123 | }
124 | }
125 |
126 | private static func moveURL(_ src: URL, to dst: URL) throws {
127 | let retCode = try Execute.rootSpawnWithOutputs(binary: mvBinaryURL.path, arguments: ["-f", src.path, dst.path])
128 | guard case .exit(let code) = retCode.terminationReason, code == 0 else {
129 | throw CommandFailureError(command: "mv", reason: retCode.terminationReason)
130 | }
131 | }
132 |
133 | private static func updateInfoPlist(at url: URL, withIconName iconName: String) throws {
134 | guard let plist = NSMutableDictionary(contentsOf: url) else {
135 | throw NSError(domain: "MyActionErrorDomain", code: 1, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Unable to read Info.plist", comment: "")])
136 | }
137 |
138 | replaceAppIconNames(in: plist, with: iconName)
139 |
140 | if !plist.write(to: url, atomically: true) {
141 | throw NSError(domain: "MyActionErrorDomain", code: 2, userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Unable to write Info.plist", comment: "")])
142 | }
143 | }
144 |
145 | private static func replaceAppIconNames(in dict: NSMutableDictionary, with iconName: String) {
146 | for (key, value) in dict {
147 | if let keyString = key as? String, keyString.hasPrefix("CFBundleIcon") || keyString.hasPrefix("AppIcon") {
148 | dict[key] = iconName
149 | } else if let subDict = value as? NSMutableDictionary {
150 | replaceAppIconNames(in: subDict, with: iconName)
151 | } else if let array = value as? [Any] {
152 | let newArray = array.map { item -> Any in
153 | if let subDict = item as? NSMutableDictionary {
154 | replaceAppIconNames(in: subDict, with: iconName)
155 | return subDict
156 | }
157 | return item
158 | }
159 | dict[key] = newArray
160 | }
161 | }
162 | }
163 |
164 | private static func showErrorAlert(error: Error) {
165 | DispatchQueue.main.async {
166 | let alert = UIAlertController(title: NSLocalizedString("Error", comment: ""), message: error.localizedDescription, preferredStyle: .alert)
167 | alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default))
168 | UIApplication.shared.windows.first?.rootViewController?.present(alert, animated: true)
169 | }
170 | }
171 |
172 | private static func showResultAlert(success: Bool) {
173 | DispatchQueue.main.async {
174 | let title = success ? NSLocalizedString("Success", comment: "") : NSLocalizedString("Failure", comment: "")
175 | let message = success ? NSLocalizedString("Operation successful. Please rebuild the icon cache.", comment: "") : NSLocalizedString("Operation failed", comment: "")
176 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
177 | alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .default) { _ in
178 | if success {
179 | openTrollStore()
180 | }
181 | })
182 | UIApplication.shared.windows.first?.rootViewController?.present(alert, animated: true)
183 | }
184 | }
185 |
186 | private static func openTrollStore() {
187 | if let url = URL(string: "apple-magnifier://") {
188 | UIApplication.shared.open(url, options: [:], completionHandler: nil)
189 | }
190 | }
191 | }
192 |
193 | struct CommandFailureError: Error {
194 | let command: String
195 | let reason: AuxiliaryExecute.TerminationReason
196 |
197 | var localizedDescription: String {
198 | return "\(command) command failed with reason: \(reason)"
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/SuperIcons/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/SuperIcons/SuperIconsApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SuperIconsApp.swift
3 | // SuperIcons
4 | //
5 | // Created by huami on 2024/7/31.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct SuperIconsApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SuperIcons/en.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | Localizable.strings
4 | SuperIcons
5 |
6 | Created by huami on 2024/7/31.
7 |
8 | */
9 |
10 | /* ContentView.swift */
11 | "Search..." = "Search...";
12 | "SuperIcons" = "SuperIcons";
13 | "Select Action" = "Select Action";
14 | "Change Icon" = "Change Icon";
15 | "Restore Icon" = "Restore Icon";
16 | "Error selecting file: %@" = "Error selecting file: %@";
17 | "SuperIcons v1.0 (1) ©️ 2024" = "SuperIcons v1.0 (1) ©️ 2024";
18 | "Get Repository" = "Get Repository";
19 |
20 | /* MyAction.swift */
21 | "Icon change successful" = "Icon change successful";
22 | "Icon restore successful" = "Icon restore successful";
23 | "Failed to change icon" = "Failed to change icon";
24 | "Failed to restore icon" = "Failed to restore icon";
25 | "An unexpected error occurred" = "An unexpected error occurred";
26 |
27 | /* FooterView.swift */
28 | "SuperIcons v1.0 (1) ©️ 2024" = "SuperIcons v1.0 (1) ©️ 2024";
29 | "Get Repository" = "Get Repository";
30 |
31 | /* MyAction.swift additional */
32 | "Unable to read Info.plist" = "Unable to read Info.plist";
33 | "Unable to write Info.plist" = "Unable to write Info.plist";
34 | "Error" = "Error";
35 | "Success" = "Success";
36 | "Operation successful. Please rebuild the icon cache." = "Operation successful. Please rebuild the icon cache.";
37 | "Operation failed" = "Operation failed";
38 | "No backup found. Please change the icon before attempting to restore." = "No backup found. Please change the icon before attempting to restore.";
39 | "OK" = "OK";
40 |
41 | "Developed by huami." = "Developed by huami.";
42 |
--------------------------------------------------------------------------------
/SuperIcons/zh-Hans.lproj/Localizable.strings:
--------------------------------------------------------------------------------
1 | /*
2 | Localizable.strings
3 | SuperIcons
4 |
5 | Created by huami on 2024/7/31.
6 |
7 | */
8 |
9 | /* ContentView.swift */
10 | "Search..." = "搜索...";
11 | "SuperIcons" = "SuperIcons";
12 | "Select Action" = "选择操作";
13 | "Change Icon" = "更换图标";
14 | "Restore Icon" = "还原图标";
15 | "Error selecting file: %@" = "选择文件时出错:%@";
16 | "SuperIcons v1.0 (1) ©️ 2024" = "SuperIcons v1.0 (1) ©️ 2024";
17 | "Get Repository" = "获取仓库";
18 |
19 | /* MyAction.swift */
20 | "Icon change successful" = "图标更换成功";
21 | "Icon restore successful" = "图标还原成功";
22 | "Failed to change icon" = "图标更换失败";
23 | "Failed to restore icon" = "图标还原失败";
24 | "An unexpected error occurred" = "发生了意外错误";
25 |
26 | /* FooterView.swift */
27 | "SuperIcons v1.0 (1) ©️ 2024" = "SuperIcons v1.0 (1) ©️ 2024";
28 | "Get Repository" = "获取仓库";
29 |
30 | /* MyAction.swift additional */
31 | "Unable to read Info.plist" = "无法读取 Info.plist";
32 | "Unable to write Info.plist" = "无法写入 Info.plist";
33 | "Error" = "错误";
34 | "Success" = "成功";
35 | "Operation successful. Please rebuild the icon cache." = "操作成功。请重新生成图标缓存。";
36 | "Operation failed" = "操作失败";
37 | "OK" = "确定";
38 |
39 | "No backup found. Please change the icon before attempting to restore." = "未找到备份文件。请勿还原。";
40 |
41 | "Developed by huami." = "由huami. 开发";
42 |
--------------------------------------------------------------------------------