{"id":13995493,"url":"https://github.com/composed-swift/ComposedUI","last_synced_at":"2025-07-22T22:30:58.489Z","repository":{"id":42443199,"uuid":"198521246","full_name":"composed-swift/ComposedUI","owner":"composed-swift","description":"A Swift framework for building User Interfaces with the Composed framework.","archived":false,"fork":false,"pushed_at":"2023-01-19T19:30:10.000Z","size":971,"stargazers_count":12,"open_issues_count":7,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-21T15:14:04.095Z","etag":null,"topics":["composed","composition","composition-api","protocol-oriented-programming","spm","swift","swift-package-manager","uicollectionview","uistackview","uitableview"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/composed-swift.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"License.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-23T23:12:04.000Z","updated_at":"2024-01-31T16:51:18.000Z","dependencies_parsed_at":"2023-02-11T18:45:46.996Z","dependency_job_id":null,"html_url":"https://github.com/composed-swift/ComposedUI","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/composed-swift/ComposedUI","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composed-swift%2FComposedUI","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composed-swift%2FComposedUI/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composed-swift%2FComposedUI/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composed-swift%2FComposedUI/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/composed-swift","download_url":"https://codeload.github.com/composed-swift/ComposedUI/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/composed-swift%2FComposedUI/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266585663,"owners_count":23952163,"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","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["composed","composition","composition-api","protocol-oriented-programming","spm","swift","swift-package-manager","uicollectionview","uistackview","uitableview"],"created_at":"2024-08-09T14:03:26.695Z","updated_at":"2025-07-22T22:30:58.084Z","avatar_url":"https://github.com/composed-swift.png","language":"Swift","readme":"\u003cimg src=\"composed.png\" width=20%/\u003e\n\n**ComposedUI** builds upon [`Composed`](http://github.com/composed-swift/composed) by adding user interface features that allow you to power the screens in an application.\n\n\u003e If you prefer to look at code, there's a demo project here: [ComposedDemo](http://github.com/composed-swift/composed-demo)\n\nThe library is comprised of 4 key types for each view type, as well as various protocols for providing optional functionality.\n\n`UICollectionView` implementations are prefixed with `Collection`\n`UITableView` implementations are prefixed with `Table`\n`UIStackView` implementations are prefixed with `Stack`\n\nFor example, a `UICollectionView`'s types are defined as follows:\n\n**CollectionSectionProvider**\nIn order for your section to be used in a `UICollectionView`, your section needs to conform to this protocol. It has only 1 requirement, a function that returns a `CollectionSection`\n\n**CollectionSection**\nThis type encapsulates 3 `CollectionElement` instances. A cell, as well as optional header and footer elements.\n\n**CollectionElement**\nAn element defines how a cell or supplementary view should be registered, dequeued and configured for display.\n\n**CollectionCoordinator**\nThe coordinator is responsible for coordinating all of the events between a provider (via its mapping) and its view. Its typically both the `delegate` \u0026 `dataSource` of the corresponding view as well as the `updateDelegate` for the root provider.\n\n## Getting Started\n\nLets define a simple section to hold our contacts:\n\n```swift\nstruct Person {\n\tvar kind: String // family or friend\n}\n\nfinal class ContactsSection: ArraySection\u003cPerson\u003e { }\n```\n\nNow we can extend this so we can show it in a collection view:\n\n```swift\nextension ContactsSection: CollectionSectionProvider {\n\n\tfunc section(with traitCollection: UITraitCollection) -\u003e CollectionSection {\n\t\t/*\n\t\tNotes:\n\t\tThe `dequeueMethod` signals to the coordinator how to register and dequeue the cell\n\t\tThe element is generic to that cell type\n\t\t*/\n\t\tlet cell = CollectionCellElement(section: self, dequeueMethod: .fromNib(PersonCell.self)) { cell, index, section in\n\t\t\t// Since everything is generic, we know both the cell and the element types\n\t\t\tcell.prepare(with: element(at: index))\n\t\t}\n\n\t\treturn CollectionSection(section: self, cell: cell, header: header)\n\t}\n\t\n}\n```\n\nFinally we need to retain a coordinator on our view controller:\n\n```swift\nfinal class ContactsViewController: UICollectionViewController {\n\t\n\tprivate var coordinator: CollectionCoordinator?\n\t\n\toverride func viewDidLoad() {\n\t\tsuper.viewDidLoad()\n\t\t\n\t\tlet contacts = ContactsSection()\n\t\t// contacts.append(...)\n\t\t\n\t\t// this single line is all that's needed to connect a collection view to our provider\n\t\tcoordinator = CollectionCoordinator(collectionView: collectionView, sections: contacts)\n\t}\n\n}\n```\n\nNow if we build and run, our collection view should be populated with our contacts as expected. Simple!\n\n## Protocols\n\nComposedUI also includes various protocols for enabling opt-in behaviour for your sections. Lets add support for selection events to our section above:\n\n```swift\nextension ContactsSection: CollectionSelectionHandler {\n\t\n\tfunc didSelect(at index: Int, cell: UICollectionViewCell) {\n\t\tprint(element(at: index))\n\t\tdeselect(at: index)\n\t}\n\t\n}\n```\n\nThat's it! Our coordinator already handles selection, so when a selection occurs it uses the indexPath to determine which section the selection occured in, it then attempts to cast that section to the protocol and on success, calls the associated method for us. As you can see this is an extremel powerful approach, yet extremely simple and elegant API that has 2 major benefits:\n\n1. You opt-in to the features you want rather than inherit them by default\n2. You can provide your own protocols and use the same infrastructure provided by Composed\n\n## Advanced Usage\n\nSo far we've built a relativel simple example that shows a single section. Lets update our view controller above to use a SectionProvider – and make things more interesting.\n\n```swift\noverride func viewDidLoad() {\n\tsuper.viewDidLoad()\n\t\n\t// ... create our contacts (family and friends)\n\t\n\tlet provider = ComposedSectionProvider()\n\tprovider.append(family)\n\tprovider.append(friends)\n\t\n\t// this single line is all that's needed to connect a collection view to our provider\n\tcoordinator = CollectionCoordinator(collectionView: collectionView, provider: provider)\n}\n```\n\nIf we now run our example again, we'll see everything works as it did before, except we now have 2 sections.\n\nThis has a number of benefits already:\n\n1. We didn't need to manage indexPaths or section indexes\n2. We were able to reuse our existing section\n3. Our section has no knowledge that its now inside of a larger structure\n\nNow lets add some custom behaviour depending on the data:\n\n```swift\nextension ContactsSection: CollectionSelectionHandler {\n\tvar allowsMultipleSelection: Bool { return isFamily }\n}\n```\nLets run the project again and we can see that the Family section now allows multiple selection, whereas the Friend section does not. This is another great benefit of using ComposedUI because the Coordinator is able to perform more advanced logic without needing to understand the underlying structure.\n","funding_links":[],"categories":["Swift"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomposed-swift%2FComposedUI","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcomposed-swift%2FComposedUI","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcomposed-swift%2FComposedUI/lists"}