Skip to content

Commit

Permalink
Add basic UIViewRepresentable support
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Ye committed Sep 2, 2024
1 parent 3337a0b commit 0adbf16
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ case 2024:
.visionOS(.v1),
]
#endif
case 2021:
case 2021: // iOS 15.5
[
.iOS(.v15),
.macOS(.v12),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// PlatformViewRepresentable.swift
// OpenSwiftUI
//
// Audited for RELEASE_2021
// Status: WIP

#if os(macOS)
import AppKit
typealias PlatformView = NSView
#elseif os(iOS)
import UIKit
typealias PlatformView = UIView
#else
import Foundation
typealias PlatformView = Void
#endif

protocol PlatformViewRepresentable: View {
associatedtype PlatformViewProvider
associatedtype Coordinator

static var dynamicProperties: DynamicPropertyCache.Fields { get }
func makeViewProvider(context: PlatformViewRepresentableContext<Self>) -> PlatformViewProvider
func updateViewProvider(_ provider: PlatformViewProvider, context: PlatformViewRepresentableContext<Self>)
func resetViewProvider(_ provider: PlatformViewProvider, coordinator: Coordinator, destroy: () -> Void)
static func dismantleViewProvider(_: PlatformViewProvider, coordinator: Coordinator)
static func platformView(for: PlatformViewProvider) -> PlatformView
func makeCoordinator() -> Coordinator
// func _identifiedViewTree(in: PlatformViewProvider) -> _IdentifiedViewTree
func overrideSizeThatFits(_ size: inout CGSize, in: _ProposedSize, platformView: PlatformViewProvider)
// func overrideLayoutTraits(_ traits: inout _LayoutTraits, for provider: PlatformViewProvider)

static func modifyBridgedViewInputs(_ inputs: inout _ViewInputs)
static var isViewController: Bool { get }
// static var safeAreaMode: _PlatformViewRepresentable_SafeAreaMode { get }
}

// MARK: - PlatformViewRepresentableValues

struct PlatformViewRepresentableValues {
var preferenceBridge: PreferenceBridge
var transaction: Transaction
var environment: EnvironmentValues

static var current: PlatformViewRepresentableValues?

func asCurrent<V>(do: () -> V) -> V {
let old = PlatformViewRepresentableValues.current
PlatformViewRepresentableValues.current = self
defer { PlatformViewRepresentableValues.current = old }
return `do`()

Check warning on line 52 in Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift

View check run for this annotation

Codecov / codecov/patch

Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift#L48-L52

Added lines #L48 - L52 were not covered by tests
}
}

struct PlatformViewRepresentableContext<RepresentableType: PlatformViewRepresentable> {
var values: PlatformViewRepresentableValues
let coordinator: RepresentableType.Coordinator
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
//
// UIViewRepresentable.swift
// OpenSwiftUI
//
// Audited for RELEASE_2021
// Status: WIP

#if os(iOS)

import UIKit

/// A wrapper for a UIKit view that you use to integrate that view into your
/// OpenSwiftUI view hierarchy.
///
/// Use a ``UIViewRepresentable`` instance to create and manage a
/// [UIView](https://developer.apple.com/documentation/UIKit/UIView) object in your OpenSwiftUI
/// interface. Adopt this protocol in one of your app's custom instances, and
/// use its methods to create, update, and tear down your view. The creation and
/// update processes parallel the behavior of OpenSwiftUI views, and you use them to
/// configure your view with your app's current state information. Use the
/// teardown process to remove your view cleanly from your OpenSwiftUI. For example,
/// you might use the teardown process to notify other objects that the view is
/// disappearing.
///
/// To add your view into your SwiftUI interface, create your
/// ``UIViewRepresentable`` instance and add it to your SwiftUI interface. The
/// system calls the methods of your representable instance at appropriate times
/// to create and update the view. The following example shows the inclusion of
/// a custom `MyRepresentedCustomView` structure in the view hierarchy.
///
/// struct ContentView: View {
/// var body: some View {
/// VStack {
/// Text("Global Sales")
/// MyRepresentedCustomView()
/// }
/// }
/// }
///
/// The system doesn't automatically communicate changes occurring within your
/// view to other parts of your SwiftUI interface. When you want your view to
/// coordinate with other SwiftUI views, you must provide a
/// ``NSViewControllerRepresentable/Coordinator`` instance to facilitate those
/// interactions. For example, you use a coordinator to forward target-action
/// and delegate messages from your view to any SwiftUI views.
@available(macOS, unavailable)
@available(watchOS, unavailable)
public protocol UIViewRepresentable: View where Self.Body == Never {

/// The type of view to present.
associatedtype UIViewType: UIView

/// A type to coordinate with the view.
associatedtype Coordinator = Void

/// Creates the view object and configures its initial state.
///
/// You must implement this method and use it to create your view object.
/// Configure the view using your app's current data and contents of the
/// `context` parameter. The system calls this method only once, when it
/// creates your view for the first time. For all subsequent updates, the
/// system calls the ``UIViewRepresentable/updateUIView(_:context:)``
/// method.
///
/// - Parameter context: A context structure containing information about
/// the current state of the system.
///
/// - Returns: Your UIKit view configured with the provided information.
@MainActor
func makeUIView(context: Self.Context) -> Self.UIViewType

/// Updates the state of the specified view with new information from
/// OpenSwiftUI.
///
/// When the state of your app changes, OpenSwiftUI updates the portions of your
/// interface affected by those changes. OpenSwiftUI calls this method for any
/// changes affecting the corresponding UIKit view. Use this method to
/// update the configuration of your view to match the new state information
/// provided in the `context` parameter.
///
/// - Parameters:
/// - uiView: Your custom view object.
/// - context: A context structure containing information about the current
/// state of the system.
@MainActor
func updateUIView(_ uiView: Self.UIViewType, context: Self.Context)

@MainActor
func _resetUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator, destory: () -> Void)

/// Cleans up the presented UIKit view (and coordinator) in anticipation of
/// their removal.
///
/// Use this method to perform additional clean-up work related to your
/// custom view. For example, you might use this method to remove observers
/// or update other parts of your OpenSwiftUI interface.
///
/// - Parameters:
/// - uiView: Your custom view object.
/// - coordinator: The custom coordinator instance you use to communicate
/// changes back to OpenSwiftUI. If you do not use a custom coordinator, the
/// system provides a default instance.
@MainActor
static func dismantleUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator)

/// Creates the custom instance that you use to communicate changes from
/// your view to other parts of your OpenSwiftUI interface.
///
/// Implement this method if changes to your view might affect other parts
/// of your app. In your implementation, create a custom Swift instance that
/// can communicate with other parts of your interface. For example, you
/// might provide an instance that binds its variables to OpenSwiftUI
/// properties, causing the two to remain synchronized. If your view doesn't
/// interact with other parts of your app, providing a coordinator is
/// unnecessary.
///
/// SwiftUI calls this method before calling the
/// ``UIViewRepresentable/makeUIView(context:)`` method. The system provides
/// your coordinator either directly or as part of a context structure when
/// calling the other methods of your representable instance.
@MainActor
func makeCoordinator() -> Self.Coordinator

// func _identifiedViewTree(in uiView: UIViewType) -> _IdentifiedViewTree
func _overrideSizeThatFits(_ size: inout CGSize, in: _ProposedSize, uiView: UIViewType)
// func _overrideLayoutTraits(_ traits: inout _LayoutTraits, for uiView: UIViewType)

static func _modifyBridgedViewInputs(_ inputs: inout _ViewInputs)

typealias Context = UIViewRepresentableContext<Self>
}

