惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
酷 壳 – CoolShell
酷 壳 – CoolShell
Google Online Security Blog
Google Online Security Blog
Engineering at Meta
Engineering at Meta
U
Unit 42
Security Latest
Security Latest
G
Google Developers Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
D
Docker
T
Tailwind CSS Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
云风的 BLOG
云风的 BLOG
Hugging Face - Blog
Hugging Face - Blog
M
MIT News - Artificial intelligence
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
MongoDB | Blog
MongoDB | Blog
H
Help Net Security
Stack Overflow Blog
Stack Overflow Blog
C
Check Point Blog
S
Security Affairs
T
The Exploit Database - CXSecurity.com
S
SegmentFault 最新的问题
N
News and Events Feed by Topic
The GitHub Blog
The GitHub Blog
Apple Machine Learning Research
Apple Machine Learning Research
S
Securelist
IT之家
IT之家
P
Palo Alto Networks Blog
D
DataBreaches.Net
Help Net Security
Help Net Security
N
Netflix TechBlog - Medium
B
Blog RSS Feed
AWS News Blog
AWS News Blog
Scott Helme
Scott Helme
爱范儿
爱范儿
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
腾讯CDC
I
Intezer
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
Microsoft Security Blog
Microsoft Security Blog
人人都是产品经理
人人都是产品经理
G
GRAHAM CLULEY
N
News | PayPal Newsroom
博客园 - 三生石上(FineUI控件)
A
Arctic Wolf
F
Fortinet All Blogs
The Register - Security
The Register - Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 【当耐特】

Peter Steinberger

