├── 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 | --------------------------------------------------------------------------------