Slide 1

Slide 1 text

How to access hidden iOS APIs and enhance development efficacy. 2024/08/23 10:50ʙ Track B Regular talkʢ20 minʣ 1/53

Slide 2

Slide 2 text

noppe • Indie app developer • DAWN for mastodon • vear - VTuber camera app 2/53

Slide 3

Slide 3 text

DeNA Co., Ltd., • 2016 ~ • Pococha • iOS Senior App Developer • Code de Crossword at DeNA booth • They are sponsoring my talk 3/53

Slide 4

Slide 4 text

Enjoy my talk! • The talk will be in Japanese • Slides are in English 4/53

Slide 5

Slide 5 text

5/53

Slide 6

Slide 6 text

6/53

Slide 7

Slide 7 text

Agenda • Perform • Usecase • Find 7/53

Slide 8

Slide 8 text

Agenda • Perform • Usecase • Find 8/53

Slide 9

Slide 9 text

ObjC Private API 9/53

Slide 10

Slide 10 text

ObjC Private API Sample *object = [Sample new]; // Success [object send]; // Error [object validate]; 10/53

Slide 11

Slide 11 text

ObjC Private API You can expose the method by adding a header file yourself. @interface Sample (Private) - (BOOL)validate; @end // Success [objcect validate]; 11/53

Slide 12

Slide 12 text

ObjC Private API You can call the method by using performSelector without adding a header file. [object performSelector:NSSelectorFromString(@"validate")]; 12/53

Slide 13

Slide 13 text

ObjC Private API object.isValidate = YES; object.performSelector( NSSelectorFromString(@"setIsValidate:"), withObject: @(YES) ); 13/53

Slide 14

Slide 14 text

Swift Hidden API • Swift does not have any header file. • Dynamic Link Framework has Swift module data. 14/53

Slide 15

Slide 15 text

Swift Hidden API • tbd file • dynamic library stub for Eager linking 4 • public and internal api list • swiftinterface file • public api list 4 https://developer.apple.com/jp/videos/play/wwdc2022/110364/ 15/53

Slide 16

Slide 16 text

Swift Hidden API /Applications /Xcode.app /Contents /Developer /Platforms /iPhoneSimulator.platform /Developer /SDKs /iPhoneSimulator.sdk /System /Library /Frameworks /SwiftUI.framework 16/53

Slide 17

Slide 17 text

Swift Hidden API SwiftUI.framework ├── Headers │ ├── SwiftUI.h │ └── SwiftUI_Metal.h ├── Modules │ ├── SwiftUI.swiftmodule │ │ ├── arm64-apple-ios-simulator.swiftdoc │ │ ├── arm64-apple-ios-simulator.swiftinterface │ │ ├── x86_64-apple-ios-simulator.swiftdoc │ │ └── x86_64-apple-ios-simulator.swiftinterface │ └── module.modulemap └── SwiftUI.tbd 17/53

Slide 18

Slide 18 text

Swift Hidden API arm64-apple-ios-simulator.swiftinterface ... @_Concurrency.MainActor open class UIHostingController : UIKit.UIViewController where Content : SwiftUICore.View { @_Concurrency.MainActor @preconcurrency public var _disableSafeArea: Swift.Bool { get set } } ... 18/53

Slide 19

Slide 19 text

Swift Hidden API let vc = UIHostingController(rootView: ContentView()) vc._disableSafeArea = true 19/53

Slide 20

Slide 20 text

Swift Hidden API SwiftUI.tbd --- !tapi-tbd tbd-version: 0 targets: [ i386-ios-simulator, x86_64-ios-simulator, arm64-ios-simulator ] install-name: '/System/Library/Frameworks/SwiftUI.framework/SwiftUI' current-version: 0.0.0 swift-abi-version: 0 exports: - targets: [ i386-ios-simulator, x86_64-ios-simulator, arm64-ios-simulator ] symbols: [ ... ] 20/53

Slide 21

Slide 21 text

Swift Hidden API SwiftUI.tbd (exports/symbols) ... '_$s7SwiftUI4ViewPAAE12userActivity...', '_$s7SwiftUI4ViewPAAE12userActivity...', '_$s7SwiftUI4ViewPAAE12userActivity...', '_$s7SwiftUI4ViewPAAE12userActivity...', '_$s7SwiftUI4ViewPAAE12variableBlur...', '_$s7SwiftUI4ViewPAAE12variableBlur...', ... 21/53

Slide 22

Slide 22 text

Swift Hidden API swift demangle '_$s7SwiftUI4....' _$s7SwiftUI4.... ---> View.variableBlur(maxRadius: CGFloat, mask: Image, opaque: Bool) -> some 22/53

Slide 23

Slide 23 text

Swift Hidden API modify swiftinterface file yourself @available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *) extension SwiftUICore.View { nonisolated public func variableBlur( maxRadius: CoreFoundation.CGFloat, mask: SwiftUI.Image, opaque: Swift.Bool ) -> some SwiftUI.View } 23/53

Slide 24

Slide 24 text

Swift Hidden API 24/53

Slide 25

Slide 25 text

