Skip to content

Commit

Permalink
Added comments on how the code works.
Browse files Browse the repository at this point in the history
  • Loading branch information
GeorgeElsham committed Jul 7, 2021
1 parent 3cfc10f commit 67415a0
Showing 1 changed file with 17 additions and 0 deletions.
17 changes: 17 additions & 0 deletions Sources/ViewExtractor/ViewExtractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum ViewExtractor {
public struct GenericView {
let body: Any

/// Get `AnyView` from a generic view.
var anyView: AnyView? {
AnyView(_fromValue: body)
}
Expand All @@ -31,15 +32,23 @@ public enum ViewExtractor {
/// - Parameter view: View of `Any` type.
/// - Returns: Views contained by this `view`.
public static func views(from view: Any) -> [AnyView] {
// Check this is not an empty view with no content.
if view is EmptyView {
return []
}

// If this view is a `ForEach`, extract all contained views.
if let forEach = view as? DynamicViewContentProvider {
return forEach.extractContent()
}

// Just a normal view. Convert it from type `Any` to `AnyView`.
return withUnsafeBytes(of: view) { ptr -> [AnyView] in
// Cast from type `Any` to `GenericView`,
// which mimics the structure of a `View`.
let binded = ptr.bindMemory(to: GenericView.self)

// Get `AnyView` from the 'fake' view body.
return binded.first?.anyView.map { [$0] } ?? []
}
}
Expand All @@ -53,6 +62,7 @@ public typealias NormalContent<Content: View> = () -> Content
// MARK: - TupleView views

public extension TupleView {
/// Convert tuple of views to array of `AnyView`s.
var views: [AnyView] {
let children = Mirror(reflecting: value).children
return children.flatMap { ViewExtractor.views(from: $0.value) }
Expand All @@ -67,20 +77,27 @@ public protocol DynamicViewContentProvider {

extension ForEach: DynamicViewContentProvider where Content: View {
public func extractContent() -> [AnyView] {
// Dynamically mirrors the current instance.
let mirror = Mirror(reflecting: self)

// Retrieving hidden properties containing the data and content.
if let data = mirror.descendant("data") as? Data,
let content = mirror.descendant("content") as? (Data.Element) -> Content
{
return data.compactMap { element in
// Create content given the data for this `ForEach` element.
let newContent = content(element)

if newContent is EmptyView {
// Content is of type `EmptyView`,
// therefore this has no actual content.
return nil
} else {
return AnyView(newContent)
}
}
} else {
// Return no content if failure.
return []
}
}
Expand Down

0 comments on commit 67415a0

Please sign in to comment.