Skip to content
Open
25 changes: 25 additions & 0 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ let package = Package(
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/Cosmo/CoreGraphicsShim.git", .branch("master")),
.package(url: "https://github.com/nerdsupremacist/AssociatedTypeRequirementsKit.git", from: "0.2.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
.target(
name: "OpenSwiftUI",
dependencies: ["CoreGraphicsShim"]),
dependencies: ["CoreGraphicsShim", "AssociatedTypeRequirementsKit"]),

.testTarget(
name: "OpenSwiftUITests",
dependencies: ["OpenSwiftUI"]),
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ Xcode 11.2 or higher is required.

| Status | Name | Notes |
| --- | --- | --- |
| ⚠️ | `struct AnyView` | `init?(_fromValue value: Any)` missing. |
| | `struct AnyView` | |
| ✅ | `struct TupleView` | |

### Drawing and Animation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import AssociatedTypeRequirementsVisitor

/**
**For internal use only.** Use to be able to call a function on a view where you don't know the concrete type at Compile Time.
Implement `callAsFunction` and a version that can take `Any` will be included as an extension.

Useful for occassions where your intuition is to cast `Any` to `View`, but Swift will stop you due to the associated type requirement.

```swift
guard let view = view as? View else { return }
// Do something
```
*/
protocol ViewAssociatedTypeRequirementsVisitor: AssociatedTypeRequirementsVisitor {
associatedtype Visitor = ViewAssociatedTypeRequirementsVisitor
associatedtype Input = View
associatedtype Output

func callAsFunction<T : View>(_ value: T) -> Output
}
18 changes: 18 additions & 0 deletions Sources/OpenSwiftUI/Views/AnyView+initFromValue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

extension AnyView {
public init?(_fromValue value: Any) {
guard let view = ViewTypeEraser.shared(value) else { return nil }
self = view
}
}

private struct ViewTypeEraser: ViewAssociatedTypeRequirementsVisitor {
func callAsFunction<T: View>(_ value: T) -> AnyView {
return AnyView(value)
}
}

extension ViewTypeEraser {
static let shared = ViewTypeEraser()
}
4 changes: 0 additions & 4 deletions Sources/OpenSwiftUI/Views/AnyView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ public struct AnyView: View {
_storage = AnyViewStorage<V>(view)
}

public init?(_fromValue value: Any) {
fatalError()
}

public typealias Body = Never
public var body: Never {
fatalError()
Expand Down
30 changes: 25 additions & 5 deletions Tests/OpenSwiftUITests/OpenSwiftUITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,35 @@ final class OpenSwiftUITests: XCTestCase {
Text("World")
}

let body = HStack {
if true {
Text("Hello")
}
Text("World")
}

func testAnyViewFromValueWithInDoesNotYieldView() {
let anyView = AnyView(_fromValue: 42)
XCTAssertNil(anyView)
}

func testAnyViewFromValueWithTextYieldsAnyView() {
let expectedText = "Hello"
let value: Any = Text(expectedText)
let anyView = AnyView(_fromValue: value)
XCTAssertNotNil(anyView)

guard let storage = anyView?._storage as? AnyViewStorage<Text> else {
XCTFail("View storage is not an AnyViewStorage of Text")
return
}

switch storage._view._storage {
case .verbatim(let string):
XCTAssertEqual(string, expectedText)
case .anyTextStorage(let storage):
XCTAssertEqual(storage.storage, expectedText)
}
}

static var allTests = [
("testExample", testExample),
("testAnyViewFromValueWithInDoesNotYieldView", testAnyViewFromValueWithInDoesNotYieldView),
("testAnyViewFromValueWithTextYieldsAnyView", testAnyViewFromValueWithTextYieldsAnyView),
]
}