@available(macOS, unavailable)
extension UIViewRepresentable where Self.Coordinator == () {

/// Creates the custom instance that you use to communicate changes from
/// your view to other parts of your SwiftUI interface.
///
/// Implement this method if changes to your view might affect other parts
/// of your app. In your implementation, create a custom Swift instance that
/// can communicate with other parts of your interface. For example, you
/// might provide an instance that binds its variables to SwiftUI
/// properties, causing the two to remain synchronized. If your view doesn't
/// interact with other parts of your app, providing a coordinator is
/// unnecessary.
///
/// SwiftUI calls this method before calling the
/// ``UIViewRepresentable/makeUIView(context:)`` method. The system provides
/// your coordinator either directly or as part of a context structure when
/// calling the other methods of your representable instance.
public func makeCoordinator() -> Self.Coordinator {
return
}
}

@available(macOS, unavailable)
extension UIViewRepresentable {
/// Cleans up the presented UIKit view (and coordinator) in anticipation of
/// their removal.
///
/// Use this method to perform additional clean-up work related to your
/// custom view. For example, you might use this method to remove observers
/// or update other parts of your SwiftUI interface.
///
/// - Parameters:
/// - uiView: Your custom view object.
/// - coordinator: The custom coordinator instance you use to communicate
/// changes back to SwiftUI. If you do not use a custom coordinator, the
/// system provides a default instance.
public static func dismantleUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator) {}