OpenClaw, OpenAI and the future | Peter Steinberger Shipping at Inference-Speed | Peter Steinberger The Signature Flicker | Peter Steinberger Just Talk To It - the no-bs Way of Agentic Engineering | Peter Steinberger Claude Code Anonymous | Peter Steinberger Live Coding Session: Building Arena | Peter Steinberger My Current AI Dev Workflow | Peter Steinberger Essential Reading for Agentic Engineers - August 2025 | Peter Steinberger Just One More Prompt | Peter Steinberger Poltergeist: The Ghost That Keeps Your Builds Fresh | Peter Steinberger Don't read this Startup Slop | Peter Steinberger Essential Reading for Agentic Engineers - July 2025 | Peter Steinberger Self-Hosting AI Models After Claude's Usage Limits | Peter Steinberger Logging Privacy Shenanigans | Peter Steinberger VibeTunnel's first AI-anniversary | Peter Steinberger Making AppleScript Work in macOS CLI Tools: The Undocumented Parts | Peter Steinberger Peekaboo 2.0 – Free the CLI from its MCP shackles | Peter Steinberger Command your Claude Code Army, Reloaded | Peter Steinberger Essential Reading for Agentic Engineers | Peter Steinberger Slot Machines for Programmers: How Peter Builds Apps 20x Faster with AI | Peter Steinberger My AI Workflow for Understanding Any Codebase | Peter Steinberger stats.store: Privacy-First Sparkle Analytics | Peter Steinberger Showing Settings from macOS Menu Bar Items: A 5-Hour Journey | Peter Steinberger VibeTunnel: Turn Any Browser into Your Mac's Terminal | Peter Steinberger Vibe Meter 2.0: Calculating Claude Code Usage with Token Counting | Peter Steinberger llm.codes: Make Apple Docs AI-Readable | Peter Steinberger Automatic Observation Tracking in UIKit and AppKit: The Feature Apple Forgot to Mention | Peter Steinberger Peekaboo MCP – lightning-fast macOS screenshots for AI agents | Peter Steinberger Migrating 700+ Tests to Swift Testing: A Real-World Experience | Peter Steinberger Commanding Your Claude Code Army | Peter Steinberger Code Signing and Notarization: Sparkle and Tears | Peter Steinberger Vibe Meter: Monitor Your AI Costs | Peter Steinberger Claude Code is My Computer | Peter Steinberger Stop Over-thinking AI Subscriptions | Peter Steinberger Introducing Demark: HTML in. MD out. Blink-fast. | Peter Steinberger The Future of Vibe Coding: Building with AI, Live and Unfiltered | Peter Steinberger MCP Best Practices | Peter Steinberger Finding My Spark Again | Peter Steinberger Top-Level Menu Visibility in SwiftUI for macOS | Peter Steinberger Fixing keyboardShortcut in SwiftUI | Peter Steinberger Supporting Both Tap and Long Press on a Button in SwiftUI | Peter Steinberger On Using Apple Silicon Mac Mini for Continuous Integration | Peter Steinberger Apple Silicon M1: A Developer's Perspective | Peter Steinberger Gardening Your Twitter: Curating Your Timeline | Peter Steinberger Gardening Your Twitter: Growing Your Followers | Peter Steinberger Disabling Keyboard Avoidance in SwiftUI's UIHostingController | Peter Steinberger The State of SwiftUI | Peter Steinberger Logging in Swift | Peter Steinberger Building with Swift Trunk Development Snapshots | Peter Steinberger Calling Super at Runtime in Swift | Peter Steinberger zld — A Faster Version of Apple's Linker | Peter Steinberger How to Fix LLDB: Couldn't IRGen Expression | Peter Steinberger Updating macOS on a Hackintosh | Peter Steinberger InterposeKit — Elegant Swizzling in Swift | Peter Steinberger The Great Mac Catalyst Text Input Crash Hunt | Peter Steinberger Jailbreaking for iOS Developers | Peter Steinberger Network Kernel Core Dump | Peter Steinberger How to macOS Core Dump | Peter Steinberger Kernel Panics and Surprise boot-args | Peter Steinberger The LG UltraFine 5K, kernel_task, and Me | Peter Steinberger Let's Try This Again | Peter Steinberger How We Work at PSPDFKit | Peter Steinberger Swizzling in Swift | Peter Steinberger WWDC for First-Timers, 2019 Edition | Peter Steinberger Challenges of Adopting Drag and Drop | Peter Steinberger Marzipan: Porting iOS Apps to the Mac | Peter Steinberger How to Use Slack and Not Go Crazy | Peter Steinberger Hardcore Debugging - Heavy Weapons for Hard Bugs | Peter Steinberger Binary Frameworks in Swift | Peter Steinberger Even Swiftier Objective-C | Peter Steinberger The Case for Deprecating UITableView | Peter Steinberger Running tests with Clang Address Sanitizer | Peter Steinberger UI testing on iOS, without busy waiting | Peter Steinberger Hiring a distributed team | Peter Steinberger Writing Good Bug Reports | Peter Steinberger Real-time collaboration, Apple, and you | Peter Steinberger Converting Xcode Test Runs to JUnit, the Fast Way | Peter Steinberger Efficient iOS Version Checking | Peter Steinberger Investigating Thread Safety of UIImage | Peter Steinberger Swifty Objective-C | Peter Steinberger Running UI Tests on iOS With Ludicrous Speed | Peter Steinberger A Pragmatic Approach to Cross-Platform | Peter Steinberger Surprises with Swift Extensions | Peter Steinberger Using ccache for Fun and Profit | Peter Steinberger UITableViewController designated initializer woes | Peter Steinberger Researching ResearchKit | Peter Steinberger The curious case of rotation with multiple windows on iOS 8 | Peter Steinberger UIKit Debug Mode | Peter Steinberger Retrofitting containsString: on iOS 7 | Peter Steinberger A Story About Swizzling "the Right Way™" and Touch Forwarding | Peter Steinberger Hacking with Aspects | Peter Steinberger Fixing UITextView On iOS 7 | Peter Steinberger Fixing What Apple Doesn't | Peter Steinberger How To Inspect The View Hierarchy Of Third-Party Apps | Peter Steinberger Fixing UISearchDisplayController On iOS 7 | Peter Steinberger Smart Proxy Delegation | Peter Steinberger Adding Keyboard Shortcuts To UIAlertView | Peter Steinberger How To Center Content Within UIScrollView | Peter Steinberger UIAppearance for Custom Views | Peter Steinberger Hacking Block Support Into UIMenuItem | Peter Steinberger
Forbidden Controls in Catalyst: Optimize Interface for Mac | Peter Steinberger
Peter Steinberger · 2020-09-23 · via Peter Steinberger

