├── .bartycrouch.toml ├── .github └── workflows │ ├── build-analyze.yml │ └── build-archive.yml ├── .gitignore ├── Artworks ├── BannerDrawer.swift ├── Icon.psd └── Screenshot.png ├── CHANGELOG.md ├── Certificates ├── Certificates.p12 └── Lessica.cer ├── LICENSE ├── README.md ├── Reveil.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ ├── Reveil.xcscheme │ ├── ReveilHelper.xcscheme │ └── library-stub.xcscheme ├── Reveil ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── AppIcon1024x1024.png │ │ └── Contents.json │ ├── Contents.json │ ├── IconShape.imageset │ │ ├── Contents.json │ │ └── R.png │ ├── MemoryActive.colorset │ │ └── Contents.json │ ├── MemoryInactive.colorset │ │ └── Contents.json │ ├── MemoryOthers.colorset │ │ └── Contents.json │ ├── MemoryPurgeable.colorset │ │ └── Contents.json │ ├── MemoryWired.colorset │ │ └── Contents.json │ ├── NetworkInterface-XHC.colorset │ │ └── Contents.json │ ├── NetworkInterface-anpi.colorset │ │ └── Contents.json │ ├── NetworkInterface-ap.colorset │ │ └── Contents.json │ ├── NetworkInterface-awdl.colorset │ │ └── Contents.json │ ├── NetworkInterface-bridge.colorset │ │ └── Contents.json │ ├── NetworkInterface-en.colorset │ │ └── Contents.json │ ├── NetworkInterface-gif.colorset │ │ └── Contents.json │ ├── NetworkInterface-ipsec.colorset │ │ └── Contents.json │ ├── NetworkInterface-iptap.colorset │ │ └── Contents.json │ ├── NetworkInterface-llw.colorset │ │ └── Contents.json │ ├── NetworkInterface-lo.colorset │ │ └── Contents.json │ ├── NetworkInterface-p2p.colorset │ │ └── Contents.json │ ├── NetworkInterface-pdp_ip.colorset │ │ └── Contents.json │ ├── NetworkInterface-pktap.colorset │ │ └── Contents.json │ ├── NetworkInterface-ppp.colorset │ │ └── Contents.json │ ├── NetworkInterface-stf.colorset │ │ └── Contents.json │ ├── NetworkInterface-utun.colorset │ │ └── Contents.json │ ├── SecurityLeaks.colorset │ │ └── Contents.json │ └── github-mark-white.imageset │ │ ├── Contents.json │ │ └── github-mark-white.png ├── BartyCrouch.swift ├── ContentView.swift ├── DataModels │ ├── ApplicationBinary.swift │ ├── Entitlements.swift │ ├── EntitlementsReader.swift │ ├── EntryExporter.swift │ ├── Presets │ │ ├── ObjCClassItem.swift │ │ ├── PortItem.swift │ │ ├── SecurityPresets.swift │ │ └── URLSchemeItem.swift │ ├── Providers │ │ ├── BatteryActivity.swift │ │ ├── CPUActivity.swift │ │ ├── DiskUsage.swift │ │ ├── FileSystem.swift │ │ ├── MemoryActivity.swift │ │ ├── NetworkIO.swift │ │ ├── NetworkInterface.swift │ │ ├── NetworkStatistics.swift │ │ └── NetworkTraffic.swift │ └── System.swift ├── Extensions │ ├── Backports │ │ ├── View+ForegroundStyle.swift │ │ ├── View+ListSectionSeparator.swift │ │ └── View+Material.swift │ ├── Color+Platform.swift │ ├── Foundation │ │ ├── CsOps.swift │ │ ├── SocketAddress.swift │ │ ├── ThreadInfo.swift │ │ ├── ThreadState.swift │ │ └── ifaddrs_safe.swift │ ├── URL+Path.swift │ └── View+Brand.swift ├── GlobalTimer.swift ├── Info.plist ├── Pages │ ├── AboutView.swift │ ├── DashboardView.swift │ ├── Details │ │ ├── BatteryInformationListView.swift │ │ ├── CPUInformationListView.swift │ │ ├── DeviceInformationListView.swift │ │ ├── DiskSpaceListView.swift │ │ ├── FileSystemListView.swift │ │ ├── FileSystemsListView.swift │ │ ├── MemoryInformationListView.swift │ │ ├── NetworkDetailListView.swift │ │ ├── NetworkDetailsListView.swift │ │ ├── NetworkInterfaceListView.swift │ │ ├── NetworkInterfacesListView.swift │ │ ├── NetworkUsageListView.swift │ │ ├── OperatingSystemListView.swift │ │ └── ScreenInformationListView.swift │ ├── DetailsListView.swift │ ├── DetailsView.swift │ └── SecurityView.swift ├── Protocols │ ├── BasicNumeric.swift │ ├── DynamicEntryProvider.swift │ ├── Entry.swift │ ├── EntryUpdater.swift │ ├── Explainable.swift │ ├── FieldCellDelegate.swift │ ├── Module.swift │ ├── ModuleListView.swift │ └── StaticEntryProvider.swift ├── Resources │ ├── PinStorage.plist │ ├── Settings.bundle │ │ ├── Root.plist │ │ ├── en.lproj │ │ │ └── Root.strings │ │ └── zh_Hans.lproj │ │ │ └── Root.strings │ ├── library_stub.zip │ ├── rsc-001-country-mapping.json │ ├── rsc-002-ios-versions.json │ ├── rsc-003-iphone-models.json │ ├── rsc-004-carriers.json │ ├── rsc-005-ipad-models.json │ └── rsc-006-ipod-models.json ├── Reveil.entitlements ├── ReveilApp.swift ├── SecuritySuite │ ├── CheckResult.swift │ ├── DebuggerChecker.swift │ ├── EmulatorChecker.swift │ ├── FailedChecks.swift │ ├── FileChecker.swift │ ├── FileIntegrityCheck.swift │ ├── FileIntegrityCheckResult.swift │ ├── FishHookChecker.swift │ ├── IOSSecuritySuite.swift │ ├── IntegrityChecker.swift │ ├── IntegrityCheckerTarget.swift │ ├── JailbreakChecker.swift │ ├── MSHookFunctionChecker.swift │ ├── ProxyChecker.swift │ ├── ReverseEngineeringToolsChecker.swift │ └── RuntimeHookChecker.swift ├── Storage │ ├── AppCodableStorage.swift │ ├── DefaultsKeyObservation.swift │ ├── DefaultsWriter.swift │ ├── Pin.swift │ ├── PinStorage.swift │ ├── PropertyListRepresentable.swift │ └── StandardUserDefaults.swift ├── ViewModels │ ├── Dashboard.swift │ ├── Entries │ │ ├── BasicEntry.swift │ │ ├── HighlightedEntryKey.swift │ │ ├── TrafficEntry.swift │ │ ├── TrafficEntryIO.swift │ │ └── UsageEntry.swift │ ├── Enums │ │ ├── EntryKey.swift │ │ ├── NetworkPrefix.swift │ │ ├── SecurityCheckType.swift │ │ └── ValueStyle.swift │ ├── Modules │ │ ├── BatteryInformation.swift │ │ ├── CPUInformation.swift │ │ ├── DeviceInformation.swift │ │ ├── DiskSpace.swift │ │ ├── FileSystems.swift │ │ ├── MemoryInformation.swift │ │ ├── NetworkDetails.swift │ │ ├── NetworkInterfaces.swift │ │ ├── NetworkUsage.swift │ │ ├── OperatingSystem.swift │ │ └── ScreenInformation.swift │ ├── Security.swift │ └── SecurityCheck.swift ├── Views │ ├── AnimatedText.swift │ ├── ColorfulBackground.swift │ ├── Dashboard │ │ ├── ActivityWidget.swift │ │ ├── CheckmarkWidget.swift │ │ ├── FieldWidget.swift │ │ ├── TrafficWidget.swift │ │ └── UsageWidget.swift │ ├── Details │ │ ├── FieldCell.swift │ │ ├── ToastView.swift │ │ ├── TrafficCell.swift │ │ └── UsageCell.swift │ └── PinButton.swift ├── en.lproj │ └── Localizable.strings ├── es.lproj │ └── Localizable.strings ├── zh-Hans.lproj │ └── Localizable.strings └── zh-Hant.lproj │ └── Localizable.strings ├── ReveilHelper └── PresetsHelper.swift └── library-stub ├── Headers ├── IOPSKeys.h ├── IOPowerSources.h ├── libproc.h ├── net │ └── route.h └── sys │ ├── kern_control.h │ └── proc_info.h ├── csblob.h ├── library_stub.h ├── library_stub.m ├── macho.h └── macho.m /.bartycrouch.toml: -------------------------------------------------------------------------------- 1 | [update] 2 | tasks = ["interfaces", "code", "transform", "normalize"] 3 | 4 | [update.interfaces] 5 | paths = ["Reveil"] 6 | subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"] 7 | defaultToBase = false 8 | ignoreEmptyStrings = false 9 | unstripped = false 10 | ignoreKeys = ["#bartycrouch-ignore!", "#bc-ignore!", "#i!"] 11 | 12 | [update.code] 13 | codePaths = ["."] 14 | subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"] 15 | localizablePaths = ["Reveil"] 16 | defaultToKeys = false 17 | additive = false 18 | customFunction = "LocalizedStringResource" 19 | unstripped = false 20 | plistArguments = true 21 | ignoreKeys = ["#bartycrouch-ignore!", "#bc-ignore!", "#i!"] 22 | overrideComments = false 23 | 24 | [update.transform] 25 | codePaths = ["Reveil"] 26 | subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"] 27 | localizablePaths = ["Reveil"] 28 | transformer = "foundation" 29 | supportedLanguageEnumPath = "." 30 | typeName = "BartyCrouch" 31 | translateMethodName = "translate" 32 | separateWithEmptyLine = true 33 | 34 | [update.normalize] 35 | paths = ["Reveil"] 36 | subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"] 37 | sourceLocale = "en" 38 | harmonizeWithSource = true 39 | sortByKeys = true 40 | separateWithEmptyLine = true 41 | 42 | [lint] 43 | paths = ["Reveil"] 44 | subpathsToIgnore = [".git", "carthage", "pods", "build", ".build", "docs"] 45 | duplicateKeys = true 46 | emptyValues = true 47 | -------------------------------------------------------------------------------- /.github/workflows/build-analyze.yml: -------------------------------------------------------------------------------- 1 | name: Xcode - Build and Analyze 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | name: Build and analyse using xcodebuild command 12 | runs-on: macos-13 13 | 14 | steps: 15 | - name: Setup Xcode version 16 | uses: maxim-lobanov/setup-xcode@v1 17 | with: 18 | xcode-version: 15.1 19 | 20 | - name: Checkout 21 | uses: actions/checkout@v3 22 | 23 | - name: Build 24 | run: | 25 | xcodebuild clean build analyze -scheme Reveil -project Reveil.xcodeproj CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]} 26 | -------------------------------------------------------------------------------- /.github/workflows/build-archive.yml: -------------------------------------------------------------------------------- 1 | name: Xcode - Build Archive 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v2.*.*' 7 | 8 | env: 9 | MARKETING_VERSION: 0.0.0 10 | PATCH_VERSION: 0 11 | 12 | jobs: 13 | build: 14 | name: Build sideloading package for TrollStore 15 | runs-on: macos-13 16 | 17 | steps: 18 | - name: Setup Xcode version 19 | uses: maxim-lobanov/setup-xcode@v1 20 | with: 21 | xcode-version: 15.1 22 | 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | 26 | - name: Update version strings 27 | run: | 28 | export PATCH_VERSION=$(echo "${GITHUB_REF#refs/tags/v}" | cut -d. -f3) 29 | echo "PATCH_VERSION=${PATCH_VERSION}" >> $GITHUB_ENV 30 | echo "MARKETING_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV 31 | xcrun agvtool new-version -all "${PATCH_VERSION}" 32 | xcrun agvtool new-marketing-version "${GITHUB_REF#refs/tags/v}" 33 | 34 | - name: Build helper 35 | run: | 36 | xcodebuild clean build archive -scheme ReveilHelper -project Reveil.xcodeproj -archivePath ReveilHelper.xcarchive CODE_SIGNING_ALLOWED=NO SKIP_INSTALL=NO | xcpretty && exit ${PIPESTATUS[0]} 37 | 38 | - name: Build 39 | run: | 40 | PATH=$PWD/ReveilHelper.xcarchive/Products/usr/local/bin:$PATH xcodebuild clean build archive -scheme Reveil -project Reveil.xcodeproj -sdk iphoneos -destination 'generic/platform=iOS' -archivePath Reveil CODE_SIGNING_ALLOWED=NO | xcpretty && exit ${PIPESTATUS[0]} 41 | 42 | - name: Package for sideloading 43 | run: | 44 | cd Reveil.xcarchive/Products/Applications 45 | codesign --remove-signature Reveil.app 46 | cd - 47 | cd Reveil.xcarchive/Products 48 | mv Applications Payload 49 | zip -qr Reveil_${{ env.MARKETING_VERSION }}.tipa Payload 50 | cd - 51 | mv Reveil.xcarchive/Products/Reveil_${{ env.MARKETING_VERSION }}.tipa . 52 | 53 | - name: Upload artifact 54 | uses: actions/upload-artifact@v3 55 | with: 56 | name: Reveil_${{ env.MARKETING_VERSION }} 57 | path: Reveil.xcarchive 58 | 59 | - name: Upload release 60 | uses: softprops/action-gh-release@v1 61 | with: 62 | token: ${{ secrets.RELEASE_GITHUB_TOKEN }} 63 | body_path: CHANGELOG.md 64 | files: Reveil_${{ env.MARKETING_VERSION }}.tipa 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/swift,macos,xcode 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=swift,macos,xcode 3 | 4 | ### macOS ### 5 | # General 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Icon must end with two \r 11 | Icon 12 | 13 | 14 | # Thumbnails 15 | ._* 16 | 17 | # Files that might appear in the root of a volume 18 | .DocumentRevisions-V100 19 | .fseventsd 20 | .Spotlight-V100 21 | .TemporaryItems 22 | .Trashes 23 | .VolumeIcon.icns 24 | .com.apple.timemachine.donotpresent 25 | 26 | # Directories potentially created on remote AFP share 27 | .AppleDB 28 | .AppleDesktop 29 | Network Trash Folder 30 | Temporary Items 31 | .apdisk 32 | 33 | ### macOS Patch ### 34 | # iCloud generated files 35 | *.icloud 36 | 37 | ### Swift ### 38 | # Xcode 39 | # 40 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 41 | 42 | ## User settings 43 | xcuserdata/ 44 | 45 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 46 | *.xcscmblueprint 47 | *.xccheckout 48 | 49 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 50 | build/ 51 | DerivedData/ 52 | *.moved-aside 53 | *.pbxuser 54 | !default.pbxuser 55 | *.mode1v3 56 | !default.mode1v3 57 | *.mode2v3 58 | !default.mode2v3 59 | *.perspectivev3 60 | !default.perspectivev3 61 | 62 | ## Obj-C/Swift specific 63 | *.hmap 64 | 65 | ## App packaging 66 | *.ipa 67 | *.dSYM.zip 68 | *.dSYM 69 | *.xcarchive 70 | 71 | ## Playgrounds 72 | timeline.xctimeline 73 | playground.xcworkspace 74 | 75 | # Swift Package Manager 76 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 77 | # Packages/ 78 | # Package.pins 79 | # Package.resolved 80 | # *.xcodeproj 81 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 82 | # hence it is not needed unless you have added a package configuration file to your project 83 | # .swiftpm 84 | 85 | .build/ 86 | Distribution/ 87 | 88 | # CocoaPods 89 | # We recommend against adding the Pods directory to your .gitignore. However 90 | # you should judge for yourself, the pros and cons are mentioned at: 91 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 92 | # Pods/ 93 | # Add this line if you want to avoid checking in source code from the Xcode workspace 94 | # *.xcworkspace 95 | 96 | # Carthage 97 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 98 | # Carthage/Checkouts 99 | 100 | Carthage/Build/ 101 | 102 | # Accio dependency management 103 | Dependencies/ 104 | .accio/ 105 | 106 | # fastlane 107 | # It is recommended to not store the screenshots in the git repo. 108 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 109 | # For more information about the recommended setup visit: 110 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 111 | 112 | fastlane/report.xml 113 | fastlane/Preview.html 114 | fastlane/screenshots/**/*.png 115 | fastlane/test_output 116 | 117 | # Code Injection 118 | # After new code Injection tools there's a generated folder /iOSInjectionProject 119 | # https://github.com/johnno1962/injectionforxcode 120 | 121 | iOSInjectionProject/ 122 | 123 | ### Xcode ### 124 | 125 | ## Xcode 8 and earlier 126 | 127 | ### Xcode Patch ### 128 | *.xcodeproj/* 129 | !*.xcodeproj/project.pbxproj 130 | !*.xcodeproj/xcshareddata/ 131 | !*.xcodeproj/project.xcworkspace/ 132 | !*.xcworkspace/contents.xcworkspacedata 133 | /*.gcno 134 | **/xcshareddata/WorkspaceSettings.xcsettings 135 | 136 | # End of https://www.toptal.com/developers/gitignore/api/swift,macos,xcode 137 | n -------------------------------------------------------------------------------- /Artworks/Icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Artworks/Icon.psd -------------------------------------------------------------------------------- /Artworks/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Artworks/Screenshot.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - NEW Items: Add screen module: physical/logical resolution/scales, maximum FPS. 2 | - Bugfix: App keeps updating when in background. 3 | -------------------------------------------------------------------------------- /Certificates/Certificates.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Certificates/Certificates.p12 -------------------------------------------------------------------------------- /Certificates/Lessica.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Certificates/Lessica.cer -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-2024 Lessica & Lakr Aream 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 | # Reveil 2 | 3 | [![Build and Analyze](https://github.com/Lessica/Reveil/actions/workflows/build-analyze.yml/badge.svg)](https://github.com/Lessica/Reveil/actions/workflows/build-analyze.yml) [![Build Archive](https://github.com/Lessica/Reveil/actions/workflows/build-archive.yml/badge.svg)](https://github.com/Lessica/Reveil/actions/workflows/build-archive.yml) ![Latest Release](https://img.shields.io/github/v/release/Lessica/Reveil) 4 | ![MIT License](https://img.shields.io/github/license/Lessica/Reveil) 5 | 6 | [now-on-havoc]: https://havoc.app/package/reveil 7 | 8 | [][now-on-havoc] 9 | 10 | Reveil is a replication of [Unveil](https://unveilapp.com) in pure SwiftUI. 11 | 12 | > Currently, Reveil is in early development, and we are working hard to make it better. 13 | 14 | ![Screenshot](./Artworks/Screenshot.png) 15 | 16 | ## Requirements 17 | 18 | - Xcode 15 or later 19 | - Requires iOS 15.0 or later 20 | - If you're using Xcode 14 or targetting iOS 14, please use the `backport/ios-14` branch. 21 | 22 | ## Why we developed this again? 23 | 24 | Unveil - The most advanced system and security analysis tool. Never got any updates after its initial release. 25 | 26 | So we decided to make this app alive again, and open-source it, that's cool. Also, Reveil utilizes complex and innovative techniques to detect possible software modifications or security threats within the iOS application sandbox without relying on exclusive libraries. 27 | 28 | ## Features 29 | 30 | - State-of-the-art Security Analysis 31 | - Visualizes CPU usage (Total/User/Idle), displays CPU specifications, and average load information. 32 | - Visualizes memory usage, displays memory specifications, and classifies memory allocations. 33 | - Displays system version, BootROM version, kernel version, system uptime, and other kernel information. 34 | - Visualizes storage usage and displays the technical storage usage information. 35 | - Lists mounted file systems and displays detailed statistical information such as type/attributes. 36 | - Visualizes internet usage and attributes internet usage to data sources such as WiFi/Ethernet, Cellular connection, or Personal Hotspot. 37 | - Visualizes and displays internet usage information of numerous data sources. 38 | - Displays available network interfaces (e.g. Wired/Wireless, Cellular connection, Access Point, IPSec tunnel) with detailed statistical information (e.g. MTU, Linespeed, Downloaded bytes, Uploaded bytes). 39 | 40 | ## Limitations 41 | 42 | Reveil is not breaking any sandbox rules, so it can't fetch some information shown below: 43 | 44 | - CPU frequency 45 | - CPU temperature 46 | - Battery temperature 47 | - Installed applications 48 | - Installed tweaks 49 | 50 | ... 51 | 52 | And not limited to the above. 53 | 54 | ## Special Thanks 55 | 56 | UX/UI design inspired by: 57 | 58 | - 🔗 [Unveil](https://unveilapp.com) by [@Pwn20wnd](https://twitter.com/Pwn20wnd) 59 | 60 | While creating this tool I used some codes from: 61 | 62 | - 🔗 [IOSSecuritySuite](https://github.com/securing/IOSSecuritySuite/tree/master) by [@_r3ggi](https://twitter.com/_r3ggi) 63 | 64 | ## License 65 | 66 | Reveil is licensed under the [MIT License](LICENSE). 67 | 68 | Copyright (c) 2023-2024 Lessica & Lakr Aream, All rights reserved. 69 | -------------------------------------------------------------------------------- /Reveil.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Reveil.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Reveil.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "colorfulx", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/Lakr233/ColorfulX.git", 7 | "state" : { 8 | "revision" : "d858d5cd91b5717910d40cb2e02ae55e891c640b", 9 | "version" : "2.6.2" 10 | } 11 | }, 12 | { 13 | "identity" : "dictionarycoder", 14 | "kind" : "remoteSourceControl", 15 | "location" : "https://github.com/almazrafi/DictionaryCoder", 16 | "state" : { 17 | "revision" : "ebcbaeb20ca5f62ed0365fa7f0473e0d480b931e", 18 | "version" : "1.2.0" 19 | } 20 | }, 21 | { 22 | "identity" : "springinterpolation", 23 | "kind" : "remoteSourceControl", 24 | "location" : "https://github.com/Lakr233/SpringInterpolation.git", 25 | "state" : { 26 | "revision" : "be8721cffc87ec514fa13ba5dab586a2730f51c9", 27 | "version" : "1.1.2" 28 | } 29 | }, 30 | { 31 | "identity" : "swift-argument-parser", 32 | "kind" : "remoteSourceControl", 33 | "location" : "https://github.com/apple/swift-argument-parser.git", 34 | "state" : { 35 | "revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b", 36 | "version" : "1.4.0" 37 | } 38 | }, 39 | { 40 | "identity" : "swift-collections", 41 | "kind" : "remoteSourceControl", 42 | "location" : "https://github.com/apple/swift-collections.git", 43 | "state" : { 44 | "revision" : "ee97538f5b81ae89698fd95938896dec5217b148", 45 | "version" : "1.1.1" 46 | } 47 | }, 48 | { 49 | "identity" : "zipfoundation", 50 | "kind" : "remoteSourceControl", 51 | "location" : "https://github.com/weichsel/ZIPFoundation.git", 52 | "state" : { 53 | "revision" : "02b6abe5f6eef7e3cbd5f247c5cc24e246efcfe0", 54 | "version" : "0.9.19" 55 | } 56 | } 57 | ], 58 | "version" : 2 59 | } 60 | -------------------------------------------------------------------------------- /Reveil.xcodeproj/xcshareddata/xcschemes/Reveil.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 42 | 44 | 50 | 51 | 52 | 53 | 59 | 61 | 67 | 68 | 69 | 70 | 72 | 73 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Reveil.xcodeproj/xcshareddata/xcschemes/ReveilHelper.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 43 | 45 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 66 | 68 | 74 | 75 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /Reveil.xcodeproj/xcshareddata/xcschemes/library-stub.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 42 | 43 | 49 | 50 | 56 | 57 | 58 | 59 | 61 | 62 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.294", 9 | "green" : "0.525", 10 | "red" : "0.118" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | }, 15 | { 16 | "appearances" : [ 17 | { 18 | "appearance" : "luminosity", 19 | "value" : "dark" 20 | } 21 | ], 22 | "color" : { 23 | "color-space" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.537", 27 | "green" : "0.822", 28 | "red" : "0.196" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/AppIcon.appiconset/AppIcon1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Reveil/Assets.xcassets/AppIcon.appiconset/AppIcon1024x1024.png -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "AppIcon1024x1024.png", 5 | "idiom" : "universal", 6 | "platform" : "ios", 7 | "size" : "1024x1024" 8 | } 9 | ], 10 | "info" : { 11 | "author" : "xcode", 12 | "version" : 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/IconShape.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "R.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/IconShape.imageset/R.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Reveil/Assets.xcassets/IconShape.imageset/R.png -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/MemoryActive.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "255", 9 | "green" : "234", 10 | "red" : "84" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/MemoryInactive.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "198", 9 | "green" : "175", 10 | "red" : "2" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/MemoryOthers.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "193", 9 | "green" : "108", 10 | "red" : "113" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/MemoryPurgeable.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "185", 9 | "green" : "89", 10 | "red" : "66" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/MemoryWired.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "173", 9 | "green" : "212", 10 | "red" : "10" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-XHC.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.420", 9 | "green" : "0.659", 10 | "red" : "0.345" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-anpi.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.922", 9 | "green" : "0.573", 10 | "red" : "0.255" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-ap.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.573", 9 | "green" : "0.186", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-awdl.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.502", 9 | "green" : "0.400", 10 | "red" : "0.863" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-bridge.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.525", 9 | "green" : "0.973", 10 | "red" : "0.973" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-en.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.557", 9 | "green" : "0.322", 10 | "red" : "0.714" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-gif.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.984", 9 | "green" : "0.910", 10 | "red" : "0.506" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-ipsec.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.729", 9 | "green" : "0.431", 10 | "red" : "0.439" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-iptap.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.576", 9 | "green" : "0.329", 10 | "red" : "0.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-llw.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.000", 9 | "green" : "0.578", 10 | "red" : "1.000" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-lo.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.690", 9 | "green" : "0.820", 10 | "red" : "0.384" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-p2p.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.447", 9 | "green" : "0.522", 10 | "red" : "0.933" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-pdp_ip.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.467", 9 | "green" : "0.878", 10 | "red" : "0.749" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-pktap.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.373", 9 | "green" : "0.431", 10 | "red" : "0.188" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-ppp.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.435", 9 | "green" : "0.820", 10 | "red" : "0.969" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-stf.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.761", 9 | "green" : "0.675", 10 | "red" : "0.302" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/NetworkInterface-utun.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.706", 9 | "green" : "0.341", 10 | "red" : "0.282" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | }, 20 | "properties" : { 21 | "localizable" : true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/SecurityLeaks.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.427", 9 | "green" : "0.278", 10 | "red" : "0.918" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/github-mark-white.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "github-mark-white.png", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | }, 12 | "properties" : { 13 | "template-rendering-intent" : "template" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Reveil/Assets.xcassets/github-mark-white.imageset/github-mark-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Reveil/Assets.xcassets/github-mark-white.imageset/github-mark-white.png -------------------------------------------------------------------------------- /Reveil/BartyCrouch.swift: -------------------------------------------------------------------------------- 1 | // This file is required in order for the `transform` task of the translation helper tool BartyCrouch to work. 2 | // See here for more details: https://github.com/FlineDev/BartyCrouch 3 | 4 | import Foundation 5 | 6 | enum BartyCrouch { 7 | enum SupportedLanguage: String { 8 | case chineseSimplified = "zh-Hans" 9 | case english = "en" 10 | case spanish = "es" 11 | } 12 | 13 | static func translate(key: String, translations: [SupportedLanguage: String], comment _: String? = nil) -> String { 14 | let typeName = String(describing: BartyCrouch.self) 15 | let methodName = #function 16 | 17 | print( 18 | "Warning: [BartyCrouch]", 19 | "Untransformed \(typeName).\(methodName) method call found with key '\(key)' and base translations '\(translations)'.", 20 | "Please ensure that BartyCrouch is installed and configured correctly." 21 | ) 22 | 23 | // fall back in case something goes wrong with BartyCrouch transformation 24 | return "BC: TRANSFORMATION FAILED!" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Reveil/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | @Environment(\.horizontalSizeClass) private var horizontalSizeClass 12 | 13 | var useTabs: Bool { 14 | horizontalSizeClass == .compact 15 | } 16 | 17 | var body: some View { 18 | if useTabs { 19 | TabsView() 20 | } else { 21 | SidebarView() 22 | } 23 | } 24 | } 25 | 26 | struct TabsView: View { 27 | var body: some View { 28 | TabView { 29 | NavigationView { 30 | DashboardView() 31 | .navigationBarAttachBrand() 32 | .background(ColorfulBackground()) 33 | } 34 | .tabItem { 35 | Label(NSLocalizedString("DASHBOARD", comment: "Dashboard"), systemImage: "square.grid.2x2") 36 | } 37 | 38 | NavigationView { 39 | DetailsView() 40 | .navigationBarAttachBrand() 41 | } 42 | .tabItem { 43 | Label(NSLocalizedString("DETAILS", comment: "Details"), systemImage: "doc.text") 44 | } 45 | 46 | NavigationView { 47 | AboutView() 48 | .navigationBarTitleDisplayMode(.inline) 49 | .background(ColorfulBackground()) 50 | } 51 | .tabItem { 52 | Label(NSLocalizedString("ABOUT", comment: "About"), systemImage: "info.circle") 53 | } 54 | } 55 | } 56 | } 57 | 58 | struct SidebarView: View { 59 | var body: some View { 60 | NavigationView { 61 | List { 62 | Section(NSLocalizedString("DASHBOARD", comment: "Dashboard")) { 63 | NavigationLink { 64 | DashboardView() 65 | .navigationTitle(NSLocalizedString("DASHBOARD", comment: "Dashboard")) 66 | .background(ColorfulBackground()) 67 | } label: { 68 | Label(NSLocalizedString("DASHBOARD", comment: "Dashboard"), systemImage: "square.grid.2x2") 69 | } 70 | } 71 | 72 | Section(NSLocalizedString("DETAILS", comment: "Details")) { 73 | DetailsView.createDetailsList() 74 | } 75 | 76 | Section(NSLocalizedString("ABOUT", comment: "About")) { 77 | NavigationLink { 78 | AboutView() 79 | .background(ColorfulBackground()) 80 | } label: { 81 | Label(NSLocalizedString("ABOUT", comment: "About"), systemImage: "info.circle") 82 | } 83 | } 84 | } 85 | .navigationTitle(NSLocalizedString("Reveil", comment: "Reveil")) 86 | 87 | DashboardView() 88 | .navigationTitle(NSLocalizedString("DASHBOARD", comment: "Dashboard")) 89 | .background(ColorfulBackground()) 90 | } 91 | .listStyle(SidebarListStyle()) 92 | } 93 | } 94 | 95 | // MARK: - Previews 96 | 97 | struct ContentView_Previews: PreviewProvider { 98 | static var previews: some View { 99 | ContentView() 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Reveil/DataModels/ApplicationBinary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mateusz Matrejek 3 | // 4 | 5 | import Foundation 6 | 7 | final class ApplicationBinary { 8 | private let handle: FileHandle 9 | 10 | init?(_ path: String) { 11 | guard let binaryHandle = FileHandle(forReadingAtPath: path) else { 12 | return nil 13 | } 14 | handle = binaryHandle 15 | } 16 | 17 | var currentOffset: UInt64 { handle.offsetInFile } 18 | 19 | func seek(to offset: UInt64) { 20 | handle.seek(toFileOffset: offset) 21 | } 22 | 23 | func read() -> T { 24 | handle.readData(ofLength: MemoryLayout.size).withUnsafeBytes { $0.load(as: T.self) } 25 | } 26 | 27 | func readData(ofLength length: Int) -> Data { 28 | handle.readData(ofLength: length) 29 | } 30 | 31 | deinit { 32 | handle.closeFile() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Reveil/DataModels/Entitlements.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Mateusz Matrejek 3 | // 4 | 5 | import Foundation 6 | 7 | final class Entitlements { 8 | struct Key { 9 | let rawKey: String 10 | 11 | init(_ name: String) { 12 | rawKey = name 13 | } 14 | 15 | static let autofillCredentialProvider = Key("com.apple.developer.authentication-services.autofill-credential-provider") 16 | static let signWithApple = Key("com.apple.developer.applesignin") 17 | static let contacts = Key("com.apple.developer.contacts.notes") 18 | static let classKit = Key("com.apple.developer.ClassKit-environment") 19 | static let automaticAssesmentConfiguration = Key("com.apple.developer.automatic-assessment-configuration") 20 | static let gameCenter = Key("com.apple.developer.game-center") 21 | static let healthKit = Key("com.apple.developer.healthkit") 22 | static let healthKitCapabilities = Key("com.apple.developer.healthkit.access") 23 | static let homeKit = Key("com.apple.developer.homekit") 24 | static let iCloudDevelopmentContainersIdentifiers = Key("com.apple.developer.icloud-container-development-container-identifiers") 25 | static let iCloudContainersEnvironment = Key("com.apple.developer.icloud-container-environment") 26 | static let iCloudContainerIdentifiers = Key("com.apple.developer.icloud-container-identifiers") 27 | static let iCloudServices = Key("com.apple.developer.icloud-services") 28 | static let iCloudKeyValueStore = Key("com.apple.developer.ubiquity-kvstore-identifier") 29 | static let interAppAudio = Key("inter-app-audio") 30 | static let networkExtensions = Key("com.apple.developer.networking.networkextension") 31 | static let personalVPN = Key("com.apple.developer.networking.vpn.api") 32 | static let apsEnvironment = Key("aps-environment") 33 | static let appGroups = Key("com.apple.security.application-groups") 34 | static let keychainAccessGroups = Key("keychain-access-groups") 35 | static let dataProtection = Key("com.apple.developer.default-data-protection") 36 | static let siri = Key("com.apple.developer.siri") 37 | static let passTypeIDs = Key("com.apple.developer.pass-type-identifiers") 38 | static let merchantIDs = Key("com.apple.developer.in-app-payments") 39 | static let wifiInfo = Key("com.apple.developer.networking.wifi-info") 40 | static let externalAccessoryConfiguration = Key("com.apple.external-accessory.wireless-configuration") 41 | static let multipath = Key("com.apple.developer.networking.multipath") 42 | static let hotspotConfiguration = Key("com.apple.developer.networking.HotspotConfiguration") 43 | static let nfcTagReaderSessionFormats = Key("com.apple.developer.nfc.readersession.formats") 44 | static let associatedDomains = Key("com.apple.developer.associated-domains") 45 | static let maps = Key("com.apple.developer.maps") 46 | static let driverKit = Key("com.apple.developer.driverkit.transport.pci") 47 | } 48 | 49 | static let empty: Entitlements = .init([:]) 50 | 51 | let values: [String: Any] 52 | 53 | init(_ values: [String: Any]) { 54 | self.values = values 55 | } 56 | 57 | func value(forKey key: Entitlements.Key) -> Any? { 58 | values[key.rawKey] 59 | } 60 | 61 | class func entitlements(from data: Data) -> Entitlements { 62 | guard let rawValues = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: Any] else { 63 | return .empty 64 | } 65 | return Entitlements(rawValues) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Reveil/DataModels/EntryExporter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EntryExporter.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/18. 6 | // 7 | 8 | import Foundation 9 | 10 | struct EntryExporter: Encodable { 11 | let moduleClass: String 12 | let moduleName: ModuleName 13 | let basicEntries: [BasicEntry] 14 | let usageEntry: UsageEntry? 15 | 16 | init(module: Module) { 17 | module.updateEntries() 18 | moduleClass = String(describing: module) 19 | moduleName = module.moduleName 20 | basicEntries = module.basicEntries 21 | usageEntry = module.usageEntry 22 | } 23 | 24 | init(provider: StaticEntryProvider & Explainable) { 25 | moduleClass = String(describing: provider) 26 | moduleName = provider.description 27 | basicEntries = provider.basicEntries 28 | usageEntry = provider.usageEntry 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Reveil/DataModels/Presets/ObjCClassItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjCClassItem.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/7. 6 | // 7 | 8 | import Foundation 9 | 10 | struct ObjCClassItem: Codable { 11 | let className: String 12 | let selectorName: String 13 | 14 | enum MethodType: Codable { 15 | case clazz 16 | case instance 17 | } 18 | 19 | let methodType: MethodType 20 | 21 | var description: String { (methodType == .clazz ? "+ [" : "- [") + className + " " + selectorName + "]" } 22 | } 23 | -------------------------------------------------------------------------------- /Reveil/DataModels/Presets/PortItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PortItem.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/7. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PortItem: Codable { 11 | let port: Int 12 | let description: String 13 | } 14 | -------------------------------------------------------------------------------- /Reveil/DataModels/Presets/URLSchemeItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URLSchemeItem.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/7. 6 | // 7 | 8 | import Foundation 9 | 10 | struct URLSchemeItem: Comparable, Codable, Hashable { 11 | static func < (lhs: URLSchemeItem, rhs: URLSchemeItem) -> Bool { 12 | lhs.description < rhs.description 13 | } 14 | 15 | let scheme: String 16 | let description: String 17 | } 18 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/BatteryActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BatteryActivity.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/26. 6 | // 7 | 8 | #if canImport(UIKit) 9 | import UIKit 10 | #else 11 | import Foundation 12 | #endif 13 | 14 | final class BatteryActivity { 15 | static let shared = BatteryActivity() 16 | 17 | enum BatteryState: Int, @unchecked Sendable { 18 | case unknown = 0 19 | case unplugged = 1 20 | case charging = 2 21 | case full = 3 22 | 23 | var description: String { 24 | switch self { 25 | case .unknown: 26 | NSLocalizedString("BATTERY_UNKNOWN", comment: "Unknown") 27 | case .unplugged: 28 | NSLocalizedString("BATTERY_UNPLUGGED", comment: "Unplugged") 29 | case .charging: 30 | NSLocalizedString("BATTERY_CHARGING", comment: "Charging") 31 | case .full: 32 | NSLocalizedString("BATTERY_FULL", comment: "Full") 33 | } 34 | } 35 | } 36 | 37 | private init() { 38 | #if canImport(UIKit) 39 | UIDevice.current.isBatteryMonitoringEnabled = true 40 | #endif 41 | } 42 | 43 | deinit { 44 | #if canImport(UIKit) 45 | UIDevice.current.isBatteryMonitoringEnabled = false 46 | #endif 47 | } 48 | 49 | func getBatteryLevel() -> Float { 50 | #if canImport(UIKit) 51 | return UIDevice.current.batteryLevel 52 | #else 53 | return 1.0 54 | #endif 55 | } 56 | 57 | func getBatteryState() -> BatteryState { 58 | #if canImport(UIKit) 59 | switch UIDevice.current.batteryState { 60 | case .unknown: 61 | return .unknown 62 | case .unplugged: 63 | return .unplugged 64 | case .charging: 65 | return .charging 66 | case .full: 67 | return .full 68 | @unknown default: 69 | return .unknown 70 | } 71 | #else 72 | return .unknown 73 | #endif 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/CPUActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CPUActivity.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import Foundation 9 | 10 | final class CPUActivity { 11 | static let shared = CPUActivity() 12 | private init() {} 13 | 14 | private var system = System() 15 | 16 | func getSummary() -> System.CPUUsage { 17 | system.cpuUsage() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/DiskUsage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiskUsage.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import Foundation 9 | 10 | final class DiskUsage { 11 | static let shared = DiskUsage() 12 | 13 | private init() { 14 | totalDiskSpaceInBytes = Self.getTotalDiskSpaceInBytes() 15 | freeDiskSpaceInBytes = Self.getFreeDiskSpaceInBytes() 16 | } 17 | 18 | private static func getTotalDiskSpaceInBytes() -> Int64 { 19 | if let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String), 20 | let space = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value 21 | { 22 | space 23 | } else { 24 | 0 25 | } 26 | } 27 | 28 | var totalDiskSpaceInBytes: Int64 29 | 30 | private static func getFreeDiskSpaceInBytes() -> Int64 { 31 | if let space = try? URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage { 32 | space 33 | } else { 34 | 0 35 | } 36 | } 37 | 38 | var freeDiskSpaceInBytes: Int64 39 | 40 | func reloadData() { 41 | totalDiskSpaceInBytes = Self.getTotalDiskSpaceInBytes() 42 | freeDiskSpaceInBytes = Self.getFreeDiskSpaceInBytes() 43 | } 44 | 45 | var usedDiskSpaceInBytes: Int64 { totalDiskSpaceInBytes - freeDiskSpaceInBytes } 46 | var usedRatio: Double { Double(usedDiskSpaceInBytes) / Double(totalDiskSpaceInBytes) } 47 | } 48 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/FileSystem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileSystem.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/6. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FileSystem: Identifiable, Codable { 11 | let id: UUID 12 | let path: String 13 | 14 | init(path: String) { 15 | id = UUID() 16 | self.path = path 17 | } 18 | 19 | struct Attribute: Codable { 20 | let name: String 21 | let description: String 22 | } 23 | 24 | enum CodingKeys: CodingKey { 25 | case path 26 | } 27 | 28 | init(from decoder: Decoder) throws { 29 | let container = try decoder.container(keyedBy: CodingKeys.self) 30 | try self.init(path: container.decode(String.self, forKey: .path)) 31 | } 32 | 33 | func encode(to encoder: Encoder) throws { 34 | var container = encoder.container(keyedBy: CodingKeys.self) 35 | try container.encode(path, forKey: .path) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/MemoryActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryActivity.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import Foundation 9 | 10 | final class MemoryActivity { 11 | static let shared = MemoryActivity() 12 | private init() {} 13 | 14 | var usage: Double { min(1.0, 1.0 - System.memoryUsage().free / System.physicalMemory(.gigabyte)) } 15 | } 16 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/NetworkIO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkIO.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/13. 6 | // 7 | 8 | import Foundation 9 | 10 | struct NetworkIO: Codable { 11 | init(received: Int64, sent: Int64, receivedDelta: Int64 = 0, sentDelta: Int64 = 0) { 12 | self.received = received 13 | self.sent = sent 14 | self.receivedDelta = receivedDelta 15 | self.sentDelta = sentDelta 16 | } 17 | 18 | let received: Int64 19 | let sent: Int64 20 | let receivedDelta: Int64 21 | let sentDelta: Int64 22 | } 23 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/NetworkInterface.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkInterface.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/6. 6 | // 7 | 8 | import Foundation 9 | 10 | struct NetworkInterface: Identifiable, Codable { 11 | private static func aliasByPrefix(_ name: String) -> String { 12 | var prefix = NetworkPrefix(rawValue: name) 13 | if prefix == nil { 14 | prefix = NetworkPrefix.others 15 | } 16 | guard let prefix else { 17 | return BasicEntry.unknownValue 18 | } 19 | return prefix.description 20 | } 21 | 22 | let id: UUID 23 | let name: String 24 | let alias: String 25 | let rawValue: ifaddrs_safe 26 | 27 | init(name: String, alias: String, rawValue: ifaddrs_safe) { 28 | id = UUID() 29 | self.name = name 30 | self.alias = alias 31 | self.rawValue = rawValue 32 | } 33 | 34 | init(rawValue: ifaddrs_safe) { 35 | id = UUID() 36 | name = rawValue.ifa_name 37 | alias = Self.aliasByPrefix(rawValue.ifa_name) 38 | self.rawValue = rawValue 39 | } 40 | 41 | enum CodingKeys: CodingKey { 42 | case name 43 | case alias 44 | case rawValue 45 | } 46 | 47 | init(from decoder: Decoder) throws { 48 | let container = try decoder.container(keyedBy: CodingKeys.self) 49 | try self.init( 50 | name: container.decode(String.self, forKey: .name), 51 | alias: container.decode(String.self, forKey: .alias), 52 | rawValue: container.decode(ifaddrs_safe.self, forKey: .rawValue) 53 | ) 54 | } 55 | 56 | func encode(to encoder: Encoder) throws { 57 | var container = encoder.container(keyedBy: CodingKeys.self) 58 | try container.encode(name, forKey: .name) 59 | try container.encode(alias, forKey: .alias) 60 | try container.encode(rawValue, forKey: .rawValue) 61 | } 62 | 63 | struct Attribute: Codable { 64 | let name: String 65 | let description: String 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/NetworkStatistics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkStatistics.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/13. 6 | // 7 | 8 | import QuartzCore 9 | 10 | private let gBufferFormatter: ByteCountFormatter = { 11 | let formatter = ByteCountFormatter() 12 | formatter.countStyle = .memory 13 | formatter.allowedUnits = [.useAll] 14 | return formatter 15 | }() 16 | 17 | struct NetworkStatistics: Codable { 18 | let dictionary: [NetworkPrefix: NetworkIO] 19 | let stamp: TimeInterval 20 | } 21 | 22 | extension NetworkStatistics { 23 | func io(prefix: NetworkPrefix) -> NetworkIO? { 24 | dictionary[prefix] 25 | } 26 | 27 | func ratio(prefix: NetworkPrefix) -> Double? { 28 | guard let io = dictionary[prefix], let allIO = dictionary[.all] else { 29 | return nil 30 | } 31 | return Double(io.received &+ io.sent) / Double(allIO.received &+ allIO.sent) 32 | } 33 | 34 | func receivedBytes(prefix: NetworkPrefix) -> Int64? { 35 | guard let io = dictionary[prefix] else { 36 | return nil 37 | } 38 | return io.received 39 | } 40 | 41 | func sentBytes(prefix: NetworkPrefix) -> Int64? { 42 | guard let io = dictionary[prefix] else { 43 | return nil 44 | } 45 | return io.sent 46 | } 47 | 48 | func allBytes(prefix: NetworkPrefix) -> Int64? { 49 | guard let io = dictionary[prefix] else { 50 | return nil 51 | } 52 | return io.received &+ io.sent 53 | } 54 | 55 | func entryValue(prefix: NetworkPrefix, style: ValueStyle = .detailed) -> String { 56 | var pfxValue: String 57 | if let categoryBytes = allBytes(prefix: prefix), let totalBytes = allBytes(prefix: .all) { 58 | if style == .dashboard { 59 | pfxValue = gBufferFormatter.string(fromByteCount: categoryBytes) 60 | } else { 61 | let ratio = Swift.min(1.0, Double(categoryBytes) / Double(totalBytes)) * 100.0 62 | pfxValue = String(format: "%@\n%.2f%%", gBufferFormatter.string(fromByteCount: categoryBytes), ratio) 63 | } 64 | } else { 65 | if style == .dashboard { 66 | pfxValue = gBufferFormatter.string(fromByteCount: 0) 67 | } else { 68 | pfxValue = String(format: "%@\n%.2f%%", gBufferFormatter.string(fromByteCount: 0), 0.0) 69 | } 70 | } 71 | return pfxValue 72 | } 73 | 74 | var isOverDue: Bool { CACurrentMediaTime() - stamp > 1.5 } 75 | } 76 | -------------------------------------------------------------------------------- /Reveil/DataModels/Providers/NetworkTraffic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkTraffic.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import QuartzCore 9 | 10 | final class NetworkTraffic { 11 | private var lastStatistics: NetworkStatistics? 12 | 13 | func getStatistics() -> NetworkStatistics { 14 | let isOverDue = lastStatistics?.isOverDue ?? true 15 | var result = [NetworkPrefix: NetworkIO]() 16 | let ifaddrs = System.interfaceAddresses() 17 | var allReceived: Int64 = 0 18 | var allSent: Int64 = 0 19 | ifaddrs.forEach { addrs in 20 | guard addrs.ifa_addr?.family == AF_LINK else { 21 | return 22 | } 23 | let name = addrs.ifa_name 24 | var prefix = NetworkPrefix(rawValue: name) 25 | if prefix == nil { 26 | prefix = NetworkPrefix.others 27 | } 28 | guard let prefix else { 29 | return 30 | } 31 | guard let networkData = addrs.ifa_data else { 32 | return 33 | } 34 | var received = Int64(networkData.ifi_ibytes) 35 | var sent = Int64(networkData.ifi_obytes) 36 | allReceived &+= received 37 | allSent &+= sent 38 | if let prevIO = result[prefix] { 39 | received &+= prevIO.received 40 | sent &+= prevIO.sent 41 | } 42 | 43 | let lastReceived = (lastStatistics?.io(prefix: prefix)?.received ?? 0) 44 | let lastSent = (lastStatistics?.io(prefix: prefix)?.sent ?? 0) 45 | result[prefix] = NetworkIO( 46 | received: received, sent: sent, 47 | receivedDelta: isOverDue ? Int64.max : received - lastReceived, 48 | sentDelta: isOverDue ? Int64.max : sent - lastSent 49 | ) 50 | } 51 | let lastAllReceived = (lastStatistics?.io(prefix: .all)?.received ?? 0) 52 | let lastAllSent = (lastStatistics?.io(prefix: .all)?.sent ?? 0) 53 | result[.all] = NetworkIO( 54 | received: allReceived, sent: allSent, 55 | receivedDelta: isOverDue ? Int64.max : allReceived - lastAllReceived, 56 | sentDelta: isOverDue ? Int64.max : allSent - lastAllSent 57 | ) 58 | let netStats = NetworkStatistics(dictionary: result, stamp: CACurrentMediaTime()) 59 | lastStatistics = netStats 60 | return netStats 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Reveil/Extensions/Backports/View+ForegroundStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+ForegroundStyle.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2024/1/8. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | @ViewBuilder 12 | func foregroundStyle(accent: Bool) -> some View { 13 | if #available(iOS 15.0, *), accent { 14 | self.foregroundStyle(.accent) 15 | } else { 16 | self 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Reveil/Extensions/Backports/View+ListSectionSeparator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+ListSectionSeparator.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2024/1/8. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | @ViewBuilder 12 | func listSectionSeparator(hidden: Bool = true) -> some View { 13 | if #available(iOS 15.0, *) { 14 | self.listSectionSeparator(hidden ? .hidden : .visible, edges: .all) 15 | } else { 16 | self 17 | } 18 | } 19 | 20 | @ViewBuilder 21 | func listSectionSeparator(topHidden: Bool = true) -> some View { 22 | if #available(iOS 15.0, *) { 23 | self.listSectionSeparator(topHidden ? .hidden : .visible, edges: .top) 24 | } else { 25 | self 26 | } 27 | } 28 | 29 | @ViewBuilder 30 | func listSectionSeparator(bottomHidden: Bool = true) -> some View { 31 | if #available(iOS 15.0, *) { 32 | self.listSectionSeparator(bottomHidden ? .hidden : .visible, edges: .bottom) 33 | } else { 34 | self 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reveil/Extensions/Backports/View+Material.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Material.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2024/1/8. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | @ViewBuilder 12 | func background(thinMaterial: Bool) -> some View { 13 | if #available(iOS 15.0, *), thinMaterial { 14 | self.background(.thinMaterial) 15 | } else { 16 | self 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Reveil/Extensions/Color+Platform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color+Platform.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/25. 6 | // 7 | 8 | import UIKit 9 | 10 | typealias PlatformColor = UIColor 11 | extension PlatformColor { 12 | static let labelAlias = PlatformColor.label 13 | static let secondaryLabelAlias = PlatformColor.secondaryLabel 14 | static let tertiaryLabelAlias = PlatformColor.tertiaryLabel 15 | static let secondarySystemBackgroundAlias = PlatformColor.secondarySystemBackground 16 | static let secondarySystemFillAlias = PlatformColor.secondarySystemFill 17 | static let separatorAlias = PlatformColor.separator 18 | static let systemGray4Alias = PlatformColor.systemGray4 19 | } 20 | -------------------------------------------------------------------------------- /Reveil/Extensions/Foundation/ThreadState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThreadState.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/30. 6 | // 7 | 8 | import Foundation 9 | 10 | struct GeneralThreadState: Codable { 11 | let data: thread_state_data_t 12 | 13 | enum CodingKeys: CodingKey { 14 | case data 15 | } 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | data = try container.decode(Data.self, forKey: .data).withUnsafeBytes { 20 | $0.bindMemory(to: thread_state_data_t.self)[0] 21 | } 22 | } 23 | 24 | func encode(to encoder: Encoder) throws { 25 | var container = encoder.container(keyedBy: CodingKeys.self) 26 | try container.encode(withUnsafePointer(to: data) { ptr in 27 | ptr.withMemoryRebound(to: natural_t.self, capacity: MemoryLayout.size(ofValue: ptr)) { pointer in 28 | let buffer = UnsafeBufferPointer(start: pointer, count: Mirror(reflecting: data).children.count) 29 | return Data(buffer: buffer) 30 | } 31 | }, forKey: .data) 32 | } 33 | } 34 | 35 | #if arch(arm64) 36 | struct ARM64ThreadState: Codable { 37 | let data: arm_thread_state64_t 38 | } 39 | 40 | typealias ARM64GeneralPurposeRegisters = (__uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t, __uint64_t) 41 | 42 | extension arm_thread_state64_t: Codable { 43 | enum CodingKeys: CodingKey { 44 | case __x 45 | case __fp 46 | case __lr 47 | case __sp 48 | case __pc 49 | case __cpsr 50 | case __pad 51 | } 52 | 53 | public init(from decoder: Decoder) throws { 54 | let container = try decoder.container(keyedBy: CodingKeys.self) 55 | try self.init( 56 | __x: container.decode(Data.self, forKey: .__x).withUnsafeBytes { 57 | $0.bindMemory(to: ARM64GeneralPurposeRegisters.self)[0] 58 | }, 59 | __fp: container.decode(UInt64.self, forKey: .__fp), 60 | __lr: container.decode(UInt64.self, forKey: .__lr), 61 | __sp: container.decode(UInt64.self, forKey: .__sp), 62 | __pc: container.decode(UInt64.self, forKey: .__pc), 63 | __cpsr: container.decode(UInt32.self, forKey: .__cpsr), 64 | __pad: container.decode(UInt32.self, forKey: .__pad) 65 | ) 66 | } 67 | 68 | public func encode(to encoder: Encoder) throws { 69 | var container = encoder.container(keyedBy: CodingKeys.self) 70 | try container.encode(withUnsafePointer(to: __x) { ptr in 71 | ptr.withMemoryRebound(to: __uint64_t.self, capacity: MemoryLayout.size(ofValue: ptr)) { pointer in 72 | let buffer = UnsafeBufferPointer(start: pointer, count: Mirror(reflecting: __x).children.count) 73 | return Data(buffer: buffer) 74 | } 75 | }, forKey: .__x) 76 | try container.encode(__fp, forKey: .__fp) 77 | try container.encode(__lr, forKey: .__lr) 78 | try container.encode(__sp, forKey: .__sp) 79 | try container.encode(__pc, forKey: .__pc) 80 | try container.encode(__cpsr, forKey: .__cpsr) 81 | try container.encode(__pad, forKey: .__pad) 82 | } 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /Reveil/Extensions/View+Brand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Brand.swift 3 | // Reveil 4 | // 5 | // Created by 秋星桥 on 2023/12/30. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | extension View { 12 | func navigationBarAttachBrand() -> some View { 13 | navigationTitle(NSLocalizedString("Reveil", comment: "Reveil")) 14 | .navigationBarTitleDisplayMode(.inline) 15 | .toolbar { 16 | ToolbarItem(placement: .principal) { 17 | Image("IconShape") 18 | .resizable() 19 | .aspectRatio(contentMode: .fit) 20 | .foregroundColor(.accentColor) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Reveil/GlobalTimer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GlobalTimer.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import Combine 9 | import UIKit 10 | 11 | protocol GlobalTimerObserver { 12 | var globalName: String { get } 13 | func eventOccurred(globalTimer timer: GlobalTimer) -> Void 14 | } 15 | 16 | final class GlobalTimer: ObservableObject { 17 | private struct Observer { 18 | let value: any GlobalTimerObserver 19 | var registeredCount: Int 20 | } 21 | 22 | static let shared = GlobalTimer() 23 | private var observers = [ModuleName: Observer]() 24 | 25 | @Published var ticks: Int 26 | 27 | private var timer: Timer? 28 | 29 | private init() { 30 | ticks = 0 31 | setupTimer() 32 | registerNotifications() 33 | } 34 | 35 | deinit { 36 | unregisterNotifications() 37 | } 38 | 39 | private func registerNotifications() { 40 | NotificationCenter.default.addObserver( 41 | self, 42 | selector: #selector(applicationDidEnterBackground(_:)), 43 | name: UIApplication.didEnterBackgroundNotification, 44 | object: nil 45 | ) 46 | NotificationCenter.default.addObserver( 47 | self, 48 | selector: #selector(applicationWillEnterForeground(_:)), 49 | name: UIApplication.willEnterForegroundNotification, 50 | object: nil 51 | ) 52 | } 53 | 54 | private func unregisterNotifications() { 55 | NotificationCenter.default.removeObserver(self) 56 | } 57 | 58 | @objc 59 | private func applicationWillEnterForeground(_: Notification) { 60 | setupTimer() 61 | } 62 | 63 | @objc 64 | private func applicationDidEnterBackground(_: Notification) { 65 | tearDownTimer() 66 | } 67 | 68 | private func setupTimer() { 69 | timer?.invalidate() 70 | timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [unowned self] _ in 71 | if observers.isEmpty { 72 | return 73 | } 74 | ticks += 1 75 | observers.forEach { [unowned self] (key: ModuleName, observer: Observer) in 76 | observer.value.eventOccurred(globalTimer: self) 77 | } 78 | } 79 | timer?.fire() 80 | } 81 | 82 | private func tearDownTimer() { 83 | timer?.invalidate() 84 | timer = nil 85 | } 86 | 87 | func addObserver(_ observer: T) where T: GlobalTimerObserver { 88 | let globalName = observer.globalName 89 | if observers[globalName] != nil { 90 | observers[globalName]?.registeredCount += 1 91 | return 92 | } 93 | observers[globalName] = Observer(value: observer, registeredCount: 1) 94 | } 95 | 96 | func removeObserver(_ observer: T) where T: GlobalTimerObserver { 97 | let globalName = observer.globalName 98 | guard observers[globalName] != nil else { 99 | return 100 | } 101 | observers[globalName]?.registeredCount -= 1 102 | if observers[globalName]?.registeredCount == 0 { 103 | observers.removeValue(forKey: globalName) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /Reveil/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LSApplicationQueriesSchemes 6 | 7 | cydia 8 | undecimus 9 | sileo 10 | zbra 11 | filza 12 | activator 13 | 14 | ITSAppUsesNonExemptEncryption 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/BatteryInformationListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BatteryInformationListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct BatteryInformationListView: View, Identifiable, ModuleListView, GlobalTimerObserver { 11 | let id = UUID() 12 | let module: Module = BatteryInformation.shared 13 | let globalName: String = String(describing: BatteryInformation.self) 14 | 15 | init() {} 16 | 17 | init?(entryKey: EntryKey) { 18 | guard module.updatableEntryKeys.contains(entryKey) else { 19 | return nil 20 | } 21 | } 22 | 23 | var body: some View { 24 | DetailsListView( 25 | basicEntries: module.basicEntries, 26 | usageEntry: module.usageEntry, 27 | usageStyle: .compat 28 | ) 29 | .navigationTitle(module.moduleName) 30 | .navigationBarItems(trailing: PinButton(pin: AppCodableStorage( 31 | wrappedValue: Pin(false), .BatteryInformation, 32 | store: PinStorage.shared.userDefaults 33 | ))) 34 | .onAppear { 35 | GlobalTimer.shared.addObserver(self) 36 | } 37 | .onDisappear { 38 | GlobalTimer.shared.removeObserver(self) 39 | } 40 | } 41 | 42 | func eventOccurred(globalTimer timer: GlobalTimer) { 43 | module.updateEntries() 44 | } 45 | } 46 | 47 | // MARK: - Previews 48 | 49 | struct BatteryInformationListView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | BatteryInformationListView() 52 | .environmentObject(HighlightedEntryKey()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/CPUInformationListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CPUInformationListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CPUInformationListView: View, Identifiable, ModuleListView, GlobalTimerObserver { 11 | let id = UUID() 12 | let module: Module = CPUInformation.shared 13 | let globalName: String = String(describing: CPUInformation.self) 14 | 15 | init() {} 16 | 17 | init?(entryKey: EntryKey) { 18 | guard module.updatableEntryKeys.contains(entryKey) else { 19 | return nil 20 | } 21 | } 22 | 23 | var body: some View { 24 | DetailsListView( 25 | basicEntries: module.basicEntries, 26 | usageEntry: module.usageEntry, 27 | usageStyle: .compat 28 | ) 29 | .navigationTitle(module.moduleName) 30 | .navigationBarItems(trailing: PinButton(pin: AppCodableStorage( 31 | wrappedValue: Pin(true), EntryKey.CPUInformation, 32 | store: PinStorage.shared.userDefaults 33 | ))) 34 | .onAppear { 35 | GlobalTimer.shared.addObserver(self) 36 | } 37 | .onDisappear { 38 | GlobalTimer.shared.removeObserver(self) 39 | } 40 | } 41 | 42 | func eventOccurred(globalTimer timer: GlobalTimer) { 43 | module.updateEntries() 44 | } 45 | } 46 | 47 | // MARK: - Previews 48 | 49 | struct CPUInformationListView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | CPUInformationListView() 52 | .environmentObject(HighlightedEntryKey()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/DeviceInformationListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeviceInformationListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DeviceInformationListView: View, ModuleListView { 11 | let module: Module = DeviceInformation.shared 12 | let globalName: String = String(describing: DeviceInformation.self) 13 | 14 | init() {} 15 | 16 | init?(entryKey: EntryKey) { 17 | guard module.updatableEntryKeys.contains(entryKey) else { 18 | return nil 19 | } 20 | } 21 | 22 | var body: some View { 23 | DetailsListView(basicEntries: module.basicEntries) 24 | .navigationTitle(module.moduleName) 25 | } 26 | 27 | func eventOccurred(globalTimer timer: GlobalTimer) { } 28 | } 29 | 30 | // MARK: - Previews 31 | 32 | struct DeviceInformationListView_Previews: PreviewProvider { 33 | static var previews: some View { 34 | DeviceInformationListView() 35 | .environmentObject(HighlightedEntryKey()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/DiskSpaceListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiskSpaceListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DiskSpaceListView: View, Identifiable, ModuleListView, GlobalTimerObserver { 11 | let id = UUID() 12 | let module: Module = DiskSpace.shared 13 | let globalName: String = String(describing: DiskSpace.self) 14 | 15 | init() {} 16 | 17 | init?(entryKey: EntryKey) { 18 | guard module.updatableEntryKeys.contains(entryKey) else { 19 | return nil 20 | } 21 | } 22 | 23 | var body: some View { 24 | DetailsListView( 25 | basicEntries: module.basicEntries, 26 | usageEntry: module.usageEntry, 27 | usageStyle: .compat 28 | ) 29 | .navigationTitle(module.moduleName) 30 | .navigationBarItems(trailing: PinButton(pin: AppCodableStorage( 31 | wrappedValue: Pin(true), .DiskSpace, 32 | store: PinStorage.shared.userDefaults 33 | ))) 34 | .onAppear { 35 | GlobalTimer.shared.addObserver(self) 36 | } 37 | .onDisappear { 38 | GlobalTimer.shared.removeObserver(self) 39 | } 40 | } 41 | 42 | func eventOccurred(globalTimer timer: GlobalTimer) { 43 | module.updateEntries() 44 | } 45 | } 46 | 47 | // MARK: - Previews 48 | 49 | struct DiskSpaceListView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | DiskSpaceListView() 52 | .environmentObject(HighlightedEntryKey()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/FileSystemListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileSystemListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FileSystemListView: View, ModuleListView { 11 | let module: Module = FileSystems.shared 12 | let globalName: String = String(describing: FileSystems.self) 13 | 14 | init?(entryKey _: EntryKey) { nil } 15 | 16 | init(item: FileSystem) { 17 | self.item = item 18 | } 19 | 20 | let item: FileSystem 21 | 22 | @State var entries: [BasicEntry] = [] 23 | 24 | var body: some View { 25 | DetailsListView(basicEntries: entries) 26 | .navigationTitle(item.path) 27 | .onAppear { 28 | entries = FileSystems.shared.entries(fs: item) 29 | } 30 | } 31 | 32 | func eventOccurred(globalTimer timer: GlobalTimer) { } 33 | } 34 | 35 | // MARK: - Previews 36 | 37 | struct FileSystemListView_Previews: PreviewProvider { 38 | static var previews: some View { 39 | FileSystemListView(item: FileSystem(path: "/")) 40 | .environmentObject(HighlightedEntryKey()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/FileSystemsListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileSystemsListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/6. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FileSystemsListView: View, ModuleListView { 11 | let module: Module = FileSystems.shared 12 | let globalName: String = String(describing: FileSystems.self) 13 | 14 | init() {} 15 | 16 | init?(entryKey: EntryKey) { 17 | guard module.updatableEntryKeys.contains(entryKey) else { 18 | return nil 19 | } 20 | } 21 | 22 | @Environment(\.dismiss) private var dismissAction 23 | 24 | @State var items: [FileSystem] = [] 25 | 26 | var body: some View { 27 | List { 28 | Section { 29 | ForEach(items, id: \.id) { entry in 30 | NavigationLink(entry.path) { 31 | FileSystemListView(item: entry) 32 | .environmentObject(HighlightedEntryKey()) 33 | } 34 | } 35 | } 36 | .listSectionSeparator(hidden: true) 37 | } 38 | .listStyle(.plain) 39 | .frame(maxWidth: .infinity) 40 | .navigationTitle(module.moduleName) 41 | .navigationBarItems( 42 | trailing: PinButton(pin: AppCodableStorage( 43 | wrappedValue: Pin(false), .FileSystems, 44 | store: PinStorage.shared.userDefaults 45 | )) 46 | ) 47 | .onAppear { 48 | FileSystems.shared.reloadData() 49 | items = FileSystems.shared.items 50 | } 51 | } 52 | 53 | func eventOccurred(globalTimer timer: GlobalTimer) { } 54 | } 55 | 56 | // MARK: - Previews 57 | 58 | struct FileSystemsListView_Previews: PreviewProvider { 59 | static var previews: some View { 60 | FileSystemsListView() 61 | .environmentObject(HighlightedEntryKey()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/MemoryInformationListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryInformationListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MemoryInformationListView: View, Identifiable, ModuleListView, GlobalTimerObserver { 11 | let id = UUID() 12 | let module: Module = MemoryInformation.shared 13 | let globalName: String = String(describing: MemoryInformation.self) 14 | 15 | init() {} 16 | 17 | init?(entryKey: EntryKey) { 18 | guard module.updatableEntryKeys.contains(entryKey) else { 19 | return nil 20 | } 21 | } 22 | 23 | var body: some View { 24 | DetailsListView( 25 | basicEntries: module.basicEntries, 26 | usageEntry: module.usageEntry, 27 | usageStyle: .regular 28 | ) 29 | .navigationTitle(module.moduleName) 30 | .navigationBarItems(trailing: PinButton(pin: AppCodableStorage( 31 | wrappedValue: Pin(true), .MemoryInformation, 32 | store: PinStorage.shared.userDefaults 33 | ))) 34 | .onAppear { 35 | GlobalTimer.shared.addObserver(self) 36 | } 37 | .onDisappear { 38 | GlobalTimer.shared.removeObserver(self) 39 | } 40 | } 41 | 42 | func eventOccurred(globalTimer timer: GlobalTimer) { 43 | module.updateEntries() 44 | } 45 | } 46 | 47 | // MARK: - Previews 48 | 49 | struct MemoryInformationListView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | MemoryInformationListView() 52 | .environmentObject(HighlightedEntryKey()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/NetworkDetailListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkDetailListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/12. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NetworkDetailListView: View, Identifiable, ModuleListView, GlobalTimerObserver { 11 | let id = UUID() 12 | let module: Module = NetworkDetails.shared 13 | let globalName: String = String(describing: NetworkDetails.self) 14 | 15 | init?(entryKey: EntryKey) { 16 | switch entryKey { 17 | case let .NetworkCategoryBytesDownload(prefix): fallthrough 18 | case let .NetworkCategoryBytesUpload(prefix): 19 | guard let pfx = NetworkPrefix(rawValue: prefix) else { 20 | return nil 21 | } 22 | self.item = pfx 23 | default: 24 | return nil 25 | } 26 | } 27 | 28 | init(item: NetworkPrefix) { 29 | self.item = item 30 | } 31 | 32 | let item: NetworkPrefix 33 | 34 | @State var entries: [BasicEntry] = [] 35 | @State var trafficEntries: [TrafficEntry] = [] 36 | 37 | var body: some View { 38 | DetailsListView( 39 | basicEntries: entries, 40 | trafficEntries: trafficEntries 41 | ) 42 | .navigationTitle(item.description) 43 | .onAppear { 44 | if let module = module as? NetworkDetails { 45 | entries = module.entries(prefix: item) 46 | trafficEntries = module.trafficEntries(prefix: item) 47 | trafficEntries.forEach { $0.invalidate() } 48 | } 49 | GlobalTimer.shared.addObserver(self) 50 | } 51 | .onDisappear { 52 | GlobalTimer.shared.removeObserver(self) 53 | } 54 | } 55 | 56 | func eventOccurred(globalTimer timer: GlobalTimer) { 57 | if let module = module as? NetworkDetails { 58 | module.update(prefix: item) 59 | } 60 | } 61 | } 62 | 63 | // MARK: - Previews 64 | 65 | struct NetworkDetailListView_Previews: PreviewProvider { 66 | static var previews: some View { 67 | NetworkDetailListView(item: NetworkPrefix.en) 68 | .environmentObject(HighlightedEntryKey()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/NetworkDetailsListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkDetailsListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/12. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NetworkDetailsListView: View, ModuleListView { 11 | let module: Module = NetworkDetails.shared 12 | let globalName: String = String(describing: NetworkDetails.self) 13 | 14 | init() {} 15 | 16 | init?(entryKey _: EntryKey) { nil } 17 | 18 | @Environment(\.dismiss) private var dismissAction 19 | 20 | @State var items: [NetworkPrefix] = [] 21 | 22 | @ViewBuilder 23 | func childDetailListView(prefix: NetworkPrefix) -> some View { 24 | NavigationLink(prefix.description) { 25 | NetworkDetailListView(item: prefix) 26 | .environmentObject(HighlightedEntryKey()) 27 | } 28 | } 29 | 30 | var body: some View { 31 | List { 32 | Section { 33 | ForEach(items, id: \.id) { item in 34 | childDetailListView(prefix: item) 35 | } 36 | } 37 | .listSectionSeparator(hidden: true) 38 | } 39 | .listStyle(.plain) 40 | .frame(maxWidth: .infinity) 41 | .navigationTitle(module.moduleName) 42 | .onAppear { 43 | items = NetworkPrefix.categoryCases 44 | } 45 | } 46 | 47 | func eventOccurred(globalTimer timer: GlobalTimer) { } 48 | } 49 | 50 | // MARK: - Previews 51 | 52 | struct NetworkDetailsListView_Previews: PreviewProvider { 53 | static var previews: some View { 54 | NetworkDetailsListView() 55 | .environmentObject(HighlightedEntryKey()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/NetworkInterfaceListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkInterfaceListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/6. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NetworkInterfaceListView: View, ModuleListView { 11 | let module: Module = NetworkInterfaces.shared 12 | let globalName: String = String(describing: NetworkInterfaces.self) 13 | 14 | init?(entryKey _: EntryKey) { nil } 15 | 16 | init(item: NetworkInterface) { 17 | self.item = item 18 | } 19 | 20 | let item: NetworkInterface 21 | 22 | @State var entries: [BasicEntry] = [] 23 | 24 | var body: some View { 25 | DetailsListView(basicEntries: entries) 26 | .navigationTitle(item.alias) 27 | .onAppear { 28 | entries = NetworkInterfaces.shared.entries(interface: item) 29 | } 30 | } 31 | 32 | func eventOccurred(globalTimer timer: GlobalTimer) { } 33 | } 34 | 35 | // MARK: - Previews 36 | 37 | struct NetworkInterfaceListView_Previews: PreviewProvider { 38 | static var previews: some View { 39 | if let ethernetStructs = System.interfaceAddresses(name: "en0").first { 40 | NetworkInterfaceListView(item: NetworkInterface(rawValue: ethernetStructs)) 41 | .environmentObject(HighlightedEntryKey()) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/NetworkInterfacesListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkInterfacesListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/6. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NetworkInterfacesListView: View, ModuleListView { 11 | let module: Module = NetworkInterfaces.shared 12 | let globalName: String = String(describing: NetworkInterfaces.self) 13 | 14 | init() {} 15 | 16 | init?(entryKey: EntryKey) { 17 | guard module.updatableEntryKeys.contains(entryKey) else { 18 | return nil 19 | } 20 | } 21 | 22 | @Environment(\.dismiss) private var dismissAction 23 | 24 | @State var items: [NetworkInterface] = [] 25 | 26 | var body: some View { 27 | List { 28 | Section { 29 | ForEach(items, id: \.id) { entry in 30 | NavigationLink(entry.alias) { 31 | NetworkInterfaceListView(item: entry) 32 | .environmentObject(HighlightedEntryKey()) 33 | } 34 | } 35 | } 36 | .listSectionSeparator(hidden: true) 37 | } 38 | .listStyle(.plain) 39 | .frame(maxWidth: .infinity) 40 | .navigationTitle(module.moduleName) 41 | .navigationBarItems( 42 | trailing: PinButton(pin: AppCodableStorage( 43 | wrappedValue: Pin(false), .NetworkInterfaces, 44 | store: PinStorage.shared.userDefaults 45 | )) 46 | ) 47 | .onAppear { 48 | NetworkInterfaces.shared.reloadData() 49 | items = NetworkInterfaces.shared.items 50 | } 51 | } 52 | 53 | func eventOccurred(globalTimer timer: GlobalTimer) { } 54 | } 55 | 56 | // MARK: - Previews 57 | 58 | struct NetworkInterfacesListView_Previews: PreviewProvider { 59 | static var previews: some View { 60 | NetworkInterfacesListView() 61 | .environmentObject(HighlightedEntryKey()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/NetworkUsageListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkUsageListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/10. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NetworkUsageListView: View, Identifiable, ModuleListView, GlobalTimerObserver { 11 | let id = UUID() 12 | let module: Module = NetworkUsage.shared 13 | let globalName: String = String(describing: NetworkUsage.self) 14 | 15 | init() {} 16 | 17 | init?(entryKey: EntryKey) { 18 | guard module.updatableEntryKeys.contains(entryKey) else { 19 | return nil 20 | } 21 | } 22 | 23 | var body: some View { 24 | DetailsListView( 25 | basicEntries: module.basicEntries, 26 | usageEntry: module.usageEntry, 27 | usageStyle: .regular 28 | ) 29 | .navigationTitle(module.moduleName) 30 | .navigationBarItems(trailing: PinButton(pin: AppCodableStorage( 31 | wrappedValue: Pin(false), .NetworkUsage, 32 | store: PinStorage.shared.userDefaults 33 | ))) 34 | .onAppear { 35 | GlobalTimer.shared.addObserver(self) 36 | } 37 | .onDisappear { 38 | GlobalTimer.shared.removeObserver(self) 39 | } 40 | } 41 | 42 | func eventOccurred(globalTimer timer: GlobalTimer) { 43 | module.updateEntries() 44 | } 45 | } 46 | 47 | // MARK: - Previews 48 | 49 | struct NetworkUsageListView_Previews: PreviewProvider { 50 | static var previews: some View { 51 | NetworkUsageListView() 52 | .environmentObject(HighlightedEntryKey()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/OperatingSystemListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OperatingSystemListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct OperatingSystemListView: View, Identifiable, ModuleListView, GlobalTimerObserver { 11 | let id = UUID() 12 | let module: Module = OperatingSystem.shared 13 | let globalName: String = String(describing: OperatingSystem.self) 14 | 15 | init() {} 16 | 17 | init?(entryKey: EntryKey) { 18 | guard module.updatableEntryKeys.contains(entryKey) else { 19 | return nil 20 | } 21 | } 22 | 23 | var body: some View { 24 | DetailsListView(basicEntries: module.basicEntries) 25 | .navigationTitle(module.moduleName) 26 | .onAppear { 27 | GlobalTimer.shared.addObserver(self) 28 | } 29 | .onDisappear { 30 | GlobalTimer.shared.removeObserver(self) 31 | } 32 | } 33 | 34 | func eventOccurred(globalTimer timer: GlobalTimer) { 35 | module.updateEntries() 36 | } 37 | } 38 | 39 | // MARK: - Previews 40 | 41 | struct OperatingSystemListView_Previews: PreviewProvider { 42 | static var previews: some View { 43 | OperatingSystemListView() 44 | .environmentObject(HighlightedEntryKey()) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Reveil/Pages/Details/ScreenInformationListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenInformationListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ScreenInformationListView: View, ModuleListView { 11 | let module: Module = ScreenInformation.shared 12 | let globalName: String = String(describing: ScreenInformation.self) 13 | 14 | init() {} 15 | 16 | init?(entryKey: EntryKey) { 17 | guard module.updatableEntryKeys.contains(entryKey) else { 18 | return nil 19 | } 20 | } 21 | 22 | var body: some View { 23 | DetailsListView(basicEntries: module.basicEntries) 24 | .navigationTitle(module.moduleName) 25 | } 26 | 27 | func eventOccurred(globalTimer timer: GlobalTimer) { } 28 | } 29 | 30 | // MARK: - Previews 31 | 32 | struct ScreenInformationListView_Previews: PreviewProvider { 33 | static var previews: some View { 34 | ScreenInformationListView() 35 | .environmentObject(HighlightedEntryKey()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reveil/Pages/DetailsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DetailsView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DetailsView: View { 11 | static func createEntry(title: String, icon: String, view: () -> (some View)) -> some View { 12 | NavigationLink { 13 | view() 14 | .environmentObject(HighlightedEntryKey()) 15 | } label: { 16 | Label(title, systemImage: icon) 17 | } 18 | } 19 | 20 | static func createDetailsList() -> some View { 21 | Group { 22 | Group { 23 | createEntry(title: Security.shared.moduleName, icon: "lock.shield") { 24 | SecurityView() 25 | } 26 | createEntry(title: DeviceInformation.shared.moduleName, icon: "iphone") { 27 | DeviceInformationListView() 28 | } 29 | createEntry(title: ScreenInformation.shared.moduleName, icon: "desktopcomputer") { 30 | ScreenInformationListView() 31 | } 32 | createEntry(title: OperatingSystem.shared.moduleName, icon: "gearshape") { 33 | OperatingSystemListView() 34 | } 35 | createEntry(title: CPUInformation.shared.moduleName, icon: "cpu") { 36 | CPUInformationListView() 37 | } 38 | } 39 | Group { 40 | createEntry(title: MemoryInformation.shared.moduleName, icon: "memorychip") { 41 | MemoryInformationListView() 42 | } 43 | createEntry(title: DiskSpace.shared.moduleName, icon: "externaldrive") { 44 | DiskSpaceListView() 45 | } 46 | createEntry(title: FileSystems.shared.moduleName, icon: "folder") { 47 | FileSystemsListView() 48 | } 49 | } 50 | Group { 51 | createEntry(title: NetworkInterfaces.shared.moduleName, icon: "network") { 52 | NetworkInterfacesListView() 53 | } 54 | createEntry(title: NetworkDetails.shared.moduleName, icon: "antenna.radiowaves.left.and.right") { 55 | NetworkDetailsListView() 56 | } 57 | createEntry(title: NetworkUsage.shared.moduleName, icon: "waveform.path.ecg") { 58 | NetworkUsageListView() 59 | } 60 | } 61 | Group { 62 | createEntry(title: BatteryInformation.shared.moduleName, icon: "battery.100") { 63 | BatteryInformationListView() 64 | } 65 | } 66 | } 67 | .listSectionSeparator(topHidden: true) 68 | } 69 | 70 | var body: some View { 71 | List { 72 | Self.createDetailsList() 73 | } 74 | .listStyle(.plain) 75 | .listSectionSeparator(hidden: true) 76 | } 77 | } 78 | 79 | // MARK: - Previews 80 | 81 | struct DetailsView_Previews: PreviewProvider { 82 | static var previews: some View { 83 | DetailsView() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Reveil/Pages/SecurityView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecurityView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/25. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SecurityView: View { 11 | @ObservedObject private var securityModel = Security.shared 12 | 13 | @Environment(\.dismiss) private var dismissAction 14 | 15 | var body: some View { 16 | DetailsListView(basicEntries: securityModel.basicEntries) 17 | .navigationTitle(NSLocalizedString("SECURITY", comment: "Security")) 18 | .navigationBarItems(trailing: PinButton(pin: AppCodableStorage( 19 | wrappedValue: Pin(true), .Security, 20 | store: PinStorage.shared.userDefaults 21 | ))) 22 | } 23 | } 24 | 25 | // MARK: - Previews 26 | 27 | struct SecurityView_Previews: PreviewProvider { 28 | static var previews: some View { 29 | SecurityView() 30 | .environmentObject(HighlightedEntryKey()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Reveil/Protocols/BasicNumeric.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BasicNumeric.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol BasicNumeric: ExpressibleByIntegerLiteral, Comparable, SignedNumeric, Codable { 11 | static func + (_: Self, _: Self) -> Self 12 | static func - (_: Self, _: Self) -> Self 13 | static func * (_: Self, _: Self) -> Self 14 | static func / (_: Self, _: Self) -> Self 15 | } 16 | 17 | extension Double: BasicNumeric {} 18 | extension Int64: BasicNumeric {} 19 | -------------------------------------------------------------------------------- /Reveil/Protocols/DynamicEntryProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DynamicEntryProvider.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/8. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol DynamicEntryProvider { 11 | var updatableEntryKeys: [EntryKey] { get } 12 | func basicEntry(key: EntryKey, style: ValueStyle) -> BasicEntry? 13 | func usageEntry(key: EntryKey, style: ValueStyle) -> UsageEntry? 14 | func trafficEntryIO(key: EntryKey, style: ValueStyle) -> TrafficEntryIO? 15 | } 16 | -------------------------------------------------------------------------------- /Reveil/Protocols/Entry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Entry.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/19. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol Entry: Identifiable, ObservableObject, Codable, Hashable { 11 | var key: EntryKey { get } 12 | var name: String { get } 13 | } 14 | -------------------------------------------------------------------------------- /Reveil/Protocols/EntryUpdater.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EntryUpdater.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/8. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol EntryUpdater { 11 | func updateEntries() 12 | func updateBasicEntry(_ entry: BasicEntry, style: ValueStyle) 13 | func updateUsageEntry(_ entry: UsageEntry, style: ValueStyle) 14 | func updateTrafficEntryIO(_ entry: TrafficEntryIO, style: ValueStyle) 15 | } 16 | -------------------------------------------------------------------------------- /Reveil/Protocols/Explainable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Explainable.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/29. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol Explainable { 11 | var description: String { get } 12 | } 13 | -------------------------------------------------------------------------------- /Reveil/Protocols/FieldCellDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FieldCellDelegate.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol FieldCellDelegate { 11 | func showToast(message: String, icon: String, dismissInterval: TimeInterval) 12 | } 13 | -------------------------------------------------------------------------------- /Reveil/Protocols/Module.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Module.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import Foundation 9 | 10 | typealias ModuleName = String 11 | 12 | protocol Module: StaticEntryProvider, DynamicEntryProvider, EntryUpdater { 13 | static var shared: Self { get } 14 | var moduleName: ModuleName { get } 15 | 16 | func reloadData() 17 | } 18 | -------------------------------------------------------------------------------- /Reveil/Protocols/ModuleListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModuleListView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import SwiftUI 9 | 10 | protocol ModuleListView: View { 11 | var module: Module { get } 12 | init?(entryKey: EntryKey) 13 | } 14 | -------------------------------------------------------------------------------- /Reveil/Protocols/StaticEntryProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StaticEntryProvider.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/8. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol StaticEntryProvider { 11 | var basicEntries: [BasicEntry] { get } 12 | var usageEntry: UsageEntry? { get } 13 | } 14 | -------------------------------------------------------------------------------- /Reveil/Resources/PinStorage.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Reveil/Resources/PinStorage.plist -------------------------------------------------------------------------------- /Reveil/Resources/Settings.bundle/Root.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | StringsTable 6 | Root 7 | PreferenceSpecifiers 8 | 9 | 10 | Type 11 | PSGroupSpecifier 12 | Title 13 | Performance 14 | 15 | 16 | Type 17 | PSToggleSwitchSpecifier 18 | Title 19 | Animated Text 20 | Key 21 | AnimatedText 22 | DefaultValue 23 | 24 | 25 | 26 | Type 27 | PSToggleSwitchSpecifier 28 | Title 29 | Animated Background 30 | Key 31 | AnimatedBackground 32 | DefaultValue 33 | 34 | 35 | 36 | Type 37 | PSToggleSwitchSpecifier 38 | Title 39 | Limit Frame Rate to 30 40 | Key 41 | LowFrameRate 42 | DefaultValue 43 | 44 | 45 | 46 | Type 47 | PSToggleSwitchSpecifier 48 | Title 49 | Legacy UI 50 | Key 51 | LegacyUI 52 | DefaultValue 53 | 54 | 55 | 56 | Type 57 | PSGroupSpecifier 58 | Title 59 | Dashboard 60 | FooterText 61 | Restart “Reveil” to make things happen. 62 | 63 | 64 | Type 65 | PSToggleSwitchSpecifier 66 | Title 67 | Reset Layouts 68 | Key 69 | ResetLayouts 70 | DefaultValue 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /Reveil/Resources/Settings.bundle/en.lproj/Root.strings: -------------------------------------------------------------------------------- 1 | "Animated Background" = "Animated Background"; 2 | 3 | "Animated Text" = "Animated Text"; 4 | 5 | "Dashboard" = "Dashboard"; 6 | 7 | "Enabled" = "Enabled"; 8 | 9 | "Group" = "Group"; 10 | 11 | "Legacy UI" = "Legacy UI"; 12 | 13 | "Limit Frame Rate to 30" = "Limit Frame Rate to 30"; 14 | 15 | "Name" = "Name"; 16 | 17 | "none given" = "none given"; 18 | 19 | "Performance" = "Performance"; 20 | 21 | "Reset Layouts" = "Reset Layouts"; 22 | 23 | "Restart “Reveil” to make things happen." = "Restart “Reveil” to make things happen."; 24 | -------------------------------------------------------------------------------- /Reveil/Resources/Settings.bundle/zh_Hans.lproj/Root.strings: -------------------------------------------------------------------------------- 1 | "Animated Background" = "动态背景"; 2 | 3 | "Animated Text" = "动态文字"; 4 | 5 | "Dashboard" = "仪表盘"; 6 | 7 | "Enabled" = "启用"; 8 | 9 | "Group" = "组"; 10 | 11 | "Legacy UI" = "经典 UI"; 12 | 13 | "Limit Frame Rate to 30" = "限制最高帧率为 30"; 14 | 15 | "Name" = "名称"; 16 | 17 | "none given" = "未提供"; 18 | 19 | "Performance" = "性能"; 20 | 21 | "Reset Layouts" = "还原布局"; 22 | 23 | "Restart “Reveil” to make things happen." = "重新打开 “Reveil” 以应用任何更改。"; 24 | -------------------------------------------------------------------------------- /Reveil/Resources/library_stub.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lessica/Reveil/d51b2542660917521a22f029630696dfa14f95ea/Reveil/Resources/library_stub.zip -------------------------------------------------------------------------------- /Reveil/Resources/rsc-006-ipod-models.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "cpu_arch": "arm64", 4 | "cpu_code": "T8010", 5 | "discontinued_at": null, 6 | "hw_model": "N112AP", 7 | "machine": "iPod9,1", 8 | "max_os_version": "15.7.9", 9 | "max_os_version_num": 15007009, 10 | "memory": 2, 11 | "min_os_version": "12.3.1", 12 | "min_os_version_num": 12003001, 13 | "name": "iPod touch (7th generation)", 14 | "native_height": 1136, 15 | "native_width": 640, 16 | "released_at": "2019/05/28", 17 | "screen_height": 568, 18 | "screen_scale": 2, 19 | "screen_width": 320, 20 | "serials": [ 21 | "M938", 22 | "M93R", 23 | "M93C", 24 | "M93M", 25 | "M93F", 26 | "M93Q", 27 | "M93T", 28 | "M937", 29 | "M939", 30 | "M93G", 31 | "M93N", 32 | "M93P", 33 | "M93J", 34 | "M93H", 35 | "M93V", 36 | "M93L", 37 | "M93D", 38 | "M93K" 39 | ], 40 | "storage": [ 41 | 32, 42 | 128, 43 | 256 44 | ], 45 | "battery_capacity": 1.043, 46 | "udid_type": 0, 47 | "bootrom": "2696.0.0.1.33" 48 | }, 49 | { 50 | "cpu_arch": "arm64", 51 | "cpu_code": "T7000", 52 | "discontinued_at": "2019/05/28", 53 | "hw_model": "N102AP", 54 | "machine": "iPod7,1", 55 | "max_os_version": "12.5.5", 56 | "max_os_version_num": 12005005, 57 | "memory": 1, 58 | "min_os_version": "8.4", 59 | "min_os_version_num": 8004000, 60 | "name": "iPod touch (6th generation)", 61 | "native_height": 1136, 62 | "native_width": 640, 63 | "released_at": "2015/07/15", 64 | "screen_height": 568, 65 | "screen_scale": 2, 66 | "screen_width": 320, 67 | "serials": [ 68 | "GM18", 69 | "GGNT", 70 | "GGK9", 71 | "GGK4", 72 | "GGK5", 73 | "GGNJ", 74 | "GGNK", 75 | "GGK6", 76 | "GM19", 77 | "GM17", 78 | "GGK7", 79 | "GGNQ", 80 | "GGNM", 81 | "GM1C", 82 | "GGNR", 83 | "GGK3", 84 | "GGNP", 85 | "GGK2", 86 | "GM1D", 87 | "GM16", 88 | "GGNW", 89 | "GGNN", 90 | "GGNL", 91 | "GGK8" 92 | ], 93 | "storage": [ 94 | 16, 95 | 32, 96 | 64, 97 | 128 98 | ], 99 | "battery_capacity": 1.043, 100 | "udid_type": 0, 101 | "bootrom": "1992.0.0.1.19" 102 | } 103 | ] -------------------------------------------------------------------------------- /Reveil/Reveil.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | com.apple.security.app-sandbox 8 | 9 | com.apple.security.network.client 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Reveil/ReveilApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReveilApp.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct ReveilApp: App { 12 | init() { _ = PinStorage.shared } 13 | 14 | var body: some Scene { 15 | WindowGroup { 16 | ContentView() 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/CheckResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CheckResult.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/29. 6 | // 7 | 8 | import Foundation 9 | 10 | struct CheckResult: Codable { 11 | let passed: Bool 12 | let failMessage: String 13 | } 14 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/DebuggerChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DebuggerChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | // swiftlint:disable line_length trailing_whitespace 9 | 10 | import Foundation 11 | 12 | enum DebuggerChecker { 13 | // https://developer.apple.com/library/archive/qa/qa1361/_index.html 14 | static func amIDebugged() -> Bool { 15 | var kinfo = kinfo_proc() 16 | var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()] 17 | var size = MemoryLayout.stride 18 | let sysctlRet = sysctl(&mib, UInt32(mib.count), &kinfo, &size, nil, 0) 19 | 20 | if sysctlRet != 0 { 21 | print("Error occured when calling sysctl(). The debugger check may not be reliable") 22 | } 23 | 24 | return (kinfo.kp_proc.p_flag & P_TRACED) != 0 25 | } 26 | 27 | static func denyDebugger() { 28 | // bind ptrace() 29 | let pointerToPtrace = UnsafeMutableRawPointer(bitPattern: -2) 30 | let ptracePtr = dlsym(pointerToPtrace, "ptrace") 31 | typealias PtraceType = @convention(c) (CInt, pid_t, CInt, CInt) -> CInt 32 | let ptrace = unsafeBitCast(ptracePtr, to: PtraceType.self) 33 | 34 | // PT_DENY_ATTACH == 31 35 | let ptraceRet = ptrace(31, 0, 0, 0) 36 | 37 | if ptraceRet != 0 { 38 | print("Error occured when calling ptrace(). Denying debugger may not be reliable") 39 | } 40 | } 41 | 42 | #if arch(arm64) 43 | static func hasBreakpointAt(_ functionAddr: UnsafeRawPointer, functionSize: vm_size_t?) -> Bool { 44 | let funcAddr = vm_address_t(UInt(bitPattern: functionAddr)) 45 | 46 | var vmStart: vm_address_t = funcAddr 47 | var vmSize: vm_size_t = 0 48 | let vmRegionInfo = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size / 4) 49 | defer { 50 | vmRegionInfo.deallocate() 51 | } 52 | var vmRegionInfoCount = mach_msg_type_number_t(VM_REGION_BASIC_INFO_64) 53 | var objectName: mach_port_t = 0 54 | 55 | let ret = vm_region_64(mach_task_self_, &vmStart, &vmSize, VM_REGION_BASIC_INFO_64, vmRegionInfo, &vmRegionInfoCount, &objectName) 56 | if ret != KERN_SUCCESS { 57 | return false 58 | } 59 | 60 | let vmRegion = vmRegionInfo.withMemoryRebound(to: vm_region_basic_info_64.self, capacity: 1) { $0 } 61 | 62 | if vmRegion.pointee.protection == (VM_PROT_READ | VM_PROT_EXECUTE) { 63 | let armBreakpointOpcode = 0xE7FF_DEFE 64 | let arm64BreakpointOpcode = 0xD420_0000 65 | let instructionBegin = functionAddr.bindMemory(to: UInt32.self, capacity: 1) 66 | var judgeSize = (vmSize - (funcAddr - vmStart)) 67 | if let size = functionSize, size < judgeSize { 68 | judgeSize = size 69 | } 70 | 71 | for valueToOffset in 0 ..< (judgeSize / 4) { 72 | if (instructionBegin.advanced(by: Int(valueToOffset)).pointee == armBreakpointOpcode) || (instructionBegin.advanced(by: Int(valueToOffset)).pointee == arm64BreakpointOpcode) { 73 | return true 74 | } 75 | } 76 | } 77 | 78 | return false 79 | } 80 | 81 | static func hasWatchpoint() -> Bool { 82 | var threads: thread_act_array_t? 83 | var threadCount: mach_msg_type_number_t = 0 84 | var hasWatchpoint = false 85 | 86 | if task_threads(mach_task_self_, &threads, &threadCount) == KERN_SUCCESS { 87 | var threadStat = arm_debug_state64_t() 88 | let capacity = MemoryLayout.size / MemoryLayout.size 89 | let threadStatPointer = withUnsafeMutablePointer(to: &threadStat) { $0.withMemoryRebound(to: natural_t.self, capacity: capacity) { $0 } } 90 | var count = mach_msg_type_number_t(MemoryLayout.size / MemoryLayout.size) 91 | 92 | for threadIndex in 0 ..< threadCount { 93 | if thread_get_state(threads![Int(threadIndex)], ARM_DEBUG_STATE64, threadStatPointer, &count) == KERN_SUCCESS { 94 | hasWatchpoint = threadStatPointer.withMemoryRebound(to: arm_debug_state64_t.self, capacity: 1) { $0 }.pointee.__wvr.0 != 0 95 | if hasWatchpoint { break } 96 | } 97 | } 98 | vm_deallocate(mach_task_self_, UInt(bitPattern: threads), vm_size_t(threadCount * UInt32(MemoryLayout.size))) 99 | } 100 | 101 | return hasWatchpoint 102 | } 103 | #endif 104 | 105 | static func isParentPidUnexpected() -> Bool { 106 | guard !EmulatorChecker.amIRunInEmulator() else { 107 | return false 108 | } 109 | 110 | let parentPid: pid_t = getppid() 111 | return parentPid != 1 // launchd is pid 1 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/EmulatorChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmulatorChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum EmulatorChecker { 12 | static func amIRunInEmulator() -> Bool { 13 | checkCompile() || checkRuntime() 14 | } 15 | 16 | private static func checkRuntime() -> Bool { 17 | ProcessInfo().environment["SIMULATOR_DEVICE_NAME"] != nil 18 | } 19 | 20 | private static func checkCompile() -> Bool { 21 | #if targetEnvironment(simulator) 22 | return true 23 | #else 24 | return false 25 | #endif 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/FailedChecks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FailedChecks.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by im on 06/02/23. 6 | // Copyright © 2023 wregula. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct FailedCheckType: Codable { 12 | let check: FailedCheck 13 | let failMessage: String 14 | } 15 | 16 | enum FailedCheck: CaseIterable, Codable { 17 | case urlSchemes 18 | case existenceOfSuspiciousFiles 19 | case suspiciousFilesCanBeOpened 20 | case restrictedDirectoriesWriteable 21 | case fork 22 | case symbolicLinks 23 | case dyld 24 | case openedPorts 25 | case pSelectFlag 26 | case suspiciousObjCClasses 27 | } 28 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/FileIntegrityCheck.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileIntegrityCheck.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/29. 6 | // 7 | 8 | import Foundation 9 | 10 | enum FileIntegrityCheck: Codable { 11 | // Compare current bundleID with a specified bundleID. 12 | case bundleID(String) 13 | 14 | // Compare current hash value(SHA256 hex string) of `embedded.mobileprovision` with a specified hash value. 15 | // Use command `"shasum -a 256 /path/to/embedded.mobileprovision"` to get SHA256 value on your macOS. 16 | case mobileProvision(String) 17 | case commonResource(String, String) 18 | 19 | // Compare current hash value(SHA256 hex string) of executable file with a specified (Image Name, Hash Value). 20 | // Only work on dynamic library and arm64. 21 | case machO(String, String) 22 | } 23 | 24 | extension FileIntegrityCheck: Explainable { 25 | var description: String { 26 | switch self { 27 | case let .bundleID(exceptedBundleID): 28 | "The expected bundle identify was \(exceptedBundleID)." 29 | case let .mobileProvision(expectedSha256Value): 30 | "The expected hash value of Mobile Provision file was \(expectedSha256Value)." 31 | case let .commonResource(resourceName, expectedSha256Value): 32 | "The expected hash value of the resource named \(resourceName) was \(expectedSha256Value)." 33 | case let .machO(imageName, expectedSha256Value): 34 | "The expected hash value of \"__TEXT.__text\" data of \(imageName) Mach-O file was \(expectedSha256Value)." 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/FileIntegrityCheckResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FileIntegrityCheckResult.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/29. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FileIntegrityCheckResult: Codable { 11 | let result: Bool 12 | let hitChecks: [FileIntegrityCheck] 13 | } 14 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/IntegrityCheckerTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegrityCheckerTarget.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/29. 6 | // 7 | 8 | import Foundation 9 | 10 | enum IntegrityCheckerTarget { 11 | // Default image 12 | case `default` 13 | 14 | // Main executable 15 | case main 16 | 17 | // Custom image with a specified name 18 | case customImage(String) 19 | 20 | // Custom executable with a specified path 21 | case customExecutable(URL) 22 | } 23 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/ProxyChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProxyChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by Wojciech Reguła on 07/12/2020. 6 | // Copyright © 2020 wregula. All rights reserved. 7 | // 8 | // swiftlint:disable trailing_whitespace 9 | 10 | import Foundation 11 | 12 | enum ProxyChecker { 13 | static func amIProxied() -> Bool { 14 | guard let unmanagedSettings = CFNetworkCopySystemProxySettings() else { 15 | return false 16 | } 17 | 18 | let settingsOptional = unmanagedSettings.takeRetainedValue() as? [String: Any] 19 | 20 | guard let settings = settingsOptional else { 21 | return false 22 | } 23 | 24 | return settings.keys.contains("HTTPProxy") || settings.keys.contains("HTTPSProxy") 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Reveil/SecuritySuite/RuntimeHookChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeHookChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by jintao on 2020/4/24. 6 | // Copyright © 2020 wregula. All rights reserved. 7 | // 8 | // swiftlint:disable line_length 9 | 10 | import Foundation 11 | import MachO 12 | 13 | enum RuntimeHookChecker { 14 | private static let swiftOnceDenyFishHooK: Void = { 15 | #if arch(arm64) 16 | FishHookChecker.denyFishHook("dladdr") 17 | #endif 18 | }() 19 | 20 | static func amIRuntimeHook(dyldWhiteList: [String], detectionClass: AnyClass, selector: Selector, isClassMethod: Bool) -> Bool { 21 | let method: Method? = if isClassMethod { 22 | class_getClassMethod(detectionClass, selector) 23 | } else { 24 | class_getInstanceMethod(detectionClass, selector) 25 | } 26 | 27 | if method == nil { 28 | // method not found 29 | return true 30 | } 31 | 32 | let imp = method_getImplementation(method!) 33 | var info = Dl_info() 34 | 35 | _ = swiftOnceDenyFishHooK 36 | 37 | // dladdr will look through vm range of allImages for vm range of an Image that contains pointer of method and return info of the Image 38 | if dladdr(UnsafeRawPointer(imp), &info) != 1 { 39 | return false 40 | } 41 | 42 | let impDyldPath = String(cString: info.dli_fname).lowercased() 43 | 44 | // at system framework 45 | if impDyldPath.contains("/System/Library".lowercased()) { 46 | return false 47 | } 48 | 49 | // at binary of app 50 | let binaryPath = String(cString: _dyld_get_image_name(0)).lowercased() 51 | if impDyldPath.contains(binaryPath) { 52 | return false 53 | } 54 | 55 | // at whiteList 56 | if let impFramework = impDyldPath.components(separatedBy: "/").last { 57 | return !dyldWhiteList.map { $0.lowercased() }.contains(impFramework) 58 | } 59 | 60 | // at injected framework 61 | return true 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Reveil/Storage/AppCodableStorage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCodableStorage.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @propertyWrapper 11 | struct AppCodableStorage: DynamicProperty { 12 | private let triggerUpdate: ObservedObject> 13 | private let writer: DefaultsWriter 14 | 15 | init(wrappedValue: Value, _ key: EntryKey, store: UserDefaults? = nil) { 16 | writer = DefaultsWriter.shared(defaultValue: wrappedValue, key: key.rawValue, defaults: store ?? .standard) 17 | triggerUpdate = .init(wrappedValue: writer) 18 | } 19 | 20 | var wrappedValue: Value { 21 | get { writer.state } 22 | nonmutating set { writer.state = newValue } 23 | } 24 | 25 | var projectedValue: Binding { 26 | Binding( 27 | get: { writer.state }, 28 | set: { writer.state = $0 } 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Reveil/Storage/DefaultsKeyObservation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultsKeyObservation.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import Foundation 9 | 10 | /// API to observe UserDefaults with a `String` key (not `KeyPath`), as `AppStorage` and `.string( forKey:)` use 11 | extension UserDefaults { 12 | /// Just a BS object b/c we can't use the newer observation syntax 13 | class UserDefaultsStringKeyObservation: NSObject { 14 | /// Handler recieves the updated value from userdefaults 15 | fileprivate init(defaults: UserDefaults, key: String, handler: @escaping (Any?) -> Void) { 16 | self.defaults = defaults 17 | self.key = key 18 | self.handler = handler 19 | super.init() 20 | if !key.isEmpty { 21 | defaults.addObserver(self, forKeyPath: key, options: .new, context: nil) 22 | } 23 | } 24 | 25 | let defaults: UserDefaults 26 | let key: String 27 | 28 | /// This prevents us from double-removing ourselves as the observer (if we are cancelled, then deinit) 29 | private var isCancelled: Bool = false 30 | 31 | private let handler: (Any?) -> Void 32 | 33 | override func observeValue(forKeyPath _: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context _: UnsafeMutableRawPointer?) { 34 | guard (object as? UserDefaults) == defaults else { fatalError("AppCodableStorage: Somehow observing wrong defaults") } 35 | let newValue = change?[.newKey] 36 | handler(newValue) 37 | } 38 | 39 | func cancel() { 40 | guard !isCancelled else { return } 41 | isCancelled = true 42 | if !key.isEmpty { 43 | defaults.removeObserver(self, forKeyPath: key) 44 | } 45 | } 46 | 47 | deinit { 48 | cancel() 49 | } 50 | } 51 | 52 | func observe(key: String, changeHandler: @escaping (Any?) -> Void) -> UserDefaultsStringKeyObservation { 53 | UserDefaultsStringKeyObservation(defaults: self, key: key, handler: changeHandler) 54 | } 55 | } 56 | 57 | import Combine 58 | 59 | @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 60 | extension UserDefaults.UserDefaultsStringKeyObservation: Cancellable {} 61 | -------------------------------------------------------------------------------- /Reveil/Storage/DefaultsWriter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultsWriter.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import Combine 9 | import Foundation 10 | 11 | /// DefaultsWiriter syncs a Codable object tree to UserDefaults 12 | /// - Writes any Codable object to user defaults **as an object tree**, not a String or coded Data. 13 | /// - Observes any external changes to its UserDefaults key 14 | /// After I wrote this, I realized it's pretty similar to [Mike Ash's "Type-Safe User Defaults"](https://github.com/mikeash/TSUD) 15 | /// 16 | final class DefaultsWriter: ObservableObject { 17 | public let key: String 18 | public let defaults: UserDefaults 19 | 20 | /// Var to get around issue with init and callback fn 21 | private var defaultsObserver: UserDefaults.UserDefaultsStringKeyObservation! 22 | private let defaultValue: Value 23 | private var pausedObservation: Bool = false 24 | 25 | /// Experimental API… I had some situations outside of SwiftUI where I need to get the value AFTER the change 26 | public let objectDidChange: AnyPublisher 27 | private let _objectDidChange: PassthroughSubject 28 | 29 | public var state: Value { 30 | willSet { 31 | objectWillChange.send() 32 | } 33 | didSet { 34 | if let encoded = try? state.propertyListValue { 35 | /// We don't want to observe the redundant notification that will come from UserDefaults 36 | pausedObservation = true 37 | defaults.set(encoded, forKey: key) 38 | pausedObservation = false 39 | } 40 | _objectDidChange.send(state) 41 | } 42 | } 43 | 44 | public init(defaultValue: Value, key: String, defaults: UserDefaults? = nil) { 45 | let defaults = defaults ?? .standard 46 | self.key = key 47 | state = Self.read(from: defaults, key: key) ?? defaultValue 48 | self.defaults = defaults 49 | self.defaultValue = defaultValue 50 | defaultsObserver = nil 51 | _objectDidChange = PassthroughSubject() 52 | objectDidChange = _objectDidChange.eraseToAnyPublisher() 53 | 54 | /// When defaults change externally, update our value 55 | /// We cannot use the newer defaults.observe() because we have a keyPath String not a KeyPath 56 | /// This means we don't force you to declare your keypath in a UserDefaults extension 57 | defaultsObserver = defaults.observe(key: key) { [weak self] newValue in 58 | guard let self, pausedObservation == false else { return } 59 | observeDefaultsUpdate(newValue) 60 | } 61 | } 62 | 63 | /// Take in a new object value from UserDefaults, updating our state 64 | func observeDefaultsUpdate(_ newValue: Any?) { 65 | if newValue is NSNull { 66 | state = defaultValue 67 | } else if let newValue { 68 | do { 69 | let newState = try Value(propertyList: newValue) 70 | state = newState 71 | } catch { 72 | print("DefaultsWriter could not deserialize update from UserDefaults observation. Not updating. \(error)") 73 | } 74 | } else { 75 | state = defaultValue 76 | } 77 | } 78 | 79 | static func read(from defaults: UserDefaults, key: String) -> Value? { 80 | if let o = defaults.object(forKey: key) { 81 | try? Value(propertyList: o) 82 | } else { 83 | nil 84 | } 85 | } 86 | } 87 | 88 | var sharedDefaultsWriters: [WhichDefaultsAndKey: Any] = [:] 89 | 90 | struct WhichDefaultsAndKey: Hashable { 91 | let defaults: UserDefaults 92 | let key: String 93 | } 94 | 95 | extension DefaultsWriter { 96 | public static func shared(defaultValue: PropertyListRepresentable, key: String, defaults: UserDefaults) -> Self { 97 | let kdPr = WhichDefaultsAndKey(defaults: defaults, key: key) 98 | if let existing = sharedDefaultsWriters[kdPr] { 99 | guard let typed = existing as? Self else { 100 | fatalError("Type \(Value.self) must remain consistent for key \(key). Existing: \(existing)") 101 | } 102 | return typed 103 | } 104 | let neue = Self(defaultValue: defaultValue as! Value, key: key, defaults: defaults) 105 | sharedDefaultsWriters[kdPr] = neue 106 | return neue 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Reveil/Storage/Pin.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Pin.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Pin: PropertyListRepresentable { 11 | let isPinned: Bool 12 | let lastChange: TimeInterval 13 | 14 | init(_ isPinned: Bool) { 15 | self.isPinned = isPinned 16 | lastChange = Date().timeIntervalSinceReferenceDate 17 | } 18 | 19 | init(negate pin: Self) { 20 | self.init(!pin.isPinned) 21 | } 22 | 23 | enum CodingKeys: CodingKey { 24 | case isPinned 25 | case lastChange 26 | } 27 | 28 | init(from decoder: Decoder) throws { 29 | let container = try decoder.container(keyedBy: CodingKeys.self) 30 | isPinned = try container.decode(Bool.self, forKey: .isPinned) 31 | lastChange = try container.decode(TimeInterval.self, forKey: .lastChange) 32 | } 33 | 34 | func encode(to encoder: Encoder) throws { 35 | var container = encoder.container(keyedBy: CodingKeys.self) 36 | try container.encode(isPinned, forKey: .isPinned) 37 | try container.encode(lastChange, forKey: .lastChange) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Reveil/Storage/PinStorage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PinStorage.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import Foundation 9 | 10 | private let plistDecoder = PropertyListDecoder() 11 | 12 | final class PinStorage: ObservableObject { 13 | private static let suiteName = "\(Bundle.main.bundleIdentifier!).\(String(describing: PinStorage.self))" 14 | 15 | static let shared = PinStorage() 16 | 17 | private init() { 18 | pinnedEntryKeys = [] 19 | if StandardUserDefaults.shared.shouldResetLayouts { 20 | resetDefaults() 21 | StandardUserDefaults.shared.didResetLayouts() 22 | } 23 | reloadData() 24 | registerNotifications() 25 | try? registerDefaults() 26 | } 27 | 28 | lazy var userDefaults: UserDefaults? = UserDefaults(suiteName: PinStorage.suiteName) 29 | 30 | private var persistentDomain: [String: Any]? { 31 | userDefaults?.persistentDomain(forName: Self.suiteName) 32 | } 33 | 34 | @Published var pinnedEntryKeys: [EntryKey] 35 | 36 | func isPinned(forKey key: EntryKey) -> Bool { 37 | return pinnedEntryKeys.contains(key) 38 | } 39 | 40 | func reloadData() { 41 | guard let dictRepr = persistentDomain else { 42 | return 43 | } 44 | 45 | pinnedEntryKeys.removeAll(keepingCapacity: true) 46 | 47 | let entryKeys = dictRepr 48 | .compactMap { (key: String, value: Any) -> (String, Pin)? in 49 | guard let protectedValue = value as? [String: Any], 50 | let pin = try? Pin(propertyList: protectedValue) 51 | else { 52 | return nil 53 | } 54 | return (key, pin) 55 | } 56 | .filter(\.1.isPinned) 57 | .sorted(by: { $0.1.lastChange < $1.1.lastChange }) 58 | .compactMap { EntryKey(rawValue: $0.0) } 59 | 60 | pinnedEntryKeys.append(contentsOf: entryKeys) 61 | } 62 | 63 | func registerDefaults() throws { 64 | guard let userDefaults, 65 | let defaultURL = Bundle.main.url(forResource: String(describing: PinStorage.self), withExtension: "plist") 66 | else { 67 | return 68 | } 69 | 70 | guard persistentDomain == nil 71 | else { 72 | return 73 | } 74 | 75 | let defaultData = try Data(contentsOf: defaultURL) 76 | guard let defaultDictionary = try PropertyListSerialization.propertyList(from: defaultData, format: nil) as? [String: Any] 77 | else { 78 | return 79 | } 80 | 81 | userDefaults.setPersistentDomain(defaultDictionary, forName: Self.suiteName) 82 | } 83 | 84 | func resetDefaults() { 85 | guard let userDefaults 86 | else { 87 | return 88 | } 89 | 90 | userDefaults.removePersistentDomain(forName: Self.suiteName) 91 | } 92 | 93 | private var observer: Any? 94 | 95 | func registerNotifications() { 96 | guard let userDefaults 97 | else { 98 | return 99 | } 100 | 101 | observer = NotificationCenter.default.addObserver(forName: UserDefaults.didChangeNotification, object: userDefaults, queue: .main) { [weak self] _ in 102 | self?.reloadData() 103 | } 104 | } 105 | 106 | deinit { 107 | if let observer { 108 | NotificationCenter.default.removeObserver(observer) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Reveil/Storage/PropertyListRepresentable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PropertyListRepresentable.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/14. 6 | // 7 | 8 | import DictionaryCoder 9 | import Foundation 10 | 11 | protocol PropertyListRepresentable: Codable { 12 | init(propertyList: Any) throws 13 | var propertyListValue: Any { get throws } 14 | } 15 | 16 | private let plistDecoder = DictionaryDecoder() 17 | private let plistEncoder = DictionaryEncoder() 18 | 19 | /// Default implementation of PropertyListRepresentable for objects that are Decobable. 20 | extension PropertyListRepresentable where Self: Decodable { 21 | init(propertyList: Any) throws { 22 | self = try plistDecoder.decode(Self.self, from: propertyList as! [String: Any]) 23 | } 24 | } 25 | 26 | /// Default implementation of PropertyListRepresentable for objects that are Encodable. 27 | extension PropertyListRepresentable where Self: Encodable { 28 | var propertyListValue: Any { 29 | get throws { 30 | /// Encode to plist, decode :( 31 | /// We can copy https://github.com/apple/swift-corelibs-foundation/blob/main/Darwin/Foundation-swiftoverlay/PlistEncoder.swift 32 | /// to fix this, just not slow enough afaik. 33 | try plistEncoder.encode(self) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Reveil/Storage/StandardUserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StandardUserDefaults.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2024/1/1. 6 | // 7 | 8 | import Foundation 9 | 10 | private let gDefaultsKeyLegacyUI = "LegacyUI" 11 | private let gDefaultsKeyAnimatedText = "AnimatedText" 12 | private let gDefaultsKeyAnimatedBackground = "AnimatedBackground" 13 | private let gDefaultsKeyLowFrameRate = "LowFrameRate" 14 | private let gDefaultsKeyResetLayouts = "ResetLayouts" 15 | 16 | class StandardUserDefaults { 17 | static let shared = StandardUserDefaults() 18 | 19 | private init() { 20 | UserDefaults.standard.register(defaults: [ 21 | gDefaultsKeyLegacyUI: false, 22 | gDefaultsKeyAnimatedText: true, 23 | gDefaultsKeyAnimatedBackground: true, 24 | gDefaultsKeyLowFrameRate: false, 25 | gDefaultsKeyResetLayouts: false, 26 | ]) 27 | } 28 | 29 | lazy var isLegacyUIEnabled: Bool = { 30 | UserDefaults.standard.bool(forKey: gDefaultsKeyLegacyUI) 31 | }() 32 | 33 | lazy var isAnimatedTextEnabled: Bool = { 34 | UserDefaults.standard.bool(forKey: gDefaultsKeyAnimatedText) 35 | }() 36 | 37 | lazy var isAnimatedBackgroundEnabled: Bool = { 38 | UserDefaults.standard.bool(forKey: gDefaultsKeyAnimatedBackground) 39 | }() 40 | 41 | lazy var isLowFrameRateEnabled: Bool = { 42 | UserDefaults.standard.bool(forKey: gDefaultsKeyLowFrameRate) 43 | }() 44 | 45 | lazy var shouldResetLayouts: Bool = { 46 | UserDefaults.standard.bool(forKey: gDefaultsKeyResetLayouts) 47 | }() 48 | 49 | func didResetLayouts() { 50 | UserDefaults.standard.removeObject(forKey: gDefaultsKeyResetLayouts) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Entries/BasicEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BasicEntry.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct BasicEntryIO: Codable { 11 | let download: BasicEntry 12 | let upload: BasicEntry 13 | var pair: [BasicEntry] { [download, upload] } 14 | } 15 | 16 | final class BasicEntry: Entry { 17 | init(key: EntryKey, name: String, value: String = "", color: Color? = nil, children: [BasicEntry]? = nil) { 18 | id = UUID() 19 | self.key = key 20 | self.name = name 21 | self.value = value 22 | self.color = color 23 | self.children = children 24 | } 25 | 26 | convenience init(customLabel: String, allowedToCopy: Bool = false) { 27 | self.init(key: allowedToCopy ? .AllowedToCopy(name: customLabel) : .Custom(name: customLabel), name: customLabel) 28 | } 29 | 30 | convenience init(sectionName: String) { 31 | self.init(key: .Section(name: sectionName), name: sectionName) 32 | } 33 | 34 | static let emptySection = BasicEntry(sectionName: "") 35 | 36 | let id: UUID 37 | let key: EntryKey 38 | let name: String 39 | @Published var value: String 40 | let color: Color? 41 | let children: [BasicEntry]? 42 | 43 | enum CodingKeys: CodingKey { 44 | case name 45 | case value 46 | case key 47 | case children 48 | } 49 | 50 | required convenience init(from decoder: Decoder) throws { 51 | let container = try decoder.container(keyedBy: CodingKeys.self) 52 | try self.init(key: container.decode(EntryKey.self, forKey: .key), 53 | name: container.decode(String.self, forKey: .name), 54 | value: container.decode(String.self, forKey: .value), 55 | children: container.decodeIfPresent([BasicEntry].self, forKey: .children)) 56 | } 57 | 58 | func encode(to encoder: Encoder) throws { 59 | var container = encoder.container(keyedBy: CodingKeys.self) 60 | try container.encode(name, forKey: .name) 61 | try container.encode(value, forKey: .value) 62 | try container.encodeIfPresent(key, forKey: .key) 63 | try container.encodeIfPresent(children, forKey: .children) 64 | } 65 | 66 | static let unknownValue = NSLocalizedString("UNKNOWN", comment: "Unknown") 67 | 68 | func hash(into hasher: inout Hasher) { 69 | hasher.combine(id) 70 | } 71 | 72 | static func == (lhs: BasicEntry, rhs: BasicEntry) -> Bool { 73 | lhs.id == rhs.id 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Entries/HighlightedEntryKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HighlightedEntryKey.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/23. 6 | // 7 | 8 | import Foundation 9 | 10 | final class HighlightedEntryKey: ObservableObject { 11 | @Published var object: EntryKey? 12 | 13 | init() { 14 | object = nil 15 | } 16 | 17 | init(object: EntryKey) { 18 | self.object = object 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Entries/TrafficEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrafficEntry.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/12. 6 | // 7 | 8 | import Combine 9 | import DequeModule 10 | import SwiftUI 11 | 12 | typealias ActivityEntry = TrafficEntry 13 | 14 | final class TrafficEntry: Entry where T: BasicNumeric { 15 | private let maximumValueCount: Int 16 | private let overrideName: String? 17 | 18 | init(child: any Entry, values: [T] = [], maximumValueCount: Int = 1000, overrideName: String? = nil) { 19 | id = UUID() 20 | self.child = child 21 | self.maximumValueCount = maximumValueCount 22 | self.values = Deque(minimumCapacity: maximumValueCount) 23 | self.overrideName = overrideName 24 | self.values.append(contentsOf: values) 25 | if let usageChild = child as? UsageEntry { 26 | childObserver = usageChild.$items.sink(receiveValue: { [weak self] items in 27 | self?.push(value: items.dropLast().reduce(0) { $0 + $1.value }) 28 | }) 29 | } 30 | } 31 | 32 | @Published var child: any Entry 33 | @Published var values: Deque 34 | 35 | private var childObserver: AnyCancellable? 36 | var basicChild: BasicEntry? { child as? BasicEntry } 37 | var usageChild: UsageEntry? { child as? UsageEntry } 38 | 39 | let id: UUID 40 | var key: EntryKey { child.key } 41 | var name: String { overrideName ?? child.name } 42 | 43 | enum CodingKeys: CodingKey { 44 | case child 45 | case values 46 | case maximumValueCount 47 | case overrideName 48 | } 49 | 50 | required convenience init(from decoder: Decoder) throws { 51 | let container = try decoder.container(keyedBy: CodingKeys.self) 52 | var childEntry: any Entry 53 | if let basicEntry = try? container.decode(BasicEntry.self, forKey: .child) { 54 | childEntry = basicEntry 55 | } else if let usageEntry = try? container.decode(UsageEntry.self, forKey: .child) { 56 | childEntry = usageEntry 57 | } else { 58 | throw DecodingError.dataCorruptedError(forKey: CodingKeys.child, in: container, 59 | debugDescription: "The only child is neither a basic entry or an usage entry.") 60 | } 61 | try self.init( 62 | child: childEntry, 63 | values: container.decode([T].self, forKey: .values), 64 | maximumValueCount: container.decodeIfPresent(Int.self, forKey: .maximumValueCount) ?? 1000, 65 | overrideName: container.decodeIfPresent(String.self, forKey: .overrideName) 66 | ) 67 | } 68 | 69 | func encode(to encoder: Encoder) throws { 70 | var container = encoder.container(keyedBy: CodingKeys.self) 71 | try container.encode(child, forKey: .child) 72 | try container.encode(values, forKey: .values) 73 | try container.encode(maximumValueCount, forKey: .maximumValueCount) 74 | try container.encodeIfPresent(overrideName, forKey: .overrideName) 75 | } 76 | 77 | func push(value: T) { 78 | if values.count > maximumValueCount { 79 | _ = values.popFirst() 80 | } 81 | values.append(value) 82 | } 83 | 84 | func invalidate() { 85 | let prevValues = values.map { -abs($0) } 86 | values.removeAll(keepingCapacity: true) 87 | values.append(contentsOf: prevValues) 88 | } 89 | 90 | func hash(into hasher: inout Hasher) { 91 | hasher.combine(id) 92 | } 93 | 94 | static func == (lhs: TrafficEntry, rhs: TrafficEntry) -> Bool { 95 | lhs.id == rhs.id 96 | } 97 | 98 | deinit { 99 | childObserver?.cancel() 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Entries/TrafficEntryIO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrafficEntryIO.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/22. 6 | // 7 | 8 | import Foundation 9 | 10 | final class TrafficEntryIO: Entry { 11 | private let overrideName: String? 12 | 13 | init(child: any Entry, download: TrafficEntry, upload: TrafficEntry, overrideName: String? = nil) { 14 | id = UUID() 15 | self.child = child 16 | self.download = download 17 | self.upload = upload 18 | self.overrideName = overrideName 19 | } 20 | 21 | @Published var child: any Entry 22 | var basicChild: BasicEntry? { child as? BasicEntry } 23 | var usageChild: UsageEntry? { child as? UsageEntry } 24 | 25 | @Published var download: TrafficEntry 26 | @Published var upload: TrafficEntry 27 | var pair: [TrafficEntry] { [download, upload] } 28 | 29 | let id: UUID 30 | var key: EntryKey { child.key } 31 | var name: String { overrideName ?? child.name } 32 | 33 | enum CodingKeys: CodingKey { 34 | case child 35 | case download 36 | case upload 37 | case overrideName 38 | } 39 | 40 | required convenience init(from decoder: Decoder) throws { 41 | let container = try decoder.container(keyedBy: CodingKeys.self) 42 | var childEntry: any Entry 43 | if let basicEntry = try? container.decode(BasicEntry.self, forKey: .child) { 44 | childEntry = basicEntry 45 | } else if let usageEntry = try? container.decode(UsageEntry.self, forKey: .child) { 46 | childEntry = usageEntry 47 | } else { 48 | throw DecodingError.dataCorruptedError(forKey: CodingKeys.child, in: container, 49 | debugDescription: "The only child is neither a basic entry or an usage entry.") 50 | } 51 | try self.init(child: childEntry, 52 | download: container.decode(TrafficEntry.self, forKey: .download), 53 | upload: container.decode(TrafficEntry.self, forKey: .upload), 54 | overrideName: container.decodeIfPresent(String.self, forKey: .overrideName)) 55 | } 56 | 57 | func encode(to encoder: Encoder) throws { 58 | var container = encoder.container(keyedBy: CodingKeys.self) 59 | try container.encode(child, forKey: .child) 60 | try container.encode(download, forKey: .download) 61 | try container.encode(upload, forKey: .upload) 62 | try container.encodeIfPresent(overrideName, forKey: .overrideName) 63 | } 64 | 65 | func hash(into hasher: inout Hasher) { 66 | hasher.combine(id) 67 | } 68 | 69 | static func == (lhs: TrafficEntryIO, rhs: TrafficEntryIO) -> Bool { 70 | lhs.id == rhs.id 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Entries/UsageEntry.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsageEntry.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | final class UsageEntry: Entry where T: BasicNumeric { 11 | init(key: EntryKey, name: String, items: [UsageEntry.Item]) { 12 | id = UUID() 13 | self.key = key 14 | self.name = name 15 | self.items = items 16 | } 17 | 18 | struct Item: Identifiable, Codable, Equatable { 19 | let id: UUID 20 | let label: String 21 | let value: T 22 | let color: Color 23 | let description: String? 24 | 25 | init(label: String, value: T, color: Color, description: String? = nil) { 26 | id = UUID() 27 | self.label = label 28 | self.value = value 29 | self.color = color 30 | self.description = description 31 | } 32 | 33 | enum CodingKeys: CodingKey { 34 | case label 35 | case value 36 | case description 37 | } 38 | 39 | init(from decoder: Decoder) throws { 40 | let container = try decoder.container(keyedBy: CodingKeys.self) 41 | try self.init(label: container.decode(String.self, forKey: .label), 42 | value: container.decode(T.self, forKey: .value), 43 | color: Color.clear, 44 | description: container.decodeIfPresent(String.self, forKey: .description)) 45 | } 46 | 47 | func encode(to encoder: Encoder) throws { 48 | var container = encoder.container(keyedBy: UsageEntry.Item.CodingKeys.self) 49 | try container.encode(label, forKey: UsageEntry.Item.CodingKeys.label) 50 | try container.encode(value, forKey: UsageEntry.Item.CodingKeys.value) 51 | try container.encodeIfPresent(description, forKey: UsageEntry.Item.CodingKeys.description) 52 | } 53 | } 54 | 55 | let id: UUID 56 | let key: EntryKey 57 | let name: String 58 | @Published var items: [Item] 59 | 60 | enum CodingKeys: CodingKey { 61 | case name 62 | case items 63 | case key 64 | } 65 | 66 | required convenience init(from decoder: Decoder) throws { 67 | let container = try decoder.container(keyedBy: CodingKeys.self) 68 | try self.init(key: container.decode(EntryKey.self, forKey: .key), 69 | name: container.decode(String.self, forKey: .name), 70 | items: container.decode([Item].self, forKey: .items)) 71 | } 72 | 73 | func encode(to encoder: Encoder) throws { 74 | var container = encoder.container(keyedBy: CodingKeys.self) 75 | try container.encode(name, forKey: .name) 76 | try container.encode(items, forKey: .items) 77 | try container.encodeIfPresent(key, forKey: .key) 78 | } 79 | 80 | var firstDescription: String? { items.first?.description } 81 | var lastDescription: String? { items.last?.description } 82 | 83 | var totalValue: T { items.reduce(0) { $0 + $1.value } } 84 | 85 | func ratio(item: Item?) -> T? { 86 | guard let item else { 87 | return nil 88 | } 89 | return item.value / totalValue 90 | } 91 | 92 | var firstRatio: T? { ratio(item: items.first) } 93 | var lastRatio: T? { ratio(item: items.last) } 94 | 95 | func hash(into hasher: inout Hasher) { 96 | hasher.combine(id) 97 | } 98 | 99 | static func == (lhs: UsageEntry, rhs: UsageEntry) -> Bool { 100 | lhs.id == rhs.id 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Enums/NetworkPrefix.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkPrefix.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/13. 6 | // 7 | 8 | import SwiftUI 9 | 10 | enum NetworkPrefix: String, CaseIterable, Codable, Identifiable { 11 | case lo // Loopback 12 | case gif // RFC2893 Tunnel 13 | case stf // RFC3056 Tunnel 14 | case utun // User Mode Tunnel 15 | case ipsec // IPSec Tunnel 16 | case en // Wired/Wireless 17 | case ap // Access Point 18 | case awdl // Apple Wireless Direct Link 19 | case p2p // Wi-Fi Peer to Peer 20 | case llw // Low Latency WAN 21 | case ppp // Point-to-Point Protocol 22 | case bridge // Personal Hotspot 23 | case pdp_ip // Cellular Connection 24 | case XHC // USB Packet Capture 25 | case pktap // Packet Layer Capture 26 | case iptap // IP Layer Capture 27 | case anpi // Virtual USB-C Dual Role Device 28 | 29 | case others = "__OTHERS__" 30 | case all = "__ALL__" 31 | 32 | var id: String { rawValue } 33 | 34 | var description: String { 35 | switch self { 36 | case .lo: NSLocalizedString("LOOPBACK", comment: "Loopback") 37 | case .gif: NSLocalizedString("RFC2893_TUNNEL", comment: "RFC2893 Tunnel") 38 | case .stf: NSLocalizedString("RFC3056_TUNNEL", comment: "RFC3056 Tunnel") 39 | case .utun: NSLocalizedString("USER_MODE_TUNNEL", comment: "User Mode Tunnel") 40 | case .ipsec: NSLocalizedString("IPSEC_TUNNEL", comment: "IPSec Tunnel") 41 | case .en: NSLocalizedString("WIRED_WIRELESS", comment: "Wired/Wireless") 42 | case .ap: NSLocalizedString("ACCESS_POINT", comment: "Access Point") 43 | case .awdl: NSLocalizedString("APPLE_WIRELESS_DIRECT_LINK", comment: "Apple Wireless Direct Link") 44 | case .p2p: NSLocalizedString("WIFI_PEER_TO_PEER", comment: "Wi-Fi Peer to Peer") 45 | case .llw: NSLocalizedString("LOW_LATENCY_WAN", comment: "Low Latency WAN") 46 | case .ppp: NSLocalizedString("POINT_TO_POINT_PROTOCOL", comment: "Point-to-Point Protocol") 47 | case .bridge: NSLocalizedString("PERSONAL_HOTSPOT", comment: "Personal Hotspot") 48 | case .pdp_ip: NSLocalizedString("CELLULAR_CONNECTION", comment: "Cellular Connection") 49 | case .XHC: NSLocalizedString("USB_PACKET_CAPTURE", comment: "USB Packet Capture") 50 | case .pktap: NSLocalizedString("PACKET_LAYER_CAPTURE", comment: "Packet Layer Capture") 51 | case .iptap: NSLocalizedString("IP_LAYER_CAPTURE", comment: "IP Layer Capture") 52 | case .anpi: NSLocalizedString("USB_DUAL_ROLE_DEVICE", comment: "USB Dual Role Device") 53 | case .others: NSLocalizedString("OTHERS", comment: "Others") 54 | case .all: NSLocalizedString("NETWORK", comment: "Network") 55 | } 56 | } 57 | } 58 | 59 | extension NetworkPrefix { 60 | init?(rawValue: String) { 61 | guard let prefix = Self.allCases.first(where: { rawValue.lowercased().hasPrefix($0.rawValue.lowercased()) }) 62 | else { 63 | return nil 64 | } 65 | self = prefix 66 | } 67 | 68 | private static let colorPrefix = String(describing: NetworkInterface.self) + "-" 69 | private var colorName: String { Self.colorPrefix + rawValue } 70 | var color: Color? { self == .others ? nil : Color(colorName) } 71 | 72 | private static let keyPrefix = String(describing: NetworkUsage.self) + "-" 73 | var keyName: String { Self.keyPrefix + rawValue } 74 | 75 | init?(keyName: String) { 76 | guard keyName.hasPrefix(Self.keyPrefix) else { 77 | return nil 78 | } 79 | let rawVal = keyName[keyName.index(keyName.startIndex, offsetBy: Self.keyPrefix.count)...] 80 | self.init(rawValue: String(rawVal)) 81 | } 82 | 83 | private var isCategoryCase: Bool { self != .all } 84 | static let categoryCases: [NetworkPrefix] = NetworkPrefix.allCases.filter(\.isCategoryCase) 85 | } 86 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Enums/SecurityCheckType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecurityCheckType.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/11/6. 6 | // 7 | 8 | import Foundation 9 | 10 | enum SecurityCheckType: String, CaseIterable, Codable, Equatable, Hashable { 11 | case debuggerEmulator 12 | case staticIntegrity 13 | case dynamicIntegrity 14 | case jailbreakEnvironment 15 | case sandboxViolation 16 | case networkProxy 17 | } 18 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Enums/ValueStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ValueStyle.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/20. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ValueStyle { 11 | case detailed 12 | case dashboard 13 | } 14 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Modules/DiskSpace.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiskSpace.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/6. 6 | // 7 | 8 | import SwiftUI 9 | 10 | final class DiskSpace: Module { 11 | static let shared = DiskSpace() 12 | private let id = UUID() 13 | 14 | let moduleName = NSLocalizedString("DISK_SPACE", comment: "Disk Space") 15 | 16 | private let diskUsage = DiskUsage.shared 17 | 18 | private let gBufferFormatter: ByteCountFormatter = { 19 | let formatter = ByteCountFormatter() 20 | formatter.countStyle = .file 21 | formatter.allowedUnits = [.useMB, .useGB] 22 | return formatter 23 | }() 24 | 25 | private var usageItems: [UsageEntry.Item] { 26 | [ 27 | UsageEntry.Item(label: "Used", value: Double(diskUsage.usedDiskSpaceInBytes) / System.Unit.gigabyte.rawValue, color: Color.accentColor, description: String(format: NSLocalizedString("USED_DESCRIPTION", comment: "Used: %@"), gBufferFormatter.string(fromByteCount: diskUsage.usedDiskSpaceInBytes))), 28 | UsageEntry.Item(label: "Free", value: Double(diskUsage.freeDiskSpaceInBytes) / System.Unit.gigabyte.rawValue, color: Color.clear, description: String(format: NSLocalizedString("FREE_DESCRIPTION", comment: "Free: %@"), gBufferFormatter.string(fromByteCount: diskUsage.freeDiskSpaceInBytes))), 29 | ] 30 | } 31 | 32 | lazy var usageEntry: UsageEntry? = usageEntry(key: .DiskSpace) 33 | 34 | lazy var basicEntries: [BasicEntry] = { 35 | reloadData() 36 | return updatableEntryKeys.compactMap { basicEntry(key: $0) } 37 | }() 38 | 39 | func reloadData() { 40 | diskUsage.reloadData() 41 | } 42 | 43 | func updateEntries() { 44 | reloadData() 45 | usageEntry?.items = usageItems 46 | basicEntries.forEach { updateBasicEntry($0) } 47 | } 48 | 49 | let updatableEntryKeys: [EntryKey] = [ 50 | .DiskSpace, 51 | .DiskTotal, 52 | .DiskUsed, 53 | .DiskFree, 54 | ] 55 | 56 | func basicEntry(key: EntryKey, style: ValueStyle = .detailed) -> BasicEntry? { 57 | switch key { 58 | case .DiskTotal: 59 | return BasicEntry( 60 | key: .DiskTotal, 61 | name: style == .dashboard ? NSLocalizedString("DISK_TOTAL_LONG", comment: "Disk Total") : NSLocalizedString("DISK_TOTAL", comment: "Total"), 62 | value: gBufferFormatter.string(fromByteCount: diskUsage.totalDiskSpaceInBytes) 63 | ) 64 | case .DiskUsed: 65 | return BasicEntry( 66 | key: .DiskUsed, 67 | name: style == .dashboard ? NSLocalizedString("DISK_USED_LONG", comment: "Disk Used") : NSLocalizedString("DISK_USED", comment: "Used"), 68 | value: gBufferFormatter.string(fromByteCount: diskUsage.usedDiskSpaceInBytes) 69 | ) 70 | case .DiskFree: 71 | return BasicEntry( 72 | key: .DiskFree, 73 | name: style == .dashboard ? NSLocalizedString("DISK_FREE_LONG", comment: "Disk Free") : NSLocalizedString("DISK_FREE", comment: "Free"), 74 | value: gBufferFormatter.string(fromByteCount: diskUsage.freeDiskSpaceInBytes) 75 | ) 76 | default: 77 | break 78 | } 79 | return nil 80 | } 81 | 82 | func updateBasicEntry(_ entry: BasicEntry, style _: ValueStyle = .detailed) { 83 | switch entry.key { 84 | case .DiskTotal: 85 | entry.value = gBufferFormatter.string(fromByteCount: diskUsage.totalDiskSpaceInBytes) 86 | case .DiskUsed: 87 | entry.value = gBufferFormatter.string(fromByteCount: diskUsage.usedDiskSpaceInBytes) 88 | case .DiskFree: 89 | entry.value = gBufferFormatter.string(fromByteCount: diskUsage.freeDiskSpaceInBytes) 90 | default: 91 | break 92 | } 93 | } 94 | 95 | func usageEntry(key: EntryKey, style: ValueStyle = .detailed) -> UsageEntry? { 96 | switch key { 97 | case .DiskSpace: 98 | return UsageEntry(key: .DiskSpace, name: style == .dashboard ? NSLocalizedString("DISK_USED_LONG", comment: "Disk Used") : moduleName, items: usageItems) 99 | default: 100 | break 101 | } 102 | return nil 103 | } 104 | 105 | func updateUsageEntry(_ entry: UsageEntry, style _: ValueStyle) { 106 | entry.items = usageItems 107 | } 108 | 109 | func trafficEntryIO(key _: EntryKey, style _: ValueStyle) -> TrafficEntryIO? { nil } 110 | 111 | func updateTrafficEntryIO(_: TrafficEntryIO, style _: ValueStyle) {} 112 | } 113 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Modules/NetworkUsage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkUsage.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/10. 6 | // 7 | 8 | import SwiftUI 9 | 10 | final class NetworkUsage: Module { 11 | static let shared = NetworkUsage() 12 | 13 | private var netStats: NetworkStatistics 14 | private let netTraffic = NetworkTraffic() 15 | 16 | private init() { 17 | netStats = netTraffic.getStatistics() 18 | } 19 | 20 | let moduleName = NSLocalizedString("NETWORK_USAGE", comment: "Network Usage") 21 | 22 | private var usageItems: [UsageEntry.Item] { 23 | NetworkPrefix.categoryCases.compactMap { pfx in 24 | guard let trafficBytes = netStats.allBytes(prefix: pfx) else { 25 | return nil 26 | } 27 | return UsageEntry.Item( 28 | label: pfx.description, 29 | value: Double(trafficBytes), 30 | color: pfx.color ?? Color.clear 31 | ) 32 | } 33 | } 34 | 35 | lazy var usageEntry: UsageEntry? = usageEntry(key: .NetworkUsage) 36 | 37 | lazy var basicEntries: [BasicEntry] = { 38 | reloadData() 39 | return updatableEntryKeys.compactMap { basicEntry(key: $0) } 40 | }() 41 | 42 | func reloadData() { 43 | netStats = netTraffic.getStatistics() 44 | } 45 | 46 | func updateEntries() { 47 | reloadData() 48 | usageEntry?.items = usageItems 49 | basicEntries.forEach { updateBasicEntry($0) } 50 | } 51 | 52 | lazy var updatableEntryKeys: [EntryKey] = [.NetworkUsage] + NetworkPrefix.categoryCases.compactMap { .NetworkCategoryUsage(prefix: $0.rawValue) } 53 | 54 | func basicEntry(key: EntryKey, style: ValueStyle = .detailed) -> BasicEntry? { 55 | switch key { 56 | case let .NetworkCategoryUsage(prefix): 57 | if let pfx = NetworkPrefix(rawValue: prefix) { 58 | return BasicEntry( 59 | key: key, 60 | name: pfx.description, 61 | value: netStats.entryValue(prefix: pfx, style: style), 62 | color: pfx.color ?? Color(PlatformColor.secondarySystemFillAlias) 63 | ) 64 | } 65 | default: 66 | break 67 | } 68 | return nil 69 | } 70 | 71 | func updateBasicEntry(_ entry: BasicEntry, style: ValueStyle = .detailed) { 72 | switch entry.key { 73 | case let .NetworkCategoryUsage(prefix): 74 | if let pfx = NetworkPrefix(keyName: prefix) { 75 | entry.value = netStats.entryValue(prefix: pfx, style: style) 76 | } 77 | default: 78 | break 79 | } 80 | } 81 | 82 | func usageEntry(key: EntryKey, style _: ValueStyle = .detailed) -> UsageEntry? { 83 | switch key { 84 | case .NetworkUsage: 85 | return UsageEntry(key: .NetworkUsage, name: moduleName, items: usageItems) 86 | default: 87 | break 88 | } 89 | return nil 90 | } 91 | 92 | func updateUsageEntry(_ entry: UsageEntry, style _: ValueStyle) { 93 | entry.items = usageItems 94 | } 95 | 96 | func trafficEntryIO(key _: EntryKey, style _: ValueStyle) -> TrafficEntryIO? { nil } 97 | 98 | func updateTrafficEntryIO(_: TrafficEntryIO, style _: ValueStyle) {} 99 | } 100 | -------------------------------------------------------------------------------- /Reveil/ViewModels/Modules/ScreenInformation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScreenInformation.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ScreenInformation: Module { 11 | static let shared = ScreenInformation() 12 | private init() {} 13 | 14 | let moduleName = NSLocalizedString("SCREEN_INFORMATION", comment: "Screen Information") 15 | 16 | private let mainScreen = UIScreen.main 17 | 18 | private lazy var displaySizeDescription: String = { 19 | let displayWidth = Int(mainScreen.fixedCoordinateSpace.bounds.width * mainScreen.scale) 20 | let displayHeight = Int(mainScreen.fixedCoordinateSpace.bounds.height * mainScreen.scale) 21 | return "\(displayWidth)×\(displayHeight)" 22 | }() 23 | 24 | private lazy var physicalSizeDescription: String = { 25 | let physicalWidth = Int(mainScreen.nativeBounds.width) 26 | let physicalHeight = Int(mainScreen.nativeBounds.height) 27 | return "\(physicalWidth)×\(physicalHeight)" 28 | }() 29 | 30 | private lazy var logicalSizeDescription: String = { 31 | let logicalWidth = Int(mainScreen.fixedCoordinateSpace.bounds.width) 32 | let logicalHeight = Int(mainScreen.fixedCoordinateSpace.bounds.height) 33 | return "\(logicalWidth)×\(logicalHeight)" 34 | }() 35 | 36 | lazy var basicEntries: [BasicEntry] = updatableEntryKeys.compactMap { basicEntry(key: $0) } 37 | 38 | let usageEntry: UsageEntry? = nil 39 | 40 | func reloadData() {} 41 | 42 | func updateEntries() { 43 | basicEntries.forEach { updateBasicEntry($0) } 44 | } 45 | 46 | let updatableEntryKeys: [EntryKey] = [ 47 | .DisplayResolution, 48 | .ScreenPhysicalResolution, 49 | .ScreenPhysicalScale, 50 | .ScreenLogicalResolution, 51 | .ScreenLogicalScale, 52 | .ScreenMaximumFramesPerSecond, 53 | ] 54 | 55 | func basicEntry(key: EntryKey, style: ValueStyle = .detailed) -> BasicEntry? { 56 | switch key { 57 | case .DisplayResolution: 58 | return BasicEntry( 59 | key: .DisplayResolution, 60 | name: NSLocalizedString("DISPLAY_RESOLUTION", comment: "Display Resolution"), 61 | value: displaySizeDescription 62 | ) 63 | case .ScreenPhysicalResolution: 64 | return BasicEntry( 65 | key: .ScreenPhysicalResolution, 66 | name: style == .dashboard ? NSLocalizedString("SCREEN_PHYSICAL_RESOLUTION", comment: "Screen Physical Resolution") : NSLocalizedString("PHYSICAL_RESOLUTION", comment: "Physical Resolution"), 67 | value: physicalSizeDescription 68 | ) 69 | case .ScreenPhysicalScale: 70 | return BasicEntry( 71 | key: .ScreenPhysicalScale, 72 | name: style == .dashboard ? NSLocalizedString("SCREEN_PHYSICAL_SCALE", comment: "Screen Physical Scale") : NSLocalizedString("PHYSICAL_SCALE", comment: "Physical Scale"), 73 | value: String(format: "%.3f", mainScreen.nativeScale) 74 | ) 75 | case .ScreenLogicalResolution: 76 | return BasicEntry( 77 | key: .ScreenLogicalResolution, 78 | name: style == .dashboard ? NSLocalizedString("SCREEN_LOGICAL_RESOLUTION", comment: "Screen Logical Resolution") : NSLocalizedString("LOGICAL_RESOLUTION", comment: "Logical Resolution"), 79 | value: logicalSizeDescription 80 | ) 81 | case .ScreenLogicalScale: 82 | return BasicEntry( 83 | key: .ScreenLogicalScale, 84 | name: style == .dashboard ? NSLocalizedString("SCREEN_LOGICAL_SCALE", comment: "Screen Logical Scale") : NSLocalizedString("LOGICAL_SCALE", comment: "Logical Scale"), 85 | value: String(format: "%.3f", mainScreen.scale) 86 | ) 87 | case .ScreenMaximumFramesPerSecond: 88 | return BasicEntry( 89 | key: .ScreenMaximumFramesPerSecond, 90 | name: style == .dashboard ? NSLocalizedString("SCREEN_MAXIMUM_FPS", comment: "Screen Maximum FPS") : NSLocalizedString("MAXIMUM_FPS", comment: "Maximum FPS"), 91 | value: String(format: "%d", mainScreen.maximumFramesPerSecond) 92 | ) 93 | default: 94 | break 95 | } 96 | return nil 97 | } 98 | 99 | func updateBasicEntry(_ basicEntry: BasicEntry, style _: ValueStyle = .detailed) { 100 | switch basicEntry.key { 101 | case .HostName: 102 | basicEntry.value = System.hostName() ?? BasicEntry.unknownValue 103 | default: 104 | break 105 | } 106 | } 107 | 108 | func usageEntry(key _: EntryKey, style _: ValueStyle = .detailed) -> UsageEntry? { nil } 109 | 110 | func updateUsageEntry(_: UsageEntry, style _: ValueStyle) {} 111 | 112 | func trafficEntryIO(key _: EntryKey, style _: ValueStyle) -> TrafficEntryIO? { nil } 113 | 114 | func updateTrafficEntryIO(_: TrafficEntryIO, style _: ValueStyle) {} 115 | } 116 | -------------------------------------------------------------------------------- /Reveil/Views/AnimatedText.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnimatedText.swift 3 | // Reveil 4 | // 5 | // Created by 秋星桥 on 2023/12/30. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct AnimatedText: View { 11 | let text: String 12 | init(_ text: String) { 13 | self.text = text 14 | } 15 | 16 | var isAnimatedTextEnabled: Bool { 17 | StandardUserDefaults.shared.isAnimatedTextEnabled && !ProcessInfo.processInfo.isLowPowerModeEnabled 18 | } 19 | 20 | var isLegacyUIEnabled: Bool = StandardUserDefaults.shared.isLegacyUIEnabled 21 | 22 | var body: some View { 23 | if !isLegacyUIEnabled && isAnimatedTextEnabled { 24 | if #available(iOS 17.0, *) { 25 | Text(text) 26 | .contentTransition(.numericText()) 27 | .animation(.spring(duration: 0.2), value: text) 28 | } else { 29 | Text(text) 30 | } 31 | } else { 32 | Text(text) 33 | } 34 | } 35 | } 36 | 37 | // MARK: - Previews 38 | 39 | struct AnimatedText_Previews: PreviewProvider { 40 | static var previews: some View { 41 | AnimatedText("+1-234-456-7890") 42 | .padding() 43 | .previewLayout(.sizeThatFits) 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Reveil/Views/ColorfulBackground.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorfulBackground.swift 3 | // Reveil 4 | // 5 | // Created by 秋星桥 on 2023/12/30. 6 | // 7 | 8 | import ColorfulX 9 | import SwiftUI 10 | 11 | 12 | struct ColorfulBackground: View { 13 | @State var isPaused: Bool = false 14 | @Environment(\.colorScheme) var colorScheme 15 | 16 | var isAnimatedBackgroundEnabled: Bool { 17 | StandardUserDefaults.shared.isAnimatedBackgroundEnabled && !ProcessInfo.processInfo.isLowPowerModeEnabled 18 | } 19 | 20 | var isLowFrameRateEnabled: Bool = StandardUserDefaults.shared.isLowFrameRateEnabled 21 | var isLegacyUIEnabled: Bool = StandardUserDefaults.shared.isLegacyUIEnabled 22 | 23 | var colorfulView: some View { 24 | if colorScheme == .light { 25 | ColorfulView( 26 | color: .constant(ColorfulPreset.winter.colors), 27 | speed: isAnimatedBackgroundEnabled && !isPaused ? .constant(0.5) : .constant(0), 28 | frameLimit: isLowFrameRateEnabled ? 30 : 60 29 | ) 30 | .opacity(0.5) 31 | } else { 32 | ColorfulView( 33 | color: .constant(ColorfulPreset.aurora.colors), 34 | speed: isAnimatedBackgroundEnabled && !isPaused ? .constant(0.5) : .constant(0), 35 | frameLimit: isLowFrameRateEnabled ? 30 : 60 36 | ) 37 | .opacity(0.25) 38 | } 39 | } 40 | 41 | var body: some View { 42 | Group { 43 | if !isLegacyUIEnabled { 44 | self.colorfulView 45 | } 46 | } 47 | .background(Color(PlatformColor.systemBackground)) 48 | .ignoresSafeArea() 49 | .onAppear { 50 | isPaused = false 51 | } 52 | .onDisappear { 53 | isPaused = true 54 | } 55 | } 56 | } 57 | 58 | // MARK: - Previews 59 | 60 | struct ColorfulBackground_Previews: PreviewProvider { 61 | static var previews: some View { 62 | ColorfulBackground() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Reveil/Views/Dashboard/ActivityWidget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ActivityWidget.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ActivityWidget: View { 11 | @ObservedObject var entry: ActivityEntry 12 | 13 | private let columnWidth: CGFloat = 2 14 | private let minColumnSpacing: CGFloat = 3 15 | private let rowHeight: CGFloat = 24 16 | 17 | private func limitedValues(metricWidth: CGFloat) -> [(offset: Int, element: Double)] { 18 | let entryValues = entry.values 19 | let limitedCount = Int((metricWidth - columnWidth) / (columnWidth + minColumnSpacing)) 20 | let beginIndex = max(0, entryValues.count - limitedCount) 21 | let limitedItems = Array(entryValues[beginIndex...]) 22 | let missingItems = Array(repeating: 0.0, count: max(0, limitedCount - entryValues.count)) 23 | let fulfilledItems = missingItems + limitedItems 24 | let enumatedItems = Array(fulfilledItems.enumerated()) 25 | return enumatedItems 26 | } 27 | 28 | var body: some View { 29 | VStack { 30 | HStack { 31 | Text(entry.name.uppercased()) 32 | .font(Font.system(.body)) 33 | .fontWeight(.bold) 34 | .foregroundColor(Color(PlatformColor.labelAlias)) 35 | .lineLimit(1) 36 | Spacer() 37 | Image(systemName: "chevron.right") 38 | .font(Font.system(.body).weight(.regular)) 39 | .foregroundColor(Color(PlatformColor.tertiaryLabelAlias)) 40 | } 41 | 42 | Spacer(minLength: 2) 43 | .frame(height: 2) 44 | 45 | HStack { 46 | AnimatedText(String(format: "%.2f%%", Double(entry.values.last ?? 0) * 100.0)) 47 | .font(Font.system(.title).weight(.medium).monospacedDigit()) 48 | .foregroundColor(.accentColor) 49 | .lineLimit(1) 50 | Spacer() 51 | } 52 | 53 | Spacer(minLength: 8) 54 | .frame(height: 8) 55 | 56 | GeometryReader { metrics in 57 | HStack(spacing: 0) { 58 | ForEach(limitedValues(metricWidth: metrics.size.width), id: \.offset) { value in 59 | Spacer(minLength: minColumnSpacing) 60 | VStack { 61 | Spacer(minLength: 0) 62 | Rectangle() 63 | .foregroundColor(.secondary) 64 | .frame(width: columnWidth, 65 | height: max(metrics.size.height * min(value.element, 1), 1)) 66 | } 67 | } 68 | } 69 | .frame(maxWidth: metrics.size.width) 70 | } 71 | .frame(height: rowHeight) 72 | } 73 | .frame(maxWidth: .infinity, alignment: .leading) 74 | } 75 | } 76 | 77 | // MARK: - Previews 78 | 79 | struct ActivityWidget_Previews: PreviewProvider { 80 | static let values = [ 81 | 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 82 | 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, 83 | 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 84 | 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, 85 | 0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 86 | 1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0, 87 | ] 88 | static var previews: some View { 89 | ActivityWidget(entry: ActivityEntry( 90 | child: BasicEntry(key: .CPUUsageLoad, name: "Usage"), 91 | values: values 92 | )) 93 | .padding(.all) 94 | .previewLayout(.sizeThatFits) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Reveil/Views/Dashboard/CheckmarkWidget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CheckmarkWidget.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/3. 6 | // 7 | 8 | import ColorfulX 9 | import SwiftUI 10 | 11 | struct CheckmarkWidget: View { 12 | @ObservedObject var securityModel: Security = .shared 13 | @State var openDetail: Bool = false 14 | 15 | var isAnimatedBackgroundEnabled: Bool { 16 | StandardUserDefaults.shared.isAnimatedBackgroundEnabled && !ProcessInfo.processInfo.isLowPowerModeEnabled 17 | } 18 | 19 | var isLowFrameRateEnabled: Bool = StandardUserDefaults.shared.isLowFrameRateEnabled 20 | var isLegacyUIEnabled: Bool = StandardUserDefaults.shared.isLegacyUIEnabled 21 | 22 | var usesLegacyStyle: Bool { 23 | isLegacyUIEnabled || !isAnimatedBackgroundEnabled 24 | } 25 | 26 | var label: String { securityModel.description } 27 | var isLoading: Bool { securityModel.isLoading } 28 | var isInsecure: Bool { securityModel.isInsecure } 29 | var description: String { 30 | ( 31 | securityModel.isLoading 32 | ? NSLocalizedString("SCANNING", comment: "Scanning…") 33 | : (securityModel.isInsecure ? 34 | NSLocalizedString("MODIFICATIONS_FOUND", comment: "Modifications found") 35 | : NSLocalizedString("NO_ISSUES_FOUND", comment: "No issues found") 36 | ) 37 | ).capitalized 38 | } 39 | 40 | var labelColor: Color { 41 | if usesLegacyStyle { 42 | return isInsecure ? .white : .primary 43 | } 44 | return .white 45 | } 46 | 47 | var descriptionColor: Color { 48 | if usesLegacyStyle { 49 | return isInsecure ? .white : .accent 50 | } 51 | return .white 52 | } 53 | 54 | var backgroundColor: Color { 55 | if isInsecure { return Color("SecurityLeaks") } 56 | return Color(PlatformColor.secondarySystemBackgroundAlias) 57 | } 58 | 59 | var animatedBackgroundColors: [Color] { 60 | if isLoading { return ColorfulPreset.aurora.colors } 61 | if isInsecure { return [.red, .pink, .red, .pink] } 62 | return [.accent, .accent, .accent, .accent] 63 | } 64 | 65 | var colorfulBackground: some View { 66 | ColorfulView( 67 | color: .init(get: { animatedBackgroundColors }, set: { _ in }), 68 | speed: isAnimatedBackgroundEnabled ? .constant(0.5) : .constant(0), 69 | transitionInterval: isAnimatedBackgroundEnabled ? .constant(1) : .constant(0), 70 | frameLimit: isLowFrameRateEnabled ? 30 : 60 71 | ) 72 | } 73 | 74 | var body: some View { 75 | VStack(spacing: 8) { 76 | HStack { 77 | Text(label.uppercased()) 78 | .font(Font.system(.body)) 79 | .fontWeight(.bold) 80 | .lineLimit(1) 81 | Spacer() 82 | if !isLoading, isInsecure { 83 | Image(systemName: "chevron.right") 84 | .font(Font.system(.body).weight(.regular)) 85 | } 86 | } 87 | .foregroundColor(labelColor) 88 | 89 | HStack { 90 | Image(systemName: isLoading ? "magnifyingglass.circle.fill" : (isInsecure ? "xmark.circle.fill" : "checkmark.circle.fill")) 91 | .font(Font.system(.title).weight(.medium)) 92 | Text(description) 93 | .font(Font.system(.title).weight(.medium)) 94 | .lineLimit(1) 95 | Spacer() 96 | } 97 | .foregroundColor(descriptionColor) 98 | } 99 | .frame(maxWidth: .infinity, alignment: .leading) 100 | .padding(.all, 12) 101 | .background { 102 | if usesLegacyStyle { 103 | RoundedRectangle(cornerRadius: 4) 104 | .foregroundColor(backgroundColor) 105 | .opacity(isInsecure ? 0.75 : 0.25) 106 | } else { 107 | self.colorfulBackground 108 | .opacity(0.75) 109 | .clipShape(RoundedRectangle(cornerRadius: 4)) 110 | } 111 | } 112 | .overlay { 113 | if usesLegacyStyle && !isInsecure { 114 | RoundedRectangle(cornerRadius: 4) 115 | .stroke(Color(PlatformColor.separatorAlias), lineWidth: 1) 116 | .opacity(isInsecure ? 0 : 1) 117 | } 118 | } 119 | .onChange(of: isLoading) { _ in 120 | if isLoading { openDetail = false } 121 | } 122 | .background( 123 | NavigationLink( 124 | "", 125 | destination: SecurityView().environmentObject(HighlightedEntryKey()), 126 | isActive: $openDetail 127 | ) 128 | ) 129 | .contentShape(Rectangle()) 130 | .onTapGesture { 131 | if isLoading { return } 132 | openDetail = true 133 | } 134 | } 135 | } 136 | 137 | // MARK: - Previews 138 | 139 | struct CheckmarkWidget_Previews: PreviewProvider { 140 | static var previews: some View { 141 | CheckmarkWidget() 142 | .padding() 143 | .previewLayout(.sizeThatFits) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Reveil/Views/Dashboard/FieldWidget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FieldWidget.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/3. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FieldWidget: View { 11 | @ObservedObject var entry: BasicEntry 12 | 13 | var body: some View { 14 | VStack(spacing: 8) { 15 | HStack { 16 | Text(entry.name.uppercased()) 17 | .font(Font.system(.body)) 18 | .fontWeight(.bold) 19 | .foregroundColor(Color(PlatformColor.labelAlias)) 20 | .lineLimit(1) 21 | Spacer() 22 | Image(systemName: "chevron.right") 23 | .font(Font.system(.body).weight(.regular)) 24 | .foregroundColor(Color(PlatformColor.tertiaryLabelAlias)) 25 | } 26 | 27 | HStack { 28 | Text(entry.value) 29 | .font(Font.system(.body).weight(.regular).monospacedDigit()) 30 | .foregroundColor(Color(PlatformColor.secondaryLabelAlias)) 31 | .multilineTextAlignment(.leading) 32 | .lineLimit(3) 33 | Spacer() 34 | } 35 | } 36 | .frame(maxWidth: .infinity, alignment: .leading) 37 | } 38 | } 39 | 40 | // MARK: - Previews 41 | 42 | struct FieldWidget_Previews: PreviewProvider { 43 | static var previews: some View { 44 | FieldWidget(entry: BasicEntry( 45 | key: .KernelVersion, 46 | name: "Kernel version", 47 | value: "Darwin Kernel Version 21.4.0: Mon Feb 21 21:27:55 PST 2022; root:xnu-8020.102.3~1/RELEASE_ARM64_T8101" 48 | )) 49 | .padding(.all) 50 | .previewLayout(.sizeThatFits) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Reveil/Views/Dashboard/UsageWidget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsageWidget.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/2. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct UsageWidget: View { 11 | @ObservedObject var entry: UsageEntry 12 | 13 | private let rowHeight: CGFloat = 18 14 | 15 | var body: some View { 16 | VStack { 17 | HStack { 18 | Text(entry.name.uppercased()) 19 | .font(Font.system(.body)) 20 | .fontWeight(.bold) 21 | .foregroundColor(Color(PlatformColor.labelAlias)) 22 | .lineLimit(1) 23 | Spacer() 24 | Image(systemName: "chevron.right") 25 | .font(Font.system(.body).weight(.regular)) 26 | .foregroundColor(Color(PlatformColor.tertiaryLabelAlias)) 27 | } 28 | 29 | Spacer(minLength: 2) 30 | .frame(height: 2) 31 | 32 | HStack(alignment: .lastTextBaseline) { 33 | AnimatedText(String(format: "%.2f%%", (entry.firstRatio ?? 0.0) * 100.0)) 34 | .font(Font.system(.title).weight(.medium).monospacedDigit()) 35 | .foregroundColor(.accentColor) 36 | .lineLimit(1) 37 | Spacer() 38 | 39 | if let lastDescription = entry.lastDescription { 40 | Text(lastDescription) 41 | .font(Font.system(.body).weight(.regular).monospacedDigit()) 42 | .foregroundColor(Color(PlatformColor.secondaryLabelAlias)) 43 | .lineLimit(1) 44 | } 45 | } 46 | 47 | Spacer(minLength: 8) 48 | .frame(height: 8) 49 | 50 | GeometryReader { metrics in 51 | ZStack(alignment: .leading) { 52 | Color(PlatformColor.secondarySystemFillAlias) 53 | 54 | Color.accentColor 55 | .frame(width: metrics.size.width * (entry.firstRatio ?? 0.0)) 56 | } 57 | .frame(maxWidth: metrics.size.width) 58 | .cornerRadius(4) 59 | } 60 | .frame(height: rowHeight) 61 | } 62 | .frame(maxWidth: .infinity, alignment: .leading) 63 | } 64 | } 65 | 66 | // MARK: - Previews 67 | 68 | struct UsageWidget_Previews: PreviewProvider { 69 | static var previews: some View { 70 | UsageWidget(entry: UsageEntry(key: .DiskSpace, name: "Usage", items: [ 71 | UsageEntry.Item( 72 | label: "Used", 73 | value: 124.37, 74 | color: Color.accentColor 75 | ), 76 | UsageEntry.Item( 77 | label: "Free", 78 | value: 78.42, 79 | color: Color.clear, 80 | description: "THIS IS A LONG DESCRIPTION." 81 | ), 82 | ])) 83 | .padding(.all) 84 | .previewLayout(.sizeThatFits) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Reveil/Views/Details/FieldCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FieldCell.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | private struct FieldCell_Internal: View { 11 | let label: String 12 | let description: String 13 | let color: Color? 14 | let delegate: FieldCellDelegate? 15 | let isPinnable: Bool 16 | 17 | @AppCodableStorage var pin: Pin { 18 | didSet { 19 | delegate?.showToast( 20 | message: String(format: pin.isPinned ? NSLocalizedString("PINNED_FMT", comment: "Pinned %@") : NSLocalizedString("UNPINNED_FMT", comment: "Unpinned %@"), label), 21 | icon: pin.isPinned ? "pin.fill" : "pin", 22 | dismissInterval: 3.0 23 | ) 24 | } 25 | } 26 | 27 | var body: some View { 28 | HStack(alignment: .top) { 29 | HStack { 30 | if let color { 31 | Image(systemName: "circle.fill") 32 | .font(Font.system(.caption2)) 33 | .foregroundColor(color) 34 | } 35 | 36 | Text(label) 37 | .font(Font.system(.body)) 38 | 39 | if #available(iOS 15.0, *), isPinnable { 40 | Menu { 41 | if pin.isPinned { 42 | Button { 43 | pin = Pin(false) 44 | } label: { 45 | Label(NSLocalizedString("UNPIN", comment: "Unpin"), systemImage: "pin") 46 | } 47 | } else { 48 | Button { 49 | pin = Pin(true) 50 | } label: { 51 | Label(NSLocalizedString("PIN", comment: "Pin"), systemImage: "pin.fill") 52 | } 53 | } 54 | } label: { 55 | Image(systemName: pin.isPinned ? "pin.fill" : "pin") 56 | .font(Font.system(.footnote)) 57 | .foregroundColor(Color(PlatformColor.secondaryLabelAlias)) 58 | .rotationEffect(.degrees(45)) 59 | } primaryAction: { 60 | pin = Pin(negate: pin) 61 | } 62 | } else { 63 | // Fallback on earlier versions 64 | } 65 | } 66 | 67 | Spacer() 68 | 69 | Text(description) 70 | .font(.system(.body).monospacedDigit()) 71 | .foregroundColor(Color(PlatformColor.secondaryLabelAlias)) 72 | .multilineTextAlignment(.trailing) 73 | } 74 | } 75 | } 76 | 77 | struct FieldCell: View { 78 | @ObservedObject var entry: BasicEntry 79 | let delegate: FieldCellDelegate? 80 | 81 | var body: some View { 82 | FieldCell_Internal( 83 | label: entry.name, 84 | description: entry.value, 85 | color: entry.color, 86 | delegate: delegate, 87 | isPinnable: entry.key.isPinnable, 88 | pin: entry.key.isPinnable ? AppCodableStorage( 89 | wrappedValue: Pin(false), entry.key, 90 | store: PinStorage.shared.userDefaults 91 | ) : AppCodableStorage(wrappedValue: Pin(false), .Custom(name: String())) 92 | ) 93 | } 94 | } 95 | 96 | // MARK: - Previews 97 | 98 | struct FieldCell_Previews: PreviewProvider { 99 | static var previews: some View { 100 | FieldCell(entry: BasicEntry( 101 | key: .HostName, 102 | name: "Host Name", 103 | value: "ZMini" 104 | ), delegate: nil) 105 | .padding() 106 | .previewLayout(.sizeThatFits) 107 | 108 | FieldCell(entry: BasicEntry( 109 | key: .CPUUsageUser, 110 | name: "User", 111 | value: "10.68%", 112 | color: Color.accentColor 113 | ), delegate: nil) 114 | .padding() 115 | .previewLayout(.sizeThatFits) 116 | 117 | FieldCell(entry: BasicEntry( 118 | key: .KernelVersion, 119 | name: "Kernel Version", 120 | value: "Darwin Kernel Version 21.4.0: Mon Feb 21 21:27:55 PST 2022; root:xnu-8020.102.3~1/RELEASE_ARM64_T8101" 121 | ), delegate: nil) 122 | .padding() 123 | .previewLayout(.sizeThatFits) 124 | 125 | FieldCell(entry: BasicEntry( 126 | key: .MemoryBytesWired, 127 | name: "Wired", 128 | value: "689.50 MB\n17.88%", 129 | color: Color("MemoryWired") 130 | ), delegate: nil) 131 | .padding() 132 | .previewLayout(.sizeThatFits) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Reveil/Views/Details/ToastView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ToastView.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ToastView: View { 11 | let label: String 12 | let iconName: String 13 | 14 | var body: some View { 15 | VStack { 16 | HStack { 17 | Image(systemName: iconName) 18 | Text(label) 19 | .font(Font.system(.footnote).bold()) 20 | .frame(maxWidth: .infinity, alignment: .leading) 21 | } 22 | .padding() 23 | .frame(maxWidth: .infinity, alignment: .leading) 24 | .background(thinMaterial: true) 25 | .cornerRadius(8) 26 | } 27 | .padding() 28 | .frame(maxWidth: .infinity, alignment: .leading) 29 | } 30 | } 31 | 32 | // MARK: - Previews 33 | 34 | struct ToastView_Previews: PreviewProvider { 35 | static var previews: some View { 36 | ToastView( 37 | label: "Copied to clipboard", 38 | iconName: "info.circle.fill" 39 | ) 40 | .previewLayout(.sizeThatFits) 41 | 42 | ToastView( 43 | label: "Unpinned Radio Tech", 44 | iconName: "pin" 45 | ) 46 | .previewLayout(.sizeThatFits) 47 | 48 | ToastView( 49 | label: "Pinned Radio Tech", 50 | iconName: "pin.fill" 51 | ) 52 | .previewLayout(.sizeThatFits) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Reveil/Views/Details/TrafficCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrafficCell.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/3. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TrafficCell: View { 11 | @ObservedObject var entry: TrafficEntry 12 | 13 | private let columnWidth: CGFloat = 2 14 | private let minColumnSpacing: CGFloat = 3 15 | private let rowHeight: CGFloat = 36 16 | 17 | private func limitedValues(metricWidth: CGFloat) -> [(offset: Int, element: Double)] { 18 | let limitedCount = Int((metricWidth - columnWidth) / (columnWidth + minColumnSpacing)) 19 | let beginIndex = max(0, entry.values.count - limitedCount) 20 | let limitedItems = Array(entry.values[beginIndex...]) 21 | let missingItems = Array(repeating: Int64(0), count: max(0, limitedCount - entry.values.count)) 22 | let fulfilledItems = missingItems + limitedItems 23 | let maxItem = fulfilledItems.map { abs($0) }.max() ?? 0 24 | var maxValue = Double(maxItem) 25 | if maxItem == 0 { 26 | maxValue = Double.infinity 27 | } 28 | let enumatedItems = Array(fulfilledItems.map { 29 | Double($0) / maxValue 30 | }.enumerated()) 31 | return enumatedItems 32 | } 33 | 34 | var body: some View { 35 | VStack { 36 | GeometryReader { metrics in 37 | HStack(spacing: 0) { 38 | ForEach(limitedValues(metricWidth: metrics.size.width), id: \.offset) { value in 39 | Spacer(minLength: minColumnSpacing) 40 | Rectangle() 41 | .foregroundColor(.secondary 42 | .opacity(value.element > 0 ? 1.0 : 0.5)) 43 | .frame( 44 | width: columnWidth, 45 | height: max(metrics.size.height * min(abs(value.element), 1), 1) 46 | ) 47 | .frame(maxHeight: .infinity, alignment: .bottom) 48 | } 49 | } 50 | .frame(maxWidth: metrics.size.width) 51 | } 52 | .frame(height: rowHeight) 53 | } 54 | .frame(maxWidth: .infinity, alignment: .leading) 55 | .padding([.top, .bottom]) 56 | } 57 | } 58 | 59 | // MARK: - Previews 60 | 61 | struct TrafficCell_Previews: PreviewProvider { 62 | static var previews: some View { 63 | TrafficCell( 64 | entry: TrafficEntry( 65 | child: BasicEntry( 66 | key: .NetworkCategoryBytesDownload(prefix: NetworkPrefix.en.rawValue), 67 | name: "Download", 68 | value: "0 Bps\n0 B" 69 | ), 70 | values: [ 71 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 72 | 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 73 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 74 | 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 75 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 76 | 0, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 77 | ] 78 | ) 79 | ) 80 | .padding([.leading, .trailing]) 81 | .previewLayout(.sizeThatFits) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Reveil/Views/Details/UsageCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UsageCell.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/3. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct UsageCell: View { 11 | enum Style { 12 | case regular 13 | case compat 14 | } 15 | 16 | private struct RatioItem: Identifiable, Hashable, Equatable { 17 | var id: Int { hashValue } 18 | 19 | let label: String 20 | let ratio: Double 21 | let color: Color 22 | 23 | init(label: String, ratio: Double, color: Color) { 24 | self.label = label 25 | self.ratio = ratio 26 | self.color = color 27 | } 28 | 29 | func hash(into hasher: inout Hasher) { 30 | hasher.combine(label) 31 | hasher.combine(color) 32 | } 33 | 34 | static func == (lhs: Self, rhs: Self) -> Bool { 35 | lhs.label == rhs.label && lhs.ratio == rhs.ratio && lhs.color == rhs.color 36 | } 37 | } 38 | 39 | @ObservedObject var entry: UsageEntry 40 | var style: Style = .regular 41 | 42 | private let minimumDisplayableRatio: Double = 0.001 43 | private let regularRowHeight: CGFloat = 24 44 | private let compactRowHeight: CGFloat = 18 45 | 46 | private var ratioItem: [RatioItem] { 47 | let totalValue = entry.items.reduce(0) { $0 + $1.value } 48 | guard !totalValue.isNaN else { 49 | return [] 50 | } 51 | return entry.items.compactMap { 52 | let itemRatio = $0.value / totalValue 53 | if itemRatio < minimumDisplayableRatio { return nil } 54 | return RatioItem(label: $0.label, ratio: itemRatio, color: $0.color) 55 | } 56 | } 57 | 58 | var body: some View { 59 | GeometryReader { metrics in 60 | ZStack(alignment: .leading) { 61 | Color(PlatformColor.secondarySystemFillAlias) 62 | 63 | HStack(spacing: 0) { 64 | ForEach(ratioItem, id: \.id) { item in 65 | item.color.frame(width: metrics.size.width * item.ratio) 66 | .animation(.spring(duration: 0.25, bounce: 0, blendDuration: 0.8), value: item) 67 | } 68 | } 69 | } 70 | .frame(maxWidth: metrics.size.width) 71 | .cornerRadius(4) 72 | } 73 | .frame(height: style == .regular ? regularRowHeight : compactRowHeight) 74 | .frame(maxWidth: .infinity, alignment: .leading) 75 | } 76 | } 77 | 78 | // MARK: - Previews 79 | 80 | struct UsageCell_Previews: PreviewProvider { 81 | static var previews: some View { 82 | UsageCell(entry: UsageEntry( 83 | key: .MemoryInformation, 84 | name: MemoryInformation.shared.moduleName, 85 | items: [ 86 | UsageEntry.Item(label: "Wired", value: 0.1771, color: Color("MemoryWired")), 87 | UsageEntry.Item(label: "Active", value: 0.3179, color: Color("MemoryActive")), 88 | UsageEntry.Item(label: "Inactive", value: 0.2474, color: Color("MemoryInactive")), 89 | UsageEntry.Item(label: "Purgeable", value: 0.0173, color: Color("MemoryPurgeable")), 90 | UsageEntry.Item(label: "Others", value: 0.1195, color: Color("MemoryOthers")), 91 | UsageEntry.Item(label: "Free", value: 0.1208, color: Color.clear), 92 | ] 93 | )) 94 | .padding() 95 | .previewLayout(.sizeThatFits) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Reveil/Views/PinButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PinButton.swift 3 | // Reveil 4 | // 5 | // Created by Lessica on 2023/10/4. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct PinButton: View { 11 | @AppCodableStorage var pin: Pin 12 | 13 | var body: some View { 14 | Button { 15 | pin = Pin(negate: pin) 16 | } label: { 17 | Image(systemName: pin.isPinned ? "pin.fill" : "pin") 18 | .font(Font.system(.body)) 19 | .foregroundColor(Color(PlatformColor.labelAlias)) 20 | .rotationEffect(.degrees(45)) 21 | } 22 | } 23 | } 24 | 25 | // MARK: - Previews 26 | 27 | struct PinButton_Previews: PreviewProvider { 28 | static var previews: some View { 29 | PinButton(pin: AppCodableStorage(wrappedValue: Pin(false), .Security)) 30 | .padding() 31 | .previewLayout(.sizeThatFits) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ReveilHelper/PresetsHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresetsHelper.swift 3 | // ReveilHelper 4 | // 5 | // Created by Lessica on 2023/12/28. 6 | // 7 | 8 | import ArgumentParser 9 | import Foundation 10 | 11 | @main 12 | struct PresetsHelper: ParsableCommand { 13 | @Argument(help: "The path to protect with security presets.") 14 | var bundlePath: String? = nil 15 | 16 | @Option(name: [.customShort("e"), .customLong("example")], help: "A path where example presets profile written to.") 17 | var examplePath: String? = nil 18 | 19 | func validate() throws { 20 | if bundlePath == nil, examplePath == nil { 21 | throw ValidationError("") 22 | } 23 | } 24 | 25 | mutating func run() throws { 26 | if let examplePath { 27 | let exampleURL = URL(fileURLWithPath: examplePath) 28 | try SecurityPresets.default.writeAhead(to: exampleURL) 29 | throw ExitCode.success 30 | } 31 | 32 | if let bundlePath { 33 | let bundleURL = URL(fileURLWithPath: bundlePath) 34 | let updatedPresets = try SecurityPresets(bundleURL: bundleURL) 35 | var targetURL: URL 36 | let contentsURL = bundleURL.appendingPathComponent("Contents/Resources") 37 | if contentsURL.directoryExists { 38 | targetURL = contentsURL 39 | } else { 40 | targetURL = bundleURL 41 | } 42 | targetURL = targetURL.appendingPathComponent( 43 | String(describing: SecurityPresets.self).components(separatedBy: ".").last!) 44 | .appendingPathExtension("plist") 45 | try updatedPresets.writeAhead(to: targetURL) 46 | } 47 | 48 | throw ExitCode.success 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /library-stub/Headers/libproc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006, 2007 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://www.opensource.apple.com/apsl/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPLE_LICENSE_HEADER_END@ 22 | */ 23 | #ifndef _LIBPROC_H_ 24 | #define _LIBPROC_H_ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | /* 36 | * This header file contains private interfaces to obtain process information. 37 | * These interfaces are subject to change in future releases. 38 | */ 39 | 40 | /*! 41 | @define PROC_LISTPIDSPATH_PATH_IS_VOLUME 42 | @discussion This flag indicates that all processes that hold open 43 | file references on the volume associated with the specified 44 | path should be returned. 45 | */ 46 | #define PROC_LISTPIDSPATH_PATH_IS_VOLUME 1 47 | 48 | 49 | /*! 50 | @define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 51 | @discussion This flag indicates that file references that were opened 52 | with the O_EVTONLY flag should be excluded from the matching 53 | criteria. 54 | */ 55 | #define PROC_LISTPIDSPATH_EXCLUDE_EVTONLY 2 56 | 57 | __BEGIN_DECLS 58 | 59 | int proc_listpids(uint32_t type, uint32_t typeinfo, void *buffer, int buffersize); 60 | 61 | /*! 62 | @function proc_listpidspath 63 | @discussion A function which will search through the current 64 | processes looking for open file references which match 65 | a specified path or volume. 66 | @param type types of processes to be searched (see proc_listpids) 67 | @param typeinfo adjunct information for type 68 | @param path file or volume path 69 | @param pathflags flags to control which files should be considered 70 | during the process search. 71 | @param buffer a C array of int-sized values to be filled with 72 | process identifiers that hold an open file reference 73 | matching the specified path or volume. Pass NULL to 74 | obtain the minimum buffer size needed to hold the 75 | currently active processes. 76 | @param buffersize the size (in bytes) of the provided buffer. 77 | @result the number of bytes of data returned in the provided buffer; 78 | -1 if an error was encountered; 79 | */ 80 | int proc_listpidspath(uint32_t type, 81 | uint32_t typeinfo, 82 | const char *path, 83 | uint32_t pathflags, 84 | void *buffer, 85 | int buffersize); 86 | 87 | int proc_pidinfo(int pid, int flavor, uint64_t arg, void *buffer, int buffersize); 88 | int proc_pidfdinfo(int pid, int fd, int flavor, void * buffer, int buffersize); 89 | int proc_name(int pid, void * buffer, uint32_t buffersize); 90 | int proc_regionfilename(int pid, uint64_t address, void * buffer, uint32_t buffersize); 91 | int proc_kmsgbuf(void * buffer, uint32_t buffersize); 92 | int proc_pidpath(int pid, void * buffer, uint32_t buffersize); 93 | int proc_libversion(int *major, int * minor); 94 | /* 95 | * A process can use the following api to set its own process control 96 | * state on resoure starvation. The argument can have one of the PROC_SETPC_XX values 97 | */ 98 | #define PROC_SETPC_NONE 0 99 | #define PROC_SETPC_THROTTLEMEM 1 100 | #define PROC_SETPC_SUSPEND 2 101 | #define PROC_SETPC_TERMINATE 3 102 | 103 | int proc_setpcontrol(const int control); 104 | __END_DECLS 105 | 106 | #endif /*_LIBPROC_H_ */ 107 | -------------------------------------------------------------------------------- /library-stub/library_stub.h: -------------------------------------------------------------------------------- 1 | // 2 | // library_stub.h 3 | // library-stub 4 | // 5 | // Created by Lessica on 2023/11/2. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for library_stub. 11 | FOUNDATION_EXPORT double library_stubVersionNumber; 12 | 13 | //! Project version string for library_stub. 14 | FOUNDATION_EXPORT const unsigned char library_stubVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | FOUNDATION_EXPORT int evaluateSignature(NSURL *fileURL, NSData **cdHashOut, BOOL *isAdhocSignedOut); 18 | FOUNDATION_EXPORT BOOL isCdHashInTrustCache(NSData *cdHash); 19 | -------------------------------------------------------------------------------- /library-stub/library_stub.m: -------------------------------------------------------------------------------- 1 | // 2 | // library_stub.m 3 | // library-stub 4 | // 5 | // Created by Lessica on 2023/11/2. 6 | // 7 | 8 | #import "library_stub.h" 9 | #import 10 | #import 11 | #import "macho.h" 12 | 13 | #define AMFI_IS_CD_HASH_IN_TRUST_CACHE 6 14 | 15 | int evaluateSignature(NSURL* fileURL, NSData **cdHashOut, BOOL *isAdhocSignedOut) 16 | { 17 | if (!fileURL || (!cdHashOut && !isAdhocSignedOut)) return 1; 18 | if (![fileURL checkResourceIsReachableAndReturnError:nil]) return 2; 19 | 20 | FILE *machoFile = fopen(fileURL.fileSystemRepresentation, "rb"); 21 | if (!machoFile) return 3; 22 | 23 | BOOL isMacho = NO; 24 | machoGetInfo(machoFile, &isMacho, NULL); 25 | 26 | if (!isMacho) { 27 | fclose(machoFile); 28 | return 4; 29 | } 30 | 31 | int64_t archOffset = machoFindBestArch(machoFile); 32 | if (archOffset < 0) { 33 | fclose(machoFile); 34 | return 5; 35 | } 36 | 37 | uint32_t CSDataStart = 0, CSDataSize = 0; 38 | machoFindCSData(machoFile, (uint32_t)archOffset, &CSDataStart, &CSDataSize); 39 | if (CSDataStart == 0 || CSDataSize == 0) { 40 | fclose(machoFile); 41 | return 6; 42 | } 43 | 44 | BOOL isAdhocSigned = machoCSDataIsAdHocSigned(machoFile, CSDataStart, CSDataSize); 45 | if (isAdhocSignedOut) { 46 | *isAdhocSignedOut = isAdhocSigned; 47 | } 48 | 49 | // we only care about the cd hash on stuff that's already verified to be ad hoc signed 50 | if (isAdhocSigned && cdHashOut) { 51 | *cdHashOut = machoCSDataCalculateCDHash(machoFile, CSDataStart, CSDataSize); 52 | } 53 | 54 | fclose(machoFile); 55 | return 0; 56 | } 57 | 58 | BOOL isCdHashInTrustCache(NSData *cdHash) 59 | { 60 | kern_return_t kr; 61 | 62 | CFMutableDictionaryRef amfiServiceDict = IOServiceMatching("AppleMobileFileIntegrity"); 63 | if (amfiServiceDict) 64 | { 65 | io_connect_t connect; 66 | if (@available(iOS 15.0, *)) { 67 | io_service_t amfiService = IOServiceGetMatchingService(kIOMainPortDefault, amfiServiceDict); 68 | kr = IOServiceOpen(amfiService, mach_task_self(), 0, &connect); 69 | } else { 70 | // Fallback on earlier versions 71 | io_service_t amfiService = IOServiceGetMatchingService(0 /* kIOMasterPortDefault */, amfiServiceDict); 72 | kr = IOServiceOpen(amfiService, mach_task_self(), 0, &connect); 73 | } 74 | if (kr != KERN_SUCCESS) 75 | { 76 | NSLog(@"Failed to open amfi service %d %s", kr, mach_error_string(kr)); 77 | return NO; 78 | } 79 | 80 | uint64_t includeLoadedTC = YES; 81 | kr = IOConnectCallMethod(connect, AMFI_IS_CD_HASH_IN_TRUST_CACHE, &includeLoadedTC, 1, CFDataGetBytePtr((__bridge CFDataRef)cdHash), CFDataGetLength((__bridge CFDataRef)cdHash), 0, 0, 0, 0); 82 | NSLog(@"Is %s in TrustCache? %s", cdHash.description.UTF8String, kr == 0 ? "Yes" : "No"); 83 | 84 | IOServiceClose(connect); 85 | return kr == KERN_SUCCESS; 86 | } 87 | 88 | return NO; 89 | } 90 | -------------------------------------------------------------------------------- /library-stub/macho.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import "csblob.h" 11 | 12 | void machoEnumerateArchs(FILE* machoFile, void (^archEnumBlock)(struct fat_arch* arch, uint32_t archMetadataOffset, uint32_t archOffset, BOOL* stop)); 13 | void machoGetInfo(FILE* candidateFile, bool *isMachoOut, bool *isLibraryOut); 14 | int64_t machoFindArch(FILE *machoFile, uint32_t subtypeToSearch); 15 | int64_t machoFindBestArch(FILE *machoFile); 16 | 17 | void machoEnumerateLoadCommands(FILE *machoFile, uint32_t archOffset, void (^enumerateBlock)(struct load_command cmd, uint32_t cmdOffset)); 18 | void machoFindLoadCommand(FILE *machoFile, uint32_t cmd, void *lcOut, size_t lcSize); 19 | void machoFindCSData(FILE* machoFile, uint32_t archOffset, uint32_t* outOffset, uint32_t* outSize); 20 | 21 | void machoEnumerateDependencies(FILE *machoFile, uint32_t archOffset, NSString *machoPath, void (^enumerateBlock)(NSString *dependencyPath)); 22 | 23 | void machoCSDataEnumerateBlobs(FILE *machoFile, uint32_t CSDataStart, uint32_t CSDataSize, void (^enumerateBlock)(struct CSBlob blobDescriptor, uint32_t blobDescriptorOffset, BOOL *stop)); 24 | NSData *machoCSDataCalculateCDHash(FILE *machoFile, uint32_t CSDataStart, uint32_t CSDataSize); 25 | bool machoCSDataIsAdHocSigned(FILE *machoFile, uint32_t CSDataStart, uint32_t CSDataSize); 26 | --------------------------------------------------------------------------------