/// Declares the content and behavior of this view.
public var body: Never { bodyError() }
}

extension UIViewRepresentable {
func _resetUIView(_ uiView: Self.UIViewType, coordinator: Self.Coordinator, destory: () -> Void) {
destory()
}

// func _identifiedViewTree(in uiView: UIViewType) -> _IdentifiedViewTree
func _overrideSizeThatFits(_ size: inout CGSize, in: _ProposedSize, uiView: UIViewType) {}
// func _overrideLayoutTraits(_ traits: inout _LayoutTraits, for uiView: UIViewType)

static func _modifyBridgedViewInputs(_ inputs: inout _ViewInputs) {}

public static func _makeView(view: _GraphValue<Self>, inputs: _ViewInputs) -> _ViewOutputs {
fatalError("TODO")
}

public static func _makeViewList(view: _GraphValue<Self>, inputs: _ViewListInputs) -> _ViewListOutputs {
fatalError("TODO")
}
}

/// Contextual information about the state of the system that you use to create
/// and update your UIKit view.
///
/// A ``UIViewRepresentableContext`` structure contains details about the
/// current state of the system. When creating and updating your view, the
/// system creates one of these structures and passes it to the appropriate
/// method of your custom ``UIViewRepresentable`` instance. Use the information
/// in this structure to configure your view. For example, use the provided
/// environment values to configure the appearance of your view. Don't create
/// this structure yourself.
@available(macOS, unavailable)
@available(watchOS, unavailable)
@MainActor
public struct UIViewRepresentableContext<Representable> where Representable: UIViewRepresentable {

/// The view's associated coordinator.
public let coordinator: Representable.Coordinator

/// The current transaction.
public private(set) var transaction: Transaction

/// The current environment.
///
/// Use the environment values to configure the state of your view when
/// creating or updating it.
public private(set) var environment: EnvironmentValues

var preferenceBridge: PreferenceBridge

init(coordinator: Representable.Coordinator, transaction: Transaction, environment: EnvironmentValues, preferenceBridge: PreferenceBridge) {
self.coordinator = coordinator
self.transaction = transaction
self.environment = environment
self.preferenceBridge = preferenceBridge
}
}

@available(macOS, unavailable)
@available(watchOS, unavailable)
extension UIViewRepresentableContext : Sendable {
}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import Foundation

struct _ProposedSize: Hashable {
public struct _ProposedSize: Hashable {
var width: CGFloat?
var height: CGFloat?

Expand Down

0 comments on commit 0adbf16

Please sign in to comment.