While working on our PDF Viewer update for Big Sur and switching to the new Catalyst Mac Interface Idiom, I was greeted with a new exception coming directly from UIKit:

[General] UIStepper is not supported when running Catalyst apps in the Mac idiom.
[General] (
	0   CoreFoundation                      0x00007fff2067fbdf __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007fff2029f469 objc_exception_throw + 48
	2   UIKitCore                           0x00007fff464af1e5 -[UIView(UICatalystMacIdiomUnsupported_Internal) _throwForUnsupportedNonMacIdiomBehaviorWithReason:] + 0
	3   UIKitCore                           0x00007fff45c7b5b6 -[UIStepper _didMoveFromWindow:toWindow:] + 218

Catalyst Mac Idiom

Let’s take a step back — what’s the Catalyst Mac Idiom? With macOS 11 Big Sur, Catalyst learned a new presentation mode. Next to the classic mode where Catalyst apps are scaled to 77 percent and retain their iPad look, there’s a new Optimize Interface for Mac mode that doesn’t use scaling and replaces various UIKit controls with AppKit counterparts.

The new mode is available with Big Sur, and apps can be built so that they use scaling on Catalina and the new Mac mode on Big Sur. We’ll be releasing a new version of PDF Viewer for Mac using the new optimized mode as soon as Apple finalizes Big Sur.

If we write code that works on both Catalina and Big Sur, a category like this will be useful:

extension UIDevice {
    /// Checks if we run in Mac Catalyst Optimized For Mac Idiom
    var isCatalystMacIdiom: Bool {
        if #available(iOS 14, *) {
            return UIDevice.current.userInterfaceIdiom == .mac
        } else {
            return false
        }
    }
}

This can later be used, for example, in SwiftUI (SwiftUI’s Stepper maps to UIStepper, which is disallowed):

// UIStepper is not allowed for Catalyst Mac Idiom.
if !UIDevice.current.isCatalystMacIdiom {
    Stepper("Current Page: \(pageIndex + 1)", value: $pageIndex, in: 0...document.pageCount - 1)
    .padding()
}

AppKit In UIKit

Internally, Apple uses a private _UINSView class to host an actual NSView. It’s too bad Apple didn’t consider making this class public API, which would’ve allowed us to freely mix AppKit with UIKit, but it’s a start.

UIButton Mac Idiom

If we look into the runtime, the class does pretty much what we’d expect (I omitted some less useful methods for the sake of brevity):

lldb) po [NSClassFromString(@"_UINSView") _shortMethodDescription]
<_UINSView: 0x7fff86fac238>:
in _UINSView:
	Properties:
		@property (readonly) struct CGSize _intrinsicFrameSize;
		@property (readonly) NSView* contentNSView;  (@synthesize contentNSView = _contentNSView;)

Forbidden Controls

Back to our crash — things make a bit more sense now. There’s no great equivalent for UIStepper in AppKit, so the folks at Apple decided it’s better to throw an exception if this control is used (FB8727188).

The problem: It isn’t documented which controls are disallowed, and what’s even more problematic is some controls are allowed, but customizations are disallowed. What does UISlider map toward? We can get the pointer from the visual debugger and then use our knowledge of the class structure to call directly into the AppKit view:

(lldb) po [0x7f9503c488f0 contentNSView]
<NSSlider: 0x7f9503c37a40>

NSSlider is the obvious choice, however, the Mac version lacks the appearance customization options UIKit has. Calling any of these customization methods will simply throw (crash) at runtime:

