├── .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 | [](https://github.com/Lessica/Reveil/actions/workflows/build-analyze.yml) [](https://github.com/Lessica/Reveil/actions/workflows/build-archive.yml) 
4 | 
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 | 
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 |
--------------------------------------------------------------------------------