Compare commits

..

36 Commits

Author SHA1 Message Date
Duraid Abdul a522748e3c Merge branch 'main' of https://github.com/duraidabdul/LocalConsole into main 2021-06-22 10:56:58 -07:00
Duraid Abdul 58aecf3a9d Update LCManager.swift 2021-06-22 10:56:56 -07:00
Duraid Abdul 971de252bf Delete Additional Files directory 2021-06-15 12:14:04 -07:00
Duraid Abdul ef0bd6cd8a Offload Demo files 2021-06-15 12:13:45 -07:00
Duraid Abdul 5f8c210c62 Update LCManager.swift
Improved debug border implementation.
2021-06-12 14:36:27 -07:00
Duraid Abdul 0bbfabc568 Reverse text in console, brand new debug. 2021-06-10 11:48:37 -07:00
Duraid Abdul 157ab2153f Delete Demo_Test.gif 2021-06-04 13:09:21 -07:00
Duraid Abdul 51ddd6c2c2 Delete Demo_test2.gif 2021-06-04 13:09:14 -07:00
Duraid Abdul c5a5641906 Update README.md 2021-06-04 13:08:59 -07:00
Duraid Abdul cbbbf2c6db Update README.md 2021-06-04 13:08:25 -07:00
Duraid Abdul a75c5763a4 Update README.md 2021-06-04 13:06:53 -07:00
Duraid Abdul a66afaef04 Update README.md 2021-06-04 13:04:54 -07:00
Duraid Abdul 7ca52868ff Add files via upload 2021-06-04 13:04:38 -07:00
Duraid Abdul 417bdf2fb3 Add files via upload 2021-06-04 13:02:41 -07:00
Duraid Abdul 92299c62de Delete Demo_Test.gif 2021-06-04 13:01:30 -07:00
Duraid Abdul 22cd8d12ff Update README.md 2021-06-04 12:59:44 -07:00
Duraid Abdul de62ed79af Add files via upload 2021-06-04 12:58:32 -07:00
Duraid Abdul 261ba2a83b Update README.md 2021-06-04 12:43:30 -07:00
Duraid Abdul 697d636cbc Update README.md 2021-06-04 12:42:59 -07:00
Duraid Abdul cdbb4fef4d Update README.md 2021-06-04 12:42:32 -07:00
Duraid Abdul dea5e6e4e0 Update README.md 2021-06-04 12:40:55 -07:00
Duraid Abdul b28605e52e Merge branch 'main' of https://github.com/duraidabdul/LocalConsole into main 2021-06-03 16:27:47 -07:00
Duraid Abdul 35a5350f17 Update LCManager.swift 2021-06-03 16:27:43 -07:00
Duraid Abdul b319499056 Merge pull request #3 from hxperl/add-uicolor-context
🎨 Add UIColor context to fix CI Build compile
2021-06-02 22:12:51 -07:00
hxperl 17d30ad554 🎨 Add UIColor context to fix CI Build compile 2021-06-03 13:09:15 +09:00
Duraid Abdul b56d27ff26 Add OS Compile Date to System Report 2021-06-01 14:46:05 -07:00
Duraid Abdul c7fe1daf26 Update System Report
Reorganized system report code for readability. Added some more valued to the system report.
2021-06-01 14:17:25 -07:00
Duraid Abdul 6db60e25b2 Asynchronous System Report 2021-05-31 21:24:23 -07:00
Duraid Abdul 5094ddc710 Added System Report 2021-05-31 21:15:22 -07:00
Duraid Abdul 9b5e0d84ac Merge branch 'main' of https://github.com/duraidabdul/LocalConsole into main 2021-05-24 01:46:08 -07:00
Duraid Abdul 1620fad461 Update Demo_Resize.gif 2021-05-24 01:44:17 -07:00
Duraid Abdul 86d9e18613 Update README.md 2021-05-24 01:40:04 -07:00
Duraid Abdul 832f507ab1 Update README.md 2021-05-24 01:38:59 -07:00
Duraid Abdul 2406568789 Update README.md 2021-05-24 01:35:52 -07:00
Duraid Abdul f57800e8a4 Update Demos 2021-05-24 01:27:11 -07:00
Duraid Abdul e7bc5d221b Update LCManager.swift 2021-05-24 01:01:40 -07:00
8 changed files with 361 additions and 78 deletions
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 MiB