setMinimumTrackImage:forState: is not supported on PSPDFBrightnessSlider when running Catalyst apps in the Mac idiom.
 (
	0   CoreFoundation                      0x00007fff2067fbdf __exceptionPreprocess + 242
	1   libobjc.A.dylib                     0x00007fff2029f469 objc_exception_throw + 48
	2   UIKitCore                           0x00007fff464af1e5 -[UIView(UICatalystMacIdiomUnsupported_Internal) _throwForUnsupportedNonMacIdiomBehaviorWithReason:] + 0
	3   UIKitCore                           0x00007fff4582af70 -[UISlider setMinimumTrackImage:forState:] + 197

This is problematic, as it’s yet another restriction that isn’t documented. A better choice would’ve been to keep the UIKit variant in case customization exceeds what AppKit can do. It’s a surprising late change, and folks working on Catalyst apps are frustrated:

{% twitter https://twitter.com/_imaginethis/status/1308412481375801344?s=21 %}

Finding What’s Forbidden

Since there’s no documentation and there are no release notes about any of these behaviors, that just leaves Hopper and decompiling UIKitCore.

We find references to following controls:

  • UIStepper (styling properties such as setMinimumTrackImage:forState:)
  • UIPickerView
  • UIRefreshControl
  • UISwitch (inside setTitle:)
  • UIButton

This includes any subclasses like AVScrubber (part of AVPlayerView), so this can become a real problem.

The main method throwing is _throwForUnsupportedNonMacIdiomBehaviorWithReason, so it makes sense to search for it. It checks if the bundle identifier starts with “com.apple”, and if it does, it just logs an error, while all other apps get an exception. There’s yet another check for _allowsUnsupportedMacIdiomBehavior, which is interesting. It seems the above controls at least have partial support in Big Sur. This can be enabled via calling _setAllowsUnsupportedMacIdiomBehavior:] on them. And indeed, calling [UIStepper _setAllowsUnsupportedMacIdiomBehavior:1]; (I’m using Objective-C here since it’s easier to just redeclare the method in the header) does result in a working app.

UIStepper on macOS via a hack

There’s a stepper (next to the “Current Page: 2” label), however, clicking it doesn’t work. This is obviously not something we should ever use for shipping, but it’s fun to play with the internals!

Digging Deeper

However most of the throw calls seem to be missing. I’ve been looking at the iOS version this whole time, when clearly this is conditional code and we need to look at the macOS version instead.

iOS Path: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/UIKitCore.framework1

macOS Path: /System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.framework

However, when we open the framework on macOS, there’s no binary. WTH? I remembered reading that Big Sur ships with a built-in dynamic linker cache of all system-provided libraries. Luckily, Hopper has already been updated to be informed of that shared cache, so we can load it via /System/Library/dyld/ and then select UIKitCore as the target.

Wait until everything is loaded and then select File > Produce Pseudo-Code File for All Procedures. This might take a few hours. Once the file is generated, pick a fast text editor (my weapon of choice for something like this is Sublime Text) and load the file. There, search for _throwForUnsupportedNonMacIdiomBehaviorWithReason: again.

The problem: The file is heavily obfuscated; Hopper can’t read the selector names. We can search for the string “Unsupported iOS or Mac Catalyst iPad Idiom” to find the selector matching _throwForUnsupportedNonMacIdiomBehaviorWithReason:. In my case, that’s sub_7fff465801e5.

Conclusion

However, that’s the end of the story for now. The data is there, but the tools can’t yet (!) get a useful format out. We know there are at least five controls that throw an exception on some usage at runtime, however, which one it is exactly is currently hard to know. Shipping a Catalyst app in the new Mac idiom is definitely an adventure.

Decompile via LLDB

Jeff Johnson points out that one can abuse LLDB to decompile methods individually. That approach would take far too long to find all the calls that throw here, but it’s a start.

The dyld-shared-cache-big-sur project uses modifications to Apple’s dyld project to fix Objective-C information when extracting the dyld_shared_cache from macOS Big Sur to help Hopper generate readable pseudocode. (Thanks @lclhrst for the hint!)

Using this project, we can extract the dyld cache into a folder:

./dyld_shared_cache_util -extract ~/Developer/macOS\ Big\ Sur /System/Library/dyld/dyld_shared_cache_x86_64

And then we can decompile UIKitCore with selector names. This doesn’t resolve the individual selector calls, but it’s a step forward.

Update: Apple added the list of disallowed controls in the macOS Big Sur 11.0.1 Release Notes

This answers which controls are affected. Curiously this currently is the only place, and there’s no mention in the headers or compile-time support to warn when these methods are used - but it’s better than no docs.

  1. In the early days, it was just UIKit. A few years ago, Apple created an internal framework called UIKitCore, which exports more APIs and can be used for internal apps. UIKit is the smaller API for external developers (us). ↩