{"id":25764662,"url":"https://github.com/ipedro/swiftui-backport","last_synced_at":"2026-05-15T06:10:55.300Z","repository":{"id":263324046,"uuid":"890028196","full_name":"ipedro/swiftui-backport","owner":"ipedro","description":null,"archived":false,"fork":false,"pushed_at":"2024-11-28T02:05:55.000Z","size":41,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-28T02:28:30.073Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ipedro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-17T20:38:16.000Z","updated_at":"2024-11-28T02:05:58.000Z","dependencies_parsed_at":"2024-11-18T01:21:22.208Z","dependency_job_id":null,"html_url":"https://github.com/ipedro/swiftui-backport","commit_stats":null,"previous_names":["ipedro/swiftui-subviews-backport"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipedro%2Fswiftui-backport","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipedro%2Fswiftui-backport/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipedro%2Fswiftui-backport/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ipedro%2Fswiftui-backport/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ipedro","download_url":"https://codeload.github.com/ipedro/swiftui-backport/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240935032,"owners_count":19881085,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-26T21:19:45.440Z","updated_at":"2026-05-15T06:10:55.270Z","avatar_url":"https://github.com/ipedro.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Backport for SwiftUI\n\n[![Swift Package Manager](https://img.shields.io/badge/SPM-compatible-brightgreen.svg)](https://swift.org/package-manager/)\n[![Platforms](https://img.shields.io/badge/platforms-iOS%2013%2B%20%7C%20macOS%2010.15%2B%20%7C%20tvOS%2013%2B%20%7C%20watchOS%206%2B%20%7C%20visionOS%201%2B-blue.svg)](https://developer.apple.com/swift/)\n[![Swift Version](https://img.shields.io/badge/Swift-5.9%2B-orange.svg)](https://swift.org/download/)\n[![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)](LICENSE)\n[![Build Status](https://github.com/ipedro/swiftui-backport/actions/workflows/ci.yml/badge.svg)](https://github.com/ipedro/swiftui-backport/actions)\n[![GitHub Stars](https://img.shields.io/github/stars/ipedro/swiftui-backport.svg)](https://github.com/ipedro/swiftui-backport/stargazers)\n[![GitHub Issues](https://img.shields.io/github/issues/ipedro/swiftui-backport.svg)](https://github.com/ipedro/swiftui-backport/issues)\n[![Twitter](https://img.shields.io/twitter/follow/ipedroalmeida.svg?style=social\u0026label=Follow)](https://twitter.com/ipedroalmeida)\n\nA lightweight Swift package that backports SwiftUI APIs to iOS versions earlier than latest (18). This allows developers to use newer APIs while maintaining compatibility with older iOS versions.\n\n## Features\n\n- Backports ForEach and Group initializers: Use the subview-based initializers on iOS versions earlier than 18.\n- Programmatic access to subviews: Easily manipulate and transform subviews in your SwiftUI views.\n- Lightweight and easy to integrate: Minimal code addition to your project.\n\n## Installation\n\n### Swift Package Manager\n\n#### Xcode Project**\n\nYou can add the package to your project using Swift Package Manager. In Xcode:\n\n1. Go to File \u003e Add Packages…\n2. Enter the repository URL: https://github.com/ipedro/swiftui-backport.git\n3. Choose the package and add it to your project.\n\n#### Package.swift\n\nIf you’re developing a Swift package and want to add Subviews Backport as a dependency, modify your `Package.swift` file as follows:\n\n1) Add the package to your dependencies array:\n\n    ```swift\n    dependencies: [\n        .package(url: \"https://github.com/ipedro/swiftui-backport.git\", from: \"1.0.0\")\n    ]\n    ```\n\n2) Add SwiftUIBackport to your target’s dependencies:\n\n    ```swift\n    targets: [\n        .target(\n            name: \"YourPackage\",\n            dependencies: [\n                .product(name: \"SwiftUIBackport\", package: \"swiftui-backport\")\n            ]\n        ),\n        // Other targets...\n    ]\n    ```\n\n## Usage\n\nImport the package in your Swift file:\n\n```swift\nimport SwiftUIBackport\n```\n\n### Backported ForEach Initializer\n\nUse the ForEach(subviews:content:) initializer to iterate over the subviews of a given view.\n\n```swift\nForEach(subviews: YourParentView()) { subview in\n    // Customize each subview\n    subview\n}\n```\n\n### Backported Group Initializer\n\nUse the Group(subviews:transform:) initializer to create a group from the subviews of a given view.\n\n```swift\nGroup(subviews: YourParentView()) { subviews in\n    VStack {\n        ForEach(subviews) { subview in\n            subview\n        }\n    }\n}\n```\n\n## Examples\n\n### Iterating Over Subviews with ForEach\n\n```swift\nstruct ContentView: View {\n    var body: some View {\n        ForEach(subviews: HStack {\n            Text(\"First\")\n            Text(\"Second\")\n            Text(\"Third\")\n        }) { subview in\n            subview\n                .font(.headline)\n        }\n    }\n}\n```\n\n### Custom Layout with Group\n\n```swift\nstruct CardsView: View {\n    var body: some View {\n        Group(subviews: VStack {\n            Text(\"Card 1\")\n            Text(\"Card 2\")\n            Text(\"Card 3\")\n        }) { subviews in\n            HStack {\n                if subviews.count \u003e= 2 {\n                    SecondaryCard { subviews[1] }\n                }\n                if let first = subviews.first {\n                    FeatureCard { first }\n                }\n                if subviews.count \u003e= 3 {\n                    SecondaryCard { subviews[2] }\n                }\n            }\n        }\n    }\n}\n```\n\n### Customizing Subviews in a List\n\n```swift\nimport SwiftUI\nimport SwiftUIBackport\n\nstruct CustomListView: View {\n    var body: some View {\n        Group(subviews: VStack {\n            Text(\"Item 1\")\n            Text(\"Item 2\")\n            Text(\"Item 3\")\n        }) { subviews in\n            List {\n                ForEach(subviews) { subview in\n                    HStack {\n                        Image(systemName: \"circle.fill\")\n                            .foregroundColor(.blue)\n                        subview\n                            .font(.headline)\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n**Explanation:**\n\n- Purpose: Display a list where each item is a customized version of the subviews.\n- Usage: We use Group to access the subviews of a VStack and then construct a List where each subview is customized with additional views and modifiers.\n\n### Custom Stack that Applies Modifiers to Subviews\n\n```swift\nimport SwiftUI\nimport SwiftUIBackport\n\nstruct CustomStack\u003cContent: View\u003e: View {\n    let content: Content\n    let spacing: CGFloat\n    let alignment: HorizontalAlignment\n\n    init(spacing: CGFloat = 10, alignment: HorizontalAlignment = .center, @ViewBuilder content: () -\u003e Content) {\n        self.spacing = spacing\n        self.alignment = alignment\n        self.content = content()\n    }\n\n    var body: some View {\n        VStack(alignment: alignment, spacing: spacing) {\n            Group(subviews: content) { subview in\n                subview\n                    .padding()\n                    .background(Color.yellow.opacity(0.3))\n                    .cornerRadius(8)\n            }\n        }\n    }\n}\n\n// Usage\nstruct CustomStackView: View {\n    var body: some View {\n        CustomStack(spacing: 20, alignment: .leading) {\n            Text(\"First Item\")\n            Text(\"Second Item\")\n            Text(\"Third Item\")\n        }\n        .padding()\n    }\n}\n```\n\n**Explanation:**\n\n- Purpose: Apply the same set of modifiers to each subview.\n- Usage: UnaryViewTree processes each subview individually, allowing us to apply modifiers directly.\n\n### Adaptive Stack that Switches Between V/HStack\n\n```swift\nimport SwiftUI\nimport SwiftUIBackport\n\nstruct AdaptiveStack\u003cContent: View\u003e: View {\n    let content: Content\n    let threshold: Int\n\n    init(threshold: Int = 3, @ViewBuilder content: () -\u003e Content) {\n        self.threshold = threshold\n        self.content = content()\n    }\n\n    var body: some View {\n        Group(subviews: content) { subviews in\n            if subviews.count \u003c= threshold {\n                HStack {\n                    subviews\n                }\n            } else {\n                VStack {\n                    subviews\n                }\n            }\n        }\n    }\n}\n\n// Usage\nstruct AdaptiveStackExampleView: View {\n    var body: some View {\n        AdaptiveStack {\n            Text(\"Item 1\")\n            Text(\"Item 2\")\n            Text(\"Item 3\")\n            Text(\"Item 4\")\n            Text(\"Item 5\")\n        }\n        .padding()\n    }\n}\n```\n\n**Explanation:**\n\n- Purpose: Create a custom stack that switches between HStack and VStack based on the number of subviews.\n- Usage: The AdaptiveStack view takes a threshold parameter. If the number of subviews is less than or equal to the threshold, it uses an HStack; otherwise, it uses a VStack.\n- Implementation:\n- MultiViewTree: We use MultiViewTree to access the subviews of the provided content.\n- Conditional Layout: In the closure, we check subviews.count and choose between HStack and VStack.\n- Using subviews Directly: We pass subviews directly into the chosen stack without iterating over them.\n\n**Example Usage:**\n\n```swift\nstruct AdaptiveStackDemo: View {\n    var body: some View {\n        VStack(spacing: 20) {\n            Text(\"Adaptive Stack with 2 Items:\")\n                .font(.headline)\n            AdaptiveStack {\n                Text(\"Item A\")\n                Text(\"Item B\")\n            }\n            .border(Color.blue)\n\n            Text(\"Adaptive Stack with 5 Items:\")\n                .font(.headline)\n            AdaptiveStack {\n                Text(\"Item 1\")\n                Text(\"Item 2\")\n                Text(\"Item 3\")\n                Text(\"Item 4\")\n                Text(\"Item 5\")\n            }\n            .border(Color.green)\n        }\n        .padding()\n    }\n}\n```\n\n**Explanation:**\n\n- The first AdaptiveStack has two items and displays them in an HStack.\n- The second AdaptiveStack has five items and displays them in a VStack.\n\n### Conditionally Transforming Subviews\n\n```swift\nimport SwiftUI\nimport SwiftUIBackport\n\nstruct ConditionalTransformationView: View {\n    var body: some View {\n        UnaryViewTree(subviews: VStack {\n            Text(\"Title\").font(.title)\n            Divider()\n            Text(\"Subtitle\").font(.subheadline)\n            Divider()\n            Text(\"Body text goes here.\").font(.body)\n        }) { subview in\n            if subview.body is Divider {\n                subview\n            } else {\n                subview\n                    .foregroundColor(.purple)\n            }\n        }\n    }\n}\n```\n\n**Explanation:**\n\n- Purpose: Apply transformations based on the type of subview.\n- Usage: In the closure, we check if the subview is a Divider and handle it differently from other subviews.\n\n#### Customize Subviews\n\n```swift\nimport SwiftUI\nimport SwiftUIBackport\n\nstruct CustomizedSubviewsExample: View {\n    var body: some View {\n        Group(subviews: HStack {\n            Text(\"Apple\")\n            Text(\"Banana\")\n            Text(\"Cherry\")\n        }) { subviews in\n            VStack(alignment: .leading) {\n                ForEach(subviews.indices, id: \\.self) { index in\n                    HStack {\n                        Text(\"\\(index + 1).\")\n                            .bold()\n                        subviews[index]\n                            .foregroundColor(.blue)\n                    }\n                }\n            }\n            .padding()\n        }\n    }\n}\n```\n\n## How It Works\n\nThe package provides extensions to ForEach and Group that enable the use of subview-based initializers on older iOS versions. It leverages SwiftUI’s _VariadicView system to access and manipulate subviews programmatically.\n\n### Key Components\n\n- _Subview: A proxy representation of a subview, conforming to View and Identifiable.\n- `_Subviews`: A collection of `_Subview` instances, conforming to RandomAccessCollection.\n- `MultiViewTree`: A view that creates a tree of subviews from an input view.\n- `UnaryViewTree`: A view that creates a tree of subviews from an input view.\n\n## Compatibility\n\n- iOS: 13.0+\n- macOS: 10.15+\n- tvOS: 13.0+\n- watchOS: 6.0+\n\n## Limitations\n\n- Subviews are proxies to their resolved views, so modifiers applied to the original view take effect before modifiers applied to the subview.\n- Subviews might represent views after styles have been applied; applying additional styles might not always have the desired effect.\n\n## License\n\nCopyright (c) 2024 Pedro Almeida\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## Acknowledgments\n\n- Inspired by SwiftUI’s new subview APIs introduced in iOS 18.\n- Thanks to the SwiftUI community for ongoing support and contributions.\n\n## Contributing\n\nContributions are welcome! Please open an issue or submit a pull request for any bugs, feature requests, or improvements.\n\n## Contact\n\nCreated by [Pedro Almeida](https://twitter.com/ipedro). Feel free to reach out for any questions or feedback.\n\nNote: This package is a backport and is not affiliated with or endorsed by Apple Inc.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipedro%2Fswiftui-backport","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fipedro%2Fswiftui-backport","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fipedro%2Fswiftui-backport/lists"}