+4 -1
View File
@@ -2,7 +2,10 @@
Welcome to LocalConsole! This Swift Package makes on-device debugging easy with a convenient PiP-style console that can display items in the same way ```print()``` will in Xcode. This tool can also dynamically display view frames and restart SpringBoard right from your live app.
<img src="https://github.com/duraidabdul/LocalConsole/blob/main/Demo.gif?raw=true" width="320">
<div>
<img src="https://github.com/duraidabdul/Demos/blob/main/Demo_Pan.gif?raw=true" width="320">
<img src="https://github.com/duraidabdul/Demos/blob/main/Demo_Resize.gif?raw=true" width="320">
</div>
## **Setup**
-4
View File
@@ -5,8 +5,6 @@
// Copyright © 2021 Duraid Abdul. All rights reserved.
//
#if canImport(UIKit)
import UIKit
/// This class handles enabling and disabling debug borders on a specified view.
@@ -64,5 +62,3 @@ class BorderManager {
}
}
}
#endif
-4
View File
@@ -5,8 +5,6 @@
// Copyright © 2021 Duraid Abdul. All rights reserved.
//
#if canImport(UIKit)
import UIKit
extension UIScreen {
@@ -59,5 +57,3 @@ extension UIView {
frame.origin.y = (round(frame.origin.y * UIScreen.main.scale)) / UIScreen.main.scale
}
}
#endif
@@ -5,8 +5,6 @@
// Copyright © 2021 Duraid Abdul. All rights reserved.
//
#if canImport(UIKit)
import UIKit
extension CGPoint {
@@ -80,5 +78,3 @@ func nearestTargetTo(_ point: CGPoint, possibleTargets: [CGPoint]) -> CGPoint {
}
return nearestEndpoint
}
#endif
+230 -59
View File
@@ -5,11 +5,11 @@
// Copyright © 2021 Duraid Abdul. All rights reserved.
//
#if canImport(UIKit)
//#if canImport(UIKit)
import UIKit
import SwiftUI
var GLOBAL_DEBUG_BORDERS = false
var GLOBAL_BORDER_TRACKERS: [BorderManager] = []
public class LCManager: NSObject, UIGestureRecognizerDelegate {
@@ -26,17 +26,35 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
let defaultConsoleSize = CGSize(width: 212, height: 124)
let defaultConsoleSize = CGSize(width: 228, height: 142)
/// The fixed size of the console view.
lazy var consoleSize = defaultConsoleSize {
didSet {
consoleView.frame.size = consoleSize
// Update text view width.
if consoleView.frame.size.width > ResizeController.kMaxConsoleWidth {
consoleTextView.frame.size.width = ResizeController.kMaxConsoleWidth
consoleTextView.frame.size.width = ResizeController.kMaxConsoleWidth - 4
} else if consoleView.frame.size.width < ResizeController.kMinConsoleWidth {
consoleTextView.frame.size.width = ResizeController.kMinConsoleWidth - 4
} else {
consoleTextView.frame.size.width = consoleSize.width
consoleTextView.frame.size.width = consoleSize.width - 4
}
// Update text view height.
if consoleView.frame.size.height > ResizeController.kMaxConsoleHeight {
consoleTextView.frame.size.height = ResizeController.kMaxConsoleHeight - 4
+ (consoleView.frame.size.height - ResizeController.kMaxConsoleHeight) * 2 / 3
} else if consoleView.frame.size.height < ResizeController.kMinConsoleHeight {
consoleTextView.frame.size.height = ResizeController.kMinConsoleHeight - 4
+ (consoleView.frame.size.height - ResizeController.kMinConsoleHeight) * 2 / 3
} else {
consoleTextView.frame.size.height = consoleSize.height - 4
}
consoleTextView.contentOffset.y = consoleTextView.contentSize.height - consoleTextView.bounds.size.height
// TODO: Snap to nearest position.
UserDefaults.standard.set(consoleSize.width, forKey: "LocalConsole_Width")
@@ -52,12 +70,12 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
lazy var consoleView = viewController.view!
/// Text view that displays printed items.
let consoleTextView = UITextView()
let consoleTextView = InvertedTextView()
/// Button that reveals menu.
lazy var menuButton = UIButton()
/// Tracks whether the PiP console is in text view scroll mode or pan mode.
/// Tracks whether the PiP console is in text view scroll mode or pan mode.
var scrollLocked = true
/// Feedback generator for the long press action.
@@ -74,14 +92,14 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
CGPoint(x: UIScreen.portraitSize.width - consoleSize.width / 2 - 12,
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: consoleSize.width / 2 + 12,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (consoleWindow?.safeAreaInsets.bottom ?? 0) - 12),
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12),
CGPoint(x: UIScreen.portraitSize.width - consoleSize.width / 2 - 12,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
} else {
return [CGPoint(x: UIScreen.portraitSize.width / 2,
y: (UIScreen.hasRoundedCorners ? 44 : 16) + consoleSize.height / 2 + 12),
CGPoint(x: UIScreen.portraitSize.width / 2,
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
y: UIScreen.portraitSize.height - consoleSize.height / 2 - (keyboardHeight ?? consoleWindow?.safeAreaInsets.bottom ?? 0) - 12)]
}
}
@@ -103,7 +121,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleView.center = possibleEndpoints.first!
consoleView.alpha = 0
consoleView.layer.cornerRadius = 20
consoleView.layer.cornerRadius = 22
consoleView.layer.cornerCurve = .continuous
let borderView = UIView()
@@ -118,17 +136,19 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleView.addSubview(borderView)
// Configure text view.
consoleTextView.frame = CGRect(x: 0, y: 2, width: consoleSize.width, height: consoleSize.height - 4)
consoleTextView.frame = CGRect(x: 2, y: 2, width: consoleSize.width - 4, height: consoleSize.height - 4)
consoleTextView.isEditable = false
consoleTextView.backgroundColor = .clear
consoleTextView.textContainerInset = UIEdgeInsets(top: 8, left: 10, bottom: 8, right: 10)
consoleTextView.textContainerInset = UIEdgeInsets(top: 10, left: 8, bottom: 10, right: 8)
consoleTextView.isSelectable = false
consoleTextView.showsVerticalScrollIndicator = false
consoleTextView.contentInsetAdjustmentBehavior = .never
consoleTextView.autoresizingMask = [.flexibleHeight]
consoleView.addSubview(consoleTextView)
consoleTextView.layer.cornerRadius = consoleView.layer.cornerRadius - 2
consoleTextView.layer.cornerCurve = .continuous
// Configure gesture recognizers.
panRecognizer.maximumNumberOfTouches = 1
panRecognizer.delegate = self
@@ -143,7 +163,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
consoleView.addGestureRecognizer(longPressRecognizer)
// Prepare menu button.
let diameter = CGFloat(26)
let diameter = CGFloat(28)
// This tuned button frame is used to adjust where the menu appears.
menuButton = UIButton(frame: CGRect(x: consoleView.bounds.width - 44,
@@ -163,7 +183,7 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
circle.isUserInteractionEnabled = false
menuButton.addSubview(circle)
let ellipsisImage = UIImageView(image: UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 16)))
let ellipsisImage = UIImageView(image: UIImage(systemName: "ellipsis", withConfiguration: UIImage.SymbolConfiguration(pointSize: 17)))
ellipsisImage.frame.size = circle.bounds.size
ellipsisImage.contentMode = .center
circle.addSubview(ellipsisImage)
@@ -173,7 +193,8 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
menuButton.showsMenuAsPrimaryAction = true
consoleView.addSubview(menuButton)
UIView.swizzleDebugBehaviour
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
}
/// Adds a LocalConsole window to the app's main scene.
@@ -235,32 +256,68 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
UIViewPropertyAnimator(duration: 0.5, dampingRatio: 0.6) { [self] in
consoleView.transform = .init(scaleX: 1, y: 1)
}.startAnimation()
UIViewPropertyAnimator(duration: 0.3, dampingRatio: 1) { [self] in
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleView.alpha = 1
}.startAnimation()
let animation = CABasicAnimation(keyPath: "shadowOpacity")
animation.fromValue = 0
animation.toValue = 0.5
animation.duration = 0.6
consoleView.layer.add(animation, forKey: animation.keyPath)
consoleView.layer.shadowOpacity = 0.5
} else {
UIViewPropertyAnimator(duration: 0.25, dampingRatio: 1) { [self] in
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 0.9, y: 0.9)
}.startAnimation()
UIViewPropertyAnimator(duration: 0.3, dampingRatio: 1) { [self] in
consoleView.alpha = 0
}.startAnimation()
}
}
}
private var _hasRelayedOffsetChange = false
/// Print items to the console view.
public func print(_ items: Any) {
let string: String = {
if consoleTextView.text == "" {
return "\(items)"
} else {
return "\(items)\n" + consoleTextView.text
func performActions() {
if consoleTextView.contentOffset.y > consoleTextView.contentSize.height - 20 - consoleTextView.bounds.size.height ||
_hasRelayedOffsetChange == false {
consoleTextView.pendingOffsetChange = true
_hasRelayedOffsetChange = true
}
}()
let needsMenuUpdate = consoleTextView.text == ""
let string: String = {
if consoleTextView.text == "" {
return "\(items)"
} else {
return consoleTextView.text + "\n\(items)"
}
}()
setAttributedText(string)
if needsMenuUpdate {
// Update the context menu to show the clipboard/clear actions.
menuButton.menu = makeMenu()
}
}
setAttributedText(string)
// Update the context menu to show the clipboard/clear actions.
menuButton.menu = makeMenu()
// Ensure we are performing UI updates on the main thread.
DispatchQueue.main.async {
// Ensure the console doesn't get caught into any external animation blocks.
UIView.performWithoutAnimation {
performActions()
}
}
}
/// Clear text in the console view.
@@ -278,9 +335,37 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
// MARK: - Private
// MARK: Handle keyboard show/hide.
private var keyboardHeight: CGFloat? = nil {
didSet {
if consoleView.center != possibleEndpoints[0] && consoleView.center != possibleEndpoints[1] {
let nearestTargetPosition = nearestTargetTo(consoleView.center, possibleTargets: possibleEndpoints.suffix(2))
UIViewPropertyAnimator(duration: 0.55, dampingRatio: 1) {
self.consoleView.center = nearestTargetPosition
}.startAnimation()
}
}
}
@objc func keyboardWillShow(_ notification: Notification) {
if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue {
let keyboardRectangle = keyboardFrame.cgRectValue
self.keyboardHeight = keyboardRectangle.height
}
}
@objc func keyboardWillHide() {
keyboardHeight = nil
}
private var debugBordersEnabled = false {
didSet {
GLOBAL_DEBUG_BORDERS = debugBordersEnabled
UIView.swizzleDebugBehaviour_UNTRACKABLE_TOGGLE()
guard debugBordersEnabled else {
GLOBAL_BORDER_TRACKERS.forEach {
$0.deactivate()
@@ -306,29 +391,48 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
}
@objc func toggleLock() {
scrollLocked.toggle()
func systemReport() {
DispatchQueue.main.async { [self] in
print(
"""
\n
Model Name: \(SystemReport.shared.gestaltMarketingName)
Model Identifier: \(SystemReport.shared.gestaltModelIdentifier)
Architecture: \(SystemReport.shared.gestaltArchitecture)
Firmware: \(SystemReport.shared.gestaltFirmwareVersion)
Kernel Version: \(SystemReport.shared.kernel) \(SystemReport.shared.kernelVersion)
System Version: \(SystemReport.shared.versionString)
OS Compile Date: \(SystemReport.shared.compileDate)
Memory: \(round(100 * Double(ProcessInfo.processInfo.physicalMemory) * pow(10, -9)) / 100) GB
Processor Cores: \(Int(ProcessInfo.processInfo.processorCount))
Thermal State: \(SystemReport.shared.thermalState)
System Uptime: \(Int(ProcessInfo.processInfo.systemUptime))s
Low Power Mode: \(ProcessInfo.processInfo.isLowPowerModeEnabled)
"""
)
}
}
func toggleVisibility() {
if isVisible {
UIViewPropertyAnimator(duration: 0.25, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 0.9, y: 0.9)
consoleView.alpha = 0
}.startAnimation()
func displayReport() {
DispatchQueue.main.async { [self] in
isVisible = false
} else {
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) { [self] in
consoleView.transform = .init(scaleX: 1, y: 1)
consoleView.alpha = 1
}.startAnimation()
isVisible = true
print(
"""
\n
Screen Size: \(UIScreen.main.bounds.size)
Screen Corner Radius: \(UIScreen.main.value(forKey: "_displayCornerRadius") as! CGFloat)
Screen Scale: \(UIScreen.main.scale)
Max Frame Rate: \(UIScreen.main.maximumFramesPerSecond) Hz
Brightness: \(UIScreen.main.brightness)
"""
)
}
// Renders color properly (for dark appearance).
consoleView.backgroundColor = .black
}
@objc func toggleLock() {
scrollLocked.toggle()
}
func setAttributedText(_ string: String) {
@@ -366,12 +470,49 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
let consoleActions = UIMenu(title: "", options: .displayInline, children: [clear, resize])
var frameSymbol = "rectangle.3.offgrid"
if #available(iOS 15, *) {
frameSymbol = "square.inset.filled"
}
let viewFrames = UIAction(title: debugBordersEnabled ? "Hide View Frames" : "Show View Frames",
image: UIImage(systemName: "rectangle.3.offgrid"), handler: { _ in
image: UIImage(systemName: frameSymbol), handler: { _ in
self.debugBordersEnabled.toggle()
self.menuButton.menu = self.makeMenu()
})
let systemReport = UIAction(title: "System Report",
image: UIImage(systemName: "cpu"), handler: { _ in
self.systemReport()
})
// Show the right glyph for the current device being used.
let deviceSymbol: String = {
let hasHomeButton = UIScreen.main.value(forKey: "_displayCornerRadius") as! CGFloat == 0
if UIDevice.current.userInterfaceIdiom == .pad {
if hasHomeButton {
return "ipad.homebutton"
} else {
return "ipad"
}
} else if UIDevice.current.userInterfaceIdiom == .phone {
if hasHomeButton {
return "iphone.homebutton"
} else {
return "iphone"
}
} else {
return "rectangle"
}
}()
let displayReport = UIAction(title: "Display Report",
image: UIImage(systemName: deviceSymbol), handler: { _ in
self.displayReport()
})
let respring = UIAction(title: "Restart SpringBoard",
image: UIImage(systemName: "apps.iphone"), handler: { _ in
guard let window = UIApplication.shared.windows.first else { return }
@@ -390,7 +531,9 @@ public class LCManager: NSObject, UIGestureRecognizerDelegate {
}
animator.startAnimation()
})
let debugActions = UIMenu(title: "", options: .displayInline, children: [viewFrames, respring])
let debugActions = UIMenu(title: "", options: .displayInline,
children: [UIMenu(title: "Debug", image: UIImage(systemName: "ant"),
children: [viewFrames, systemReport, displayReport, respring])])
var menuContent: [UIMenuElement] = []
@@ -558,20 +701,18 @@ public class UITapStartEndGestureRecognizer: UITapGestureRecognizer {
// MARK: Fun hacks!
extension UIView {
/// Swizzle UIView to use custom frame system when needed.
static let swizzleDebugBehaviour: Void = {
static func swizzleDebugBehaviour_UNTRACKABLE_TOGGLE() {
guard let originalMethod = class_getInstanceMethod(UIView.self, #selector(layoutSubviews)),
let swizzledMethod = class_getInstanceMethod(UIView.self, #selector(swizzled_layoutSubviews)) else { return }
method_exchangeImplementations(originalMethod, swizzledMethod)
}()
}
@objc func swizzled_layoutSubviews() {
swizzled_layoutSubviews()
if GLOBAL_DEBUG_BORDERS {
let tracker = BorderManager(view: self)
GLOBAL_BORDER_TRACKERS.append(tracker)
tracker.activate()
}
let tracker = BorderManager(view: self)
GLOBAL_BORDER_TRACKERS.append(tracker)
tracker.activate()
}
}
@@ -590,4 +731,34 @@ extension UIWindow {
}
}
#endif
//#endif
class InvertedTextView: UITextView {
var pendingOffsetChange = false
// Thanks to WWDC21 Lab!
override func layoutSubviews() {
super.layoutSubviews()
if panGestureRecognizer.numberOfTouches == 0 && pendingOffsetChange {
contentOffset.y = contentSize.height - bounds.size.height
} else {
pendingOffsetChange = false
}
}
var cancelNextContentSizeDidSet = false
override var contentSize: CGSize {
didSet {
cancelNextContentSizeDidSet = true
if contentSize.height < bounds.size.height {
contentInset.top = bounds.size.height - contentSize.height
} else {
contentInset.top = 0
}
}
}
}
+19 -6
View File
@@ -116,6 +116,14 @@ class ResizeController {
if isActive {
UIViewPropertyAnimator(duration: 0.75, dampingRatio: 1) {
let textView = LCManager.shared.consoleTextView
textView.contentOffset.y = textView.contentSize.height - textView.bounds.size.height
}.startAnimation()
if LCManager.shared.consoleView.traitCollection.userInterfaceStyle == .light {
LCManager.shared.consoleView.layer.shadowOpacity = 0.25
}
@@ -189,12 +197,15 @@ class ResizeController {
var initialHeight = CGFloat.zero
static let kMinConsoleHeight: CGFloat = 108
static let kMaxConsoleHeight: CGFloat = 346
@objc func verticalPanner(recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translation(in: bottomGrabber.superview)
let maxHeight: CGFloat = 346
let minHeight: CGFloat = 108
let minHeight = Self.kMinConsoleHeight
let maxHeight = Self.kMaxConsoleHeight
switch recognizer.state {
case .began:
@@ -252,14 +263,15 @@ class ResizeController {
var initialWidth = CGFloat.zero
static let kMinConsoleWidth: CGFloat = 112
static let kMaxConsoleWidth: CGFloat = UIScreen.portraitSize.width - 56
@objc func horizontalPanner(recognizer: UIPanGestureRecognizer) {
let translation = recognizer.translation(in: bottomGrabber.superview)
let maxWidth: CGFloat = Self.kMaxConsoleWidth
let minWidth: CGFloat = 112
let minWidth = Self.kMinConsoleWidth
let maxWidth = Self.kMaxConsoleWidth
switch recognizer.state {
case .began:
@@ -377,6 +389,7 @@ class PlatterView: UIView {
let subtitleLabel = UILabel()
subtitleLabel.text = "Use the grabbers to resize the console."
subtitleLabel.font = .systemFont(ofSize: 17, weight: .medium)
subtitleLabel.sizeToFit()
subtitleLabel.alpha = 0.5
subtitleLabel.center.x = bounds.width / 2
@@ -408,7 +421,7 @@ class PlatterView: UIView {
lazy var doneButton: UIButton = {
let button = UIButton(type: .custom)
button.backgroundColor = .systemBlue.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))
button.backgroundColor = UIColor.systemBlue.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark))
button.setTitle("Done", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 17, weight: .medium)
@@ -455,7 +468,7 @@ class PlatterView: UIView {
// Resolves a text view frame animation bug that occurs when *decreasing* text view width.
if LCManager.shared.consoleSize.width > LCManager.shared.defaultConsoleSize.width {
LCManager.shared.consoleTextView.frame.size.width = LCManager.shared.defaultConsoleSize.width
LCManager.shared.consoleTextView.frame.size.width = LCManager.shared.defaultConsoleSize.width - 4
}
UIViewPropertyAnimator(duration: 0.4, dampingRatio: 1) {
+108
View File
@@ -0,0 +1,108 @@
//
// SystemReport.swift
// LocalConsole
//
// Created by Duraid Abdul on 2021-06-01.
//
import Foundation
import MachO
class SystemReport {
static let shared = SystemReport()
var versionString: String {
ProcessInfo.processInfo.operatingSystemVersionString
.replacingOccurrences(of: "Build ", with: "")
.replacingOccurrences(of: "Version ", with: "")
}
// Current device thermal state.
var thermalState: String {
let state = ProcessInfo.processInfo.thermalState
switch state {
case .nominal: return "Nominal"
case .fair : return "Fair"
case .serious : return "Serious"
case .critical : return "Critical"
default: return "Unknown"
}
}
// Retrieve device mobile gestalt cache.
lazy var gestaltCacheExtra: NSDictionary? = {
let url = URL(fileURLWithPath: "/private/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist")
let dictionary = NSDictionary(contentsOf: url)
return dictionary?.value(forKey: "CacheExtra") as? NSDictionary
}()
// Device marketing name.
lazy var gestaltMarketingName: Any = gestaltCacheExtra?.value(forKey: "Z/dqyWS6OZTRy10UcmUAhw") ?? "Unknown"
// iBoot (second-stage loader) version.
lazy var gestaltFirmwareVersion: Any = gestaltCacheExtra?.value(forKey: "LeSRsiLoJCMhjn6nd6GWbQ") ?? "Unknown"
// CPU architecture.
lazy var gestaltArchitecture: Any = gestaltCacheExtra?.value(forKey: "k7QIBwZJJOVw+Sej/8h8VA") ?? deviceArchitecture
// Fallback in case gestaltArchitecture doesn't return a value.
var deviceArchitecture: String {
let info = NXGetLocalArchInfo()
return String(utf8String: (info?.pointee.description)!) ?? "Unknown"
}
lazy var gestaltModelIdentifier: Any = gestaltCacheExtra?.value(forKey: "h9jDsbgj7xIVeIQ8S3/X3Q") ?? modelIdentifier
// Fallback in case gestaltModelIdentifier doesn't return a value.
var modelIdentifier: String {
if let simulatorModelIdentifier = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] { return simulatorModelIdentifier }
var sysinfo = utsname()
uname(&sysinfo) // ignore return value
return String(bytes: Data(bytes: &sysinfo.machine, count: Int(_SYS_NAMELEN)), encoding: .ascii)?.trimmingCharacters(in: .controlCharacters) ?? "Unknown"
}
var kernel: String {
var size = 0
sysctlbyname("kern.ostype", nil, &size, nil, 0)
var string = [CChar](repeating: 0, count: Int(size))
sysctlbyname("kern.ostype", &string, &size, nil, 0)
return String(cString: string)
}
var kernelVersion: String {
var size = 0
sysctlbyname("kern.osrelease", nil, &size, nil, 0)
var string = [CChar](repeating: 0, count: Int(size))
sysctlbyname("kern.osrelease", &string, &size, nil, 0)
return String(cString: string)
}
var compileDate: String {
var size = 0
sysctlbyname("kern.version", nil, &size, nil, 0)
var string = [CChar](repeating: 0, count: Int(size))
sysctlbyname("kern.version", &string, &size, nil, 0)
let fullString = String(cString: string) /// Ex: Darwin Kernel Version 20.6.0: Mon May 10 03:15:29 PDT 2021; root:xnu-7195.140.13.0.1~20/RELEASE_ARM64_T8101
let detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.date.rawValue)
if let matches = detector?.matches(in: fullString, options: [], range: NSRange(location: 0, length: fullString.utf16.count)) {
for match in matches {
if let date = match.date {
let dateformatter = DateFormatter()
dateformatter.dateStyle = .medium
return dateformatter.string(from: date)
}
}
}
return "Unknown"
}
}