Click “like” to see, wechat search [iOS growth refers to north] to guide the book. Welcome to share, please feel free to ask any questions
Apple Silicon
In this chapter, we look at crashes on Apple Silicon Macs, such as crashes caused by the use of Rosetta translation systems and crashes caused by unmodified iOS applications running on macOS. In addition, we’ll look at new types of crashes that can be caused by multi-architecture code that supports both ARM and Intel cpus.
What is the Apple Silicon Mac?
Apple Silicon says the chip design comes from Apple, not a third party. Apple’s A-series chips can be considered Apple chips. However, this chapter focuses on Apple Silicon Macs. These started with the Apple M1 chip. The reason these Macs are not called ARM-based Macs is probably because Apple has made significant contributions at the design level while still conforming to the ARM ABI. When switching from Intel-based Macs to Apple Silicon Macs, this gives customers better marketing benefits, such as longer battery life and higher performance.
What is the Rosetta?
Rosetta is an instruction translator for Apple Silicon Macs. When an application has Intel instructions as part of its binary code, it can convert them to ARM instructions and then run them. Think of it as an AOT compiler. The technology’s roots go back even further, when the MAC was transitioning from PowerPC chips to Intel chips. Apple in Transitive Technologies Ltd. The first version of Rosetta was developed with the help of technology from The Internet. In the second version of Rosetta, our system allows Intel instructions to be pre-translated into ARM instructions on a per-process basis and then run at native speed.
Rosetta binaries
On Apple Silicon Macs, Rosetta software resides
/Library/Apple/usr/libexec/oah
Copy the code
In this directory are the runtime engine runtime_t8027, the translator oahd-helper, the command line tool translate_tool, and other tools. Its operations are basically transparent to the end user, except for lower startup latency or slightly lower performance. From a crash analysis point of view, we can see it in terms of memory usage, exception helpers, and runtime helpers.
Limitations of Rosetta
Rosetta is a powerful system, but it has some limitations. These focus on high-performance multimedia applications and operating system virtualization solutions.
Rosetta does not include the following features:
- Kernel extensions
x86_64
Virtualization Support- Vector instructions, such as AVX, AVX2 and AVX512
Interestingly, Rosetta supports just-in-time compilation of applications. These applications are special because they generate and then execute the code themselves. Most applications have only fixed read-only code (program text) that is then executed, and their data is mutable (but not executable). This is probably because JIT is a common technique for JavaScript runtime.
Apple recommends checking optional hardware features before calling code that uses this feature. We can run sysctl hw | grep optional to determine what are the optional hardware platform support. In the code, we can call the sysctlByName method to do the same thing.
Enforce Rosetta
If we use standard Build options for our projects by default, we will Only see native binaries when we set Build Active Architecture Only to Yes for Debug and No for Release builds and then Debug. This is because when debugging, we don’t want to waste time building an architecture that isn’t relevant to the machine we’re testing.
If we do an Archive build, Product > Archive, and select Distribute App we end up with a distributable version. By default, this will be Fat Binary \index{file! Fat} (which we call Fat binary) files, x86 and ARM64 are available in multi-architecture binaries.
Once we have a Fat Binary File, we can use the Finder application to right click on File Info and set Rosetta to perform the translation of our Binary so that on an Apple Silicon Mac, The Intel instructions will be translated from the Fat Binary.
Sample translated application
The working example for this chapter is the icdab_thread program. You can find it online. The program tries to call thread_set_state, and then 60 seconds later calls Abort to actively crash. It doesn’t actually have a way to do that because of recent security enhancements to macOS to prevent the use of such apis, which are malware attack vectors. Still, the program is interesting because a closely related part of task_for_pid is called multiple times during the crash.
We have modified the command line executable, ICdab_thread, to an application that only calls the same underlying code. This application is icdab_rosetta_thread. This is because UNIX command-line executables are not suitable for running converted programs, whereas applications can.
icdab_rosetta_thread
Lipo information
The following command shows that our application supports both ARM and Intel instructions.
# lipo -archs
icdab_rosetta_thread.app/Contents/MacOS/icdab_rosetta_thread
x86_64 arm64
Copy the code
The translated program crashes
If we run the icdab_rosetta_thread application and click Start Threads Test, after one minute the application crashes. Comparing crash analysis between native and translated cases, we can find differences in crash reports.
Code type
Code Type: ARM-64 (Native)
Copy the code
When run locally, it becomes translated
Code Type: X86-64 (Translated)
Copy the code
A thread dump
The crashed thread (and other threads) looks very similar, except that the pointer is based on a higher pointer in the translated case. For native crashes, we have:
Thread 1 Crashed:: Dispatch queue: com.apple.root.default-qos
0 libsystem_kernel.dylib 0x00000001de3015d8
__pthread_kill + 8
1 libsystem_pthread.dylib 0x00000001de3accbc
pthread_kill + 292
2 libsystem_c.dylib 0x00000001de274904 abort
+ 104
3 perivalebluebell.com.icdab-rosetta-thread
0x00000001002cd478 start_threads + 244
4 perivalebluebell.com.icdab-rosetta-thread
0x00000001002cd858 thunk for @escaping @callee_guaranteed () ->
() + 20
5 libdispatch.dylib 0x00000001de139658
_dispatch_call_block_and_release + 32
6 libdispatch.dylib 0x00000001de13b150
_dispatch_client_callout + 20
7 libdispatch.dylib 0x00000001de13e090
_dispatch_queue_override_invoke + 692
8 libdispatch.dylib 0x00000001de14b774
_dispatch_root_queue_drain + 356
9 libdispatch.dylib 0x00000001de14bf6c
_dispatch_worker_thread2 + 116
10 libsystem_pthread.dylib 0x00000001de3a9110
_pthread_wqthread + 216
11 libsystem_pthread.dylib 0x00000001de3a7e80
start_wqthread + 8
Copy the code
In the case of post-translation collapse, yes
Thread 1 Crashed:: Dispatch queue: com.apple.root.default-qos
0 ??? 0x00007fff0144ff40 ???
1 libsystem_kernel.dylib 0x00007fff6bdc4812
__pthread_kill + 10
2 libsystem_c.dylib 0x00007fff6bd377f0 abort
+ 120
3 perivalebluebell.com.icdab-rosetta-thread
0x0000000100d1c5ab start_threads + 259
4 perivalebluebell.com.icdab-rosetta-thread
0x0000000100d1ca1e thunk for @escaping @callee_guaranteed () ->
() + 14
5 libdispatch.dylib 0x00007fff6bbf753d
_dispatch_call_block_and_release + 12
6 libdispatch.dylib 0x00007fff6bbf8727
_dispatch_client_callout + 8
7 libdispatch.dylib 0x00007fff6bbfad7c
_dispatch_queue_override_invoke + 777
8 libdispatch.dylib 0x00007fff6bc077a5
_dispatch_root_queue_drain + 326
9 libdispatch.dylib 0x00007fff6bc07f06
_dispatch_worker_thread2 + 92
10 libsystem_pthread.dylib 0x00007fff6be8c4ac
_pthread_wqthread + 244
11 libsystem_pthread.dylib 0x00007fff6be8b4c3
start_wqthread + 15
Copy the code
Notice that in the translated case, the actual line of code in thread stack 0 is?? . Presumably this is the actual translation code synthesized by Rosetta.
In addition, in the translated case, we have two more threads, the exception server and the runtime environment:
Thread 3:: com.apple.rosetta.exceptionserver
0 runtime_t8027 0x00007ffdfff76af8
0x7ffdfff74000 + 11000
1 runtime_t8027 0x00007ffdfff803cc
0x7ffdfff74000 + 50124
2 runtime_t8027 0x00007ffdfff82738
0x7ffdfff74000 + 59192
Thread 4:
0 runtime_t8027 0x00007ffdfffce8ac
0x7ffdfff74000 + 370860
Copy the code
Crashed thread status register
In the native example, we get the thread status register:
Thread 1 crashed with ARM Thread State (64-bit):
x0: 0x0000000000000000 x1: 0x0000000000000000 x2:
0x0000000000000000 x3: 0x0000000000000000
x4: 0x000000000000003c x5: 0x0000000000000000 x6:
0x0000000000000000 x7: 0x0000000000000000
x8: 0x00000000000005b9 x9: 0xb91ed5337c66d7ee x10:
0x0000000000003ffe x11: 0x0000000206c1fa22
x12: 0x0000000206c1fa22 x13: 0x000000000000001e x14:
0x0000000000000881 x15: 0x000000008000001f
x16: 0x0000000000000148 x17: 0x0000000200e28528 x18:
0x0000000000000000 x19: 0x0000000000000006
x20: 0x000000016fbbb000 x21: 0x0000000000001707 x22:
0x000000016fbbb0e0 x23: 0x0000000000000114
x24: 0x000000016fbbb0e0 x25: 0x000000020252d184 x26:
0x00000000000005ff x27: 0x000000020252d6c0
x28: 0x0000000002ffffff fp: 0x000000016fbbab70 lr:
0x00000001de3accbc
sp: 0x000000016fbbab50 pc: 0x00000001de3015d8 cpsr:
0x40000000
far: 0x0000000100ff8000 esr: 0x56000080
Copy the code
In the translated case, there is also a thread status register:
Thread 1 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000000 rbx: 0x000000030600b000 rcx:
0x0000000000000000 rdx: 0x0000000000000000
rdi: 0x0000000000000000 rsi: 0x0000000000000003 rbp:
0x0000000000000000 rsp: 0x000000000000003c
r8: 0x000000030600ad40 r9: 0x0000000000000000 r10:
0x000000030600b000 r11: 0x00007fff6bd37778
r12: 0x0000000000003d03 r13: 0x0000000000000000 r14:
0x0000000000000006 r15: 0x0000000000000016
rip: <unavailable> rfl: 0x0000000000000287
Copy the code
Translated code information
In the translated case, we’ll get more information that might be useful to engineers working on debugging Rosetta:
Translated Code Information:
tmp0: 0xffffffffffffffff tmp1: 0x00007fff0144ff14 tmp2:
0x00007fff6bdc4808
Copy the code
Summary of external modifications
In the native example, we see:
External Modification Summary:
Calls made by other processes targeting this process:
task_for_pid: 0
thread_create: 0
thread_set_state: 0
Calls made by this process:
task_for_pid: 0
thread_create: 0
thread_set_state: 0
Calls made by all processes on this machine:
task_for_pid: 914636
thread_create: 0
thread_set_state: 804
Copy the code
Our code tried to call thread_set_state but failed (not under any platform configuration due to macOS restrictions).
Then let’s look at the translated example:
External Modification Summary:
Calls made by other processes targeting this process:
task_for_pid: 1
thread_create: 0
thread_set_state: 0
Calls made by this process:
task_for_pid: 0
thread_create: 0
thread_set_state: 0
Calls made by all processes on this machine:
task_for_pid: 915091
thread_create: 0
thread_set_state: 804
Copy the code
We see almost the same statistics, but interestingly, we set task_for_PID to 1. Thus, the translation environment makes minimal observation/modification of the translation process.
Virtual memory area
The translated version of the program has higher RAM usage than the native version.
In the native case, we can see:
VIRTUAL REGION
REGION TYPE SIZE COUNT (non-coalesced)
=========== ======= =======
TOTAL 1.7G 2053
TOTAL, minus reserved VM space 1.3G 2053
Copy the code
And the translated situation is:
REGION TYPE SIZE COUNT (non - coalesced) = = = = = = = = = = = = = = = = = = = = = = = = = TOTAL 5.4 G 1512 TOTAL, Minus reserved VM space 5.1g 1512Copy the code
Note that in the post-translation case, we provide additional virtual memory areas for Rosetta:
Rosetta Arena 2048K 1
Rosetta Generic 864K 19
Rosetta IndirectBranch 512K 1
Rosetta JIT 128.0M 1
Rosetta Return Stack 192K 12
Rosetta Thread Context 192K 12
Copy the code
Rosetta collapse
Rosetta is a powerful translation system. But it can’t translate all x86-64 instructions. For example, vector instructions cannot be translated and crash when encountered.
Before diagnosing specific problems, it is worthwhile to be familiar with Apple’s Porting Guide, as this can help us come up with reasonable assumptions about why the program might crash.
icdab_avx
Vector instruction crash
We crashed when we encountered Intel AVX vector instructions on an Apple Silicon Mac running an application using translation. We demonstrate this with a sample application, ICdab_avx.
The crash code type will be:
Code Type: X86-64 (Translated)
Copy the code
The crash type will be EXC_BAD_INSTRUCTION as follows:
Exception Type: EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes: 0x0000000000000001, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Signal: Illegal instruction: 4
Termination Reason: Namespace SIGNAL, Code 0x4
Terminating Process: exc handler [26823]
Copy the code
In our case, the thread state at crash is:
Thread 0 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000001 rbx: 0x0000600001fcf5c0 rcx:
0x00007f87d143f8c0 rdx: 0x00007f87d143f8c0
rdi: 0x00000001047d6fa0 rsi: 0x00000001047d770a rbp:
0x000000030d132ab0 rsp: 0x000000030d132ab0
r8: 0x0000000000000003 r9: 0x0000000104b0e000 r10:
0x00000001047dc702 r11: 0x00000001047d57d0
r12: 0x00006000012d5100 r13: 0x00007fff6a9d4000 r14:
0x00007f87d143f8c0 r15: 0x00000001047d770a
rip: 0x00000001047d56cb rfl: 0x0000000000000206
Copy the code
The application binaries (program text) are loaded as follows:
Binary Images: 0 x1047d4000 x1047d7fff 0 + perivalebluebell.com.icdab-avx (1.0 1) < 3 d9e0ded - 2 c66-30 ee - AC6C - 7 c426246332e > /Users/USER/Desktop/*/icdab_avx.app/Contents/MacOS/icdab_avxCopy the code
If we find that Apple Silicon Macs crash our apps in this way, we can quickly search for any vector instruction if we have such suspicions.
# objdump -d icdab_avx.app/Contents/MacOS/icdab_avx | grep vmov |
head
100004527: c5 fa 10 84 24 a4 00 00 00 vmovss 164(%rsp),
%xmm0
100004530: c5 fa 10 8c 24 a0 00 00 00 vmovss 160(%rsp),
%xmm1
10000453f: c5 fa 10 8c 24 a8 00 00 00 vmovss 168(%rsp),
%xmm1
10000454e: c5 fa 10 8c 24 ac 00 00 00 vmovss 172(%rsp),
%xmm1
10000455d: c5 fa 10 8c 24 b4 00 00 00 vmovss 180(%rsp),
%xmm1
100004566: c5 fa 10 94 24 b0 00 00 00 vmovss 176(%rsp),
%xmm2
100004575: c5 fa 10 94 24 b8 00 00 00 vmovss 184(%rsp),
%xmm2
100004584: c5 fa 10 94 24 bc 00 00 00 vmovss 188(%rsp),
%xmm2
100004593: c5 f8 29 8c 24 90 00 00 00 vmovaps %xmm1,
144(%rsp)
10000459c: c5 f8 29 84 24 80 00 00 00 vmovaps %xmm0,
128(%rsp)
Copy the code
But, to be more precise, we can use instruction Pointers at crash time. As we see from the state of the crashed thread, we have:
rip: 0x00000001047d56cb rfl: 0x0000000000000206
Copy the code
We can see from Binary Images that the program is loaded at address 0x1047d4000.
Using the technique we discussed in the Symbolification section, we can load the ICdab_avx binary in Hopper, changing the base address of the binary to 0x1047d4000, Then go to the instruction pointer RIP and address 0x00000001047D56CB.
Then, we see the assembly dump:
_compute_delta:
push rbp ; CODE
XREF=_$s9icdab_avx14ViewControllerC31runVectorOperationsButtonAc
tionyySo12NSButtonCellCF+32
mov rbp, rsp
and rsp, 0xffffffffffffffe0
sub rsp, 0x160
mov dword [rsp+0x160+var_A4], 0x40000000
mov dword [rsp+0x160+var_A8], 0x40800000
mov dword [rsp+0x160+var_AC], 0x40c00000
mov dword [rsp+0x160+var_B0], 0x41000000
mov dword [rsp+0x160+var_B4], 0x41200000
mov dword [rsp+0x160+var_B8], 0x41400000
mov dword [rsp+0x160+var_BC], 0x41600000
mov dword [rsp+0x160+var_C0], 0x41800000
vmovss xmm0, dword [rsp+0x160+var_BC]
vmovss xmm1, dword [rsp+0x160+var_C0]
Copy the code
As a result, although we didn’t find the exact instructions of failure, but found the error function compute_delta, it is located in runVectorOperationsButtonAction method, it seems to have been an inline to this version of binary file. Nevertheless, we have had enough help to explore the binaries in the relevant areas and confirm that the vector operation VMOVSS has taken place. Rosetta does not support this feature.
The original code causing the problem is:
Void compute_delta() {/* Initialize the two argument vectors */ __m256 evens = _mm256_set_ps(2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0); __m256 odds = _mm256_set_ps (1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0); /* Compute the difference between the two vectors */ __m256 result = _mm256_sub_ps(evens, odds); /* Display the elements of the result vector */ float* f = (float*)&result; printf("%f %f %f %f %f %f %f %f\n", f[0], f[1], f[2], f[3], f[4], f[5], f[6], f[7]); return; }Copy the code
To avoid this problem, we should use a useful method to check whether the current environment supports AVX, as follows:
bool avx_v1_supported() { int ret = 0; size_t size = sizeof(ret); if (sysctlbyname("hw.optional.avx1_0", &ret, &size, NULL, 0) == -1) { if (errno == ENOENT) return false; return false; } bool supported = (ret ! = 0); return supported; }Copy the code
This function returns true if AVX version 1 is supported (and there are no errors in retrieving the information).
Run iOS on a Mac
Since Apple Silicon Macs and iOS devices share the same ARM CPU architecture, Apple provides additional features. Unmodified iOS apps may run on ARM-based macOS. To that end, ARM-based macOS has some special iOS support libraries.
We can think of macOS (subject) as providing the supporting libraries and frameworks, providing the UIKit that iOS (object) applications expect.
When such an application crashes, we get a crash report, which is a macOS crash report, but most of the details involve the iOS library.
icdab_wrap
IOS app crashes on macOS
If we run the iOS app ICdab_wrap on an Apple Silicon Mac, it loads normally because macOS provides the UIKit framework, and ICdab_wrap assumes that the framework already exists. The application is designed to demonstrate a problem that unpacking Nil is optional.
When it crashes, we can see:
Code Type: ARM-64 (Native)
Parent Process: ??? [1]
Responsible: icdab_wrap [2802]
User ID: 501
Copy the code
This indicates that the application is running native code, not translated code.
Date/Time: 2020-11-14 11:58:17.668 +0000
OS Version: Mac OS X 10.16 (20A5343i)
Report Version: 12
Anonymous UUID: 0118DF8D-2876-0263-8668-41B1482DDC38
Copy the code
That means we’re obviously running on a Mac.
System Integrity Protection: enabled
Crashed Thread: 0 Dispatch queue: com.apple.main-thread
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: EXC_ARM_BREAKPOINT at 0x00000001c6c8f1f0
(brk 1)
Exception Note: EXC_CORPSE_NOTIFY
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [2802]
Application Specific Information:
dyld3 mode
Fatal error: Unexpectedly found nil while implicitly unwrapping
an Optional value: file icdab_wrap/PlanetViewController.swift,
line 45
Copy the code
This means the program crashed while unpacking the Nil option.
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libswiftCore.dylib 0x00000001c6c8f1f0
closure #1 in closure #1 in closure #1 in
_assertionFailure(_:_:file:line:flags:) + 404
1 libswiftCore.dylib 0x00000001c6c8f1f0
closure #1 in closure #1 in closure #1 in
_assertionFailure(_:_:file:line:flags:) + 404
2 libswiftCore.dylib 0x00000001c6c8e660
_assertionFailure(_:_:file:line:flags:) + 488
3 www.perivalebluebell.icdab-wrap 0x0000000104937da4
PlanetViewController.imageDownloaded(_:) + 196
(PlanetViewController.swift:45)
.
.
.
16 com.apple.AppKit 0x0000000189740824
_DPSNextEvent + 880
17 com.apple.AppKit 0x000000018973f1fc
-[NSApplication(NSEvent)
_nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 1300
18 com.apple.AppKit 0x00000001897313c4
-[NSApplication run] + 600
19 com.apple.AppKit 0x0000000189703550
NSApplicationMain + 1064
20 com.apple.AppKit 0x00000001899e92f8
_NSApplicationMainWithInfoDictionary + 24
21 com.apple.UIKitMacHelper 0x00000001bd9a6038
UINSApplicationMain + 476
22 com.apple.UIKitCore 0x00000001d333d1b0
UIApplicationMain + 2108
23 www.perivalebluebell.icdab-wrap 0x0000000104933a38 main +
88 (AppDelegate.swift:12)
24 libdyld.dylib 0x00000001c758ca50 start
+ 4
Copy the code
This indicates that when the program is loaded libdyld. Dylib, its expected UIKit UIKitCore is implemented through UIKitMacHelper support layer.
Binary Images: 0 x10492c000 x10493bfff 0 + www.perivalebluebell.icdab-wrap (1.0 1) < E9A2E2CC b0 E879 to 37-820 - c - F336DF2AACDA > /Users/USER/Library/Developer/Xcode/DerivedData/icdab-gbtgrhpqeh gqogaglrpuvzajteku/Build/Products/Debug-iphoneos/icdab_wrap.app/i cdab_wrap 0x104a1c000 - 0x104a27fff libobjc-trampolines.dylib (817) <4A2C66DE-9358-3AE9-A69F-36687DB19CE3> /usr/lib/libobjc-trampolines.dylib 0x104b34000 - 0x104baffff dyld (828) <7A9F335B-50E3-3018-A9CC-26E57B61D907> /usr/lib/dyld . . 0x189700000 - 0x18a400fff Com.apple.appkit (6.9-2004.102) < 96941aAC-01D7-36e7-9253-2C1187864719 > / System/Library/Frameworks/AppKit framework Versions/C/AppKit.. x1bd77f000 0-0 x1bd9a1fff com. Apple. UIFoundation (1.0) - 714) <D3335C2E-2366-30AD-A5F3-6058164D69EE> /System/Library/PrivateFrameworks/UIFoundation.framework/Version S /A/UIFoundation 0x1bd9a2000-0x1Bda37ffF com.apple.UIKitMacHelper (1.0-3979.1.400) <F2F6D8F7-8178-3113-856E-F99614A4F13E> /System/Library/PrivateFrameworks/UIKitMacHelper.framework/Versi Ons /A/UIKitMacHelper 0x1bda38000-0x1Bda4bfff com.apple.UIKitServices (1.0-1) < d8c4d101-a04C-37e6-87A3-6ad9adFec787 > /System/Library/PrivateFrameworks/UIKitServices.framework/Versio ns/A/UIKitServices . . 0x1c9b1b000 - 0x1c9b1bfff Com. Apple. MobileCoreServices (1112.0.10-1112.0.10) < 992 daec7-6964-3686 - b353d925 A910-4365 > /System/iOSSupport/System/Library/Frameworks/MobileCoreServices. framework/Versions/A/MobileCoreServices . . 0x1d333a000 -0x1D462FFFF com.apple.UIKitCore (1.0-3979.1.400) < 023078DD-44DA-3a11-82CA-12F8412661A2 > /System/iOSSupport/System/Library/PrivateFrameworks/UIKitCore.fr amework/Versions/A/UIKitCore . . 0x1d72fa000 - 0x1d7337fff libswiftUIKit.dylib (15) <68377BCA-6493-3E34-920E-0765BD07F2A7> /System/iOSSupport/usr/lib/swift/libswiftUIKit.dylibCopy the code
In most cases, these crashes can be analyzed as directly as crashes on iOS. The problem is most likely due to the different physical environment. For example, iOS devices have gyroscopes, while macOS devices do not.
Support iOS apps on Mac
Apple provides guidance on best practices for deploying iOS applications on the Mac. You can perform the following operations:
- If you are sure that your iOS application is not suitable for macOS. You can unconfigure it in App Store Connect.
- Allows applications installed on iOS to be installed on macOS, but adds optional hardware functionality to check availability.
- Enhance the application to make it easier for Mac users to use alternative features. For example, add iOS keyboard support.
- Add code to detectiOS-on-MacThe scene.
- Connect to Mac through Mac Catalyst technology. That means having a great iPadOS app first.
- Write pure native macOS applications.
For example, in the application icdab_gyro we will show how to detect ios-on-Mac scenarios:
Let info = ProcessInfo() if #available(iOS 14.0, *) { if info.isiOSAppOnMac { print("We are an iOS app running on a Mac") } }Copy the code
In addition, when using a gyroscope
var motion = CMMotionManager()
Copy the code
And use gyroscopes only when available:
If motion. IsGyroAvailable {self. Motion. GyroUpdateInterval = 1.0/60.0 self. Motion. StartGyroUpdates (.).Copy the code
communication
The article continues to update every week, you can search “iOS growth refers to north” on wechat to read and urge more. Learn together and grow