From c1450d88b74b7b2b5a4283b608eccbd67a5ffae1 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Mon, 12 Oct 2026 08:33:57 +0800 Subject: [PATCH 1/5] Match the DEBUG-only types by their Swift names in the release check When the host's types lost their LK prefixes and Objective-C names, the DEBUG-only end-to-end dump and UI snapshots in LookInside/DebugE2E/ became DebugE2EDump, DebugUISnapshots, DebugWindowImage and SnapshotNormalizer. check-host-release-clean.sh kept looking for LKDebugE2EDump, LKDebugUISnapshots, LKDebugWindowImage and LookinSnapshotNormalizer, names no binary contains any more, so that part of the check could no longer fail. It now looks for the new names. Scripts/test-check-host-release-clean.sh, which the build workflow now runs, keeps the check and the code in step. For every file in LookInside/DebugE2E/ it builds a throwaway app bundle that declares the file's types and expects the check to reject it, both with a symbol table and stripped; a bundle of ordinary host types must pass. Renaming a DebugE2E type without updating the check fails the test. Comments that still named the old types are updated as well. --- .github/workflows/build.yml | 3 + Scripts/check-host-release-clean.sh | 19 ++-- Scripts/test-check-host-release-clean.sh | 119 +++++++++++++++++++++++ Scripts/test-mcp-bridge.sh | 2 +- 4 files changed, 134 insertions(+), 9 deletions(-) create mode 100755 Scripts/test-check-host-release-clean.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df3a78a..b57bad5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,6 +30,9 @@ jobs: - name: Validate Release Workflow run: bash Scripts/test-release-workflow.sh + - name: Test Release Cleanliness Check + run: bash Scripts/test-check-host-release-clean.sh + - name: Sync Derived Source run: bash Scripts/sync-derived-source.sh diff --git a/Scripts/check-host-release-clean.sh b/Scripts/check-host-release-clean.sh index 5f0f0a2..1b8f925 100755 --- a/Scripts/check-host-release-clean.sh +++ b/Scripts/check-host-release-clean.sh @@ -11,17 +11,17 @@ # which reads LOOKINSIDE_ACTIVATION_API_BASE_URL (a different activation # service) and LOOKINSIDE_ACTIVATION_TEST_ROOT_ID / # LOOKINSIDE_ACTIVATION_TEST_ROOT_PUBLIC_KEY_PEM (an extra trusted root) from -# the environment, and the end-to-end dump (DebugE2E/: LKDebugE2EDump, -# LookinSnapshotNormalizer) reads LOOKINSIDE_DEBUG_E2E_*, and the UI -# snapshots (DebugE2E/: LKDebugUISnapshots, LKDebugWindowImage) read +# the environment, and the end-to-end dump (DebugE2E/: DebugE2EDump, +# SnapshotNormalizer) reads LOOKINSIDE_DEBUG_E2E_*, and the UI +# snapshots (DebugE2E/: DebugUISnapshots, DebugWindowImage) read # LOOKINSIDE_DEBUG_UI_SNAPSHOT_*. A Release build # compiles them out; this check proves it on the shipped bundle. # # A path fails when any of the following holds: # - a file in it contains ActivationDebugOverrides, any # LOOKINSIDE_ACTIVATION_*, LOOKINSIDE_DEBUG_E2E_* or -# LOOKINSIDE_DEBUG_UI_SNAPSHOT_* string, the LKDebugE2EDump, -# LKDebugUISnapshots, LKDebugWindowImage or LookinSnapshotNormalizer +# LOOKINSIDE_DEBUG_UI_SNAPSHOT_* string, the DebugE2EDump, +# DebugUISnapshots, DebugWindowImage or SnapshotNormalizer Swift type # name, or a LookinServer # license test-hook # marker (test_setOverrideRootCertificateDER, sLKSTestOverrideRoot, @@ -34,7 +34,7 @@ # - release mode only: it holds no app bundle, or an outermost app bundle's # Contents/Info.plist has an empty or missing SUPublicEDKey or SUFeedURL. # Only the release workflows inject SPARKLE_PUBLIC_ED_KEY; a build without -# it silently skips the updater (LKAppMenuManager), so a release must +# it silently skips the updater (AppMenuManager), so a release must # never ship one. # Release mode (--release-strict) is the default. --local-build skips only # the Sparkle check, for local Release builds that have no updater key. @@ -84,8 +84,11 @@ mkdir -p "$SCRATCH_PARENT" || exit 2 WORK_DIR="$(mktemp -d "$SCRATCH_PARENT/check-host-release-clean.XXXXXX")" || exit 2 trap 'rm -rf "$WORK_DIR"' EXIT -MARKERS_RE='ActivationDebugOverrides|LOOKINSIDE_ACTIVATION_[A-Z0-9_]+|LOOKINSIDE_DEBUG_E2E_[A-Z0-9_]+|LOOKINSIDE_DEBUG_UI_SNAPSHOT_[A-Z0-9_]+|LKDebugE2EDump|LKDebugUISnapshots|LKDebugWindowImage|LookinSnapshotNormalizer|test_setOverrideRootCertificateDER|sLKSTestOverrideRoot|LOOKINSIDE_ENABLE_LICENSE_TEST_HOOKS' -SYMBOL_RE='ActivationDebugOverrides|LKDebugE2EDump|LKDebugUISnapshots|LKDebugWindowImage|LookinSnapshotNormalizer' +# The DebugE2E types have no Objective-C names, so they are matched by their +# Swift type names. Scripts/test-check-host-release-clean.sh fails when a type +# there is renamed and these lists are not. +MARKERS_RE='ActivationDebugOverrides|LOOKINSIDE_ACTIVATION_[A-Z0-9_]+|LOOKINSIDE_DEBUG_E2E_[A-Z0-9_]+|LOOKINSIDE_DEBUG_UI_SNAPSHOT_[A-Z0-9_]+|DebugE2EDump|DebugUISnapshots|DebugWindowImage|SnapshotNormalizer|test_setOverrideRootCertificateDER|sLKSTestOverrideRoot|LOOKINSIDE_ENABLE_LICENSE_TEST_HOOKS' +SYMBOL_RE='ActivationDebugOverrides|DebugE2EDump|DebugUISnapshots|DebugWindowImage|SnapshotNormalizer' findings=0 usage_errors=0 diff --git a/Scripts/test-check-host-release-clean.sh b/Scripts/test-check-host-release-clean.sh new file mode 100755 index 0000000..38c28db --- /dev/null +++ b/Scripts/test-check-host-release-clean.sh @@ -0,0 +1,119 @@ +#!/bin/bash +# Tests check-host-release-clean.sh with throwaway app bundles built for this +# run only. +# +# The DEBUG-only end-to-end dump and UI snapshots in LookInside/DebugE2E/ are +# Swift types without Objective-C names, so the check recognises them by their +# Swift type names. For every file there, a bundle whose binary declares that +# file's file-scope types must fail the check, with its symbol table and +# without it. Renaming one of those types without updating the check fails +# this test. A bundle declaring ordinary host types must pass. + +set -euo pipefail + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +check="$script_dir/check-host-release-clean.sh" +debug_sources="$script_dir/../LookInside/DebugE2E" + +work_dir="$(mktemp -d "${TMPDIR:-/tmp}/check-host-release-clean-test.XXXXXX")" +trap 'rm -rf "$work_dir"' EXIT + +failures=0 + +# The file-scope types a DebugE2E file declares. Its code sits inside +# `#if DEBUG`, one level in, so nested types are indented further. +file_scope_types() { + sed -n -E 's/^( {4})?((public|internal|fileprivate|private) )?(final )?(class|enum|struct|actor) ([A-Za-z_][A-Za-z0-9_]*).*/\6/p' "$1" | + grep -v -x -E 'func|var|let|override|static|final|public|internal|fileprivate|private' || true +} + +# Builds /LookInside.app around a binary of module LookInside, the +# host's module name, that declares the given types and uses them. +make_bundle() { + local bundle="$1" + shift + local source="$work_dir/$bundle.swift" + local executable="$work_dir/$bundle/LookInside.app/Contents/MacOS/LookInside" + mkdir -p "$(dirname "$executable")" + { + local type_name + for type_name in "$@"; do + echo "final class $type_name {}" + done + echo "print([$(printf '%s.self, ' "$@")] as [AnyObject.Type])" + } >"$source" + swiftc -module-name LookInside -module-cache-path "$work_dir/ModuleCache" "$source" -o "$executable" +} + +# Copies a bundle and strips its binary's symbol table, as a Release build does. +make_stripped_copy() { + local bundle="$1" copy="$2" + cp -R "$work_dir/$bundle" "$work_dir/$copy" + strip "$work_dir/$copy/LookInside.app/Contents/MacOS/LookInside" 2>/dev/null +} + +run_check() { + local status=0 + bash "$check" --local-build "$work_dir/$1" >"$work_dir/output" 2>&1 || status=$? + return "$status" +} + +expect_rejected() { + local label="$1" bundle="$2" + local status=0 + run_check "$bundle" || status=$? + if [ "$status" -ne 1 ]; then + echo "FAIL ($label): expected exit status 1 for DEBUG-only types, got $status" >&2 + cat "$work_dir/output" >&2 + failures=$((failures + 1)) + return + fi + echo "ok - $label" +} + +expect_clean() { + local label="$1" bundle="$2" + local status=0 + run_check "$bundle" || status=$? + if [ "$status" -ne 0 ]; then + echo "FAIL ($label): expected exit status 0, got $status" >&2 + cat "$work_dir/output" >&2 + failures=$((failures + 1)) + return + fi + echo "ok - $label" +} + +checked_files=0 +for source_file in "$debug_sources"/*.swift; do + file_name="$(basename "$source_file" .swift)" + types=() + while IFS= read -r type_name; do + types+=("$type_name") + done < <(file_scope_types "$source_file") + if [ "${#types[@]}" -eq 0 ]; then + echo "skip - $file_name.swift declares no file-scope type" + continue + fi + make_bundle "$file_name" "${types[@]}" + expect_rejected "$file_name.swift (${types[*]})" "$file_name" + make_stripped_copy "$file_name" "$file_name-stripped" + expect_rejected "$file_name.swift, stripped (${types[*]})" "$file_name-stripped" + checked_files=$((checked_files + 1)) +done + +if [ "$checked_files" -eq 0 ]; then + echo "FAIL: found no DebugE2E file to build a bundle from in $debug_sources" >&2 + failures=$((failures + 1)) +fi + +make_bundle "Ordinary" AppDelegate HierarchyDataSource StaticWindowController +expect_clean "ordinary host types" "Ordinary" +make_stripped_copy "Ordinary" "Ordinary-stripped" +expect_clean "ordinary host types, stripped" "Ordinary-stripped" + +if [ "$failures" -gt 0 ]; then + echo "$failures check-host-release-clean test(s) failed" >&2 + exit 1 +fi +echo "check-host-release-clean tests passed" diff --git a/Scripts/test-mcp-bridge.sh b/Scripts/test-mcp-bridge.sh index 5f290bd..87acb40 100755 --- a/Scripts/test-mcp-bridge.sh +++ b/Scripts/test-mcp-bridge.sh @@ -28,7 +28,7 @@ swiftc -parse-as-library "$SEARCH_QUERY" "$SEARCH_QUERY_TEST" -o "$TMPDIR/search "$TMPDIR/search-query-test" # The identifier list needs MCPBridgeFrame.swift alongside it for -# LKMCPBridgeJSONValue; that file is Foundation-only, so it compiles here. +# MCPBridgeJSONValue; that file is Foundation-only, so it compiles here. FRAME="$ROOT/LookInside/MCPBridge/MCPBridgeFrame.swift" OBJECT_IDENTIFIER_LIST="$ROOT/LookInside/MCPBridge/MCPBridgeObjectIdentifierList.swift" OBJECT_IDENTIFIER_LIST_TEST="$ROOT/Tests/MCPBridge/MCPBridgeObjectIdentifierListTests.swift" From 210e0e572298753bf8ce03f46bad1b5534a73e59 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Mon, 12 Oct 2026 08:34:05 +0800 Subject: [PATCH 2/5] Drop @objc from host members that only Swift code uses The Swift host kept the @objc of the Objective-C host it replaced, but most of those members are now called only from Swift, which calls them directly rather than through the Objective-C runtime. The attribute only added Objective-C metadata and a thunk per member. This removes it from 405 declarations in 86 files: methods, initializers, properties, and explicit selectors written as @objc(name:). Nine of the properties were also dynamic although nothing observes them with KVO or animates them, and lose that too. Ten enums are no longer @objc, and AppDelegate and DanceScriptManager give up their Objective-C names, which nothing looks up. @objc stays where the Objective-C runtime has to find the member: #selector targets, properties observed with KVO or animated through animator(), methods AppKit calls by name such as the window's drag-and-drop methods, optional requirements of @objc protocols, the types those declarations use, and the three document classes Info.plist names. In a Debug build, 489 @objc thunks go away, each for one of these members or an override that inherited @objc from it, and none is added. --- LookInside/About/AboutWindowController.swift | 2 +- LookInside/AppDelegate.swift | 1 - LookInside/Base/BaseControl.swift | 11 ++-- LookInside/Base/BaseView.swift | 2 +- .../AttributesGroup+LookinClient.swift | 1 - .../AutoLayoutConstraint+LookinClient.swift | 4 -- .../Category/DisplayItem+LookinClient.swift | 38 +++++------- .../InspectedObject+LookinClient.swift | 4 +- .../Category/NSAppearance+LookinClient.swift | 2 +- .../Base/Category/NSArray+LookinClient.swift | 2 +- .../Base/Category/NSButton+LookinClient.swift | 2 - .../Base/Category/NSColor+LookinClient.swift | 4 +- .../Category/NSControl+LookinClient.swift | 7 +-- .../Base/Category/NSString+LookinClient.swift | 5 +- .../Base/Category/NSView+LookinClient.swift | 5 +- LookInside/Base/ColorIndicatorLayer.swift | 3 +- LookInside/Base/ConnectionTipsBinding.swift | 1 - LookInside/Base/DeviceIconProvider.swift | 7 +-- LookInside/Base/FrameLayout.swift | 59 +++++++++---------- LookInside/Base/ImageTextView.swift | 4 +- .../Base/Message/MessageAttribute.swift | 4 -- LookInside/Base/NumberInputView.swift | 9 ++- LookInside/Base/OutlineRowView.swift | 2 +- LookInside/Base/PerformanceReporter.swift | 8 +-- LookInside/Base/PopPanel.swift | 1 - LookInside/Base/ProgressIndicatorView.swift | 5 +- LookInside/Base/SplitView.swift | 2 +- LookInside/Base/SwiftDemangler.swift | 4 +- LookInside/Base/TableView.swift | 17 +++--- ...ableViewHorizontalScrollWidthManager.swift | 5 +- LookInside/Base/TextControl.swift | 10 ++-- LookInside/Base/TextFieldView.swift | 14 ++--- LookInside/Base/TextLabel.swift | 4 +- LookInside/Base/TextsMenuView.swift | 15 +++-- LookInside/Base/TipsView.swift | 29 ++++----- LookInside/Base/TwoColors.swift | 7 +-- LookInside/Connection/AppsManager.swift | 1 - LookInside/Connection/ConnectionManager.swift | 1 - LookInside/Connection/InspectableApp.swift | 7 +-- LookInside/Connection/LiveDocument.swift | 5 +- .../Connection/LiveDocumentController.swift | 3 - .../Console/ConsoleViewController.swift | 6 +- .../DanceScript/DanceScriptManager.swift | 4 +- .../DashboardAttributeNumberInputView.swift | 2 +- .../DashboardAttributeView.swift | 13 ++-- .../JSON/JSONAttributeViews.swift | 3 +- .../Dashboard/DashboardViewController.swift | 12 ++-- LookInside/Dashboard/EnumListRegistry.swift | 9 +-- LookInside/DebugE2E/DebugE2EDumpWriter.swift | 4 -- LookInside/DebugE2E/DebugUISnapshots.swift | 5 +- LookInside/DebugE2E/DebugWindowImage.swift | 1 - LookInside/Export/ExportAccessoryView.swift | 2 +- LookInside/Export/ExportManager.swift | 4 +- .../GestureDebugWindowController.swift | 1 - .../HierarchyDataSource+KeyDown.swift | 1 - .../Hierarchy/HierarchyDataSource.swift | 46 +++++---------- LookInside/Hierarchy/HierarchyView.swift | 1 - .../Device/DeviceInjectionFlow.swift | 4 +- LookInside/Injection/InjectionFlow.swift | 4 +- LookInside/Launch/LaunchAppView.swift | 4 +- LookInside/Launch/LaunchViewController.swift | 2 - .../Launch/LaunchWindowController.swift | 5 +- LookInside/MCPBridge/MCPBridgeServer.swift | 6 +- LookInside/Manager/NavigationManager.swift | 18 +++--- LookInside/Manager/PreferenceManager.swift | 49 ++++++++------- LookInside/Measure/MeasureController.swift | 1 - LookInside/Other/VersionComparer.swift | 2 - .../PrivateDiscriminatorStore.swift | 33 ++++------- LookInside/Read/ArchiveDocument.swift | 3 +- LookInside/Read/ReadHierarchyDataSource.swift | 1 - LookInside/Read/ReadViewController.swift | 5 +- LookInside/Read/ReadWindowController.swift | 3 +- LookInside/Static/MessageManager.swift | 8 +-- .../Static/Preview/PreviewController.swift | 5 +- .../Static/StaticAsyncUpdateManager.swift | 15 ++--- LookInside/Static/StaticConstants.swift | 2 +- .../Static/StaticHierarchyDataSource.swift | 6 +- LookInside/Static/StaticViewController.swift | 11 ++-- .../Static/StaticWindowController.swift | 8 +-- .../SwiftUIHierarchyDisplayMode.swift | 2 +- .../SwiftUISupportGatekeeper.swift | 20 +------ .../MenuPopoverAppsListController.swift | 5 +- .../MenuPopoverSettingController.swift | 1 - LookInside/Toolbar/ToolbarRules.swift | 2 +- .../Toolbar/WindowToolbarAppButton.swift | 2 +- LookInside/Toolbar/WindowToolbarHelper.swift | 3 - 86 files changed, 256 insertions(+), 405 deletions(-) diff --git a/LookInside/About/AboutWindowController.swift b/LookInside/About/AboutWindowController.swift index ae3401b..1b67979 100644 --- a/LookInside/About/AboutWindowController.swift +++ b/LookInside/About/AboutWindowController.swift @@ -9,7 +9,7 @@ import AppKit final class AboutWindowController: WindowController { - @objc init() { + init() { let window = AppWindow(contentRect: NSRect(x: 0, y: 0, width: 480, height: 460), styleMask: [.titled, .closable, .fullSizeContentView], backing: .buffered, defer: true) diff --git a/LookInside/AppDelegate.swift b/LookInside/AppDelegate.swift index 3204d7f..24f4184 100644 --- a/LookInside/AppDelegate.swift +++ b/LookInside/AppDelegate.swift @@ -11,7 +11,6 @@ import FoundationToolbox @Loggable(subsystem: "com.lookinside.app") @main -@objc(AppDelegate) @MainActor final class AppDelegate: NSObject, NSApplicationDelegate { private var appearanceObservation: NSKeyValueObservation? diff --git a/LookInside/Base/BaseControl.swift b/LookInside/Base/BaseControl.swift index c31962e..40a0cfb 100644 --- a/LookInside/Base/BaseControl.swift +++ b/LookInside/Base/BaseControl.swift @@ -9,11 +9,11 @@ import AppKit class BaseControl: NSControl { - @objc var clickAction: Selector? + var clickAction: Selector? - @objc var adjustAlphaWhenClick = false + var adjustAlphaWhenClick = false - @objc var didChangeAppearance: ((BaseControl?, Bool) -> Void)? { + var didChangeAppearance: ((BaseControl?, Bool) -> Void)? { didSet { triggerDidChangeAppearanceBlock() } @@ -47,13 +47,12 @@ class BaseControl: NSControl { } } - @objc(addTarget:clickAction:) func addTarget(_ target: AnyObject?, clickAction action: Selector?) { self.target = target clickAction = action } - @objc func triggerClickAction() { + func triggerClickAction() { if let clickAction, target != nil { sendAction(clickAction, to: target) } @@ -74,7 +73,7 @@ class BaseControl: NSControl { } /// 如果子类返回 true,则 mouseEntered: 和 mouseExited: 会被调用。默认为 false - @objc func shouldTrackMouseEnteredAndExited() -> Bool { + func shouldTrackMouseEnteredAndExited() -> Bool { false } diff --git a/LookInside/Base/BaseView.swift b/LookInside/Base/BaseView.swift index 60df43f..7982266 100644 --- a/LookInside/Base/BaseView.swift +++ b/LookInside/Base/BaseView.swift @@ -10,7 +10,7 @@ import AppKit private let effectiveAppearanceContext = UnsafeMutableRawPointer.allocate(byteCount: 1, alignment: 1) -@objc enum ViewBorderPosition: Int { +enum ViewBorderPosition: Int { case none case top case left diff --git a/LookInside/Base/Category/AttributesGroup+LookinClient.swift b/LookInside/Base/Category/AttributesGroup+LookinClient.swift index 470a4c4..52a78f1 100644 --- a/LookInside/Base/Category/AttributesGroup+LookinClient.swift +++ b/LookInside/Base/Category/AttributesGroup+LookinClient.swift @@ -9,7 +9,6 @@ extension AttributesGroup { /// The group's card title. `isMacTarget` describes the inspected app, /// not the Host; it only changes the ViewLayer group, whose title names /// the view class (`NSView` vs `UIView`), and the layout guide group. - @objc(queryDisplayTitleForMacTarget:) func queryDisplayTitle(forMacTarget isMacTarget: Bool) -> String { if let userCustomTitle, !userCustomTitle.isEmpty { return userCustomTitle diff --git a/LookInside/Base/Category/AutoLayoutConstraint+LookinClient.swift b/LookInside/Base/Category/AutoLayoutConstraint+LookinClient.swift index 50ddec8..d7e9eca 100644 --- a/LookInside/Base/Category/AutoLayoutConstraint+LookinClient.swift +++ b/LookInside/Base/Category/AutoLayoutConstraint+LookinClient.swift @@ -11,7 +11,6 @@ import AppKit import LookInsideHostCore extension AutoLayoutConstraint { - @objc(descriptionWithItemObject:type:detailed:) static func description(withItemObject object: InspectedObject?, type: LookinConstraintItemType, detailed: Bool) -> String { switch type { case .`nil`: @@ -32,7 +31,6 @@ extension AutoLayoutConstraint { } } - @objc(descriptionWithAttributeInt:) static func description(withAttributeInt attribute: Int) -> String { if let name = ClientDisplayText.layoutAttributeName(attribute) { return name @@ -41,7 +39,6 @@ extension AutoLayoutConstraint { return "unknownAttr(\(attribute))" } - @objc(symbolWithRelation:) static func symbol(with relation: NSLayoutConstraint.Relation) -> String { if let symbol = ClientDisplayText.layoutRelationSymbol(relation.rawValue) { return symbol @@ -50,7 +47,6 @@ extension AutoLayoutConstraint { return "?" } - @objc(descriptionWithRelation:) static func description(with relation: NSLayoutConstraint.Relation) -> String { if let name = ClientDisplayText.layoutRelationName(relation.rawValue) { return name diff --git a/LookInside/Base/Category/DisplayItem+LookinClient.swift b/LookInside/Base/Category/DisplayItem+LookinClient.swift index 3174f96..c24fdb9 100644 --- a/LookInside/Base/Category/DisplayItem+LookinClient.swift +++ b/LookInside/Base/Category/DisplayItem+LookinClient.swift @@ -14,7 +14,7 @@ import LookInsideHostCore extension DisplayItem { /// The row text in the hierarchy, usually the class name without module. - @objc func title() -> String { + func title() -> String { let baseTitle: String? if let customInfo { baseTitle = customInfo.title @@ -28,7 +28,7 @@ extension DisplayItem { /// The owning window or view controller, a special trace, or the ivar /// names that point at the object; nil when there is none. - @objc func subtitle() -> String? { + func subtitle() -> String? { if let customInfo { return customInfo.subtitle } @@ -61,7 +61,6 @@ extension DisplayItem { return groupScreenshot } - @objc(bestObjectOidPreferView:) func bestObjectOidPreferView(_: Bool) -> UInt { switch resolvedNodeKind() { case .window, .windowScene: @@ -99,7 +98,7 @@ extension DisplayItem { /// layer oid (backing-layer toggle ON); requests for this node must then /// route by the view oid. Read off the tree rather than the preference, /// so it stays false against servers that ignored the toggle. - @objc func ownsSeparateBackingLayerNode() -> Bool { + func ownsSeparateBackingLayerNode() -> Bool { guard let layerOid = layerObject?.oid, layerOid != 0, let viewOid = viewObject?.oid, viewOid != 0 else { return false } @@ -119,7 +118,6 @@ extension DisplayItem { } /// The preferred oid first, then every other oid this node carries. - @objc(availableObjectOidsPreferView:) func availableObjectOidsPreferView(_ preferView: Bool) -> [NSNumber] { var oids: [UInt] = [] func add(_ oid: UInt?) { @@ -135,24 +133,24 @@ extension DisplayItem { } /// Whether the title starts like a system class ("UI", "CA", "NS", "_"). - @objc var representedForSystemClass: Bool { + var representedForSystemClass: Bool { let title = title() return title.hasPrefix("UI") || title.hasPrefix("CA") || title.hasPrefix("_") || title.hasPrefix("NS") } - @objc func isUserCustom() -> Bool { + func isUserCustom() -> Bool { customInfo != nil } /// Whether the preview can draw a box for this node. - @objc func hasPreviewBoxAbility() -> Bool { + func hasPreviewBoxAbility() -> Bool { guard let customInfo else { return true } return customInfo.hasValidFrame() } /// Whether an expanded node shows its group screenshot in the preview /// because its solo screenshot is (nearly) empty. - @objc func usesGroupScreenshotFallbackInPreview() -> Bool { + func usesGroupScreenshotFallbackInPreview() -> Bool { guard isExpandable, isExpanded else { return false } // A window root's solo screenshot is always nearly transparent (just // the frame) — its children draw the content. Falling back here @@ -170,7 +168,7 @@ extension DisplayItem { ) } - @objc func hasAncestorUsingGroupScreenshotFallbackInPreview() -> Bool { + func hasAncestorUsingGroupScreenshotFallbackInPreview() -> Bool { var found = false enumerateAncestors { item, stop in if item.usesGroupScreenshotFallbackInPreview() { @@ -183,7 +181,6 @@ extension DisplayItem { /// Whether the node matches a non-empty hierarchy filter: its title, /// subtitle or one of its objects' addresses contains the text. - @objc(isMatchedWithSearchString:) func isMatched(withSearch string: String) -> Bool { if string.isEmpty { assertionFailure("an empty search string matches nothing") @@ -207,7 +204,6 @@ extension DisplayItem { } /// Visits this item, then each ancestor up to the root, until stopped. - @objc(enumerateSelfAndAncestors:) func enumerateSelfAndAncestors(_ block: (DisplayItem, UnsafeMutablePointer) -> Void) { var item: DisplayItem? = self while let current = item { @@ -220,13 +216,11 @@ extension DisplayItem { } } - @objc(enumerateAncestors:) func enumerateAncestors(_ block: (DisplayItem, UnsafeMutablePointer) -> Void) { self.`super`?.enumerateSelfAndAncestors(block) } /// Visits this item and then its whole subtree, depth first. - @objc(enumerateSelfAndChildren:) func enumerateSelfAndChildren(_ block: (DisplayItem) -> Void) { block(self) for subitem in subitems ?? [] { @@ -234,14 +228,12 @@ extension DisplayItem { } } - @objc(itemIsKindOfClassWithName:) func itemIsKindOfClass(withName className: String) -> Bool { itemIsKindOfClasses(withNames: [className]) } /// Whether any class in the displayed object's class chain, without its /// module prefix, is one of `classNames`. - @objc(itemIsKindOfClassesWithNames:) func itemIsKindOfClasses(withNames classNames: Set) -> Bool { guard !classNames.isEmpty, let object = displayingObject() else { return false } let chain = object.classChainList ?? [] @@ -252,7 +244,7 @@ extension DisplayItem { // MARK: - SwiftUI - @objc func isSwiftUISupportRelated() -> Bool { + func isSwiftUISupportRelated() -> Bool { func looksLikeSwiftUISupport(_ object: InspectedObject?) -> Bool { guard let object else { return false } if ClientDisplayText.looksLikeSwiftUISupport(object.rawClassName()) { @@ -270,7 +262,7 @@ extension DisplayItem { /// Addresses of the layers named by "… Backed By" rows in the /// "SwiftUI Layers" group. - @objc func swiftUIBackingLayerMemoryAddresses() -> [String] { + func swiftUIBackingLayerMemoryAddresses() -> [String] { var addresses: [String] = [] for group in allAttributeGroups where group.userCustomTitle == "SwiftUI Layers" { for attribute in group.allAttributes { @@ -287,7 +279,7 @@ extension DisplayItem { /// Display-list IDs from "… Display List ID" rows of "SwiftUI Layers" /// and "Identity IDs" rows of "SwiftUI Display List". - @objc func swiftUIBackingDisplayListIDs() -> [NSNumber] { + func swiftUIBackingDisplayListIDs() -> [NSNumber] { var displayListIDs: [UInt64] = [] for group in allAttributeGroups { let isLayersGroup = group.userCustomTitle == "SwiftUI Layers" @@ -308,7 +300,7 @@ extension DisplayItem { } /// The first "Display List ID" of the "SwiftUI" group, for layer nodes. - @objc func swiftUILayerDisplayListID() -> NSNumber? { + func swiftUILayerDisplayListID() -> NSNumber? { guard layerObject != nil else { return nil } for group in allAttributeGroups where group.userCustomTitle == "SwiftUI" { for attribute in group.allAttributes { @@ -322,7 +314,7 @@ extension DisplayItem { } /// "Source" and "Full Source" values of the "SwiftUI" group. - @objc func swiftUILayerSourceTypeNames() -> [String] { + func swiftUILayerSourceTypeNames() -> [String] { var sourceNames: [String] = [] for group in allAttributeGroups where group.userCustomTitle == "SwiftUI" { for attribute in group.allAttributes { @@ -337,7 +329,7 @@ extension DisplayItem { /// The custom node's title, then "Type" and "Full Type" values of the /// "SwiftUI Type" group. - @objc func swiftUITypeNames() -> [String] { + func swiftUITypeNames() -> [String] { var typeNames: [String] = [] if let customTitle = customInfo?.title { typeNames.append(customTitle) @@ -353,12 +345,10 @@ extension DisplayItem { return typeNames } - @objc(lk_validSwiftUIDisplayListIDsInString:) static func validSwiftUIDisplayListIDs(in string: String) -> [NSNumber] { ClientDisplayText.swiftUIDisplayListIDs(in: string).map { NSNumber(value: $0) } } - @objc(lk_memoryAddressInObjectDescription:) static func memoryAddress(inObjectDescription description: String) -> String? { ClientDisplayText.memoryAddress(inObjectDescription: description) } diff --git a/LookInside/Base/Category/InspectedObject+LookinClient.swift b/LookInside/Base/Category/InspectedObject+LookinClient.swift index edce78b..960776b 100644 --- a/LookInside/Base/Category/InspectedObject+LookinClient.swift +++ b/LookInside/Base/Category/InspectedObject+LookinClient.swift @@ -8,13 +8,13 @@ import LookInsideHostCore extension InspectedObject { /// The demangled class name, module prefix included. - @objc func completedDemangledClassName() -> String { + func completedDemangledClassName() -> String { SwiftDemangler.completedParse(input: rawClassName() ?? "") } /// The demangled class name without its module prefix; generic /// arguments keep theirs. - @objc func simpleDemangledClassName() -> String { + func simpleDemangledClassName() -> String { ClientDisplayText.removingModulePrefix(SwiftDemangler.simpleParse(input: rawClassName() ?? "")) } } diff --git a/LookInside/Base/Category/NSAppearance+LookinClient.swift b/LookInside/Base/Category/NSAppearance+LookinClient.swift index 3fb1cf6..fb1aef1 100644 --- a/LookInside/Base/Category/NSAppearance+LookinClient.swift +++ b/LookInside/Base/Category/NSAppearance+LookinClient.swift @@ -8,7 +8,7 @@ import AppKit extension NSAppearance { /// Whether this is one of the dark appearances, including the vibrant /// and high-contrast variants. - @objc var isDarkMode: Bool { + var isDarkMode: Bool { [.darkAqua, .vibrantDark, .accessibilityHighContrastDarkAqua, .accessibilityHighContrastVibrantDark].contains(name) } } diff --git a/LookInside/Base/Category/NSArray+LookinClient.swift b/LookInside/Base/Category/NSArray+LookinClient.swift index 6d3ff62..da029c8 100644 --- a/LookInside/Base/Category/NSArray+LookinClient.swift +++ b/LookInside/Base/Category/NSArray+LookinClient.swift @@ -7,7 +7,7 @@ import AppKit extension NSArray { /// The views that are not hidden. Every element must be an NSView. - @objc func visibleViews() -> [Any] { + func visibleViews() -> [Any] { filter { object in guard let view = object as? NSView else { assertionFailure("visibleViews expects views only") diff --git a/LookInside/Base/Category/NSButton+LookinClient.swift b/LookInside/Base/Category/NSButton+LookinClient.swift index c0f03bf..9c1ae81 100644 --- a/LookInside/Base/Category/NSButton+LookinClient.swift +++ b/LookInside/Base/Category/NSButton+LookinClient.swift @@ -7,7 +7,6 @@ import AppKit extension NSButton { /// A rounded push button, already sized 84×40. - @objc(lk_normalButtonWithTitle:target:action:) static func normalButton(withTitle title: String, target: AnyObject?, action: Selector?) -> NSButton { let button = NSButton() button.bezelStyle = .rounded @@ -20,7 +19,6 @@ extension NSButton { } /// A borderless image-only button; size it yourself. - @objc(lk_buttonWithImage:target:action:) static func borderlessImageButton(with image: NSImage?, target: AnyObject?, action: Selector?) -> NSButton { let button = NSButton() button.image = image diff --git a/LookInside/Base/Category/NSColor+LookinClient.swift b/LookInside/Base/Category/NSColor+LookinClient.swift index 3a09e77..8f45f38 100644 --- a/LookInside/Base/Category/NSColor+LookinClient.swift +++ b/LookInside/Base/Category/NSColor+LookinClient.swift @@ -15,13 +15,13 @@ extension NSColor { } /// "(15, 17, 19)" when opaque, "(15, 17, 19, 0.50)" otherwise. - @objc func rgbaString() -> String { + func rgbaString() -> String { let rgba = sRGBComponentValues return ClientDisplayText.rgbaString(red: rgba.red, green: rgba.green, blue: rgba.blue, alpha: rgba.alpha) } /// "#0f1113" when opaque, "#0f111380" otherwise. - @objc func hexString() -> String { + func hexString() -> String { let rgba = sRGBComponentValues return ClientDisplayText.hexString(red: rgba.red, green: rgba.green, blue: rgba.blue, alpha: rgba.alpha) } diff --git a/LookInside/Base/Category/NSControl+LookinClient.swift b/LookInside/Base/Category/NSControl+LookinClient.swift index c5e1ead..8b51ff1 100644 --- a/LookInside/Base/Category/NSControl+LookinClient.swift +++ b/LookInside/Base/Category/NSControl+LookinClient.swift @@ -8,20 +8,19 @@ import AppKit extension NSControl { private static let unlimitedSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: .greatestFiniteMagnitude) - @objc(heightForWidth:) func height(forWidth width: CGFloat) -> CGFloat { sizeThatFits(NSSize(width: width, height: .greatestFiniteMagnitude)).height } - @objc func bestHeight() -> CGFloat { + func bestHeight() -> CGFloat { sizeThatFits(Self.unlimitedSize).height } - @objc func bestWidth() -> CGFloat { + func bestWidth() -> CGFloat { sizeThatFits(Self.unlimitedSize).width } - @objc func bestSize() -> NSSize { + func bestSize() -> NSSize { sizeThatFits(Self.unlimitedSize) } } diff --git a/LookInside/Base/Category/NSString+LookinClient.swift b/LookInside/Base/Category/NSString+LookinClient.swift index 0917812..e055a04 100644 --- a/LookInside/Base/Category/NSString+LookinClient.swift +++ b/LookInside/Base/Category/NSString+LookinClient.swift @@ -8,24 +8,21 @@ import LookInsideHostCore extension NSString { /// The string with its first UTF-16 unit uppercased; nil when empty. - @objc func capitalizingFirstLetter() -> String? { + func capitalizingFirstLetter() -> String? { guard length > 0 else { return nil } return (substring(to: 1) as NSString).uppercased + substring(from: 1) } - @objc(scoreAgainst:) func score(against otherString: String) -> CGFloat { score(against: otherString, fuzziness: nil) } - @objc(scoreAgainst:fuzziness:) func score(against otherString: String, fuzziness: NSNumber?) -> CGFloat { score(against: otherString, fuzziness: fuzziness, options: StringScore.Options.none.rawValue) } /// Fuzzy match score of `otherString` (an abbreviation) against this /// string; `options` takes the NSStringScoreOption bits. - @objc(scoreAgainst:fuzziness:options:) func score(against otherString: String, fuzziness: NSNumber?, options: UInt) -> CGFloat { StringScore.score(self as String, against: otherString, fuzziness: fuzziness?.doubleValue, options: StringScore.Options(rawValue: options)) } diff --git a/LookInside/Base/Category/NSView+LookinClient.swift b/LookInside/Base/Category/NSView+LookinClient.swift index 2e0c633..c512491 100644 --- a/LookInside/Base/Category/NSView+LookinClient.swift +++ b/LookInside/Base/Category/NSView+LookinClient.swift @@ -15,7 +15,7 @@ extension NSView { /// An asset catalog colour name painted onto the layer's background; nil /// clears it. - @objc var backgroundColorName: String? { + var backgroundColorName: String? { get { objc_getAssociatedObject(self, &backgroundColorNameKey) as? String } @@ -30,7 +30,6 @@ extension NSView { } /// Adds `view` below every existing subview. - @objc(lk_insertSubviewAtBottom:) func insertSubviewAtBottom(_ view: NSView) { if let first = subviews.first { addSubview(view, positioned: .below, relativeTo: first) @@ -39,7 +38,7 @@ extension NSView { } } - @objc func showDebugBorder() { + func showDebugBorder() { layer?.borderWidth = 1 layer?.borderColor = NSColor.white.cgColor } diff --git a/LookInside/Base/ColorIndicatorLayer.swift b/LookInside/Base/ColorIndicatorLayer.swift index 736198f..ef614d9 100644 --- a/LookInside/Base/ColorIndicatorLayer.swift +++ b/LookInside/Base/ColorIndicatorLayer.swift @@ -12,7 +12,7 @@ class ColorIndicatorLayer: CALayer { private var imageLayer: CALayer? /// Defaults to black. - @objc var color: NSColor? = .rgb255(0, 0, 0) { + var color: NSColor? = .rgb255(0, 0, 0) { didSet { updateForColor() } } @@ -82,7 +82,6 @@ class ColorIndicatorLayer: CALayer { private static let renderingLayer = ColorIndicatorLayer() /// The swatch drawn into an image of `shapeSize` plus `insets`. - @objc(imageWithColor:shapeSize:insets:) static func image(with color: NSColor?, shapeSize: NSSize, insets: NSEdgeInsets) -> NSImage { let layer = renderingLayer let image = NSImage(size: NSSize(width: shapeSize.width + insets.left + insets.right, height: shapeSize.height + insets.top + insets.bottom)) diff --git a/LookInside/Base/ConnectionTipsBinding.swift b/LookInside/Base/ConnectionTipsBinding.swift index 50f1070..79ba32c 100644 --- a/LookInside/Base/ConnectionTipsBinding.swift +++ b/LookInside/Base/ConnectionTipsBinding.swift @@ -17,7 +17,6 @@ final class ConnectionTipsBinding: NSObject { /// Starts observing right away and applies the current state. Holds the /// tips view and view controller weakly; observation stops when the /// binding is released. - @objc(initWithDocument:tipsView:viewController:) init(document: LiveDocument, tipsView: RedTipsView, viewController: NSViewController) { super.init() observations = [ diff --git a/LookInside/Base/DeviceIconProvider.swift b/LookInside/Base/DeviceIconProvider.swift index 658572c..14df166 100644 --- a/LookInside/Base/DeviceIconProvider.swift +++ b/LookInside/Base/DeviceIconProvider.swift @@ -16,14 +16,14 @@ import UIFoundationToolbox final class DeviceIconProvider: NSObject { /// Height of the device icon in the window toolbar, matching the legacy /// `icon_*_small` assets (32 pixels at 2x). - @objc static let toolbarPointSize: CGFloat = 16 + static let toolbarPointSize: CGFloat = 16 /// Height of the device icon in a tips view, whose image view has a fixed 18×18 frame. - @objc static let tipsPointSize: CGFloat = 18 + static let tipsPointSize: CGFloat = 18 /// Height of the device icon on a launch app card, matching the legacy `icon_*_big` /// assets (72 pixels at 2x). - @objc static let launchPointSize: CGFloat = 36 + static let launchPointSize: CGFloat = 36 /// The fraction of its square canvas that CoreTypes device artwork actually fills; /// the rest is transparent padding. Measured from the shipped icns at several sizes. @@ -35,7 +35,6 @@ final class DeviceIconProvider: NSObject { /// - Parameters: /// - appInfo: The inspected app's info, as reported by LookinServer. /// - pointSize: The height the icon is laid out at, in points. - @objc(deviceIconForAppInfo:pointSize:) static func deviceIcon(forAppInfo appInfo: InspectedAppInfo?, pointSize: CGFloat) -> NSImage? { guard let appInfo, let modelIdentifier = appInfo.deviceModelIdentifier, diff --git a/LookInside/Base/FrameLayout.swift b/LookInside/Base/FrameLayout.swift index 9540c2b..3f9fa00 100644 --- a/LookInside/Base/FrameLayout.swift +++ b/LookInside/Base/FrameLayout.swift @@ -90,20 +90,17 @@ final class FrameLayout: NSObject { } /// ObjC entry point: `[LKFrameLayout of:view]`. - @objc(of:) static func of(_ object: AnyObject?) -> FrameLayout { FrameLayout([object]) } /// ObjC entry point for several views or layers at once. - @objc(ofAll:) static func ofAll(_ objects: [AnyObject]) -> FrameLayout { FrameLayout(objects) } /// Rounds up to the main screen's pixel grid. `CGFLOAT_MIN` and /// `CGFLOAT_MAX` pass through because callers use them as markers. - @objc static func snapToPixel(_ value: CGFloat) -> CGFloat { if value == .leastNormalMagnitude || value == .greatestFiniteMagnitude { return value @@ -128,7 +125,7 @@ final class FrameLayout: NSObject { /// Sizes an BaseView or NSControl to `sizeThatFits:` of an unlimited /// size; other views and layers are left alone. - @objc @discardableResult + @discardableResult func sizeToFit() -> FrameLayout { each { target in guard case let .view(view) = target else { return } @@ -155,7 +152,7 @@ final class FrameLayout: NSObject { /// Sets an BaseView's or NSControl's height to what it needs at its /// current width. - @objc @discardableResult + @discardableResult func heightToFit() -> FrameLayout { each { target in guard case let .view(view) = target else { return } @@ -174,27 +171,27 @@ final class FrameLayout: NSObject { return self } - @objc(width:) @discardableResult + @discardableResult func width(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { $0.frame.size.width = Self.snapToPixel(value) } return self } - @objc(height:) @discardableResult + @discardableResult func height(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { $0.frame.size.height = Self.snapToPixel(value) } return self } - @objc(size:) @discardableResult + @discardableResult func size(_ value: NSSize) -> FrameLayout { width(value.width).height(value.height) } /// Grows the width to `value` when it is narrower; never snaps. - @objc(minWidth:) @discardableResult + @discardableResult func minWidth(_ value: CGFloat) -> FrameLayout { if value.isNaN { assertionFailure("NaN passed to minWidth") @@ -210,26 +207,26 @@ final class FrameLayout: NSObject { // MARK: - Position - @objc(x:) @discardableResult + @discardableResult func x(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { $0.frame.origin.x = Self.snapToPixel(value) } return self } - @objc(y:) @discardableResult + @discardableResult func y(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { $0.frame.origin.y = Self.snapToPixel(value) } return self } - @objc(offsetX:) @discardableResult + @discardableResult func offsetX(_ value: CGFloat) -> FrameLayout { offset(x: value, y: 0) } - @objc(offsetY:) @discardableResult + @discardableResult func offsetY(_ value: CGFloat) -> FrameLayout { offset(x: 0, y: value) } @@ -246,28 +243,28 @@ final class FrameLayout: NSObject { return self } - @objc(midX:) @discardableResult + @discardableResult func midX(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { FrameLayout(targets: [$0]).x(value - $0.bounds.width / 2) } return self } - @objc(maxX:) @discardableResult + @discardableResult func maxX(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { FrameLayout(targets: [$0]).x(value - $0.bounds.width) } return self } - @objc(midY:) @discardableResult + @discardableResult func midY(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { FrameLayout(targets: [$0]).y(value - $0.bounds.height / 2) } return self } - @objc(maxY:) @discardableResult + @discardableResult func maxY(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { FrameLayout(targets: [$0]).y(value - $0.bounds.height) } @@ -275,7 +272,7 @@ final class FrameLayout: NSObject { } /// Puts the right edge `value` points from the parent's right edge. - @objc(right:) @discardableResult + @discardableResult func right(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { target in @@ -290,7 +287,7 @@ final class FrameLayout: NSObject { /// Puts the bottom edge `value` points from the parent's bottom edge, /// in the parent's own (possibly unflipped) coordinates. - @objc(bottom:) @discardableResult + @discardableResult func bottom(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { target in @@ -307,7 +304,7 @@ final class FrameLayout: NSObject { return self } - @objc @discardableResult + @discardableResult func horAlign() -> FrameLayout { each { target in guard let parentBounds = target.parentBounds else { @@ -319,7 +316,7 @@ final class FrameLayout: NSObject { return self } - @objc @discardableResult + @discardableResult func verAlign() -> FrameLayout { each { target in guard let parentBounds = target.parentBounds else { @@ -333,12 +330,12 @@ final class FrameLayout: NSObject { // MARK: - Filling the parent - @objc @discardableResult + @discardableResult func fullWidth() -> FrameLayout { x(0).toRight(0) } - @objc @discardableResult + @discardableResult func fullHeight() -> FrameLayout { each { target in guard let parentBounds = target.parentBounds else { @@ -350,14 +347,14 @@ final class FrameLayout: NSObject { return self } - @objc @discardableResult + @discardableResult func fullFrame() -> FrameLayout { fullWidth().fullHeight() } /// Stretches the width so the right edge sits `value` points from the /// parent's right edge; never negative. - @objc(toRight:) @discardableResult + @discardableResult func toRight(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { target in @@ -371,7 +368,7 @@ final class FrameLayout: NSObject { /// Stretches the width so the right edge sits at `value`, never past the /// left edge. - @objc(toMaxX:) @discardableResult + @discardableResult func toMaxX(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { target in @@ -385,7 +382,7 @@ final class FrameLayout: NSObject { /// Stretches the height so the bottom edge sits at `value`, never above /// the top edge. - @objc(toMaxY:) @discardableResult + @discardableResult func toMaxY(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { target in @@ -398,7 +395,7 @@ final class FrameLayout: NSObject { } /// Moves the top edge to `value` keeping the bottom edge in place. - @objc(toY:) @discardableResult + @discardableResult func toY(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { target in @@ -413,7 +410,7 @@ final class FrameLayout: NSObject { /// Stretches the height so the bottom edge sits `value` points from the /// parent's bottom edge. - @objc(toBottom:) @discardableResult + @discardableResult func toBottom(_ value: CGFloat) -> FrameLayout { let value = Self.checked(value) each { target in @@ -434,7 +431,7 @@ final class FrameLayout: NSObject { /// Keeps only the visible views and layers: not hidden, attached, and at /// least 1% opaque. - @objc @discardableResult + @discardableResult func visibles() -> FrameLayout { targets = targets.filter(\.isVisible) return self @@ -443,7 +440,7 @@ final class FrameLayout: NSObject { /// Centres the group's union horizontally in the shared parent, moving /// every member by the same amount. Does nothing when the members do /// not share a parent. - @objc @discardableResult + @discardableResult func groupHorAlign() -> FrameLayout { guard let first = targets.first, let parent = first.parent else { if !targets.isEmpty { diff --git a/LookInside/Base/ImageTextView.swift b/LookInside/Base/ImageTextView.swift index ccc34ce..68ae4c9 100644 --- a/LookInside/Base/ImageTextView.swift +++ b/LookInside/Base/ImageTextView.swift @@ -7,8 +7,8 @@ import AppKit /// An image followed by a one-line label, centred vertically. class ImageTextView: BaseView { - @objc let imageView = NSImageView() - @objc let label = TextLabel() + let imageView = NSImageView() + let label = TextLabel() var imageMargins = HorizontalMargins(left: 0, right: 0) diff --git a/LookInside/Base/Message/MessageAttribute.swift b/LookInside/Base/Message/MessageAttribute.swift index dfb3338..27b31b2 100644 --- a/LookInside/Base/Message/MessageAttribute.swift +++ b/LookInside/Base/Message/MessageAttribute.swift @@ -77,7 +77,6 @@ class MessageAttribute: NSObject { currentValue = value } - @objc(attributeWithValue:) class func attribute(withValue value: Any?) -> MessageAttribute { MessageAttribute(value: value) } @@ -137,7 +136,6 @@ final class DoubleMessageAttribute: MessageAttribute { self.init(value: NSNumber(value: value)) } - @objc(attributeWithDouble:) class func attribute(withDouble value: Double) -> DoubleMessageAttribute { DoubleMessageAttribute(double: value) } @@ -156,7 +154,6 @@ final class IntegerMessageAttribute: MessageAttribute { self.init(value: NSNumber(value: value)) } - @objc(attributeWithInteger:) class func attribute(withInteger value: Int) -> IntegerMessageAttribute { IntegerMessageAttribute(integer: value) } @@ -175,7 +172,6 @@ final class BoolMessageAttribute: MessageAttribute { self.init(value: NSNumber(value: value)) } - @objc(attributeWithBOOL:) class func attribute(withBOOL value: Bool) -> BoolMessageAttribute { BoolMessageAttribute(bool: value) } diff --git a/LookInside/Base/NumberInputView.swift b/LookInside/Base/NumberInputView.swift index 113c1fa..5e49312 100644 --- a/LookInside/Base/NumberInputView.swift +++ b/LookInside/Base/NumberInputView.swift @@ -11,7 +11,7 @@ extension NumberInputView { static let verticalHeight: CGFloat = 38 } -@objc enum NumberInputViewStyle: UInt { +enum NumberInputViewStyle: UInt { /// The title sits inside the field, on its right. case horizontal /// The title sits under the field. @@ -20,14 +20,14 @@ extension NumberInputView { /// A Dashboard number field with a short title. class NumberInputView: BaseView { - @objc let textFieldView = TextFieldView() + let textFieldView = TextFieldView() private let titleLabel = TextLabel() - @objc var viewStyle: NumberInputViewStyle = .horizontal { + var viewStyle: NumberInputViewStyle = .horizontal { didSet { applyViewStyle() } } - @objc var title: String? { + var title: String? { didSet { titleLabel.stringValue = title ?? "" // The title can be truncated or abbreviated to a single letter, @@ -116,7 +116,6 @@ class NumberInputView: BaseView { /// The value `string` parses to for `attrType` (an integer or a floating /// point type), or nil when it does not parse. - @objc(parsedValueWithString:attrType:) static func parsedValue(with string: String, attrType: LookinAttrType) -> Any? { switch attrType { case .int, .long, .longLong: diff --git a/LookInside/Base/OutlineRowView.swift b/LookInside/Base/OutlineRowView.swift index 0f6c272..b2d8991 100644 --- a/LookInside/Base/OutlineRowView.swift +++ b/LookInside/Base/OutlineRowView.swift @@ -11,7 +11,7 @@ import AppKit private let indentUnitWidth: CGFloat = 14 private let disclosureWidth: CGFloat = 16 -@objc enum OutlineRowViewStatus: UInt { +enum OutlineRowViewStatus: UInt { case notExpandable case expanded case collapsed diff --git a/LookInside/Base/PerformanceReporter.swift b/LookInside/Base/PerformanceReporter.swift index bf2718e..6de6500 100644 --- a/LookInside/Base/PerformanceReporter.swift +++ b/LookInside/Base/PerformanceReporter.swift @@ -10,20 +10,20 @@ import QuartzCore final class PerformanceReporter: NSObject { private static let shared = PerformanceReporter() - @objc static func sharedInstance() -> PerformanceReporter { + static func sharedInstance() -> PerformanceReporter { shared } private var reloadStartTime: CFTimeInterval = 0 private var hierarchyFetchedTime: CFTimeInterval = 0 - @objc func willStartReload() { + func willStartReload() { reloadStartTime = CACurrentMediaTime() } - @objc func didFetchHierarchy() { + func didFetchHierarchy() { hierarchyFetchedTime = CACurrentMediaTime() } - @objc func didComplete() {} + func didComplete() {} } diff --git a/LookInside/Base/PopPanel.swift b/LookInside/Base/PopPanel.swift index 5669860..6541c50 100644 --- a/LookInside/Base/PopPanel.swift +++ b/LookInside/Base/PopPanel.swift @@ -7,7 +7,6 @@ import AppKit /// A borderless rounded panel for small editors that pop up over a window. class PopPanel: NSPanel { - @objc(initWithSize:) init(size: NSSize) { // Without NSWindowStyleMaskNonactivatingPanel, clicking another app // while the panel is shown hides every LookInside window. diff --git a/LookInside/Base/ProgressIndicatorView.swift b/LookInside/Base/ProgressIndicatorView.swift index 4af63c3..67301a0 100644 --- a/LookInside/Base/ProgressIndicatorView.swift +++ b/LookInside/Base/ProgressIndicatorView.swift @@ -51,17 +51,15 @@ class ProgressIndicatorView: BaseView { return super.animation(forKey: key) } - @objc func resetToZero() { + func resetToZero() { progress = 0 } /// Animates over 0.5 seconds. - @objc(animateToProgress:) func animate(toProgress progress: CGFloat) { animate(toProgress: progress, duration: 0.5) } - @objc(animateToProgress:duration:) func animate(toProgress progress: CGFloat, duration: TimeInterval) { NSAnimationContext.runAnimationGroup { context in context.duration = duration @@ -71,7 +69,6 @@ class ProgressIndicatorView: BaseView { /// Fills the bar, calls `completion`, then empties the bar 0.2 seconds /// later. - @objc(finishWithCompletion:) func finish(completion: (() -> Void)?) { NSAnimationContext.runAnimationGroup { context in context.duration = 0.1 diff --git a/LookInside/Base/SplitView.swift b/LookInside/Base/SplitView.swift index ef02ea6..ae3f919 100644 --- a/LookInside/Base/SplitView.swift +++ b/LookInside/Base/SplitView.swift @@ -8,7 +8,7 @@ import AppKit /// A split view without visible dividers that reports its first layout. class SplitView: NSSplitView { /// Called once, after the first `layout`. - @objc var didFinishFirstLayout: ((SplitView) -> Void)? + var didFinishFirstLayout: ((SplitView) -> Void)? private var hasLaidOut = false diff --git a/LookInside/Base/SwiftDemangler.swift b/LookInside/Base/SwiftDemangler.swift index 5b7bc5d..e6fbd07 100644 --- a/LookInside/Base/SwiftDemangler.swift +++ b/LookInside/Base/SwiftDemangler.swift @@ -18,7 +18,7 @@ public class SwiftDemangler: NSObject { } /// 这里返回的结果会尽可能地短,去除了很多信息 - @objc public static func simpleParse(input: String) -> String { + public static func simpleParse(input: String) -> String { if let cachedResult = simpleCache[input] { return cachedResult } @@ -28,7 +28,7 @@ public class SwiftDemangler: NSObject { } /// 这里返回的结果会尽可能地长、包含了 module name 等各种信息 - @objc public static func completedParse(input: String) -> String { + public static func completedParse(input: String) -> String { if let cachedResult = completedCache[input] { return cachedResult } diff --git a/LookInside/Base/TableView.swift b/LookInside/Base/TableView.swift index 1925c66..940d87f 100644 --- a/LookInside/Base/TableView.swift +++ b/LookInside/Base/TableView.swift @@ -36,10 +36,10 @@ protocol TableViewDelegate: NSTableViewDelegate { protocol TableViewDataSource: NSTableViewDataSource {} class TableView: NSScrollView { - @objc let tableView: NSTableView + let tableView: NSTableView /// Defaults to true. - @objc var canScrollHorizontally: Bool = true { + var canScrollHorizontally: Bool = true { didSet { hasHorizontalScroller = canScrollHorizontally if !canScrollHorizontally { @@ -48,13 +48,13 @@ class TableView: NSScrollView { } } - @objc weak var delegate: TableViewDelegate? - @objc weak var dataSource: TableViewDataSource? + weak var delegate: TableViewDelegate? + weak var dataSource: TableViewDataSource? /// Defaults to true. - @objc var adjustsSelectionAutomatically: Bool = true + var adjustsSelectionAutomatically: Bool = true /// Defaults to true. - @objc var adjustsHoverAutomatically: Bool = true + var adjustsHoverAutomatically: Bool = true private var horizontalScrollWidthManager: TableViewHorizontalScrollWidthManager? @@ -121,7 +121,7 @@ class TableView: NSScrollView { } } - @objc func reloadData() { + func reloadData() { setSelectedRow(-1) horizontalScrollWidthManager = nil @@ -137,7 +137,7 @@ class TableView: NSScrollView { } /// Reloads but keeps the scroll position; `reloadData()` resets it. - @objc func reloadDataWithOffset() { + func reloadDataWithOffset() { let offset = documentVisibleRect.origin reloadData() needsLayout = true @@ -145,7 +145,6 @@ class TableView: NSScrollView { documentView?.scroll(offset) } - @objc(scrollRowToVisible:) func scrollRowToVisible(_ row: Int) { tableView.scrollRowToVisible(row) } diff --git a/LookInside/Base/TableViewHorizontalScrollWidthManager.swift b/LookInside/Base/TableViewHorizontalScrollWidthManager.swift index fa6ddcb..36e3741 100644 --- a/LookInside/Base/TableViewHorizontalScrollWidthManager.swift +++ b/LookInside/Base/TableViewHorizontalScrollWidthManager.swift @@ -9,11 +9,10 @@ import Foundation class TableViewHorizontalScrollWidthManager: NSObject { - @objc var maxRowWidth: CGFloat = 0 + var maxRowWidth: CGFloat = 0 - @objc var didReachNewMaxWidth: (() -> Void)? + var didReachNewMaxWidth: (() -> Void)? - @objc(rowDidLayoutWithWidth:) func rowDidLayout(withWidth width: CGFloat) { guard width > maxRowWidth else { return diff --git a/LookInside/Base/TextControl.swift b/LookInside/Base/TextControl.swift index 76bd842..2930007 100644 --- a/LookInside/Base/TextControl.swift +++ b/LookInside/Base/TextControl.swift @@ -9,15 +9,15 @@ import AppKit class TextControl: BaseControl { - @objc let label = TextLabel() + let label = TextLabel() - @objc var insets = NSEdgeInsets() { + var insets = NSEdgeInsets() { didSet { needsLayout = true } } - @objc var rightImage: NSImage? { + var rightImage: NSImage? { didSet { if let rightImage { if rightImageView == nil { @@ -33,13 +33,13 @@ class TextControl: BaseControl { } } - @objc var spaceBetweenLabelAndImage: CGFloat = 0 { + var spaceBetweenLabelAndImage: CGFloat = 0 { didSet { needsLayout = true } } - @objc var rightImageOffsetY: CGFloat = 0 { + var rightImageOffsetY: CGFloat = 0 { didSet { needsLayout = true } diff --git a/LookInside/Base/TextFieldView.swift b/LookInside/Base/TextFieldView.swift index e41599a..a819d00 100644 --- a/LookInside/Base/TextFieldView.swift +++ b/LookInside/Base/TextFieldView.swift @@ -8,29 +8,29 @@ import AppKit /// A text field with insets, an optional leading image and an optional /// trailing button that clears a search. class TextFieldView: BaseView { - @objc let textField = NSTextField() + let textField = NSTextField() - @objc var insets = NSEdgeInsetsZero { + var insets = NSEdgeInsetsZero { didSet { needsLayout = true } } - @objc var textColors: TwoColors? { + var textColors: TwoColors? { didSet { updateColors() } } - @objc var image: NSImage? { + var image: NSImage? { didSet { updateImageView() } } /// Set by `initCloseButton()`. - @objc private(set) var closeButton: NSButton? + private(set) var closeButton: NSButton? private var imageView: NSImageView? private var textDidChangeObserver: NSObjectProtocol? private var stringValueObservation: NSKeyValueObservation? /// A read-only, borderless label. - @objc(labelView) static func label() -> TextFieldView { + static func label() -> TextFieldView { let view = TextFieldView() view.textField.wantsLayer = true view.textField.backgroundColor = .clear @@ -117,7 +117,7 @@ class TextFieldView: BaseView { /// Adds the trailing "cancel search" button, shown while the field has /// text. - @objc func initCloseButton() { + func initCloseButton() { guard closeButton == nil else { return } let closeButton = NSButton() closeButton.title = NSLocalizedString("CancelSearch", comment: "") diff --git a/LookInside/Base/TextLabel.swift b/LookInside/Base/TextLabel.swift index e8f62c6..959d4a2 100644 --- a/LookInside/Base/TextLabel.swift +++ b/LookInside/Base/TextLabel.swift @@ -10,14 +10,14 @@ import AppKit class TextLabel: NSTextField { /// 默认为 nil - @objc var textColors: TwoColors? { + var textColors: TwoColors? { didSet { updateColors() } } /// 默认为 nil - @objc var backgroundColors: TwoColors? { + var backgroundColors: TwoColors? { didSet { updateColors() } diff --git a/LookInside/Base/TextsMenuView.swift b/LookInside/Base/TextsMenuView.swift index f15dc3b..f38d2fb 100644 --- a/LookInside/Base/TextsMenuView.swift +++ b/LookInside/Base/TextsMenuView.swift @@ -5,7 +5,7 @@ import AppKit -@objc enum TextsMenuViewType: Int { +enum TextsMenuViewType: Int { /// Left column left-aligned, right column right-aligned. case justified /// Both columns meet in the middle. @@ -16,22 +16,22 @@ import AppKit /// value. class TextsMenuView: BaseView { /// Defaults to {0, 3, 0, 5}. - @objc var insets = NSEdgeInsets(top: 0, left: 3, bottom: 0, right: 5) + var insets = NSEdgeInsets(top: 0, left: 3, bottom: 0, right: 5) - @objc var type: TextsMenuViewType = .justified { + var type: TextsMenuViewType = .justified { didSet { updateAlignments() } } - @objc var texts: [StringTwoTuple] = [] { + var texts: [StringTwoTuple] = [] { didSet { reloadLabels() } } - @objc var font: NSFont? + var font: NSFont? /// Defaults to 2. - @objc var verSpace: CGFloat = 2 + var verSpace: CGFloat = 2 /// Defaults to 10. - @objc var horSpace: CGFloat = 10 + var horSpace: CGFloat = 10 private var leftLabels: [TextLabel] = [] private var rightLabels: [TextLabel] = [] @@ -163,7 +163,6 @@ class TextsMenuView: BaseView { /// Puts `button` after the value on row `idx`; the caller handles its /// action. - @objc(addButton:atIndex:) func add(_ button: NSButton, at idx: UInt) { let idx = Int(idx) guard buttons[idx] == nil else { diff --git a/LookInside/Base/TipsView.swift b/LookInside/Base/TipsView.swift index b2cd5bb..7801e4f 100644 --- a/LookInside/Base/TipsView.swift +++ b/LookInside/Base/TipsView.swift @@ -8,34 +8,34 @@ import AppKit /// A pill-shaped notice: optional image, a title, and an optional button /// after a separator. class TipsView: BaseView { - @objc weak var bindingObject: AnyObject? + weak var bindingObject: AnyObject? - @objc var title: String? { + var title: String? { didSet { titleLabel.stringValue = title ?? "" } } /// Set `buttonText` or `buttonImage` rather than editing the button. - @objc let button = NSButton() + let button = NSButton() /// Only one of `buttonText` and `buttonImage` takes effect; set one. - @objc var buttonText: String? { + var buttonText: String? { didSet { updateButton() } } - @objc var buttonImage: NSImage? { + var buttonImage: NSImage? { didSet { updateButton() } } - @objc var image: NSImage? { + var image: NSImage? { didSet { imageView.image = image imageView.isHidden = image == nil } } - @objc weak var target: AnyObject? - @objc var clickAction: Selector? - @objc var didClick: ((TipsView) -> Void)? + weak var target: AnyObject? + var clickAction: Selector? + var didClick: ((TipsView) -> Void)? let titleLabel = TextLabel() private let imageView = NSImageView() @@ -134,7 +134,6 @@ class TipsView: BaseView { updateButton() } - @objc(setImageByDeviceType:) func setImage(byDeviceType type: LookinAppInfoDevice) { switch type { case .mac, .macCatalyst: @@ -154,7 +153,6 @@ class TipsView: BaseView { /// Prefers the icon of the hardware model the app runs on, falling back /// to the device family's icon when the Server reports no model. - @objc(setImageByAppInfo:) func setImage(byAppInfo appInfo: InspectedAppInfo?) { if let deviceIcon = DeviceIconProvider.deviceIcon(forAppInfo: appInfo, pointSize: DeviceIconProvider.tipsPointSize) { image = deviceIcon @@ -167,7 +165,6 @@ class TipsView: BaseView { setImage(byDeviceType: appInfo?.deviceType ?? .simulator) } - @objc(setInternalInsetsRight:) func setInternalInsetsRight(_ value: CGFloat) { insetRightWithButton = value insetRightWithoutButton = value @@ -211,7 +208,7 @@ class TipsView: BaseView { class YellowTipsView: TipsView { private var isAnimating = false - @objc func startAnimation() { + func startAnimation() { guard !isAnimating else { return } let animation = CABasicAnimation(keyPath: "backgroundColor") if effectiveAppearance.isDarkMode { @@ -229,7 +226,7 @@ class YellowTipsView: TipsView { isAnimating = true } - @objc func endAnimation() { + func endAnimation() { layer?.removeAllAnimations() isAnimating = false } @@ -248,7 +245,7 @@ class YellowTipsView: TipsView { /// A red notice that pulses, used for connection loss. class RedTipsView: TipsView { - @objc func startAnimation() { + func startAnimation() { let animation = CABasicAnimation(keyPath: "backgroundColor") animation.fromValue = NSColor.rgb255(208, 2, 27, 0.9).cgColor animation.toValue = NSColor.rgb255(208, 2, 27, 0.7).cgColor @@ -259,7 +256,7 @@ class RedTipsView: TipsView { layer?.add(animation, forKey: nil) } - @objc func endAnimation() { + func endAnimation() { layer?.removeAllAnimations() } diff --git a/LookInside/Base/TwoColors.swift b/LookInside/Base/TwoColors.swift index a74af63..dcb580c 100644 --- a/LookInside/Base/TwoColors.swift +++ b/LookInside/Base/TwoColors.swift @@ -7,10 +7,9 @@ import AppKit /// A colour pair resolved against the app's current appearance. final class TwoColors: NSObject { - @objc var colorInLightMode: NSColor? - @objc var colorInDarkMode: NSColor? + var colorInLightMode: NSColor? + var colorInDarkMode: NSColor? - @objc(colorsWithColorInLightMode:colorInDarkMode:) static func colors(inLightMode colorInLightMode: NSColor?, inDarkMode colorInDarkMode: NSColor?) -> TwoColors { let colors = TwoColors() colors.colorInLightMode = colorInLightMode @@ -28,7 +27,7 @@ final class TwoColors: NSObject { /// The dark colour while the app's effective appearance is dark, the /// light one otherwise. - @objc var color: NSColor? { + var color: NSColor? { let isDarkMode = NSApp?.effectiveAppearance.isDarkMode ?? false return isDarkMode ? colorInDarkMode : colorInLightMode } diff --git a/LookInside/Connection/AppsManager.swift b/LookInside/Connection/AppsManager.swift index 9d82293..9e2e184 100644 --- a/LookInside/Connection/AppsManager.swift +++ b/LookInside/Connection/AppsManager.swift @@ -16,7 +16,6 @@ import FoundationToolbox @Loggable(subsystem: "com.lookinside.app") @MainActor final class AppsManager: NSObject { - @objc(sharedInstance) static let shared = AppsManager() override private init() { diff --git a/LookInside/Connection/ConnectionManager.swift b/LookInside/Connection/ConnectionManager.swift index 3e4fecd..4dfca6e 100644 --- a/LookInside/Connection/ConnectionManager.swift +++ b/LookInside/Connection/ConnectionManager.swift @@ -70,7 +70,6 @@ private final class ConnectionPort { @Loggable(.internal, subsystem: "com.lookinside.app") @MainActor final class ConnectionManager: NSObject { - @objc(sharedInstance) static let shared = ConnectionManager() private let simulatorPorts: [ConnectionPort] diff --git a/LookInside/Connection/InspectableApp.swift b/LookInside/Connection/InspectableApp.swift index 175fd7b..b3db658 100644 --- a/LookInside/Connection/InspectableApp.swift +++ b/LookInside/Connection/InspectableApp.swift @@ -22,11 +22,11 @@ enum AppResponseEvent { final class InspectableApp: NSObject { /// Set when the app's Server is too old or too new for this Host, or /// answered the app info request with another error. - @objc dynamic var serverVersionError: NSError? + var serverVersionError: NSError? - @objc dynamic var appInfo: InspectedAppInfo? + var appInfo: InspectedAppInfo? - @objc dynamic weak var channel: ServerChannel? + weak var channel: ServerChannel? // MARK: - Requests @@ -215,7 +215,6 @@ extension InspectableApp { /// Stops the running hierarchy details request: it completes, and the /// Server is told to stop sending. - @objc @MainActor func cancelHierarchyDetailFetching() { cancelRequest(type: UInt32(LookinRequestTypeHierarchyDetails)) diff --git a/LookInside/Connection/LiveDocument.swift b/LookInside/Connection/LiveDocument.swift index 00333da..4196d41 100644 --- a/LookInside/Connection/LiveDocument.swift +++ b/LookInside/Connection/LiveDocument.swift @@ -54,7 +54,6 @@ final class LiveDocument: NSDocument { } /// The live document whose window is `window`, or nil. - @objc(documentInWindow:) static func document(in window: NSWindow?) -> LiveDocument? { guard let window else { return nil } return NSDocumentController.shared.document(for: window) as? LiveDocument @@ -66,12 +65,12 @@ final class LiveDocument: NSDocument { } /// Nil before `makeWindowControllers()` ran. - @objc var hierarchyDataSource: StaticHierarchyDataSource? { + var hierarchyDataSource: StaticHierarchyDataSource? { staticWindowController?.hierarchyDataSource } /// Nil before `makeWindowControllers()` ran. - @objc var asyncUpdateManager: StaticAsyncUpdateManager? { + var asyncUpdateManager: StaticAsyncUpdateManager? { staticWindowController?.asyncUpdateManager } diff --git a/LookInside/Connection/LiveDocumentController.swift b/LookInside/Connection/LiveDocumentController.swift index 312f569..ca5a55e 100644 --- a/LookInside/Connection/LiveDocumentController.swift +++ b/LookInside/Connection/LiveDocumentController.swift @@ -13,7 +13,6 @@ import AppKit @MainActor final class LiveDocumentController: NSObject { - @objc(sharedInstance) static let shared = LiveDocumentController() override private init() { @@ -42,7 +41,6 @@ final class LiveDocumentController: NSObject { /// Objective-C form of `openLiveDocument(for:)`. `completion` runs on /// the main thread before this returns; with a nil app it gets /// `LookinErr_Inner` and no document. - @objc(openLiveDocumentForInspectableApp:completion:) func openLiveDocument( for app: InspectableApp?, completion: ((LiveDocument?, Bool, NSError?) -> Void)? @@ -56,7 +54,6 @@ final class LiveDocumentController: NSObject { } /// The live document whose app uses `channel`, or nil. - @objc(liveDocumentForChannel:) func liveDocument(for channel: ServerChannel?) -> LiveDocument? { guard let channel else { return nil } return NSDocumentController.shared.documents diff --git a/LookInside/Console/ConsoleViewController.swift b/LookInside/Console/ConsoleViewController.swift index 55983a7..2f3d0e6 100644 --- a/LookInside/Console/ConsoleViewController.swift +++ b/LookInside/Console/ConsoleViewController.swift @@ -20,18 +20,17 @@ final class ConsoleViewController: BaseViewController, TableViewDelegate, TableV private lazy var measuringReturnRowView = ConsoleReturnRowView() private var previousWidth: CGFloat = 0 - @objc weak var liveDocument: LiveDocument? { + weak var liveDocument: LiveDocument? { didSet { dataSource.liveDocument = liveDocument } } /// Whether the Console is on screen; while it is, the highlighted /// hierarchy item can become the call target. - @objc var isControllerShowing: Bool { + var isControllerShowing: Bool { get { dataSource.isShowingConsole } set { dataSource.isShowingConsole = newValue } } - @objc(initWithHierarchyDataSource:) init(hierarchyDataSource: HierarchyDataSource) { dataSource = ConsoleDataSource(hierarchyDataSource: hierarchyDataSource) inputRowView = ConsoleInputRowView(dataSource: dataSource) @@ -120,7 +119,6 @@ final class ConsoleViewController: BaseViewController, TableViewDelegate, TableV } /// Calls `text` on `obj` and adds the call to the transcript; errors are only logged. - @objc(submitWithObj:text:) func submit(with obj: InspectedObject?, text: String) { Task { do { diff --git a/LookInside/DanceScript/DanceScriptManager.swift b/LookInside/DanceScript/DanceScriptManager.swift index c4aaa2c..86e3fe9 100644 --- a/LookInside/DanceScript/DanceScriptManager.swift +++ b/LookInside/DanceScript/DanceScriptManager.swift @@ -13,11 +13,10 @@ import AppKit import FoundationToolbox @Loggable(subsystem: "com.lookinside.app") -@objc(DanceScriptManager) final class DanceScriptManager: NSObject { private static let sharedManager = DanceScriptManager() - @objc class func shared() -> DanceScriptManager { + class func shared() -> DanceScriptManager { sharedManager } @@ -49,7 +48,6 @@ final class DanceScriptManager: NSObject { } @MainActor - @objc(handleText:) func handleText(_ json: String?) { guard let json else { PeripheralAlerts.show(ConnectionError.inner, in: PeripheralAlerts.keyWindow) diff --git a/LookInside/Dashboard/AttributeView/DashboardAttributeNumberInputView.swift b/LookInside/Dashboard/AttributeView/DashboardAttributeNumberInputView.swift index 1fd8501..616df94 100644 --- a/LookInside/Dashboard/AttributeView/DashboardAttributeNumberInputView.swift +++ b/LookInside/Dashboard/AttributeView/DashboardAttributeNumberInputView.swift @@ -12,7 +12,7 @@ import FoundationToolbox /// A single number: opacity, corner radius, font size and the like. @Loggable(subsystem: "com.lookinside.app") final class DashboardAttributeNumberInputView: DashboardAttributeView, NSTextFieldDelegate { - @objc let inputView = NumberInputView() + let inputView = NumberInputView() /// Laid out horizontally (title inside the field) with three decimals; /// everything else is vertical (title below) with two. diff --git a/LookInside/Dashboard/AttributeView/DashboardAttributeView.swift b/LookInside/Dashboard/AttributeView/DashboardAttributeView.swift index 1eedfdf..9d31a0a 100644 --- a/LookInside/Dashboard/AttributeView/DashboardAttributeView.swift +++ b/LookInside/Dashboard/AttributeView/DashboardAttributeView.swift @@ -15,11 +15,11 @@ import FoundationToolbox @Loggable(subsystem: "com.lookinside.app") class DashboardAttributeView: BaseView { /// Setting the attribute renders it. - @objc var attribute: InspectedAttribute? { + var attribute: InspectedAttribute? { didSet { renderWithAttribute() } } - @objc weak var dashboardViewController: DashboardViewController? { + weak var dashboardViewController: DashboardViewController? { didSet { dashboardViewControllerDidChange() } } @@ -35,7 +35,7 @@ class DashboardAttributeView: BaseView { /// Whether the user can change the value: a user-custom attribute with /// a setter, or a known attribute with a setter on a live (not read /// from file) dashboard. - @objc func canEdit() -> Bool { + func canEdit() -> Bool { guard let attribute else { return false } if attribute.isUserCustom() { return !(attribute.customSetterID ?? "").isEmpty @@ -45,11 +45,11 @@ class DashboardAttributeView: BaseView { } /// Reads `attribute` and renders it. - @objc func renderWithAttribute() {} + func renderWithAttribute() {} /// How many of this view fit in a row: 1 takes the whole row, 0 sizes /// the view to its content. - @objc func numberOfColumnsOccupied() -> Int { + func numberOfColumnsOccupied() -> Int { 1 } @@ -92,10 +92,9 @@ class DashboardAttributeView: BaseView { /// fields ignore that second end-of-editing so the value is not submitted /// twice. final class DashboardTextControlEditingFlag: NSObject { - @objc(sharedInstance) static let shared = DashboardTextControlEditingFlag() - @objc var shouldIgnoreTextEditingChangeEvent = false + var shouldIgnoreTextEditingChangeEvent = false override private init() { super.init() diff --git a/LookInside/Dashboard/AttributeView/JSON/JSONAttributeViews.swift b/LookInside/Dashboard/AttributeView/JSON/JSONAttributeViews.swift index 71efe32..e3c07e4 100644 --- a/LookInside/Dashboard/AttributeView/JSON/JSONAttributeViews.swift +++ b/LookInside/Dashboard/AttributeView/JSON/JSONAttributeViews.swift @@ -226,7 +226,6 @@ final class JSONAttributeViewController: BaseViewController, TableViewDelegate, tableView.dashboardLayout.fullFrame().toY(28) } - @objc(renderWithJSON:) func render(json: String?) { tree.render(json: json) tableView.reloadData() @@ -273,7 +272,7 @@ final class JSONAttributeViewController: BaseViewController, TableViewDelegate, /// The window of `JSONAttributeViewController`. final class JSONAttributeWindowController: WindowController { - @objc convenience init() { + convenience init() { let window = AppWindow( contentRect: NSRect(x: 0, y: 0, width: 600, height: 320), styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView], diff --git a/LookInside/Dashboard/DashboardViewController.swift b/LookInside/Dashboard/DashboardViewController.swift index d245d37..7571285 100644 --- a/LookInside/Dashboard/DashboardViewController.swift +++ b/LookInside/Dashboard/DashboardViewController.swift @@ -39,20 +39,19 @@ final class DashboardViewController: BaseViewController, DashboardCardViewDelega /// Whether this dashboard shows a live document (rather than one read /// from a file). - @objc let isStaticMode: Bool + let isStaticMode: Bool /// The update manager of the owning inspector, which patches the /// preview after modifications that need it. - @objc weak var asyncUpdateManager: StaticAsyncUpdateManager? + weak var asyncUpdateManager: StaticAsyncUpdateManager? /// The live document that owns this dashboard; its inspectable app /// receives attribute modifications and method invocations. Nil on a /// document read from a file. - @objc weak var liveDocument: LiveDocument? { + weak var liveDocument: LiveDocument? { didSet { methodsDataSource?.liveDocument = liveDocument } } - @objc(initWithStaticDataSource:) init(staticDataSource dataSource: StaticHierarchyDataSource) { staticDataSource = dataSource readDataSource = nil @@ -61,7 +60,6 @@ final class DashboardViewController: BaseViewController, DashboardCardViewDelega didInitialize() } - @objc(initWithReadDataSource:) init(readDataSource dataSource: ReadHierarchyDataSource) { staticDataSource = nil readDataSource = dataSource @@ -229,11 +227,11 @@ final class DashboardViewController: BaseViewController, DashboardCardViewDelega view.needsLayout = true } - @objc func reloadCurrentDisplayItem() { + func reloadCurrentDisplayItem() { reload(groupList: groupList(for: currentDataSource()?.selectedItem)) } - @objc func currentDataSource() -> HierarchyDataSource? { + func currentDataSource() -> HierarchyDataSource? { if let staticDataSource { return staticDataSource } diff --git a/LookInside/Dashboard/EnumListRegistry.swift b/LookInside/Dashboard/EnumListRegistry.swift index 78e138e..8c89219 100644 --- a/LookInside/Dashboard/EnumListRegistry.swift +++ b/LookInside/Dashboard/EnumListRegistry.swift @@ -15,9 +15,9 @@ import Foundation /// One case of an enum: its name, raw value, and the first OS major /// version that has it (0 for every version). final class EnumListRegistryKeyValueItem: NSObject { - @objc let desc: String - @objc let value: Int - @objc let availableOSVersion: Int + let desc: String + let value: Int + let availableOSVersion: Int init(desc: String, value: Int, availableOSVersion: Int) { self.desc = desc @@ -27,7 +27,6 @@ final class EnumListRegistryKeyValueItem: NSObject { } final class EnumListRegistry: NSObject { - @objc(sharedInstance) static let shared = EnumListRegistry() private let data: [String: [EnumListRegistryKeyValueItem]] @@ -38,14 +37,12 @@ final class EnumListRegistry: NSObject { } /// The cases of `enumName` in menu order, or nil for an unknown list. - @objc(itemsForEnumName:) func items(forEnumName enumName: String?) -> [EnumListRegistryKeyValueItem]? { guard let enumName else { return nil } return data[enumName] } /// The name of the first case of `enumName` whose value is `value`. - @objc(descForEnumName:value:) func desc(forEnumName enumName: String?, value: Int) -> String? { guard let items = items(forEnumName: enumName) else { assertionFailure("unknown enum list \(enumName ?? "nil")") diff --git a/LookInside/DebugE2E/DebugE2EDumpWriter.swift b/LookInside/DebugE2E/DebugE2EDumpWriter.swift index 2178a70..26ea71b 100644 --- a/LookInside/DebugE2E/DebugE2EDumpWriter.swift +++ b/LookInside/DebugE2E/DebugE2EDumpWriter.swift @@ -14,7 +14,6 @@ /// node's `host` record names their pixel sizes; the pixels themselves are /// covered by lookin-probe. final class DebugE2EDumpWriter: NSObject { - @objc(writeSnapshotForDataSource:app:platform:toDirectory:error:) static func writeSnapshot( for dataSource: StaticHierarchyDataSource, app: InspectableApp, @@ -130,7 +129,6 @@ /// `AppsManager.scanApps(needImages: true, localInfos: nil)`: /// nil when no channel is connected. `completion` runs on the main /// thread, after this method returns. - @objc(fetchAppsWithCompletion:) @MainActor static func fetchApps(completion: @escaping ([InspectableApp]?) -> Void) { Task { @MainActor in @@ -141,7 +139,6 @@ /// `app`'s hierarchy, or the error the request failed with. /// `completion` runs on the main thread, after this method returns. - @objc(fetchHierarchyForApp:completion:) @MainActor static func fetchHierarchy(for app: InspectableApp, completion: @escaping (HierarchyInfo?, Error?) -> Void) { Task { @MainActor in @@ -155,7 +152,6 @@ /// Whether the 220/221 license handshake succeeded on `app`'s channel, /// as the Host's connection layer records it. - @objc(isChannelLicensedForApp:) static func isChannelLicensed(for app: InspectableApp) -> Bool { MainActor.assumeIsolated { app.channel?.connectionState.licenseHandshake.isVerified ?? false diff --git a/LookInside/DebugE2E/DebugUISnapshots.swift b/LookInside/DebugE2E/DebugUISnapshots.swift index bf20686..22f01e8 100644 --- a/LookInside/DebugE2E/DebugUISnapshots.swift +++ b/LookInside/DebugE2E/DebugUISnapshots.swift @@ -72,7 +72,7 @@ self.directory = directory } - @objc static var isEnabled: Bool { + static var isEnabled: Bool { !(ProcessInfo.processInfo.environment[directoryVariable] ?? "").isEmpty } @@ -92,7 +92,7 @@ /// Step 1: captures the launch window before any app is inspectable, /// then writes the marker. Called once the app finished launching. - @objc static func captureLaunchScene() { + static func captureLaunchScene() { guard let snapshots = instance() else { return } Task { @MainActor in do { @@ -109,7 +109,6 @@ /// Steps 2 and 3: runs every scene against the loaded live document, /// then calls `completion` with the exit status. - @objc(runScenesForDocument:completion:) static func runScenes(for document: LiveDocument, completion: @escaping (Int32) -> Void) { guard let snapshots = instance() else { completion(0) diff --git a/LookInside/DebugE2E/DebugWindowImage.swift b/LookInside/DebugE2E/DebugWindowImage.swift index 0429bb7..49cc69e 100644 --- a/LookInside/DebugE2E/DebugWindowImage.swift +++ b/LookInside/DebugE2E/DebugWindowImage.swift @@ -13,7 +13,6 @@ /// toolbar). final class DebugWindowImage: NSObject { /// PNG of `window`'s frame view: title bar and toolbar included. - @objc(pngDataOfWindow:) static func pngData(of window: NSWindow?) -> Data? { guard let window, let frameView = window.contentView?.superview ?? window.contentView else { return nil diff --git a/LookInside/Export/ExportAccessoryView.swift b/LookInside/Export/ExportAccessoryView.swift index 0842467..00d9697 100644 --- a/LookInside/Export/ExportAccessoryView.swift +++ b/LookInside/Export/ExportAccessoryView.swift @@ -16,7 +16,7 @@ final class ExportAccessoryView: BaseView { private static let sizeLabelTop: CGFloat = 5 /// The exported size in bytes, shown in megabytes. - @objc var dataSize: UInt = 0 { + var dataSize: UInt = 0 { didSet { sizeLabel.stringValue = String(format: NSLocalizedString("File Size: %.2f M", comment: ""), ExportNaming.megabytes(dataSize)) diff --git a/LookInside/Export/ExportManager.swift b/LookInside/Export/ExportManager.swift index cba4cd6..9ac13a1 100644 --- a/LookInside/Export/ExportManager.swift +++ b/LookInside/Export/ExportManager.swift @@ -14,14 +14,13 @@ import UniformTypeIdentifiers final class ExportManager: NSObject { private static let shared = ExportManager() - @objc class func sharedInstance() -> ExportManager { + class func sharedInstance() -> ExportManager { shared } /// The archive data of `info`, with every screenshot scaled by /// `compression` (clamped to 0.01...1). When `fileName` is given it /// receives the suggested file name. - @objc(dataFromHierarchyInfo:imageCompression:fileName:) func data(from info: HierarchyInfo, imageCompression compression: CGFloat, fileName: AutoreleasingUnsafeMutablePointer?) -> Data? @@ -86,7 +85,6 @@ final class ExportManager: NSObject { /// Asks where to save `displayItem`'s group screenshot and writes it as TIFF. @MainActor - @objc(exportScreenshotWithDisplayItem:) class func exportScreenshot(with displayItem: DisplayItem) { guard let image = displayItem.groupScreenshot, let imageData = image.tiffRepresentation(using: .lzw, factor: 1) diff --git a/LookInside/GestureDebug/GestureDebugWindowController.swift b/LookInside/GestureDebug/GestureDebugWindowController.swift index faa3160..a877603 100644 --- a/LookInside/GestureDebug/GestureDebugWindowController.swift +++ b/LookInside/GestureDebug/GestureDebugWindowController.swift @@ -8,7 +8,6 @@ final class GestureDebugWindowController: NSWindowController, NSWindowDelegate { private var appObservation: NSKeyValueObservation? private weak var inspectionOwner: StaticWindowController? - @objc(initWithOwner:) init(owner: StaticWindowController) { let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 1180, height: 740), diff --git a/LookInside/Hierarchy/HierarchyDataSource+KeyDown.swift b/LookInside/Hierarchy/HierarchyDataSource+KeyDown.swift index 1c7f94d..72cdb4b 100644 --- a/LookInside/Hierarchy/HierarchyDataSource+KeyDown.swift +++ b/LookInside/Hierarchy/HierarchyDataSource+KeyDown.swift @@ -12,7 +12,6 @@ import LookInsideHostCore extension HierarchyDataSource { /// Arrow keys move the selection and expand or collapse rows. Returns /// whether the key was handled. - @objc(keyDown:) func keyDown(_ event: NSEvent) -> Bool { let rows = displayingFlatItems ?? [] let selectedRow = selectedItem.flatMap { selected in rows.firstIndex { $0 === selected } } diff --git a/LookInside/Hierarchy/HierarchyDataSource.swift b/LookInside/Hierarchy/HierarchyDataSource.swift index c61bc87..cfa28fc 100644 --- a/LookInside/Hierarchy/HierarchyDataSource.swift +++ b/LookInside/Hierarchy/HierarchyDataSource.swift @@ -118,7 +118,7 @@ class HierarchyDataSource: NSObject { final let didReloadFlatItemsWithSearchOrFocus = SyncSignal() /// Every display item of the tree, visible or not. - @objc var rawFlatItems: [DisplayItem]? { + var rawFlatItems: [DisplayItem]? { get { storedRawFlatItems } set { storedRawFlatItems = newValue @@ -128,7 +128,7 @@ class HierarchyDataSource: NSObject { /// All items in normal state; the shown subset while searching or /// focusing. - @objc dynamic var flatItems: [DisplayItem]? + var flatItems: [DisplayItem]? /// The rows: the items of `flatItems` that no collapsed ancestor hides. /// KVO-observable. @@ -148,17 +148,17 @@ class HierarchyDataSource: NSObject { } } - @objc var selectColorMenu: NSMenu? { + var selectColorMenu: NSMenu? { storedSelectColorMenu } /// The tag of the color menu's "Other…" item. - @objc var customColorMenuItemTag: Int { + var customColorMenuItemTag: Int { 10 } /// The tag of the color menu's "switch color format" item. - @objc var toggleColorFormatMenuItemTag: Int { + var toggleColorFormatMenuItemTag: Int { 11 } @@ -169,9 +169,9 @@ class HierarchyDataSource: NSObject { /// While the dashboard is being searched, the preview must not change the /// selection when a layer is clicked. - @objc var shouldAvoidChangingPreviewSelectionDueToDashboardSearch: Bool = false + var shouldAvoidChangingPreviewSelectionDueToDashboardSearch: Bool = false - @objc var serverSideIsSwiftProject: Bool { + var serverSideIsSwiftProject: Bool { storedServerSideIsSwiftProject } @@ -222,7 +222,6 @@ class HierarchyDataSource: NSObject { // MARK: - Reload - @objc(reloadWithHierarchyInfo:keepState:) func reload(with info: HierarchyInfo?, keepState: Bool) { // Path identifiers of the old items resolve their root index against // the old root items, so take them before replacing the info. @@ -338,11 +337,10 @@ class HierarchyDataSource: NSObject { // MARK: - Rows - @objc func numberOfRows() -> Int { + func numberOfRows() -> Int { displayingFlatItems?.count ?? 0 } - @objc(itemAtRow:) func item(atRow index: Int) -> DisplayItem? { guard let rows = displayingFlatItems, rows.indices.contains(index) else { return nil @@ -350,17 +348,15 @@ class HierarchyDataSource: NSObject { return rows[index] } - @objc(rowForItem:) func row(for item: DisplayItem?) -> Int { displayingFlatItems?.firstIndex { $0 === item } ?? NSNotFound } - @objc(displayItemWithOid:) func displayItem(withOid oid: UInt) -> DisplayItem? { oidToDisplayItem[oid] } - @objc func buildDisplayingFlatItems() { + func buildDisplayingFlatItems() { let showSystemLayoutGuides = PreferenceManager.shared.showSystemLayoutGuides.currentBOOLValue let rows = (flatItems ?? []).filter { item in guard item.displayingInHierarchy else { @@ -420,7 +416,6 @@ class HierarchyDataSource: NSObject { item?.notifyHoverChangeToDelegates() } - @objc(selectAndRevealItem:) func selectAndRevealItem(_ item: DisplayItem?) { guard let item else { return @@ -456,7 +451,6 @@ class HierarchyDataSource: NSObject { /// - referenceDict: expansion by path identifier; an item with an /// entry keeps it instead of the preset. /// - selectedItem: receives the item the preset suggests selecting. - @objc(adjustExpansionByIndex:referenceDict:selectedItem:) func adjustExpansion( by index: Int, referenceDict: [String: NSNumber]?, @@ -662,7 +656,6 @@ class HierarchyDataSource: NSObject { } } - @objc(collapseItem:) func collapse(_ item: DisplayItem?) { guard let item, item.isExpandable, item.isExpanded else { return @@ -672,7 +665,6 @@ class HierarchyDataSource: NSObject { persistExpansionStateToPreferences() } - @objc(expandItem:) func expand(_ item: DisplayItem?) { guard let item, item.isExpandable, !item.isExpanded else { return @@ -682,7 +674,6 @@ class HierarchyDataSource: NSObject { persistExpansionStateToPreferences() } - @objc(expandToShowItem:) func expand(toShow item: DisplayItem?) { var didChange = false item?.enumerateAncestors { ancestor, _ in @@ -698,7 +689,6 @@ class HierarchyDataSource: NSObject { } } - @objc(expandItemsRootedByItem:) func expandItemsRooted(by item: DisplayItem?) { let includePreferredCollapsed = item?.preferToBeCollapsed ?? false item?.enumerateSelfAndChildren { node in @@ -713,7 +703,6 @@ class HierarchyDataSource: NSObject { persistExpansionStateToPreferences() } - @objc(collapseAllChildrenOfItem:) func collapseAllChildren(of item: DisplayItem?) { item?.enumerateSelfAndChildren { node in guard node !== item, node.isExpandable, node.isExpanded else { @@ -730,7 +719,6 @@ class HierarchyDataSource: NSObject { /// "/:/..." from the root to `item`; nil /// when a node of the chain has no class (UserCustom-only nodes), when /// a sibling index cannot be resolved, or when `rootItems` is empty. - @objc(pathIdentifierForItem:inRootItems:) class func pathIdentifier(for item: DisplayItem?, inRootItems rootItems: [DisplayItem]?) -> String? { guard let item else { return nil @@ -749,7 +737,7 @@ class HierarchyDataSource: NSObject { /// the target app's bundle identifier, so a cold reload restores the /// user's collapses too. Does nothing when remembering is off or there is /// no bundle identifier. - @objc func persistExpansionStateToPreferences() { + func persistExpansionStateToPreferences() { let preferences = preferenceManager() guard preferences.rememberExpansionState else { return @@ -778,7 +766,6 @@ class HierarchyDataSource: NSObject { /// Call while the user types a non-empty search string; replaces /// `flatItems` and `displayingFlatItems`. - @objc(searchWithString:) func search(with string: String?) { guard let string, !string.isEmpty else { assertionFailure("searching for an empty string") @@ -815,7 +802,7 @@ class HierarchyDataSource: NSObject { /// Call when the search field is closed; restores the expansion from /// before the search and keeps the selection visible. - @objc func endSearch() { + func endSearch() { guard state != .normal else { return } @@ -838,7 +825,6 @@ class HierarchyDataSource: NSObject { } /// Entered from the normal or the search state. - @objc(focusDisplayItem:) func focus(_ item: DisplayItem?) { guard let item else { assertionFailure("focusing nil") @@ -862,7 +848,7 @@ class HierarchyDataSource: NSObject { buildDisplayingFlatItems() } - @objc func endFocus() { + func endFocus() { guard state != .normal else { return } @@ -886,7 +872,6 @@ class HierarchyDataSource: NSObject { // MARK: - SwiftUI jumps /// SwiftUI node -> its matched CALayer nodes; empty when there is none. - @objc(swiftUIBackingLayerItemsForItem:) func swiftUIBackingLayerItems(for item: DisplayItem?) -> [DisplayItem]? { guard let item else { return [] @@ -907,7 +892,6 @@ class HierarchyDataSource: NSObject { } /// CALayer node -> its SwiftUI node, or nil. - @objc(swiftUISourceItemForLayerItem:) func swiftUISourceItem(forLayerItem item: DisplayItem?) -> DisplayItem? { guard let item, let displayListID = item.swiftUILayerDisplayListID() else { return nil @@ -917,7 +901,6 @@ class HierarchyDataSource: NSObject { /// Dashboard row -> its SwiftUI / CALayer jump target, or nil when the /// row has none. - @objc(swiftUIJumpTargetForAttribute:) func swiftUIJumpTarget(for attribute: InspectedAttribute?) -> DisplayItem? { guard let attribute, let sourceItem = attribute.targetDisplayItem, let value = attribute.value as? String else { return nil @@ -1011,7 +994,6 @@ class HierarchyDataSource: NSObject { // MARK: - Colors /// The hierarchy's alias names of `color`, or nil. - @objc(aliasForColor:) func alias(for color: NSColor?) -> [String]? { guard let color else { return nil @@ -1125,13 +1107,13 @@ class HierarchyDataSource: NSObject { // MARK: - Others /// Subclasses return their preference manager. - @objc func preferenceManager() -> PreferenceManager { + func preferenceManager() -> PreferenceManager { assertionFailure("should implement by subclass") return PreferenceManager.shared } /// YES in read-only mode, such as an opened file. - @objc func isReadOnly() -> Bool { + func isReadOnly() -> Bool { true } diff --git a/LookInside/Hierarchy/HierarchyView.swift b/LookInside/Hierarchy/HierarchyView.swift index d11c13e..4e835c5 100644 --- a/LookInside/Hierarchy/HierarchyView.swift +++ b/LookInside/Hierarchy/HierarchyView.swift @@ -143,7 +143,6 @@ class HierarchyView: BaseView { : NSColor(red: 0, green: 0, blue: 0, alpha: 0.3).cgColor } - @objc(scrollToMakeItemVisible:) func scroll(toMakeItemVisible item: DisplayItem?) { guard let item, let row = displayItems.firstIndex(where: { $0 === item }) else { return diff --git a/LookInside/Injection/Device/DeviceInjectionFlow.swift b/LookInside/Injection/Device/DeviceInjectionFlow.swift index e2c4727..e2a14b2 100644 --- a/LookInside/Injection/Device/DeviceInjectionFlow.swift +++ b/LookInside/Injection/Device/DeviceInjectionFlow.swift @@ -20,7 +20,7 @@ import RunningApplicationKit /// are still bugs and still need fixing; this just stops them being hit every /// single time. final class DeviceInjectionFlow: NSObject { - @objc(sharedInstance) static let shared = DeviceInjectionFlow() + static let shared = DeviceInjectionFlow() private var picker: InjectionTargetPicker? private var startGate = InjectionStartGate() @@ -38,7 +38,7 @@ final class DeviceInjectionFlow: NSObject { } } - @objc func startFromWindow(_ window: NSWindow?) { + func startFromWindow(_ window: NSWindow?) { let decision = startGate.begin { SwiftUISupportGatekeeper.sharedInstance().canUseProtectedFeatureWithoutPrompt() } diff --git a/LookInside/Injection/InjectionFlow.swift b/LookInside/Injection/InjectionFlow.swift index b2449b1..63d2973 100644 --- a/LookInside/Injection/InjectionFlow.swift +++ b/LookInside/Injection/InjectionFlow.swift @@ -15,12 +15,12 @@ import Foundation /// `LaunchViewController` 1.5s refresh tick then discovers the newly /// inspectable app automatically. final class InjectionFlow: NSObject { - @objc(sharedInstance) static let shared = InjectionFlow() + static let shared = InjectionFlow() private var picker: InjectionTargetPicker? private var startGate = InjectionStartGate() - @objc func startFromWindow(_ window: NSWindow?) { + func startFromWindow(_ window: NSWindow?) { let decision = startGate.begin { SwiftUISupportGatekeeper.sharedInstance().canUseProtectedFeatureWithoutPrompt() } diff --git a/LookInside/Launch/LaunchAppView.swift b/LookInside/Launch/LaunchAppView.swift index 64fa517..62b0225 100644 --- a/LookInside/Launch/LaunchAppView.swift +++ b/LookInside/Launch/LaunchAppView.swift @@ -54,11 +54,11 @@ final class LaunchAppView: BaseControl { } /// The smaller metrics the apps popover uses. Defaults to false. - @objc var compactLayout = false { + var compactLayout = false { didSet { applyLayoutMetrics() } } - @objc var app: InspectableApp? { + var app: InspectableApp? { didSet { appDidChange() } } diff --git a/LookInside/Launch/LaunchViewController.swift b/LookInside/Launch/LaunchViewController.swift index 598e569..6837b0a 100644 --- a/LookInside/Launch/LaunchViewController.swift +++ b/LookInside/Launch/LaunchViewController.swift @@ -38,12 +38,10 @@ final class LaunchViewController: BaseViewController { /// images again. private var appInfos: [InspectedAppInfo]? - @objc(initWithWindow:) convenience init(window: NSWindow?) { self.init(window: window, autoEnterOnInitialReload: false) } - @objc(initWithWindow:autoEnterOnInitialReload:) init(window: NSWindow?, autoEnterOnInitialReload: Bool) { self.window = window self.autoEnterOnInitialReload = autoEnterOnInitialReload diff --git a/LookInside/Launch/LaunchWindowController.swift b/LookInside/Launch/LaunchWindowController.swift index 913fc95..804f494 100644 --- a/LookInside/Launch/LaunchWindowController.swift +++ b/LookInside/Launch/LaunchWindowController.swift @@ -10,11 +10,10 @@ import AppKit /// The small launch window that lists the inspectable apps. final class LaunchWindowController: WindowController { - @objc let launchViewController: LaunchViewController + let launchViewController: LaunchViewController /// - Parameter autoEnterOnInitialReload: whether the first scan opens /// the only app it finds by itself. - @objc(initWithAutoEnterOnInitialReload:) init(autoEnterOnInitialReload: Bool) { let window = AppWindow( contentRect: NSRect(x: 0, y: 0, width: 252, height: 400), @@ -33,7 +32,7 @@ final class LaunchWindowController: WindowController { contentViewController = launchViewController } - @objc convenience init() { + convenience init() { self.init(autoEnterOnInitialReload: false) } diff --git a/LookInside/MCPBridge/MCPBridgeServer.swift b/LookInside/MCPBridge/MCPBridgeServer.swift index 2a1dd06..dece2a3 100644 --- a/LookInside/MCPBridge/MCPBridgeServer.swift +++ b/LookInside/MCPBridge/MCPBridgeServer.swift @@ -25,7 +25,7 @@ import FoundationToolbox final class MCPBridgeServer: NSObject { // MARK: - Singleton - @objc static let sharedInstance = MCPBridgeServer() + static let sharedInstance = MCPBridgeServer() // MARK: - Configuration @@ -108,7 +108,7 @@ final class MCPBridgeServer: NSObject { // MARK: - Lifecycle - @objc func start() { + func start() { stateQueue.async { [weak self] in guard let self else { return } guard self.isRunning == false else { return } @@ -124,7 +124,7 @@ final class MCPBridgeServer: NSObject { } } - @objc func stop() { + func stop() { stateQueue.async { [weak self] in guard let self else { return } guard self.isRunning else { return } diff --git a/LookInside/Manager/NavigationManager.swift b/LookInside/Manager/NavigationManager.swift index 4a02e25..de80d9f 100644 --- a/LookInside/Manager/NavigationManager.swift +++ b/LookInside/Manager/NavigationManager.swift @@ -20,7 +20,7 @@ final class NavigationManager: NSObject, NSWindowDelegate { super.init() } - @objc private(set) var launchWindowController: LaunchWindowController? + private(set) var launchWindowController: LaunchWindowController? /// Kept for the app's lifetime rather than dropped on close, so the /// window opens again on the page the user left it on. @@ -41,29 +41,28 @@ final class NavigationManager: NSObject, NSWindowDelegate { private var jsonWindowController: JSONAttributeWindowController? /// Set by the hierarchy view from its top inset. - @objc var windowTitleBarHeight: CGFloat = 0 + var windowTitleBarHeight: CGFloat = 0 - @objc func showLaunch() { + func showLaunch() { showLaunch(allowingAutoEnter: false) } - @objc(showLaunchAllowingAutoEnter:) func showLaunch(allowingAutoEnter allowAutoEnter: Bool) { let controller = LaunchWindowController(autoEnterOnInitialReload: allowAutoEnter) launchWindowController = controller controller.showWindow(self) } - @objc func closeLaunch() { + func closeLaunch() { launchWindowController?.close() launchWindowController = nil } - @objc func showSettings() { + func showSettings() { settingsWindowController.showWindow(self) } - @objc func showAbout() { + func showAbout() { let controller = aboutWindowController ?? { let controller = AboutWindowController() controller.window?.delegate = self @@ -73,7 +72,6 @@ final class NavigationManager: NSObject, NSWindowDelegate { controller.showWindow(self) } - @objc(showJsonWindow:) func showJSONWindow(_ json: String?) { let controller = jsonWindowController ?? { let controller = JSONAttributeWindowController() @@ -86,7 +84,7 @@ final class NavigationManager: NSObject, NSWindowDelegate { } /// The window controller of the key window, when it is one of ours. - @objc func currentKeyWindowController() -> WindowController? { + func currentKeyWindowController() -> WindowController? { NSApplication.shared.keyWindow?.windowController as? WindowController } @@ -94,7 +92,6 @@ final class NavigationManager: NSObject, NSWindowDelegate { /// get Recent Documents, Open Recent, proxy icon dragging and the reuse /// of an already open file. Opening is asynchronous; an error is /// presented with `NSApp.presentError`, so this never throws. - @objc(showReaderWithFilePath:error:) func showReader(withFilePath filePath: String) throws { let fileURL = URL(fileURLWithPath: filePath) NSDocumentController.shared.openDocument(withContentsOf: fileURL, display: true) { document, _, openError in @@ -107,7 +104,6 @@ final class NavigationManager: NSObject, NSWindowDelegate { /// Wraps an in-memory hierarchy in an untitled `ArchiveDocument`, /// so it gets the same document life cycle (Save As, Versions, Recent on /// save) as a file-backed archive, and shows its window. - @objc(showReaderWithHierarchyFile:title:) func showReader(with file: HierarchyFile?, title: String?) { let document = ArchiveDocument() document.hierarchyFile = file diff --git a/LookInside/Manager/PreferenceManager.swift b/LookInside/Manager/PreferenceManager.swift index 78c524a..026d0dd 100644 --- a/LookInside/Manager/PreferenceManager.swift +++ b/LookInside/Manager/PreferenceManager.swift @@ -43,7 +43,7 @@ let NotificationName_DidChangeSectionShowing = "NotificationName_DidChangeSectio case focus } -@objc enum PreferredCallStackType: Int { +enum PreferredCallStackType: Int { /// Formatted and shortened. case `default` /// Formatted, complete. @@ -52,7 +52,7 @@ let NotificationName_DidChangeSectionShowing = "NotificationName_DidChangeSectio case raw } -@objc enum MeasureState: Int { +enum MeasureState: Int { /// Not measuring. case no /// Measuring until the key is released. @@ -99,7 +99,7 @@ final class PreferenceManager: NSObject { } /// 默认为 NO - @objc var shouldStoreToLocal = false + var shouldStoreToLocal = false @objc dynamic var appearanceType: PreferredAppearanceType { didSet { @@ -115,7 +115,7 @@ final class PreferenceManager: NSObject { } /// 有效值为 0 ~ 4 - @objc dynamic var expansionIndex: Int { + var expansionIndex: Int { didSet { guard oldValue != expansionIndex else { return } store(expansionIndex, forKey: Key.expansionIndex) @@ -124,21 +124,21 @@ final class PreferenceManager: NSObject { /// Not stored: the subscription in the Objective-C version targeted /// `showHiddenItems` before it existed, so changes were never written. - @objc let showOutline: BoolMessageAttribute + let showOutline: BoolMessageAttribute - @objc let showHiddenItems: BoolMessageAttribute + let showHiddenItems: BoolMessageAttribute /// 是否在层级树里显示系统自动创建的 LayoutGuide(safe area、layout margins 等)。 /// 默认显示;关闭只是宿主端过滤,数据仍在。用户自建 guide 不受此开关影响。 - @objc let showSystemLayoutGuides: BoolMessageAttribute + let showSystemLayoutGuides: BoolMessageAttribute /// 是否显示 view 的 backing layer 子树(backing-layer-toggle 提案)。 /// 默认关闭。开启后像 Xcode 的 Show Layers:每个 view 节点下展开完整 layer 子树, /// 内容显示在真正渲染它的 layer 上,view 节点变为线框。切换时重新拉取层级。 - @objc let showBackingLayers: BoolMessageAttribute + let showBackingLayers: BoolMessageAttribute /// 范围是 0 ~ 1 - @objc let zInterspace: DoubleMessageAttribute + let zInterspace: DoubleMessageAttribute @objc dynamic var rgbaFormat: Bool { didSet { @@ -173,7 +173,7 @@ final class PreferenceManager: NSObject { } /// 被折叠的 AttrGroup - @objc dynamic var collapsedAttrGroups: [String] { + var collapsedAttrGroups: [String] { didSet { store(collapsedAttrGroups, forKey: Key.collapsedGroups) } @@ -212,19 +212,19 @@ final class PreferenceManager: NSObject { } } - @objc let freeRotation: BoolMessageAttribute + let freeRotation: BoolMessageAttribute - @objc let fastMode: BoolMessageAttribute + let fastMode: BoolMessageAttribute /// 上次接收到 iOS app 里传过来的 color config 和 collapsedClasses 信息的时间,用来统计 /// Always stored, even when `shouldStoreToLocal` is off. - @objc dynamic var receivingConfigTime_Color: TimeInterval { + var receivingConfigTime_Color: TimeInterval { didSet { defaults.set(receivingConfigTime_Color, forKey: Key.receivingConfigTimeColor) } } - @objc dynamic var receivingConfigTime_Class: TimeInterval { + var receivingConfigTime_Class: TimeInterval { didSet { defaults.set(receivingConfigTime_Class, forKey: Key.receivingConfigTimeClass) } @@ -236,7 +236,7 @@ final class PreferenceManager: NSObject { private var storedCallStackType: PreferredCallStackType = .default - @objc dynamic var callStackType: PreferredCallStackType { + var callStackType: PreferredCallStackType { get { storedCallStackType } set { if newValue.rawValue < 0 || newValue.rawValue > 2 { @@ -249,15 +249,15 @@ final class PreferenceManager: NSObject { } /// 参数是 PreviewDimension - @objc let previewDimension: IntegerMessageAttribute + let previewDimension: IntegerMessageAttribute - @objc let previewScale: DoubleMessageAttribute + let previewScale: DoubleMessageAttribute /// 参数是 MeasureState - @objc let measureState: IntegerMessageAttribute + let measureState: IntegerMessageAttribute /// 是否用户正在按住 cmd 键而处于快速选择模式 - @objc let isQuickSelecting: BoolMessageAttribute + let isQuickSelecting: BoolMessageAttribute // MARK: - Init @@ -376,7 +376,6 @@ final class PreferenceManager: NSObject { /// 读取某 bundle id 下记录的展开/折叠状态。key 为结构路径,value 为 @(YES) 表示展开、@(NO) 表示折叠。 /// 未命中返回空字典。为兼容早期仅记录 expanded paths 的存档(NSArray), /// 读到旧格式时会自动转换为 dict 形式(全部视为 @(YES))。 - @objc(expansionStateForBundleIdentifier:) func expansionState(forBundleIdentifier bundleIdentifier: String?) -> [String: NSNumber] { guard let bundleIdentifier, !bundleIdentifier.isEmpty else { return [:] @@ -388,7 +387,6 @@ final class PreferenceManager: NSObject { /// 写入某 bundle id 下记录的展开/折叠状态,并将该 bundle id 提升到 LRU 队首。 /// 当 LRU 超过容量上限时,最旧的 bundle id 会被静默移除(其对应的展开状态键也会被清理)。 /// 若 rememberExpansionState 为 NO 或 bundleIdentifier 为空,则不做任何操作。 - @objc(setExpansionState:forBundleIdentifier:) func setExpansionState(_ expansionState: [String: NSNumber]?, forBundleIdentifier bundleIdentifier: String?) { guard let bundleIdentifier, !bundleIdentifier.isEmpty, rememberExpansionState, shouldStoreToLocal else { return @@ -404,7 +402,6 @@ final class PreferenceManager: NSObject { /// 将一个已记录的 bundle id 提升到 LRU 队首。若该 bundle id 未被记录则不做任何操作。 /// 当 rememberExpansionState 为 NO 或 bundleIdentifier 为空时同样不做任何操作。 - @objc(bumpExpansionStateBundleIdentifierToMostRecent:) func bumpExpansionStateBundleIdentifierToMostRecent(_ bundleIdentifier: String?) { guard let bundleIdentifier, !bundleIdentifier.isEmpty, rememberExpansionState, shouldStoreToLocal else { return @@ -446,7 +443,7 @@ final class PreferenceManager: NSObject { // MARK: - Dashboard sections /// 返回某个 section 是否应该被显示在主界面上 - @objc func isSectionShowing(_ secID: String) -> Bool { + func isSectionShowing(_ secID: String) -> Bool { if let stored = storedSectionShowConfig[secID] { return stored } @@ -454,12 +451,12 @@ final class PreferenceManager: NSObject { } /// 把某个 section 显示在主界面上 - @objc func showSection(_ secID: String) { + func showSection(_ secID: String) { setSection(secID, showing: true) } /// 把某个 section 从主界面上移除 - @objc func hideSection(_ secID: String) { + func hideSection(_ secID: String) { setSection(secID, showing: false) } @@ -474,7 +471,7 @@ final class PreferenceManager: NSObject { defaults.set(storedSectionShowConfig, forKey: Key.sectionsShow) } - @objc func reset() {} + func reset() {} /// 返回默认情况下,哪些 section 应该被显示在主界面上 private static let sectionsShowingByDefault: Set = Set(([ diff --git a/LookInside/Measure/MeasureController.swift b/LookInside/Measure/MeasureController.swift index e22e928..7d3cb64 100644 --- a/LookInside/Measure/MeasureController.swift +++ b/LookInside/Measure/MeasureController.swift @@ -23,7 +23,6 @@ final class MeasureController: BaseViewController { private var lockSwitchButton: NSButton? private var itemObservations: [NSKeyValueObservation] = [] - @objc(initWithDataSource:) init(dataSource: HierarchyDataSource) { self.dataSource = dataSource super.init(containerView: nil) diff --git a/LookInside/Other/VersionComparer.swift b/LookInside/Other/VersionComparer.swift index db83fd9..fa877da 100644 --- a/LookInside/Other/VersionComparer.swift +++ b/LookInside/Other/VersionComparer.swift @@ -11,14 +11,12 @@ import LookInsideHostCore final class VersionComparer: NSObject { /// latest 指网上最高的 LookinServer 版本号,user 指用户真实的版本号。如果用户版本号低于最高版本号,则该方法返回 NO,此时应该提示用户升级。 - @objc(compareWithNewest:user:) static func compare(newest latest: String?, user: String?) -> Bool { compare(expectedVersion: latest, realVersion: user) } /// 如果 realVersion 等于或高于 expectedVersion,则该方法返回 YES(换句话说:用户的实际版本号满足我们的需要) /// A nil version reads as unparseable. - @objc(compareWithExpectedVersion:realVersion:) static func compare(expectedVersion: String?, realVersion: String?) -> Bool { let expected = expectedVersion ?? "" let real = realVersion ?? "" diff --git a/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift b/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift index cc6ca15..f3baf80 100644 --- a/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift +++ b/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift @@ -64,7 +64,7 @@ struct PrivateDiscriminatorInvalidDiagnostic: Identifiable, Hashable { /// observes the state below through Observation. @Observable final class PrivateDiscriminatorStore: NSObject { - @objc(shared) static let shared = PrivateDiscriminatorStore() + static let shared = PrivateDiscriminatorStore() private(set) var featureEnabled = false private(set) var autosaveEnabled = false @@ -74,7 +74,7 @@ final class PrivateDiscriminatorStore: NSObject { private(set) var isUpdatingDefaultLibrary = false private(set) var lastDefaultLibraryUpdateMessage: String? - @objc var isFeatureEnabled: Bool { + var isFeatureEnabled: Bool { featureEnabled } @@ -247,7 +247,6 @@ final class PrivateDiscriminatorStore: NSObject { } } - @objc(displayTitleForDisplayItem:fallback:) func displayTitle(for _: DisplayItem, fallback: String?) -> String? { guard featureEnabled, let fallback, !fallback.isEmpty else { return fallback @@ -256,7 +255,6 @@ final class PrivateDiscriminatorStore: NSObject { return cleaned.isEmpty ? fallback : cleaned } - @objc(isPrivateDisplayItem:) func isPrivateDisplayItem(_ item: DisplayItem) -> Bool { guard featureEnabled else { return false @@ -264,7 +262,6 @@ final class PrivateDiscriminatorStore: NSObject { return parsedPrivateDiscriminator(for: item) != nil } - @objc(appendingPrivateDiscriminatorGroupToGroups:forDisplayItem:) func appendingPrivateDiscriminatorGroup(to groups: [AttributesGroup], for item: DisplayItem) -> [AttributesGroup] { guard featureEnabled, let descriptor = dashboardDescriptor(for: item) else { return groups @@ -280,7 +277,6 @@ final class PrivateDiscriminatorStore: NSObject { return result } - @objc(submitPrivateDiscriminatorForDisplayItem:module:filename:error:) func submitPrivateDiscriminator(for item: DisplayItem, module: String, filename: String, error: NSErrorPointer) -> Bool { do { guard let parsed = parsedPrivateDiscriminator(for: item) else { @@ -306,7 +302,6 @@ final class PrivateDiscriminatorStore: NSObject { } } - @objc(importPrivateDiscriminatorFromCodebaseForDisplayItem:window:error:) func importPrivateDiscriminatorFromCodebase(for item: DisplayItem, window: NSWindow?, error: NSErrorPointer) -> Bool { do { guard let parsed = parsedPrivateDiscriminator(for: item) else { @@ -340,7 +335,6 @@ final class PrivateDiscriminatorStore: NSObject { } } - @objc(beginSwiftPDGuessForDisplayItem:window:) func beginSwiftPDGuess(for item: DisplayItem, window: NSWindow?) { guard let parsed = parsedPrivateDiscriminator(for: item) else { return @@ -407,7 +401,6 @@ final class PrivateDiscriminatorStore: NSObject { } } - @objc(cancelSwiftPDGuessForDisplayItem:) func cancelSwiftPDGuess(for item: DisplayItem) { guard let parsed = parsedPrivateDiscriminator(for: item), let task = activeGuessTasksByID[parsed.id] @@ -1389,17 +1382,17 @@ private enum GuessState { } final class PrivateDiscriminatorDashboardPayload: NSObject { - @objc let discriminatorID: String - @objc let module: String - @objc let filename: String - @objc let source: String - @objc let displayClassName: String - @objc let isMatched: Bool - @objc let isVerified: Bool - @objc let warningText: String? - @objc let guessStatusText: String? - @objc let isGuessRunning: Bool - @objc let isGuessFailed: Bool + let discriminatorID: String + let module: String + let filename: String + let source: String + let displayClassName: String + let isMatched: Bool + let isVerified: Bool + let warningText: String? + let guessStatusText: String? + let isGuessRunning: Bool + let isGuessFailed: Bool fileprivate init(descriptor: DashboardDescriptor) { discriminatorID = descriptor.id diff --git a/LookInside/Read/ArchiveDocument.swift b/LookInside/Read/ArchiveDocument.swift index 5154040..6903333 100644 --- a/LookInside/Read/ArchiveDocument.swift +++ b/LookInside/Read/ArchiveDocument.swift @@ -42,14 +42,13 @@ class ArchiveDocument: NSDocument { /// show-backing-layers setting. A `.lookin` archive cannot: its tree is what /// the server sent. An imported Xcode capture can, from the decoded capture /// it keeps after import. - @objc func canRebuildHierarchyFile() -> Bool { + func canRebuildHierarchyFile() -> Bool { false } /// Rebuilds `hierarchyFile` for the setting and assigns the new file when it /// is ready; the window observes the property and rebuilds its reader. A /// no-op unless canRebuildHierarchyFile. - @objc(rebuildHierarchyFileShowingBackingLayers:) func rebuildHierarchyFile(showingBackingLayers _: Bool) { // An archive holds the tree the server sent; there is nothing to rebuild from. } diff --git a/LookInside/Read/ReadHierarchyDataSource.swift b/LookInside/Read/ReadHierarchyDataSource.swift index 40c6ce1..e938995 100644 --- a/LookInside/Read/ReadHierarchyDataSource.swift +++ b/LookInside/Read/ReadHierarchyDataSource.swift @@ -14,7 +14,6 @@ import AppKit final class ReadHierarchyDataSource: HierarchyDataSource { private let readPreferenceManager: PreferenceManager - @objc(initWithFile:preferenceManager:) init(file: HierarchyFile, preferenceManager manager: PreferenceManager) { readPreferenceManager = manager super.init() diff --git a/LookInside/Read/ReadViewController.swift b/LookInside/Read/ReadViewController.swift index b95b0c0..85bae1f 100644 --- a/LookInside/Read/ReadViewController.swift +++ b/LookInside/Read/ReadViewController.swift @@ -12,7 +12,7 @@ import AppKit /// preview, the dashboard or the measure panel on the right; and a tip /// while the tree is in focus mode. final class ReadViewController: BaseViewController, NSSplitViewDelegate { - @objc let hierarchyDataSource: ReadHierarchyDataSource + let hierarchyDataSource: ReadHierarchyDataSource private let splitView = SplitView() private let hierarchyController: ReadHierarchyController @@ -23,7 +23,6 @@ final class ReadViewController: BaseViewController, NSSplitViewDelegate { private let focusTipView = YellowTipsView() private var stateObservation: NSKeyValueObservation? - @objc(initWithFile:preferenceManager:) init(file: HierarchyFile, preferenceManager manager: PreferenceManager) { let dataSource = ReadHierarchyDataSource(file: file, preferenceManager: manager) hierarchyDataSource = dataSource @@ -96,7 +95,7 @@ final class ReadViewController: BaseViewController, NSSplitViewDelegate { } /// The reader's hierarchy list. - @objc func currentHierarchyView() -> HierarchyView? { + func currentHierarchyView() -> HierarchyView? { hierarchyController.hierarchyView } diff --git a/LookInside/Read/ReadWindowController.swift b/LookInside/Read/ReadWindowController.swift index 0faf461..0d7a210 100644 --- a/LookInside/Read/ReadWindowController.swift +++ b/LookInside/Read/ReadWindowController.swift @@ -18,7 +18,7 @@ final class ReadWindowController: WindowController, NSToolbarDelegate { /// This window's own preference set: the reader's view settings, including /// the show-backing-layers toggle, are per document window. A document that /// can rebuild its tree reads the toggle from here. - @objc let preferenceManager = PreferenceManager() + let preferenceManager = PreferenceManager() private var viewController: ReadViewController? private var toolbarItemsMap: [String: NSToolbarItem] = [:] @@ -26,7 +26,6 @@ final class ReadWindowController: WindowController, NSToolbarDelegate { private var selectedItemObservation: NSKeyValueObservation? private weak var shownFile: HierarchyFile? - @objc(initWithDocument:) init(document: ArchiveDocument) { let screenSize = NSScreen.main?.frame.size ?? .zero let window = AppWindow( diff --git a/LookInside/Static/MessageManager.swift b/LookInside/Static/MessageManager.swift index 2933a32..98dc34a 100644 --- a/LookInside/Static/MessageManager.swift +++ b/LookInside/Static/MessageManager.swift @@ -17,7 +17,7 @@ final class MessageManager: NSObject { private static let shared = MessageManager() - @objc class func sharedInstance() -> MessageManager { + class func sharedInstance() -> MessageManager { shared } @@ -27,12 +27,10 @@ final class MessageManager: NSObject { super.init() } - @objc(addMessage:) func addMessage(_ message: String) { messages.insert(message) } - @objc(removeMessage:) func removeMessage(_ message: String) { messages.remove(message) if message == jobsMessageIdentifier { @@ -41,14 +39,14 @@ final class MessageManager: NSObject { } /// The current messages, shortest first. - @objc func queryMessages() -> [String] { + func queryMessages() -> [String] { // A stable sort over an unordered set: ties keep whatever order the // set yields, as NSSet's -allObjects did. Array(messages).sorted { $0.utf16.count < $1.utf16.count } } #if DEBUG - @objc func reset() { + func reset() { UserDefaults.standard.removeObject(forKey: Self.hasReadJobsDefaultsKey) } #endif diff --git a/LookInside/Static/Preview/PreviewController.swift b/LookInside/Static/Preview/PreviewController.swift index 87f18dc..3023a34 100644 --- a/LookInside/Static/Preview/PreviewController.swift +++ b/LookInside/Static/Preview/PreviewController.swift @@ -11,10 +11,10 @@ import AppKit /// Hosts the 3D preview and turns mouse, trackpad and keyboard input into /// rotation, translation, zoom, selection and the context menu. final class PreviewController: BaseViewController, NSGestureRecognizerDelegate, NSMenuDelegate, PreviewStageViewDelegate { - @objc weak var staticViewController: StaticViewController? + weak var staticViewController: StaticViewController? /// Set by the owner in a live window; nil when reading a `.lookin` file. - @objc weak var asyncUpdateManager: StaticAsyncUpdateManager? + weak var asyncUpdateManager: StaticAsyncUpdateManager? private let dataSource: HierarchyDataSource? private let previewView: PreviewView @@ -73,7 +73,6 @@ final class PreviewController: BaseViewController, NSGestureRecognizerDelegate, } } - @objc(initWithDataSource:) init(dataSource: HierarchyDataSource?) { self.dataSource = dataSource previewView = PreviewView(dataSource: dataSource) diff --git a/LookInside/Static/StaticAsyncUpdateManager.swift b/LookInside/Static/StaticAsyncUpdateManager.swift index 85a466b..29197ba 100644 --- a/LookInside/Static/StaticAsyncUpdateManager.swift +++ b/LookInside/Static/StaticAsyncUpdateManager.swift @@ -32,11 +32,11 @@ enum ModifyingUpdateEvent { final class StaticAsyncUpdateManager: NSObject { /// Set by the owner (window controller / live document), which holds it /// strongly. - @objc weak var hierarchyDataSource: StaticHierarchyDataSource? + weak var hierarchyDataSource: StaticHierarchyDataSource? /// The app every request goes to. The owner refreshes it when a new /// hierarchy arrives or the document reconnects. - @objc weak var inspectableApp: InspectableApp? + weak var inspectableApp: InspectableApp? weak var delegate: StaticAsyncUpdateManagerDelegate? @@ -50,7 +50,6 @@ final class StaticAsyncUpdateManager: NSObject { private let dataSourceSignals = SyncSubscriptionBag() - @objc(initWithHierarchyDataSource:inspectableApp:) init(hierarchyDataSource: StaticHierarchyDataSource?, inspectableApp: InspectableApp?) { self.hierarchyDataSource = hierarchyDataSource self.inspectableApp = inspectableApp @@ -71,7 +70,7 @@ final class StaticAsyncUpdateManager: NSObject { // MARK: - Public /// With fast mode off: after a reload, fetches the details of every item. - @objc func updateAll() { + func updateAll() { assert(!PreferenceManager.shared.fastMode.currentBOOLValue) guard inspectableApp != nil, let dataSource, !(dataSource.flatItems ?? []).isEmpty else { return @@ -84,7 +83,7 @@ final class StaticAsyncUpdateManager: NSObject { /// With fast mode on: fetches the details of the visible items that do /// not have them yet. Call it whenever the displayed items change. - @objc func updateForDisplayingItems() { + func updateForDisplayingItems() { assert(PreferenceManager.shared.fastMode.currentBOOLValue) guard inspectableApp != nil else { return } let items = dataSource?.displayingFlatItems ?? [] @@ -95,7 +94,7 @@ final class StaticAsyncUpdateManager: NSObject { } /// Stops the running fetch. - @objc func endUpdating() { + func endUpdating() { guard ongoingRequest != nil else { return } #log(.default, "AsyncUpdate - endUpdating") // Completes the running details request synchronously, which runs @@ -104,7 +103,7 @@ final class StaticAsyncUpdateManager: NSObject { } /// A method rather than a property: Swift callers use `isUpdating()`. - @objc func isUpdating() -> Bool { + func isUpdating() -> Bool { ongoingRequest != nil } @@ -159,7 +158,6 @@ final class StaticAsyncUpdateManager: NSObject { } /// Fetches one item's screenshots and attributes again, not its sub items. - @objc(reloadSingleDisplayItem:) func reloadSingleDisplayItem(_ item: DisplayItem?) { let tasks = makeReloadSingleItemTasks(item) guard !tasks.isEmpty else { return } @@ -167,7 +165,6 @@ final class StaticAsyncUpdateManager: NSObject { } /// Fetches an item and its sub items again, then their details. - @objc(reloadDisplayItemAndChildren:) func reloadDisplayItemAndChildren(_ rootItem: DisplayItem?) { // First the root item's basis, attributes and sub items. let tasks = makeReloadItemAndChildrenTasks(rootItem) diff --git a/LookInside/Static/StaticConstants.swift b/LookInside/Static/StaticConstants.swift index ebbb793..b43d6c5 100644 --- a/LookInside/Static/StaticConstants.swift +++ b/LookInside/Static/StaticConstants.swift @@ -23,7 +23,7 @@ let previewMaxScale: CGFloat = 1 let previewMinZInterspace: CGFloat = 0 let previewMaxZInterspace: CGFloat = 1 -@objc enum PreviewDimension: UInt { +enum PreviewDimension: UInt { case dimension2D case dimension3D } diff --git a/LookInside/Static/StaticHierarchyDataSource.swift b/LookInside/Static/StaticHierarchyDataSource.swift index 440f8cf..0154c30 100644 --- a/LookInside/Static/StaticHierarchyDataSource.swift +++ b/LookInside/Static/StaticHierarchyDataSource.swift @@ -11,12 +11,12 @@ import AppKit /// The data source of a live document: applies the details the update /// tasks deliver and starts those tasks. class StaticHierarchyDataSource: HierarchyDataSource { - @objc var appInfo: InspectedAppInfo? { + var appInfo: InspectedAppInfo? { storedAppInfo } /// The update manager of the owning window controller. - @objc weak var asyncUpdateManager: StaticAsyncUpdateManager? + weak var asyncUpdateManager: StaticAsyncUpdateManager? /// Sends each item whose frame changed. Also reaches `events()` as /// `.itemDidChangeFrame`. @@ -40,7 +40,6 @@ class StaticHierarchyDataSource: HierarchyDataSource { // MARK: - Reload - @objc(reloadWithHierarchyInfo:keepState:) override func reload(with info: HierarchyInfo?, keepState: Bool) { super.reload(with: info, keepState: keepState) @@ -75,7 +74,6 @@ class StaticHierarchyDataSource: HierarchyDataSource { } } - @objc(modifyWithDisplayItemDetail:) func modify(with detail: DisplayItemDetail?) { guard let detail else { return diff --git a/LookInside/Static/StaticViewController.swift b/LookInside/Static/StaticViewController.swift index a01c37c..c145a1c 100644 --- a/LookInside/Static/StaticViewController.swift +++ b/LookInside/Static/StaticViewController.swift @@ -16,14 +16,14 @@ final class StaticViewController: BaseViewController, NSSplitViewDelegate { private static let ignoreFastModeTipsDefaultsKey = "IgnoreFastModeTips" /// The per-document data source, owned by StaticWindowController. - @objc private(set) weak var hierarchyDataSource: StaticHierarchyDataSource? + private(set) weak var hierarchyDataSource: StaticHierarchyDataSource? /// The per-document update manager, owned by StaticWindowController. - @objc private(set) weak var asyncUpdateManager: StaticAsyncUpdateManager? + private(set) weak var asyncUpdateManager: StaticAsyncUpdateManager? - @objc let viewsPreviewController: PreviewController + let viewsPreviewController: PreviewController - @objc let progressView = ProgressIndicatorView() + let progressView = ProgressIndicatorView() /// Shows or hides the console under the preview. Observable with KVO. @objc dynamic var showConsole = false { @@ -58,7 +58,6 @@ final class StaticViewController: BaseViewController, NSSplitViewDelegate { /// children that read the data source and the update manager, so they /// are stored first, and the child controllers are created here, in the /// order buildContent() used to create them. - @objc(initWithHierarchyDataSource:asyncUpdateManager:) init(hierarchyDataSource: StaticHierarchyDataSource, asyncUpdateManager: StaticAsyncUpdateManager) { self.hierarchyDataSource = hierarchyDataSource self.asyncUpdateManager = asyncUpdateManager @@ -409,7 +408,7 @@ final class StaticViewController: BaseViewController, NSSplitViewDelegate { } /// The hierarchy view of the tree on the left. - @objc func currentHierarchyView() -> HierarchyView? { + func currentHierarchyView() -> HierarchyView? { hierarchyController.hierarchyView } diff --git a/LookInside/Static/StaticWindowController.swift b/LookInside/Static/StaticWindowController.swift index 368c772..706be4d 100644 --- a/LookInside/Static/StaticWindowController.swift +++ b/LookInside/Static/StaticWindowController.swift @@ -64,13 +64,13 @@ final class StaticWindowController: WindowController, NSToolbarDelegate, @precon case noResponse } - @objc let viewController: StaticViewController + let viewController: StaticViewController /// Per-window hierarchy data source. - @objc let hierarchyDataSource: StaticHierarchyDataSource + let hierarchyDataSource: StaticHierarchyDataSource /// Per-window async update manager. - @objc let asyncUpdateManager: StaticAsyncUpdateManager + let asyncUpdateManager: StaticAsyncUpdateManager /// Injected by the owning LiveDocument, which also swaps it when /// the app reconnects; every request uses it. @@ -109,7 +109,6 @@ final class StaticWindowController: WindowController, NSToolbarDelegate, @precon /// Used by LiveDocument: the window, data source and update /// manager, all bound to `app`. - @objc(initWithInspectableApp:) init(inspectableApp app: InspectableApp?) { let screenSize = NSScreen.main?.frame.size ?? .zero let window = AppWindow( @@ -265,7 +264,6 @@ final class StaticWindowController: WindowController, NSToolbarDelegate, @precon } } - @objc(popupAllInspectableAppsWithSource:) func popupAllInspectableApps(with source: MenuPopoverAppsListControllerEventSource) { let appItemView = toolbarItemsMap[NSToolbarItem.Identifier(toolbarIdentifierApp)]?.view diff --git a/LookInside/SwiftUISupport/SwiftUIHierarchyDisplayMode.swift b/LookInside/SwiftUISupport/SwiftUIHierarchyDisplayMode.swift index 3c17d54..7bc98bc 100644 --- a/LookInside/SwiftUISupport/SwiftUIHierarchyDisplayMode.swift +++ b/LookInside/SwiftUISupport/SwiftUIHierarchyDisplayMode.swift @@ -5,7 +5,7 @@ import AppKit -@objc enum SwiftUIHierarchyDisplayMode: Int { +enum SwiftUIHierarchyDisplayMode: Int { case verbose = 0 case compact = 1 } diff --git a/LookInside/SwiftUISupport/SwiftUISupportGatekeeper.swift b/LookInside/SwiftUISupport/SwiftUISupportGatekeeper.swift index 58c6d97..51e514f 100644 --- a/LookInside/SwiftUISupport/SwiftUISupportGatekeeper.swift +++ b/LookInside/SwiftUISupport/SwiftUISupportGatekeeper.swift @@ -4,7 +4,7 @@ import LookInsideActivation import LookInsideActivationUI import FoundationToolbox -@objc public enum SwiftUISupportActivationState: Int, CustomDebugStringConvertible { +public enum SwiftUISupportActivationState: Int, CustomDebugStringConvertible { case unknown case notActivated case activated @@ -179,7 +179,7 @@ public final class SwiftUISupportGatekeeper: NSObject { } } - @objc public class func sharedInstance() -> SwiftUISupportGatekeeper { + public class func sharedInstance() -> SwiftUISupportGatekeeper { shared } @@ -207,7 +207,6 @@ public final class SwiftUISupportGatekeeper: NSObject { /// Stops the periodic re-evaluation when LookInside quits. The activation /// state on disk is left as it is. - @objc(shutdownRuntime) public func shutdownRuntime() { runtime.stopMonitoring() } @@ -242,7 +241,6 @@ public final class SwiftUISupportGatekeeper: NSObject { /// Callers include reloads and connection flows, so the alert for a /// warning or a block is a sheet on `window` and never a modal loop. - @objc(allowProtectedFeatureAccessForWindow:) public func allowProtectedFeatureAccess(for window: NSWindow?) -> Bool { let decision = accessDecisionForSynchronousCaller() switch decision.decision { @@ -262,7 +260,6 @@ public final class SwiftUISupportGatekeeper: NSObject { } } - @objc(canUseProtectedFeatureWithoutPrompt) public func canUseProtectedFeatureWithoutPrompt() -> Bool { accessDecisionForSynchronousCaller().grantsAccess } @@ -314,20 +311,17 @@ public final class SwiftUISupportGatekeeper: NSObject { /// wait ends. Every handshake attempt, including a retry with a fresh /// challenge, asks first. A request on a channel without a handshake /// still runs; the Server then withholds the Pro features. - @objc(shouldStartLicenseHandshakeOnChannel:) public func shouldStartLicenseHandshake(onChannel channelID: String) -> Bool { runtime.allowsLicenseHandshake(onChannel: channelID) } /// Records that the Server on `channelID` rejected the 221 or sent a /// malformed 220, so the handshake is not repeated on that channel. - @objc(noteLicenseHandshakeFailedOnChannel:) public func noteLicenseHandshakeFailed(onChannel channelID: String) { runtime.noteLicenseHandshakeFailed(onChannel: channelID) } /// Forgets `channelID` after it disconnected. - @objc(licenseHandshakeChannelDidEnd:) public func licenseHandshakeChannelDidEnd(_ channelID: String) { runtime.noteLicenseHandshakeChannelEnded(channelID) } @@ -341,7 +335,6 @@ public final class SwiftUISupportGatekeeper: NSObject { /// the key back or signing fails. /// /// Blocks until the signature is ready; call it off the main thread. - @objc(signChallengeWithNonce:serverInstanceID:signature:intermediateCertDER:udid:error:) public func signChallenge( nonce: Data, serverInstanceID: String, @@ -365,7 +358,6 @@ public final class SwiftUISupportGatekeeper: NSObject { /// on that channel. `keyUseDuration`, when given, receives how long the /// key took in its turn (the keychain's time, including a prompt), /// without the time this call waited behind other uses of the key. - @objc(signChallengeWithNonce:serverInstanceID:channelID:signature:intermediateCertDER:udid:keyUseDuration:error:) public func signChallenge( nonce: Data, serverInstanceID: String, @@ -397,14 +389,12 @@ public final class SwiftUISupportGatekeeper: NSObject { // MARK: - Windows - @objc(showActivationWindow) public func showActivationWindow() { onMain { coordinator in Task { await coordinator.showActivationWindow() } } } - @objc(showLicenseWindow) public func showLicenseWindow() { onMain { coordinator in Task { await coordinator.showLicenseWindow() } @@ -413,7 +403,6 @@ public final class SwiftUISupportGatekeeper: NSObject { /// Fetches the license status from the activation service, shows it in /// the license window and reports the result in an alert. - @objc(refreshLicenseStatus) public func refreshLicenseStatus() { let shouldStart: Bool = stateLock.withLock { guard !licenseStatusRefreshInFlight else { return false } @@ -458,13 +447,11 @@ public final class SwiftUISupportGatekeeper: NSObject { // MARK: - SwiftUI detection prompt - @objc(promptForDetectedSwiftUISupportIfNeededForWindow:) public func promptForDetectedSwiftUISupportIfNeeded(window: NSWindow?) { noteDetectedSwiftUISupport() promptForPendingDetectedSwiftUISupportIfNeeded(window: window) } - @objc(noteDetectedSwiftUISupport) public func noteDetectedSwiftUISupport() { guard activationState != .activated else { return } @@ -474,7 +461,6 @@ public final class SwiftUISupportGatekeeper: NSObject { } } - @objc(promptForPendingDetectedSwiftUISupportIfNeededForWindow:) public func promptForPendingDetectedSwiftUISupportIfNeeded(window: NSWindow?) { guard activationState != .activated else { return } @@ -613,7 +599,6 @@ private final class SwiftUISupportDecisionBox: @unchecked Sendable { /// DEBUG end-to-end hook (DebugE2EDump.swift): creates the license key /// in the test file keychain and returns a certificate request for /// it. See `ActivationRuntime.debugMakeTestKeyCertificateSigningRequestPEM`. - @objc(debugMakeTestKeyCertificateSigningRequestPEMWithCommonName:error:) func debugMakeTestKeyCertificateSigningRequestPEM(commonName: String) throws -> String { try runtime.debugMakeTestKeyCertificateSigningRequestPEM(commonName: commonName) } @@ -622,7 +607,6 @@ private final class SwiftUISupportDecisionBox: @unchecked Sendable { /// other apps there, so its window never becomes key. The dump calls /// this once a Host window is visible, the condition this gatekeeper /// itself accepts when it is created after the first window. - @objc(debugNoteFirstWindowShown) func debugNoteFirstWindowShown() { runtime.noteFirstWindowShown() } diff --git a/LookInside/Toolbar/MenuPopoverAppsListController.swift b/LookInside/Toolbar/MenuPopoverAppsListController.swift index 7e3d5e8..6b03c89 100644 --- a/LookInside/Toolbar/MenuPopoverAppsListController.swift +++ b/LookInside/Toolbar/MenuPopoverAppsListController.swift @@ -20,9 +20,8 @@ final class MenuPopoverAppsListController: BaseViewController { private var titleLabel: TextLabel? private var subtitleLabel: TextLabel? - @objc var didSelectApp: ((InspectableApp) -> Void)? + var didSelectApp: ((InspectableApp) -> Void)? - @objc(initWithApps:source:) init(apps: [InspectableApp]?, source: MenuPopoverAppsListControllerEventSource) { // The apps scan sends nil when no port is connected. let apps = apps ?? [] @@ -93,7 +92,7 @@ final class MenuPopoverAppsListController: BaseViewController { didSelectApp?(app) } - @objc func bestSize() -> NSSize { + func bestSize() -> NSSize { guard !appViews.isEmpty else { return NSSize(width: 245, height: 80) } diff --git a/LookInside/Toolbar/MenuPopoverSettingController.swift b/LookInside/Toolbar/MenuPopoverSettingController.swift index 632203e..832aaba 100644 --- a/LookInside/Toolbar/MenuPopoverSettingController.swift +++ b/LookInside/Toolbar/MenuPopoverSettingController.swift @@ -24,7 +24,6 @@ final class MenuPopoverSettingController: BaseViewController { /// `isMacTarget` describes the app being inspected, not the host. It decides whether the /// copy talks about `NSView` or `UIView`. - @objc(initWithPreferenceManager:isMacTarget:) init(preferenceManager manager: PreferenceManager, isMacTarget: Bool) { self.manager = manager super.init(containerView: nil) diff --git a/LookInside/Toolbar/ToolbarRules.swift b/LookInside/Toolbar/ToolbarRules.swift index a7f344a..a41b23e 100644 --- a/LookInside/Toolbar/ToolbarRules.swift +++ b/LookInside/Toolbar/ToolbarRules.swift @@ -27,7 +27,7 @@ let toolbarIdentifierSwiftUIMode = "20" let toolbarIdentifierGestureDebug = "21" /// What opened the apps popover. -@objc enum MenuPopoverAppsListControllerEventSource: Int { +enum MenuPopoverAppsListControllerEventSource: Int { case reloadButton case noConnectionTips case appButton diff --git a/LookInside/Toolbar/WindowToolbarAppButton.swift b/LookInside/Toolbar/WindowToolbarAppButton.swift index 8218478..58b480a 100644 --- a/LookInside/Toolbar/WindowToolbarAppButton.swift +++ b/LookInside/Toolbar/WindowToolbarAppButton.swift @@ -55,7 +55,7 @@ final class WindowToolbarAppButton: NSButton { TwoColors(colorInLightMode: NSColor(red: 65 / 255.0, green: 65 / 255.0, blue: 65 / 255.0, alpha: 1), colorInDarkMode: .labelColor) } - @objc var appInfo: InspectedAppInfo? { + var appInfo: InspectedAppInfo? { didSet { appInfoDidChange() } } diff --git a/LookInside/Toolbar/WindowToolbarHelper.swift b/LookInside/Toolbar/WindowToolbarHelper.swift index 6e34797..783b10c 100644 --- a/LookInside/Toolbar/WindowToolbarHelper.swift +++ b/LookInside/Toolbar/WindowToolbarHelper.swift @@ -15,7 +15,6 @@ import AppKit /// others itself: Reload, App, Setting, Console, Add, Remove and Message. @MainActor final class WindowToolbarHelper: NSObject { - @objc(sharedInstance) static let shared = WindowToolbarHelper() override private init() { @@ -24,7 +23,6 @@ final class WindowToolbarHelper: NSObject { /// The item for `identifier`. Use `makeAppInReadModeItem(with:)` for the /// reader's app item. - @objc(makeToolBarItemWithIdentifier:preferenceManager:) func makeToolBarItem(identifier: String, preferenceManager manager: PreferenceManager) -> NSToolbarItem? { assert(identifier != toolbarIdentifierAppInReadMode, "Use makeAppInReadModeItemWithAppInfo:") @@ -138,7 +136,6 @@ final class WindowToolbarHelper: NSObject { } /// The reader's app item, showing `appInfo`. - @objc(makeAppInReadModeItemWithAppInfo:) func makeAppInReadModeItem(with appInfo: InspectedAppInfo?) -> NSToolbarItem { let button = WindowToolbarAppButton() button.bezelStyle = .texturedRounded From a121233b69988468b6bac74f1fc92b44191a022c Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Mon, 12 Oct 2026 08:34:10 +0800 Subject: [PATCH 3/5] Call the members read by name directly Five host members were reached through the Objective-C runtime from Swift code. The DEBUG UI snapshots read StaticViewController's dashboardController, DashboardViewController's scrollView and HierarchyView's searchTextFieldView with value(forKey:); the Dashboard sent updateAfterModifyingDisplayItem: to StaticAsyncUpdateManager with perform(_:with:); and DisplayItem's optionalAppropriateScreenshot read appropriateScreenshot with value(forKey:), because the Objective-C header the method used to be declared in promised a non-nil image. All five are now ordinary property reads and calls, so the members lose @objc. optionalAppropriateScreenshot goes: appropriateScreenshot() is a Swift method now, and its optional result already says it can be nil. In a Debug build, exactly these five @objc thunks go away. --- .../Base/Category/DisplayItem+LookinClient.swift | 2 +- LookInside/Dashboard/DashboardViewController.swift | 6 +++--- LookInside/DebugE2E/DebugUISnapshots.swift | 13 +++++-------- LookInside/Hierarchy/HierarchyView.swift | 5 ++--- LookInside/Static/Preview/DisplayItemNode.swift | 13 ++----------- LookInside/Static/StaticAsyncUpdateManager.swift | 5 ++--- LookInside/Static/StaticViewController.swift | 6 +++--- 7 files changed, 18 insertions(+), 32 deletions(-) diff --git a/LookInside/Base/Category/DisplayItem+LookinClient.swift b/LookInside/Base/Category/DisplayItem+LookinClient.swift index c24fdb9..d595fd3 100644 --- a/LookInside/Base/Category/DisplayItem+LookinClient.swift +++ b/LookInside/Base/Category/DisplayItem+LookinClient.swift @@ -51,7 +51,7 @@ extension DisplayItem { /// The solo screenshot once children that draw something are shown on /// their own; the group screenshot otherwise. - @objc func appropriateScreenshot() -> NSImage? { + func appropriateScreenshot() -> NSImage? { // A node whose children are all pixelless (layout guides, cells, a // view's outer layer) has nothing to exclude, and a leaf view has no // solo screenshot at all — asking for one there blanks it out. diff --git a/LookInside/Dashboard/DashboardViewController.swift b/LookInside/Dashboard/DashboardViewController.swift index 7571285..96c37e1 100644 --- a/LookInside/Dashboard/DashboardViewController.swift +++ b/LookInside/Dashboard/DashboardViewController.swift @@ -15,8 +15,8 @@ import FoundationToolbox /// document read from a file is read-only. @Loggable(subsystem: "com.lookinside.app") final class DashboardViewController: BaseViewController, DashboardCardViewDelegate, DashboardHeaderViewDelegate, DashboardSearchPropertyViewDelegate, DashboardSearchMethodsViewDelegate { - /// Read by key by the DEBUG UI snapshots. - @objc let scrollView = NSScrollView() + /// Read by the DEBUG UI snapshots. + let scrollView = NSScrollView() private let documentView = BaseView() private let cardContainerView = BaseView() private let searchContainerView = BaseView() @@ -315,7 +315,7 @@ final class DashboardViewController: BaseViewController, DashboardCardViewDelega DashboardTextControlEditingFlag.shared.shouldIgnoreTextEditingChangeEvent = false if DashboardBlueprint.needPatchAfterModification(withAttrID: attribute.identifier), let modifyingItem { - _ = asyncUpdateManager?.perform(Selector(("updateAfterModifyingDisplayItem:")), with: modifyingItem) + asyncUpdateManager?.updateAfterModifyingDisplayItem(modifyingItem) } } diff --git a/LookInside/DebugE2E/DebugUISnapshots.swift b/LookInside/DebugE2E/DebugUISnapshots.swift index 22f01e8..d9fd069 100644 --- a/LookInside/DebugE2E/DebugUISnapshots.swift +++ b/LookInside/DebugE2E/DebugUISnapshots.swift @@ -313,18 +313,15 @@ /// Types into the hierarchy's filter field as the user would; nil /// ends the filter through its close button path. private func filterHierarchy(_ viewController: StaticViewController, text: String?) async throws { - guard let hierarchyView = viewController.currentHierarchyView(), - let field = hierarchyView.value(forKey: "searchTextFieldView") as? TextFieldView - else { throw SnapshotError("the hierarchy view has no filter field") } - field.textField.stringValue = text ?? "" + guard let hierarchyView = viewController.currentHierarchyView() + else { throw SnapshotError("the inspector has no hierarchy view") } + hierarchyView.searchTextFieldView.textField.stringValue = text ?? "" hierarchyView.delegate?.hierarchyView(hierarchyView, didInputSearch: text) } private func dashboardContentView(of viewController: StaticViewController) throws -> NSView { - guard let dashboard = viewController.value(forKey: "dashboardController") as? NSViewController, - let scrollView = dashboard.value(forKey: "scrollView") as? NSScrollView, - let content = scrollView.documentView - else { throw SnapshotError("the dashboard has no scroll view") } + guard let content = viewController.dashboardController.scrollView.documentView + else { throw SnapshotError("the dashboard's scroll view has no document view") } return content } diff --git a/LookInside/Hierarchy/HierarchyView.swift b/LookInside/Hierarchy/HierarchyView.swift index 4e835c5..b9e8921 100644 --- a/LookInside/Hierarchy/HierarchyView.swift +++ b/LookInside/Hierarchy/HierarchyView.swift @@ -64,9 +64,8 @@ protocol HierarchyViewDelegate: AnyObject { class HierarchyView: BaseView { let tableView: TableView - /// The filter field under the rows. The DEBUG UI snapshots type into it - /// and read it by key. - @objc let searchTextFieldView: TextFieldView + /// The filter field under the rows. The DEBUG UI snapshots type into it. + let searchTextFieldView: TextFieldView var dataSource: HierarchyDataSource /// Phase A 引入:由 owner(StaticHierarchyController / StaticViewController 链路)注入的 per-instance update manager(weak)。 /// 若 nil 则 fallback 到 +sharedInstance,以保留 read-only / archive workspace 的 legacy 行为。 diff --git a/LookInside/Static/Preview/DisplayItemNode.swift b/LookInside/Static/Preview/DisplayItemNode.swift index 0cd7fa4..cec36f6 100644 --- a/LookInside/Static/Preview/DisplayItemNode.swift +++ b/LookInside/Static/Preview/DisplayItemNode.swift @@ -267,7 +267,7 @@ final class DisplayItemNode: SCNNode, DisplayItemDelegate { let isSelected = dataSource?.selectedItem === displayItem let isHovered = dataSource?.hoveredItem === displayItem - let screenshot = displayItem?.optionalAppropriateScreenshot + let screenshot = displayItem?.appropriateScreenshot() if let rep = screenshot?.representations.first { assert(CGFloat(Swift.max(rep.pixelsWide, rep.pixelsHigh)) <= CGFloat(LookinNodeImageMaxLengthInPx), "image is too large") } @@ -377,7 +377,7 @@ final class DisplayItemNode: SCNNode, DisplayItemDelegate { maskPlane.width = contentPlane.width maskPlane.height = contentPlane.height if !regionNode.isHidden { - let region = region(of: self.displayItem?.optionalAppropriateScreenshot) ?? .zero + let region = region(of: self.displayItem?.appropriateScreenshot()) ?? .zero layoutRegionNode(region: region, frameToRoot: frameToRoot) } @@ -406,12 +406,3 @@ final class DisplayItemNode: SCNNode, DisplayItemDelegate { } } } - -extension DisplayItem { - /// `-appropriateScreenshot`, which returns nil for an item without a - /// screenshot although its header (inside NS_ASSUME_NONNULL) says it - /// never does; read through the runtime so Swift sees the nil. - var optionalAppropriateScreenshot: NSImage? { - value(forKey: "appropriateScreenshot") as? NSImage - } -} diff --git a/LookInside/Static/StaticAsyncUpdateManager.swift b/LookInside/Static/StaticAsyncUpdateManager.swift index 29197ba..9839b97 100644 --- a/LookInside/Static/StaticAsyncUpdateManager.swift +++ b/LookInside/Static/StaticAsyncUpdateManager.swift @@ -110,7 +110,6 @@ final class StaticAsyncUpdateManager: NSObject { /// Fetches the screenshots of `displayItem` (solo and group) and the /// group screenshots of its ancestors after an attribute was modified. /// Progress goes to `modifyingUpdates`. - @objc(updateAfterModifyingDisplayItem:) func updateAfterModifyingDisplayItem(_ displayItem: DisplayItem?) { guard let app = inspectableApp else { return } guard let displayItem else { @@ -195,7 +194,7 @@ final class StaticAsyncUpdateManager: NSObject { return nil } if item.doNotFetchScreenshotReason == .fetchScreenshotPermitted { - // Mirrors -appropriateScreenshot: a node showing its group + // Mirrors appropriateScreenshot(): a node showing its group // screenshot must not have the solo one fetched first, or the // preview waits on an image it will never display. if item.isExpandable, item.isExpanded, item.hasPixelBearingSubitems() { @@ -229,7 +228,7 @@ final class StaticAsyncUpdateManager: NSObject { if item.isUserCustom() { return nil } - if item.optionalAppropriateScreenshot != nil { + if item.appropriateScreenshot() != nil { // Has its image, and so its attributes too. return nil } diff --git a/LookInside/Static/StaticViewController.swift b/LookInside/Static/StaticViewController.swift index c145a1c..c3b63ba 100644 --- a/LookInside/Static/StaticViewController.swift +++ b/LookInside/Static/StaticViewController.swift @@ -43,8 +43,8 @@ final class StaticViewController: BaseViewController, NSSplitViewDelegate { private let fastModeTipView = TipsView() private let serverUpgradeTipView = TipsView() - /// Read with KVC by the DEBUG UI snapshots. - @objc let dashboardController: DashboardViewController + /// Read by the DEBUG UI snapshots. + let dashboardController: DashboardViewController private let hierarchyController: StaticHierarchyController private var consoleController: ConsoleViewController? private let measureController: MeasureController @@ -559,7 +559,7 @@ final class StaticViewController: BaseViewController, NSSplitViewDelegate { let item = dataSource?.selectedItem let showsTooLargeTip = item != nil - && item?.optionalAppropriateScreenshot == nil + && item?.appropriateScreenshot() == nil && item?.doNotFetchScreenshotReason == .doNotFetchScreenshotForTooLarge if tooLargeToSyncScreenshotTipsView.isHidden != !showsTooLargeTip { tooLargeToSyncScreenshotTipsView.isHidden = !showsTooLargeTip From 1ad32ce54c57989ed057f0d5a47ff6c543dce196 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Mon, 12 Oct 2026 08:34:14 +0800 Subject: [PATCH 4/5] Drop the redundant @objc on two overriding initializers TableView's init(frame:) and StaticAsyncUpdateManager's init() override @objc initializers of NSScrollView and NSObject, so Swift makes them @objc whether or not they say so. The built app's Objective-C metadata is the same before and after. --- LookInside/Base/TableView.swift | 2 +- LookInside/Static/StaticAsyncUpdateManager.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LookInside/Base/TableView.swift b/LookInside/Base/TableView.swift index 940d87f..a1a8429 100644 --- a/LookInside/Base/TableView.swift +++ b/LookInside/Base/TableView.swift @@ -71,7 +71,7 @@ class TableView: NSScrollView { /// -1 when no row is selected. private var selectedRow: Int = -1 - @objc override init(frame frameRect: NSRect) { + override init(frame frameRect: NSRect) { let tableView = KeyForwardingTableView() self.tableView = tableView super.init(frame: frameRect) diff --git a/LookInside/Static/StaticAsyncUpdateManager.swift b/LookInside/Static/StaticAsyncUpdateManager.swift index 9839b97..a5df2b1 100644 --- a/LookInside/Static/StaticAsyncUpdateManager.swift +++ b/LookInside/Static/StaticAsyncUpdateManager.swift @@ -59,7 +59,7 @@ final class StaticAsyncUpdateManager: NSObject { } } - @objc override convenience init() { + override convenience init() { self.init(hierarchyDataSource: nil, inspectableApp: nil) } From 5e9e503a3cdca4732345ab9193af84f67cc56fa5 Mon Sep 17 00:00:00 2001 From: Mx-Iris Date: Mon, 12 Oct 2026 08:34:19 +0800 Subject: [PATCH 5/5] Delete members nothing calls Nothing in the host uses these any more; most were kept as Objective-C entry points for callers that no longer exist: - VersionComparer.compare(newest:user:), which only forwarded to compare(expectedVersion:realVersion:) - LiveDocumentController's completion-handler form of openLiveDocument(for:) - PreferenceManager's callStackType and the PreferredCallStackType enum - PrivateDiscriminatorStore.isFeatureEnabled - PrivateDiscriminatorDashboardPayload.displayClassName, and the className of the descriptor it was filled from - LaunchViewController's init(window:) and LaunchWindowController's init(), shorthands for autoEnterOnInitialReload: false In a Debug build, LaunchWindowController's -init and the ivars of the two removed stored properties leave the Objective-C metadata; nothing else there changes. --- .../Connection/LiveDocumentController.swift | 15 ------------ LookInside/Launch/LaunchViewController.swift | 4 ---- .../Launch/LaunchWindowController.swift | 4 ---- LookInside/Manager/PreferenceManager.swift | 23 ------------------- LookInside/Other/VersionComparer.swift | 5 ---- .../PrivateDiscriminatorStore.swift | 9 -------- 6 files changed, 60 deletions(-) diff --git a/LookInside/Connection/LiveDocumentController.swift b/LookInside/Connection/LiveDocumentController.swift index ca5a55e..bb47b92 100644 --- a/LookInside/Connection/LiveDocumentController.swift +++ b/LookInside/Connection/LiveDocumentController.swift @@ -38,21 +38,6 @@ final class LiveDocumentController: NSObject { return (document, false) } - /// Objective-C form of `openLiveDocument(for:)`. `completion` runs on - /// the main thread before this returns; with a nil app it gets - /// `LookinErr_Inner` and no document. - func openLiveDocument( - for app: InspectableApp?, - completion: ((LiveDocument?, Bool, NSError?) -> Void)? - ) { - guard let app else { - completion?(nil, false, ConnectionError.inner) - return - } - let (document, alreadyOpen) = openLiveDocument(for: app) - completion?(document, alreadyOpen, nil) - } - /// The live document whose app uses `channel`, or nil. func liveDocument(for channel: ServerChannel?) -> LiveDocument? { guard let channel else { return nil } diff --git a/LookInside/Launch/LaunchViewController.swift b/LookInside/Launch/LaunchViewController.swift index 6837b0a..e8e5200 100644 --- a/LookInside/Launch/LaunchViewController.swift +++ b/LookInside/Launch/LaunchViewController.swift @@ -38,10 +38,6 @@ final class LaunchViewController: BaseViewController { /// images again. private var appInfos: [InspectedAppInfo]? - convenience init(window: NSWindow?) { - self.init(window: window, autoEnterOnInitialReload: false) - } - init(window: NSWindow?, autoEnterOnInitialReload: Bool) { self.window = window self.autoEnterOnInitialReload = autoEnterOnInitialReload diff --git a/LookInside/Launch/LaunchWindowController.swift b/LookInside/Launch/LaunchWindowController.swift index 804f494..fd77195 100644 --- a/LookInside/Launch/LaunchWindowController.swift +++ b/LookInside/Launch/LaunchWindowController.swift @@ -32,10 +32,6 @@ final class LaunchWindowController: WindowController { contentViewController = launchViewController } - convenience init() { - self.init(autoEnterOnInitialReload: false) - } - @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) is not supported") diff --git a/LookInside/Manager/PreferenceManager.swift b/LookInside/Manager/PreferenceManager.swift index 026d0dd..5f6e102 100644 --- a/LookInside/Manager/PreferenceManager.swift +++ b/LookInside/Manager/PreferenceManager.swift @@ -43,15 +43,6 @@ let NotificationName_DidChangeSectionShowing = "NotificationName_DidChangeSectio case focus } -enum PreferredCallStackType: Int { - /// Formatted and shortened. - case `default` - /// Formatted, complete. - case formattedCompletely - /// The raw call stack. - case raw -} - enum MeasureState: Int { /// Not measuring. case no @@ -234,20 +225,6 @@ final class PreferenceManager: NSObject { // MARK: - Not stored - private var storedCallStackType: PreferredCallStackType = .default - - var callStackType: PreferredCallStackType { - get { storedCallStackType } - set { - if newValue.rawValue < 0 || newValue.rawValue > 2 { - assertionFailure("callStackType out of range: \(newValue.rawValue)") - storedCallStackType = .default - } else { - storedCallStackType = newValue - } - } - } - /// 参数是 PreviewDimension let previewDimension: IntegerMessageAttribute diff --git a/LookInside/Other/VersionComparer.swift b/LookInside/Other/VersionComparer.swift index fa877da..6811ae9 100644 --- a/LookInside/Other/VersionComparer.swift +++ b/LookInside/Other/VersionComparer.swift @@ -10,11 +10,6 @@ import Foundation import LookInsideHostCore final class VersionComparer: NSObject { - /// latest 指网上最高的 LookinServer 版本号,user 指用户真实的版本号。如果用户版本号低于最高版本号,则该方法返回 NO,此时应该提示用户升级。 - static func compare(newest latest: String?, user: String?) -> Bool { - compare(expectedVersion: latest, realVersion: user) - } - /// 如果 realVersion 等于或高于 expectedVersion,则该方法返回 YES(换句话说:用户的实际版本号满足我们的需要) /// A nil version reads as unparseable. static func compare(expectedVersion: String?, realVersion: String?) -> Bool { diff --git a/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift b/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift index f3baf80..a70ccfc 100644 --- a/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift +++ b/LookInside/PrivateDiscriminator/PrivateDiscriminatorStore.swift @@ -74,10 +74,6 @@ final class PrivateDiscriminatorStore: NSObject { private(set) var isUpdatingDefaultLibrary = false private(set) var lastDefaultLibraryUpdateMessage: String? - var isFeatureEnabled: Bool { - featureEnabled - } - let storageDirectoryURL: URL let importedDirectoryURL: URL let autosavedDirectoryURL: URL @@ -736,7 +732,6 @@ final class PrivateDiscriminatorStore: NSObject { isMatched: false, isVerified: false, warning: nil, - className: parsed.className, guessState: dashboardGuessState(for: parsed.id, isMatched: false) ) } @@ -756,7 +751,6 @@ final class PrivateDiscriminatorStore: NSObject { isMatched: true, isVerified: verification.isMatch, warning: verification.isMatch ? nil : Self.message(for: verification), - className: parsed.className, guessState: dashboardGuessState(for: parsed.id, isMatched: true) ) } @@ -1337,7 +1331,6 @@ private struct DashboardDescriptor { let isMatched: Bool let isVerified: Bool let warning: String? - let className: String? let guessState: GuessState? } @@ -1386,7 +1379,6 @@ final class PrivateDiscriminatorDashboardPayload: NSObject { let module: String let filename: String let source: String - let displayClassName: String let isMatched: Bool let isVerified: Bool let warningText: String? @@ -1399,7 +1391,6 @@ final class PrivateDiscriminatorDashboardPayload: NSObject { module = descriptor.module filename = descriptor.filename source = descriptor.source?.localizedDisplayName ?? "" - displayClassName = descriptor.className ?? "" isMatched = descriptor.isMatched isVerified = descriptor.isVerified warningText = descriptor.warning