Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AnyView.init(_fromValue:) #20

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
25 changes: 25 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"object": {
"pins": [
{
"package": "AssociatedTypeRequirementsKit",
"repositoryURL": "https://github.com/nerdsupremacist/AssociatedTypeRequirementsKit.git",
"state": {
"branch": null,
"revision": "5c0b95758d2d65fdb98977ee1af5648d9d192a32",
"version": "0.2.0"
}
},
{
"package": "CoreGraphicsShim",
"repositoryURL": "https://github.com/Cosmo/CoreGraphicsShim.git",
"state": {
"branch": "master",
"revision": "d2aec4b41d4f92371198b9a382f35749254f0a80",
"version": null
}
}
]
},
"version": 1
}
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),
]
}