diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df3a78ac..b57bad5c 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/LookInside/About/AboutWindowController.swift b/LookInside/About/AboutWindowController.swift index ae3401bf..1b679791 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 3204d7f5..24f4184f 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 c31962e3..40a0cfbd 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 60df43fc..79822666 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 470a4c46..52a78f1d 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 50ddec89..d7e9eca2 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 3174f965..d595fd3f 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 } @@ -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. @@ -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 edce78b3..960776be 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 3fb1cf64..fb1aef1e 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 6d3ff62b..da029c8c 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 c0f03bf2..9c1ae81c 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 3a09e779..8f45f38b 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 c5e1eadd..8b51ff11 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 0917812b..e055a049 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 2e0c633e..c5124912 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 736198f0..ef614d99 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 50f10703..79ba32cd 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 658572cc..14df1661 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 9540c2b9..3f9fa00f 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 ccc34ce3..68ae4c95 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 dfb33383..27b31b2a 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 113c1faf..5e493129 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 0f6c2720..b2d89910 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 bf2718ec..6de65006 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 56698609..6541c509 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 4af63c3d..67301a0c 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 ef02ea66..ae3f9195 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 5b7bc5d8..e6fbd076 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 1925c66f..a1a84295 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? @@ -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) @@ -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 fa6ddcb4..36e37415 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 76bd8422..29300074 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 e41599a8..a819d008 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 e8f62c68..959d4a22 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 f15dc3bc..f38d2fbf 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 b2cd5bb7..7801e4f7 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 a74af635..dcb580cc 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 9d822937..9e2e1843 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 3e4fecd3..4dfca6ee 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 175fd7b0..b3db658d 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 00333da1..4196d41c 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 312f569e..bb47b92b 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() { @@ -39,24 +38,7 @@ 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. - @objc(openLiveDocumentForInspectableApp:completion:) - 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. - @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 55983a7b..2f3d0e63 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 c4aaa2c3..86e3fe9d 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 1fd85015..616df946 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 1eedfdfa..9d31a0aa 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 71efe329..e3c07e42 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 d245d374..96c37e1c 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() @@ -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 } @@ -317,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/Dashboard/EnumListRegistry.swift b/LookInside/Dashboard/EnumListRegistry.swift index 78e138e1..8c89219d 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 2178a70a..26ea71b2 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 bf206868..d9fd0698 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) @@ -314,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/DebugE2E/DebugWindowImage.swift b/LookInside/DebugE2E/DebugWindowImage.swift index 0429bb74..49cc69e2 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 08424671..00d9697a 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 cba4cd69..9ac13a10 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 faa3160d..a877603d 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 1c7f94de..72cdb4b2 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 c61bc87c..cfa28fc2 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 d11c13e5..b9e89216 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 行为。 @@ -143,7 +142,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 e2c4727e..e2a14b21 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 b2449b17..63d29739 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 64fa517a..62b02252 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 598e569e..e8e5200a 100644 --- a/LookInside/Launch/LaunchViewController.swift +++ b/LookInside/Launch/LaunchViewController.swift @@ -38,12 +38,6 @@ 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 913fc95b..fd77195d 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,10 +32,6 @@ final class LaunchWindowController: WindowController { contentViewController = launchViewController } - @objc convenience init() { - self.init(autoEnterOnInitialReload: false) - } - @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) is not supported") diff --git a/LookInside/MCPBridge/MCPBridgeServer.swift b/LookInside/MCPBridge/MCPBridgeServer.swift index 2a1dd06d..dece2a33 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 4a02e253..de80d9f8 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 78c524ae..5f6e1020 100644 --- a/LookInside/Manager/PreferenceManager.swift +++ b/LookInside/Manager/PreferenceManager.swift @@ -43,16 +43,7 @@ let NotificationName_DidChangeSectionShowing = "NotificationName_DidChangeSectio case focus } -@objc enum PreferredCallStackType: Int { - /// Formatted and shortened. - case `default` - /// Formatted, complete. - case formattedCompletely - /// The raw call stack. - case raw -} - -@objc enum MeasureState: Int { +enum MeasureState: Int { /// Not measuring. case no /// Measuring until the key is released. @@ -99,7 +90,7 @@ final class PreferenceManager: NSObject { } /// 默认为 NO - @objc var shouldStoreToLocal = false + var shouldStoreToLocal = false @objc dynamic var appearanceType: PreferredAppearanceType { didSet { @@ -115,7 +106,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 +115,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 +164,7 @@ final class PreferenceManager: NSObject { } /// 被折叠的 AttrGroup - @objc dynamic var collapsedAttrGroups: [String] { + var collapsedAttrGroups: [String] { didSet { store(collapsedAttrGroups, forKey: Key.collapsedGroups) } @@ -212,19 +203,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) } @@ -234,30 +225,16 @@ final class PreferenceManager: NSObject { // MARK: - Not stored - private var storedCallStackType: PreferredCallStackType = .default - - @objc dynamic 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 - @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 +353,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 +364,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 +379,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 +420,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 +428,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 +448,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 e22e928b..7d3cb64d 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 db83fd97..6811ae98 100644 --- a/LookInside/Other/VersionComparer.swift +++ b/LookInside/Other/VersionComparer.swift @@ -10,15 +10,8 @@ import Foundation 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 cc6ca15d..a70ccfcb 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,10 +74,6 @@ final class PrivateDiscriminatorStore: NSObject { private(set) var isUpdatingDefaultLibrary = false private(set) var lastDefaultLibraryUpdateMessage: String? - @objc var isFeatureEnabled: Bool { - featureEnabled - } - let storageDirectoryURL: URL let importedDirectoryURL: URL let autosavedDirectoryURL: URL @@ -247,7 +243,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 +251,6 @@ final class PrivateDiscriminatorStore: NSObject { return cleaned.isEmpty ? fallback : cleaned } - @objc(isPrivateDisplayItem:) func isPrivateDisplayItem(_ item: DisplayItem) -> Bool { guard featureEnabled else { return false @@ -264,7 +258,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 +273,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 +298,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 +331,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 +397,6 @@ final class PrivateDiscriminatorStore: NSObject { } } - @objc(cancelSwiftPDGuessForDisplayItem:) func cancelSwiftPDGuess(for item: DisplayItem) { guard let parsed = parsedPrivateDiscriminator(for: item), let task = activeGuessTasksByID[parsed.id] @@ -743,7 +732,6 @@ final class PrivateDiscriminatorStore: NSObject { isMatched: false, isVerified: false, warning: nil, - className: parsed.className, guessState: dashboardGuessState(for: parsed.id, isMatched: false) ) } @@ -763,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) ) } @@ -1344,7 +1331,6 @@ private struct DashboardDescriptor { let isMatched: Bool let isVerified: Bool let warning: String? - let className: String? let guessState: GuessState? } @@ -1389,24 +1375,22 @@ 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 isMatched: Bool + let isVerified: Bool + let warningText: String? + let guessStatusText: String? + let isGuessRunning: Bool + let isGuessFailed: Bool fileprivate init(descriptor: DashboardDescriptor) { discriminatorID = descriptor.id module = descriptor.module filename = descriptor.filename source = descriptor.source?.localizedDisplayName ?? "" - displayClassName = descriptor.className ?? "" isMatched = descriptor.isMatched isVerified = descriptor.isVerified warningText = descriptor.warning diff --git a/LookInside/Read/ArchiveDocument.swift b/LookInside/Read/ArchiveDocument.swift index 51540405..69033330 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 40c6ce18..e9389952 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 b95b0c07..85bae1f2 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 0faf461a..0d7a210a 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 2933a329..98dc34ae 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/DisplayItemNode.swift b/LookInside/Static/Preview/DisplayItemNode.swift index 0cd7fa40..cec36f6b 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/Preview/PreviewController.swift b/LookInside/Static/Preview/PreviewController.swift index 87f18dcf..3023a34f 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 85a466bd..a5df2b17 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 @@ -60,7 +59,7 @@ final class StaticAsyncUpdateManager: NSObject { } } - @objc override convenience init() { + override convenience init() { self.init(hierarchyDataSource: nil, inspectableApp: nil) } @@ -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,14 +103,13 @@ final class StaticAsyncUpdateManager: NSObject { } /// A method rather than a property: Swift callers use `isUpdating()`. - @objc func isUpdating() -> Bool { + func isUpdating() -> Bool { ongoingRequest != nil } /// 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 { @@ -159,7 +157,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 +164,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) @@ -198,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() { @@ -232,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/StaticConstants.swift b/LookInside/Static/StaticConstants.swift index ebbb793a..b43d6c57 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 440f8cf6..0154c300 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 a01c37c4..c3b63ba4 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 { @@ -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 @@ -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 } @@ -560,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 diff --git a/LookInside/Static/StaticWindowController.swift b/LookInside/Static/StaticWindowController.swift index 368c7722..706be4d7 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 3c17d54c..7bc98bca 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 58c6d978..51e514f3 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 7e3d5e8c..6b03c893 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 632203eb..832aaba4 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 a7f344ac..a41b23e0 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 82184782..58b480a8 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 6e347975..783b10c5 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 diff --git a/Scripts/check-host-release-clean.sh b/Scripts/check-host-release-clean.sh index 5f0f0a28..1b8f9255 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 00000000..38c28db8 --- /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 5f290bd7..87acb400 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"