Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Example/HostingExample/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import UIKit
import AppKit
#endif

import OpenSwiftUI

#if os(iOS) || os(visionOS)
class ViewController: UINavigationController {
override func viewDidAppear(_ animated: Bool) {
Expand All @@ -35,6 +37,10 @@ final class EntryViewController: UIViewController {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.pushHostingVC()
}

#if OPENSWIFTUI || DEBUG
// debugUIKitUpdateCycle()
#endif
}

@objc
Expand Down
67 changes: 65 additions & 2 deletions Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,75 @@ bool UIViewIgnoresTouchEvents(UIView *view);
OPENSWIFTUI_EXPORT
float UIAnimationDragCoefficient(void);

UIView * _UIKitCreateCustomView(Class class, CALayer *layer);

// MARK: - UIUpdate related private API from UIKitCore

OPENSWIFTUI_EXPORT
bool _UIUpdateAdaptiveRateNeeded();
bool _UIUpdateAdaptiveRateNeeded(void);

UIView * _UIKitCreateCustomView(Class class, CALayer *layer);
OPENSWIFTUI_EXPORT
bool _UIUpdateCycleEnabled(void);

typedef struct _UIUpdateTiming {
uint64_t unknown1;
uint64_t unknown2;
uint64_t unknown3;
} _UIUpdateTiming;

typedef void (^_UIUpdateSequenceCallback)(void * _Nullable context, CGFloat time, const _UIUpdateTiming * _Nonnull timing);

typedef struct _UIUpdateSequenceItem _UIUpdateSequenceItem;

typedef struct _UIUpdateSequence {
_UIUpdateSequenceItem * _Nullable first;
} _UIUpdateSequence;

typedef struct _UIUpdateSequenceItem {
const _UIUpdateSequenceItem * _Nullable next;
const _UIUpdateSequence * _Nullable sequence;
const char * name;
uint32_t flags;
void * _Nullable context;
void * _Nullable callback; // Actual type should be _UIUpdateSequenceCallback*
} _UIUpdateSequenceItem;

// MARK: - UIUpdateSequence Items
//
// UIUpdateActionPhase defines specific phases of the UI update process.
// See: https://developer.apple.com/documentation/uikit/uiupdateactionphase
//
// Each UI update consists of several phases that run in a consistent order.
// There are two phase groups: standard and low-latency.
//
// Standard phase group (runs for each UI update):
// 1. beforeEventDispatch / afterEventDispatch -> HIDEventsItem
// 2. beforeCADisplayLinkDispatch / afterCADisplayLinkDispatch -> CADisplayLinksItem
// 3. beforeCATransactionCommit / afterCATransactionCommit -> CATransactionCommitItem
//
// Low-latency phase group (optional, runs after standard phases):
// 1. beforeLowLatencyEventDispatch / afterLowLatencyEventDispatch -> LowLatencyHIDEventsItem
// 2. beforeLowLatencyCATransactionCommit / afterLowLatencyCATransactionCommit -> LowLatencyCATransactionCommitItem

OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceScheduledItem;
OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceHIDEventsItem;
OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceCADisplayLinksItem;
OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceAnimationsItem;
OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceCATransactionCommitItem;
OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceLowLatencyHIDEventsItem;
OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceLowLatencyCATransactionCommitItem;
OPENSWIFTUI_EXPORT const _UIUpdateSequenceItem * _Nonnull _UIUpdateSequenceDoneItem;

OPENSWIFTUI_EXPORT
void * _Nonnull _UIUpdateSequenceInsertItem(const _UIUpdateSequenceItem * _Nullable next,
const _UIUpdateSequence * _Nullable sequence,
const char * name,
uint32_t flags,
void * _Nullable context,
_UIUpdateSequenceCallback _Nullable callback);

OPENSWIFTUI_EXPORT
void _UIUpdateSequenceRemoveItem(_UIUpdateSequenceItem *item);

OPENSWIFTUI_ASSUME_NONNULL_END

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//
// UIKitUpdateCycle.swift
// OpenSwiftUI
//
// Audited for 6.5.4
// Status: WIP
// ID: 61722010453917A59567A84BEBF44765 (SwiftUI)

#if os(iOS) || os(visionOS)
import COpenSwiftUI
@_spi(ForOpenSwiftUIOnly)
import OpenSwiftUICore

package enum UIKitUpdateCycle {
private static var observerActions: [() -> Void] = []

private static var item: OpaquePointer?

package static var defaultUseSetNeedsLayout: Bool = {
let key = "UseSetNeedsLayoutForUpdates"
let defaults = UserDefaults.standard
guard defaults.object(forKey: key) != nil else {
return false
}
return defaults.bool(forKey: key)
}()

package static func addPreCommitObserver(_ action: @escaping () -> Void) {
guard _UIUpdateCycleEnabled() else {
return
}
if item == nil {
item = OpaquePointer(
_UIUpdateSequenceInsertItem(
_UIUpdateSequenceCATransactionCommitItem,
nil,
"OpenSwiftUIFlush",
0,
nil,
) { _, _, _ in
let actions = observerActions
guard !actions.isEmpty else { return }
observerActions = []
for action in actions {
Update.perform(action)
}
},
)

}
observerActions.append(action)
}

package static func addPreCommitObserverOrAsyncMain(_ action: @escaping () -> Void) {
if _UIUpdateCycleEnabled() {
addPreCommitObserver(action)
} else {
DispatchQueue.main.async(execute: action)
}
}

#if DEBUG
private static func debugItem(_ item: UnsafePointer<_UIUpdateSequenceItem>) {
let name = String(cString: item.pointee.name)
_ = _UIUpdateSequenceInsertItem(
item,
nil,
("OpenSwiftUIDebug" + name),
0,
nil
) { _, _, _ in
print("[UIKitUpdateCycle] \(name) phase")
}
}

package static func setupDebug() {
guard _UIUpdateCycleEnabled() else { return }
debugItem(_UIUpdateSequenceScheduledItem)
debugItem(_UIUpdateSequenceHIDEventsItem)
debugItem(_UIUpdateSequenceCADisplayLinksItem)
debugItem(_UIUpdateSequenceAnimationsItem)
debugItem(_UIUpdateSequenceCATransactionCommitItem)
debugItem(_UIUpdateSequenceLowLatencyHIDEventsItem)
debugItem(_UIUpdateSequenceLowLatencyCATransactionCommitItem)
debugItem(_UIUpdateSequenceDoneItem)
}
#endif
}

#if DEBUG
public func debugUIKitUpdateCycle() {
UIKitUpdateCycle.setupDebug()
}
#endif

#endif
2 changes: 1 addition & 1 deletion Sources/OpenSwiftUICore/Data/Update.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ package enum Update {

@inlinable
@inline(__always)
static func perform<T>(_ body: () throws -> T) rethrows -> T {
package static func perform<T>(_ body: () throws -> T) rethrows -> T {
begin()
defer { end() }
return try body()
Expand Down
Loading