UnderscoredAttributes1 5 extension View { @_disfavoredOverload func badge(_ count: Int) -> some View { // ... } } 5 https://github.com/swiftlang/swift/pull/37854 1 https://github.com/swiftlang/swift/blob/main/docs/ReferenceGuides/UnderscoredAttributes.md 25/53

Slide 26

Slide 26 text

Agenda • Perform • Usecase • Find 26/53

Slide 27

Slide 27 text

Hidden API's risk ⚠ • Semantics are subject to change. • Side-effects are not controllable. • AppStore rejection • Uncompliance in review guildeline 2.5.12 2 https://developer.apple.com/jp/app-store/review/guidelines/ 27/53

Slide 28

Slide 28 text

Lowering risk • Testing for lowing changing and side-effects risk. func testPrivateMethod() { let object = Sample() let selector = #Selector("setUserName:") object.performSelector(selector, withObject: "noppe") #expect(object.userName == "noppe") if !object.responds(selector) { fatalError("Selector not found") } } 28/53

Slide 29

Slide 29 text

Lowing risk • Use beta for early detection changes. 29/53

Slide 30

Slide 30 text

What's the best usecase? Development Phase Suitable Concept Development ! Testing ⾠ Product Development " 30/53

Slide 31

Slide 31 text

Concept Development • Concept Development, Hackathon, UI Design, and more. • Troublesome implementation • Difficult implementation • Complex visual effect 31/53

Slide 32

Slide 32 text

UITextView.setAttributedPlaceholder:8 extension UITextView { func setPlaceholder(_ placeholder: String?) { let string = placeholder.map(NSAttributedString.init) let selector = Selector(("setAttributedPlaceholder:")) if responds(to: selector) { perform(selector, with: string) } } } 8 https://gist.github.com/AdamWhitcroft/c6ffc0323b9ce227588df7145685ae26#file-wrappeduitextview-swift-L41 32/53

Slide 33

Slide 33 text

33/53

Slide 34

Slide 34 text

UINavigationItem._setWeeTitle:7 extension UINavigationItem { func setWeeTitle(_ title: String) { let selector = Selector(("_setWeeTitle:")) if responds(to: selector) { perform(selector, with: title as NSString) } } } 7 https://github.com/feedback-assistant/reports/issues/506 34/53

Slide 35

Slide 35 text

UINavigationItem._setWeeTitle: 35/53

Slide 36

Slide 36 text

_UIHostingView let view = _UIHostingView(rootView: ContentView()) 36/53

Slide 37

Slide 37 text

Testing 37/53

Slide 38

Slide 38 text

Testing UIDebuggingInformationOverla y • iOS10 • easy to use • iOS11+ • needs to jump through some hoops 38/53

Slide 39

Slide 39 text

Instruments 39/53

Slide 40

Slide 40 text

Product Development Using hidden API is risky. But, You can learn API design from hidden APIs. 40/53

Slide 41

Slide 41 text

API naming You can learn API naming rules from official SDK headers. -(void)_endScrollingCursorOverrideIfNecessary; -(UIOffset)_firstPageOffset; -(id)_frameLayoutGuideIfExists; (note) Don't override the method names, because ObjC uses Dynamic Dispatch. 41/53

Slide 42

Slide 42 text

API design You can learn UI scructures and architectures. @interface UITextView : UIScrollView { _UITextContainerView* _containerView; ... } ... @end 42/53

Slide 43

Slide 43 text

How to find hidden APIs? • Read swiftinterface, tbd and headers. • Get method name list • Read the stacktrace • Find SNS posts • Send feedback to Apple 43/53

Slide 44

Slide 44 text

Get method name list _methodDescription (located in UIKitCore) import UIKit let selector = Selector("_methodDescription") let names = scrollView.perform(selector) print(names) 44/53

Slide 45

Slide 45 text

Get method name list • Runtime Header • p-x9/swift-objc-dump9 9 https://github.com/p-x9/swift-objc-dump 45/53

Slide 46

Slide 46 text

Read stacktrace • You can find methods around breakpoint. 46/53

Slide 47

Slide 47 text

Find SNS posts • Survey the SNS landscape 47/53

Slide 48

Slide 48 text

UIReturnKeyType(rawValue: 126) textField.returnKeyType = UIReturnKeyType(rawValue: 126)! 48/53

Slide 49

Slide 49 text

Send feedback to Apple • Apple sometimes publicizes the API after receiving our feedback. • Write feedback about usecases and APIs. 49/53

Slide 50

Slide 50 text

Recap • Perform • Usecase • Find 50/53

Slide 51

Slide 51 text

Next steps • Thinking about backend everytime. 51/53

Slide 52

Slide 52 text

Thank you • Special thanks • @p_x9 • Guilherme Rosado Martins 52/53

Slide 53

Slide 53 text

References https://www.jacklandrin.com/2018/05/16/method-in-objective-c-messgae- passing/ https://xta0.me/2023/06/28/Swift-modules-1.html https://www.kodeco.com/295-swizzling-in-ios-11-with- uidebugginginformationoverlay/ 53/53