Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
221 commits
Select commit Hold shift + click to select a range
3946a41
Add FlatSection
JosephDuffy Dec 22, 2020
babdadf
Mark `append(_:)` public
JosephDuffy Dec 22, 2020
59daa65
Add `section(at:)`
JosephDuffy Dec 22, 2020
c4fe225
Add support for flat UI sections, with mixed cells
JosephDuffy Dec 22, 2020
aa527de
Aid migration from `CollectionSectionProvider`
JosephDuffy Dec 22, 2020
0b5350f
Add `public init` to `FlatSection`
JosephDuffy Dec 22, 2020
d3dee27
Fix `section(at:)` logic
JosephDuffy Dec 22, 2020
66d5fed
Conform `FlatSection` to `CustomReflectable`
JosephDuffy Dec 22, 2020
f51dc04
Become update delegate of inserted children
JosephDuffy Dec 22, 2020
e1e7fd5
Add more insert and removal functions
JosephDuffy Dec 22, 2020
c597da9
Remove associated type from `CollectionElement`
JosephDuffy Dec 22, 2020
fd21a2f
Add header invalidation
JosephDuffy Dec 23, 2020
ec63ce7
[SBI-519] Fix crash due to using removed section inside didEndDisplay…
bill201207 Dec 24, 2020
ad9dc00
[SBI-519] Cache registered nib names to avoid multiple redundant call…
bill201207 Dec 24, 2020
7320206
Merge pull request #1 from opennetltd/hotfix/SBI-519-ComposedUI-issues
bill201207 Dec 24, 2020
7b27a38
Merge branch 'main' of github.com:opennetltd/Composed into feature/Fl…
JosephDuffy Dec 28, 2020
f0ab34c
Fix test compilation
JosephDuffy Dec 28, 2020
06449c3
Fix `cellSectionMap` for per-row cells
JosephDuffy Dec 28, 2020
f6a4b2c
Delegate `SelectionHandler` calls to children
JosephDuffy Dec 28, 2020
eb3a33a
Add `FlatUICollectionViewSection`
JosephDuffy Dec 28, 2020
986674a
Support adding `SectionProvider`s to `FlatSection`
JosephDuffy Dec 29, 2020
ad73a5a
Reload header when `header` is set
JosephDuffy Dec 29, 2020
f6cd79c
Cache nib registrations to improve performance
JosephDuffy Jan 1, 2021
b35ea17
Merge branch 'cache-nib-registrations'
JosephDuffy Jan 1, 2021
1614657
Merge branch 'main' into feature/FlatSection
JosephDuffy Jan 1, 2021
ecdd0fa
Fix `SectionProvider` sections not being removed
JosephDuffy Jan 4, 2021
fd0fb6a
Account for child `AggregateSectionProvider`s
JosephDuffy Jan 4, 2021
ee87162
Update `sectionOffset(for:)` to return an optional
JosephDuffy Jan 4, 2021
9684f62
Fix `ComposedSectionProvider` compilation
JosephDuffy Jan 4, 2021
987e496
Account for empty sections
JosephDuffy Jan 4, 2021
ad450d5
Remove `defer`s to ease debugging
JosephDuffy Jan 4, 2021
c1a438e
Become delegate of inserted sections
JosephDuffy Jan 5, 2021
00cfff2
Document `sectionIndex(of:)`
JosephDuffy Jan 5, 2021
90db5e5
Call `willAppear` for headers/footers
JosephDuffy Jan 5, 2021
e7c88de
Configure headers/footers on appear
JosephDuffy Jan 5, 2021
c52b6e4
Fix element offset when inserting in `FlatSection`
JosephDuffy Jan 7, 2021
e45ea84
Fix incorrect index when provider removes sections
JosephDuffy Jan 11, 2021
9e1b8bd
Mark `updateDelegate` as `weak`
JosephDuffy Jan 12, 2021
710efde
Remove/reduce some `FlatSection` bugs
JosephDuffy Jan 12, 2021
f1cdd0d
Add foundation of `FlatSection` tests
JosephDuffy Jan 12, 2021
42c05c9
Do not check to unset update delegate of sections
JosephDuffy Jan 12, 2021
3fb3c8f
Remove `Child` from public API
JosephDuffy Jan 12, 2021
a9a3c94
Add more `FlatSection` tests
JosephDuffy Jan 12, 2021
791b088
Change `index` to `childIndex`
JosephDuffy Jan 12, 2021
a1cafc9
Remove check for unsetting update delegate
JosephDuffy Jan 12, 2021
f49d0c5
Add more `FlatSection` tests
JosephDuffy Jan 12, 2021
0345565
Propagate updates from sections
JosephDuffy Jan 13, 2021
eb49837
Fix incorrect header and footer in flat sections
JosephDuffy Jan 18, 2021
bd1f1e1
Batch collection view updates
JosephDuffy Jan 18, 2021
da4ce06
Add foundation for batched updates
JosephDuffy Jan 18, 2021
213a8c8
Merge branch 'main' into batch-collection-view-updates
JosephDuffy Jan 18, 2021
94d7ac0
Merge branch 'batch-collection-view-updates' of github.com:opennetltd…
JosephDuffy Jan 18, 2021
b2f5434
Add logged error to comment
JosephDuffy Jan 18, 2021
2ea258a
Add commented test for failing scenarios
JosephDuffy Jan 18, 2021
c1ddbad
Fix deprecation warning
JosephDuffy Jan 18, 2021
04e4249
Add more tests/fix existing tests
JosephDuffy Jan 18, 2021
403808c
Process item deletes before section deletes
JosephDuffy Jan 18, 2021
87e7d29
Fix debug log comments
JosephDuffy Jan 18, 2021
3692116
Disable logs by default
JosephDuffy Jan 18, 2021
d7f2486
Fix inserting in same index as inserted section
JosephDuffy Jan 19, 2021
d56a3b4
Fix number of updates after `invalidateAll`
JosephDuffy Jan 19, 2021
fe6c349
Remove `didUpdateSections`
JosephDuffy Jan 19, 2021
a3fc407
Enable tests for ComposedUI scheme
JosephDuffy Jan 19, 2021
cc0fd90
Various `ChangesReducer` fixes, new testing method
JosephDuffy Jan 19, 2021
e1b910e
Handle removal/reload overlaps
JosephDuffy Jan 19, 2021
b204e72
Add TODOs for areas not covered
JosephDuffy Jan 19, 2021
83ea774
Merge branch '2.0-beta' into main
JosephDuffy Jan 19, 2021
ed978d7
Merge branch 'main' into batch-collection-view-updates
JosephDuffy Jan 19, 2021
6e3c578
Add support for footers
JosephDuffy Jan 19, 2021
5532266
Merge branch '2.0-beta' into feature/FlatSection
JosephDuffy Jan 19, 2021
9ccb3ca
Add failing tests
JosephDuffy Jan 19, 2021
0e68343
Element removal fixes
JosephDuffy Jan 20, 2021
ff03506
Add failing tests
JosephDuffy Jan 20, 2021
db785db
Improve element removal handling
JosephDuffy Jan 23, 2021
6161d28
Fix memory leak of none-nil values
JosephDuffy Jan 25, 2021
b58e22f
Add TODO
JosephDuffy Jan 25, 2021
5397b0d
Remove wrapping of `cell`
JosephDuffy Jan 25, 2021
14660c5
Do not reset backgroundView when delegate is nil
JosephDuffy Jan 26, 2021
7823283
Fix swaps
JosephDuffy Feb 1, 2021
e63b56b
Fix batch removal from `ArraySection`
JosephDuffy Feb 1, 2021
0d86845
Ignore requested cells order
JosephDuffy Feb 1, 2021
2951291
Uncomment some tests
JosephDuffy Feb 1, 2021
75df0f0
Unset `updateDelegate` on section provider removal
JosephDuffy Feb 1, 2021
e6369c6
Merge branch 'feature/FlatSection' into main
JosephDuffy Feb 1, 2021
68d7675
Merge branch 'main' into batch-collection-view-updates
JosephDuffy Feb 1, 2021
4294e65
WIP: Reloads, unifying transformations
JosephDuffy Feb 2, 2021
f59b1ef
Fixes for `removeGroups(_:)`
JosephDuffy Feb 2, 2021
f404efd
Update assertion message to ease debugging
JosephDuffy Feb 8, 2021
42b5a5f
Fix headers being hidden when invalidated
JosephDuffy Feb 15, 2021
cbe9009
Add foundation for section reloading (tests fail)
JosephDuffy Feb 17, 2021
6d0a0cb
COnform `ChangesReducer` to `CustomReflectable`
JosephDuffy Feb 17, 2021
aa73316
Don't transform section for removal
JosephDuffy Feb 17, 2021
ee0323c
Remove `print`s
JosephDuffy Feb 17, 2021
65c89a8
Ignore empty index paths
JosephDuffy Mar 1, 2021
55c1793
Add failing test
JosephDuffy Mar 1, 2021
39d3c3d
Add failing test
JosephDuffy Mar 1, 2021
266d403
Split reload and insert tests
JosephDuffy Mar 1, 2021
fbfd070
Support update then remove
JosephDuffy Mar 1, 2021
90310a1
Partial support for remove then insert
JosephDuffy Mar 1, 2021
5bd09e0
Test updates
JosephDuffy Mar 1, 2021
dc1d2b7
Add test demonstrating crash from app
JosephDuffy Mar 1, 2021
4ec73e7
Use AnyObject for protocols requiring classes
JosephDuffy Mar 1, 2021
2e346ce
Fix remaining test cases
JosephDuffy Mar 1, 2021
f09d243
Add debug logs for updates from mapping
JosephDuffy Mar 1, 2021
f4070a1
Add more complex failing test
JosephDuffy Mar 1, 2021
3e59d14
Add delete-delete-delete-reload test
JosephDuffy Mar 2, 2021
08492e2
Fix removals
JosephDuffy Mar 2, 2021
98e616e
Capture section prior to invalidating header
JosephDuffy Mar 9, 2021
547ac1d
Display `FlatSection` as a `struct`
JosephDuffy Mar 9, 2021
770f760
Don’t reload inserted rows
JosephDuffy Mar 9, 2021
3c30c64
Fix reload-insert-reload
JosephDuffy Mar 15, 2021
961e875
Add test for multiple batch updates
JosephDuffy Mar 16, 2021
507134f
Add closure batch updates API
JosephDuffy Mar 17, 2021
d7ac8a3
Add more debug logging
JosephDuffy Mar 23, 2021
ba6bd01
Add assert for unsupported action
JosephDuffy Mar 23, 2021
b812482
Layout collection view before batch updates
JosephDuffy Mar 23, 2021
c051d38
Ensure updates closure is called when delegate nil
JosephDuffy Mar 23, 2021
72efe3e
Add further debugging of batch updates
JosephDuffy Mar 29, 2021
d2f58ed
Workaround batch updates triggering other updates
JosephDuffy Mar 29, 2021
ac1b74b
Imrpove debug message
JosephDuffy Mar 29, 2021
f7eaabd
Merge branch 'batch-collection-view-updates-closure' into batch-colle…
JosephDuffy Mar 29, 2021
2c826ac
Remove batchUpdatesHandler
JosephDuffy Mar 29, 2021
e544111
Revert to use `os_log` over `print`
JosephDuffy Mar 29, 2021
c84b166
Move `isPerformingUpdates` up
JosephDuffy Mar 29, 2021
c83034a
Fix tests, don’t pass ChangesReducer to `updates`
JosephDuffy Apr 5, 2021
c110824
Remove unnecessary test
JosephDuffy Apr 5, 2021
060cacc
Improve documentation around `UICollectionView`
JosephDuffy Apr 5, 2021
682c311
Remove `isPerformingBatchedUpdates`
JosephDuffy Apr 5, 2021
0e84e47
Remove begin/end updating functions
JosephDuffy Apr 5, 2021
cbb6795
Sync TableCoordinator with CollectionCoordinator
JosephDuffy Apr 5, 2021
517a704
Update README documentation
JosephDuffy Apr 5, 2021
489ce20
Add failing tests
JosephDuffy Apr 5, 2021
097f9ed
Mention elements are removed before section
JosephDuffy Apr 5, 2021
5bd2c66
Update property name in mirror
JosephDuffy Apr 5, 2021
198882d
Ensure child3 only has 2 elements
JosephDuffy Apr 5, 2021
5dda694
Further document tests
JosephDuffy Apr 5, 2021
26e6534
Add extra test
JosephDuffy Apr 6, 2021
0732a1f
Change sections used to aid with readability
JosephDuffy Apr 6, 2021
3cf60e1
Add section removals after element removals tests
JosephDuffy Apr 6, 2021
38323a5
Clear element removals when group is removed
JosephDuffy Apr 6, 2021
351f095
Add failing test for coalescing to group reload
JosephDuffy Apr 6, 2021
fbc0272
Improve group reload coalescing
JosephDuffy Apr 6, 2021
ffebed2
Fix logic to pass ChangesReducerTests
JosephDuffy Apr 6, 2021
2554f5c
Remove default elements from test sections
JosephDuffy Apr 6, 2021
761b3a2
Remove need to check for empty changes
JosephDuffy Apr 7, 2021
239af15
Ignore inserts in inserts/updated groups
JosephDuffy Apr 7, 2021
96dbfdc
Mirror `testBatchUpdates` up to failure
JosephDuffy Apr 7, 2021
27a84fc
Improve documentation
JosephDuffy Apr 13, 2021
edda28b
Add `UICollectionViewTests`
JosephDuffy Apr 13, 2021
694d6b2
Add API to force use of `reloadData` for a batch
JosephDuffy Apr 13, 2021
22d1a04
Sync TableCoordinator with CollectionCoordinator
JosephDuffy Apr 5, 2021
29fc12e
Add API to force use of `reloadData` for a batch
JosephDuffy Apr 13, 2021
b7e86e9
Document `performBatchUpdates(forceReloadData:_:)`
JosephDuffy Apr 13, 2021
2c23543
Add extra `UICollectionView` test
JosephDuffy Apr 14, 2021
2607eca
Fix inserts after reloads
JosephDuffy Apr 26, 2021
eb2d1ab
Fix Composed tests compilation
JosephDuffy May 2, 2021
146d750
Remove debug `print`
JosephDuffy May 4, 2021
cadd5d9
Remove unnecessary optional
JosephDuffy May 10, 2021
a52a61f
Document `performBatchUpdates(forceReloadData:_:)`
JosephDuffy Apr 13, 2021
ccd35eb
Add skipped failing test
JosephDuffy Jun 2, 2021
c30bb52
Perform group updates reduction at end
JosephDuffy Jun 15, 2021
07548ff
Add test validating reloads handled before deletes
JosephDuffy Jun 15, 2021
5b921ac
Don't skip now-passing test, validate final result
JosephDuffy Jun 15, 2021
57ad3ea
Mirror testSportyCrash3 test
JosephDuffy Jun 15, 2021
ba1e634
Update documentation
JosephDuffy Jun 15, 2021
2e5f661
Merge branch 'batch-collection-view-updates-force-reload-data' of git…
JosephDuffy Jun 15, 2021
d509295
Add collection view, layout, and section index to layout environmeny
JosephDuffy Jul 6, 2021
f00e65f
Add failing test for reducing element removals and inserts
JosephDuffy Jul 7, 2021
a02bdf8
Add test for mixing updates, removes, and inserts
JosephDuffy Jul 7, 2021
fbe5a49
Add test for SBI-1560
JosephDuffy Jul 19, 2021
d952e88
Partially correct testInsertAndRemovalInSameSection
JosephDuffy Jul 19, 2021
4056624
[WIP] Calculate element updates at end
JosephDuffy Jul 19, 2021
c268f57
Unify header reloading footer reloading and don't perform in a batch
JosephDuffy Jul 27, 2021
499c6d9
Merge branch 'sporty_expose-collectionview-to-layout' into sporty_main
JosephDuffy Jul 27, 2021
eacf4cd
Merge branch 'sporty_fix-header-reloading' into sporty_main
JosephDuffy Jul 27, 2021
968b1f1
Improve handling of updates and inserts
JosephDuffy Jul 28, 2021
0f127ac
Mirror `testReloadInsertReload` test
JosephDuffy Jul 28, 2021
473017b
Update test to account for reload support
JosephDuffy Aug 1, 2021
c5425ed
Fix section reload validation test
JosephDuffy Aug 1, 2021
a36a0c5
Add validation test for reloads handled before removes
JosephDuffy Aug 1, 2021
52b5c86
Update testInsertAndRemovalInSameSection to account for reloads
JosephDuffy Aug 1, 2021
1cfee04
Account for reloads in `transformItem(_:inSection:)`
JosephDuffy Aug 2, 2021
2c3848e
Account for reloads in `removeElements(at:)`
JosephDuffy Aug 2, 2021
c558d8e
Add `testInsertAndRemovalInSameSection` mirror test
JosephDuffy Aug 2, 2021
ee4eb7a
Don't also remove inserted index paths on remove
JosephDuffy Aug 2, 2021
63c807f
Move `SpyCollectionViewController` to own file
JosephDuffy Aug 2, 2021
965362d
Run full test case
JosephDuffy Aug 2, 2021
20ad838
Provide correct values in `produces` closure
JosephDuffy Aug 2, 2021
36053db
Fill in missing `produces` closures
JosephDuffy Aug 2, 2021
76b30ac
Merge branch 'sporty_batch-collection-view-updates-calculate-element-…
JosephDuffy Aug 2, 2021
7a53244
Add support for removing all children
JosephDuffy Nov 16, 2021
68e9cc8
Don't reload already inserted elements
JosephDuffy Nov 29, 2021
3f996a5
Fix handling of removals that can coalesc to become reloads
JosephDuffy Nov 30, 2021
a162195
Update Quick and Nimble to latest versions
JosephDuffy Nov 30, 2021
92408f3
Cache all cell and supplementary vie registrations
JosephDuffy Nov 30, 2021
6ac4ea9
Improve handling of removals
JosephDuffy Dec 2, 2021
ac7c475
Correctly calculate `CollectionFlowLayoutEnvironment.contentSize`
JosephDuffy Dec 7, 2021
914908d
Remove `context` when transforming index paths
JosephDuffy Dec 20, 2021
629b838
Fix transforming items after elements have been explicitly reloaded
JosephDuffy Dec 20, 2021
06431e7
Remove testing of `transformItem(_:inSection:)`
JosephDuffy Dec 20, 2021
09eacd4
Add `logger` property to provide method of custom logging
JosephDuffy Jan 14, 2022
a9927d4
Fix indentation
JosephDuffy Mar 1, 2022
93fe010
Fix removing elements affecting incorrect updated elements
JosephDuffy Mar 1, 2022
79d41af
Add various assertions to aid with debugging misuse
JosephDuffy Mar 1, 2022
9ddf06c
Fix typo
JosephDuffy Mar 29, 2022
0f6d9e1
Correctly track header/footer registrations
JosephDuffy Mar 29, 2022
f8b4f6c
Perform header/footer updates inside a batch updates
JosephDuffy Mar 29, 2022
e0599f7
Reconfigure header/footer when invalidating
JosephDuffy Mar 30, 2022
3388f2a
Ensure headers/footers are setup inside invalidate* functions
JosephDuffy Jun 8, 2022
96fd762
Mark `open` symbols that can’t be overridden as `public`
JosephDuffy Jun 8, 2022
9ad99f6
Cache supplementary view registrations
JosephDuffy Jun 9, 2022
a73142a
Fix cells not being requested in `CollectionCoordinatorTests`
JosephDuffy Jun 13, 2022
cea9321
Update tests to remove section add/remove coalescing
JosephDuffy Jun 14, 2022
9ff3cb0
Update tests to account for reloading in iOS 15
JosephDuffy Jul 9, 2022
b159947
Replace use of `invalidateAll` with specific delegate calls
JosephDuffy Sep 19, 2022
b17ce82
Support initialising with array
JosephDuffy Sep 20, 2022
efc969d
Support updating sizing mode and clearing cached sizes
JosephDuffy Sep 20, 2022
3349105
Allow overriding delegate methods
JosephDuffy Nov 10, 2022
cb3db4f
Allow overriding `replace(element:)`
JosephDuffy Feb 1, 2023
2c7c07f
Update tests to account for section reloads
JosephDuffy Jan 25, 2024
491bb72
Remove tests that no longer work
JosephDuffy Jan 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .swiftpm/xcode/xcshareddata/xcschemes/ComposedUI.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,19 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "ComposedUITests"
BuildableName = "ComposedUITests"
BlueprintName = "ComposedUITests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
12 changes: 6 additions & 6 deletions Package.resolved

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

14 changes: 7 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,24 @@ let package = Package(
targets: ["ComposedUI"]),
],
dependencies: [
.package(name: "Quick", url: "https://github.com/Quick/Quick.git", from: "3.0.0"),
.package(name: "Nimble", url: "https://github.com/Quick/Nimble.git", from: "9.0.0"),
.package(name: "Quick", url: "https://github.com/Quick/Quick.git", from: "4.0.0"),
.package(name: "Nimble", url: "https://github.com/Quick/Nimble.git", from: "9.2.1"),
],
targets: [
.target(name: "Composed"),
.testTarget(
name: "ComposedTests",
dependencies: ["Quick", "Nimble", "Composed"]),

.target(
name: "ComposedLayouts",
dependencies: ["Composed", "ComposedUI"]),

.target(
name: "ComposedUI",
dependencies: ["Composed"]),
.testTarget(
name: "ComposedUITests",
dependencies: ["Quick", "Nimble", "ComposedUI"]),
dependencies: ["Quick", "Nimble", "ComposedLayouts", "ComposedUI"]),

.target(
name: "ComposedLayouts",
dependencies: ["Composed", "ComposedUI"]),
]
)
163 changes: 78 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,105 @@
<img src="composed.png" width=20%/>

**Composed** is a protocol oriented framework for composing data from various sources in our app. It provides various concrete implementations for common use cases.
`Composed` is a protocol oriented framework for composing data from multiple sources and building flexible UIs that display the data.

The primary benefits of using Composed include:

- The library makes heavy use of protocol-oriented design allowing your types to opt-in to behaviour rather than inherit it by default.
- Each section is isolated from the others, removing the need to think about how the data is composed.

> If you prefer to look at code, there's a demo project here: [ComposedDemo](http://github.com/composed-swift/composed-demo)

The library bases everything on just 2 primitives, `Section` and `SectionProvider`.
The package contains 3 libraries, each built on top of each other:

The primary benefits of using Composed include:
- Composed
- ComposedUI
- ComposedLayouts

- The library makes heavy use of protocol-oriented design allowing your types to opt-in to behaviour rather than inherit it by default.
- From an API perspective you generally only care about a single section at a time, and so it's irrelevant to you how it's being composed.
## Composed

1. [Getting Started](#Getting-Started)
2. [Behind the Scenes](#Behind-the-Scenes)
3. [User Interfaces](http://github.com/composed-swift/composedui)
The `Composed` library provides the data layer. `Composed` is centered around primitives, `Section` and `SectionProvider`.

## Getting Started

Composed includes 3 pre-defined sections as well as 2 providers that should satisfy a large majority of applications.

### Sections

**ArraySection**
Represents a section that manages its elements via an `Array`. This type of section is useful for representing in-memory data.
A `Section` is a collection of data with a simple set of requirements:

```swift
/// Represents a single section of data.
public protocol Section: AnyObject {
/// The number of elements in this section
var numberOfElements: Int { get }

/// The delegate that will respond to updates
var updateDelegate: SectionUpdateDelegate? { get set }
}
```

`Composed` provides some sections that should cover a majority of use cases.

#### `ArraySection`

Represents a section that manages a collection of same-type elements by using an `Array` backing store. This type of section is useful for representing in-memory data, e.g. data loaded from a network or read from the file system.

#### `SingleElementSection`

Represents a section that manages a single element. This section is useful when only have a single element to manage.

If the stored value is `nil` it will return `0` for `numberOfElements`, allowing for any UI elements the section provides to be hidden (more on this later).

#### `FlatSection`

A `FlatSection` behaved similarly to a `ComposedSectionProvider` but rather than providing a collections of sections it returns a single section that contains every element in the flattened collection of `Section`s and `SectionProvider`s. This has limited use for data alone but proves useful when representing the data in the UI; `FlatSection` allows for multiple sections to be displayed in a single UI section, enabling features such as headers that pin to visible bounds ("sticky headers").

#### `ManagedSection`

`ManagedSection` wraps an `NSManagedObjectContext` and responds to the `NSFetchedResultsControllerDelegate` functions by forwarding them to the `SectionUpdateDelegate`. This enables a single data hierarchy to include a mixture of sections that are backed by core data and other storage mechanisms.

### `SectionProvider`s

`SectionProvider`s are the next layer up, dealing with `Section`s directly by providing an ordered collection of sections:

```swift
/// Represents a collection of `Section`'s.
public protocol SectionProvider: AnyObject {
/// The child sections contained in this provider
var sections: [Section] { get }

**ManagedSection**
Represents a section that provides its elements via an `NSFetchedResultsController`. This section is useful for representing data managed by CoreData.
/// The delegate that will respond to updates
var updateDelegate: SectionProviderUpdateDelegate? { get set }
}
```

**SingleElementSection**
Represents a section that manages a single element. This section is useful when only have a single element to manage. Hint: Use `Optional<T>` to represent an element that may or may not exist.
#### `ComposedSectionProvider`

### Providers
Represents an collection of `Section`'s and `SectionProvider`'s. The provider supports infinite nesting, including other `ComposedSectionProvider`'s, by providing a flattened hierarchy.

**ComposedSectionProvider**
Represents an collection of `Section`'s and `SectionProvider`'s. The provider supports infinite nesting, including other `ComposedSectionProvider`'s. All children will be active at all times, so `numberOfSections` and `numberOfElements(in:)` will return values representative of all children.
#### `SegmentedSectionProvider`

**SegmentedSectionProvider**
Represents an collection of `Section`'s and `SectionProvider`'s. The provider supports infinite nesting, including other `SegmentedSectionProvider`'s. One or zero children may be active at any time, so `numberOfSections` and `numberOfElements(in:)` will return values representative of the currenly active child only.
Provides the same nesting support as `ComposedSectionProvider` but allows for different segments of children to be active. This could be used to represent a series of tabs with different section providers in each tab.

### Example

Lets say we wanted to represent a users contacts library. Our contacts will have 2 groups, family and friends. Using Composed, we can easily model that as such:

```swift
let family = ArraySection<Person>()
family.append(Person(name: "Dad"))
family.append(Person(name: "Mum"))

let friends = ArraySection<Person>()
friends.append(Person(name: "Best mate"))
let businesses = ArraySection<Business>()
businesses.append(Person(name: "ACME Inc."))
```

At this point we have 2 separate sections for representing our 2 groups of contacts. Now we can use a provider to compose these 2 together:

```swift
let contacts = ComposedSectionProvider()
contacts.append(family)
contacts.append(friends)
contacts.append(businesses)
```

That's it! Now we can query our data using the provider without either of the individual sections even being aware that they're now contained in a larger structure:
Expand All @@ -66,75 +109,25 @@ contacts.numberOfSections // 2
contacts.numberOfElements(in: 1) // 1
```

If we want to query individual data in a section (assuming we don't already have a reference to it):
Swapping to a `FlatSection` would flatten these sections while maintaining all the data:

```swift
let people = contacts.sections[0] as? ArraySection<Person>
people.element(at: 1) // Mum
```

> Note: we have to cast the section to a known type because SectionProvider's can contain _any_ type of section as well as other nested providers.

### Opt-In Behaviours

If we now subclass ArraySection, we can extend our section through protocol conformance to do something more interesting:

```swift
final class People: ArraySection<Person> { ... }

protocol SelectionHandling: Section {
func didSelect(at index: Int)
}

extension People: SelectionHandling {
func didSelect(at index: Int) {
let person = element(at: index)
print(person.name)
}
}
```

In order to make this work, _something_ needs to call `didSelect`, so for the purposes of this example we'll leave out some details but to give you a preview for how you can build something like this yourself:

```swift
// Assume we want to select the 2nd element in the 1st section
let section = provider.sections[0] as? SelectionHandling
section?.didSelect(at: 1) // Mum
let contacts = FlatSection()
contacts.append(family)
contacts.append(businesses)
contacts.numberOfElements // 3
```

Composed is handling all of the mapping and structure, allowing us to focus entirely on behavious and extension.

## Behind the Scenes
## ComposedUI

### Section
The `ComposedUI` library builds on top of `Composed` by providing protocols that enable `Section`s to provide UI elements that can then be displayed by a view coordinator.

A section represents exactly what it says, a single section. The best thing about that is that we have no need for `IndexPath`'s within a section. Just indexes!
### UI Coordinators

### SectionProvider
Various coordinators are provided that enable interfacing with `UIKit` by adding extra protocol conformances to your `Section`s.

A section provider is a container type that contains either sections or other providers. Allowing infinite nesting and therefore infinite possibilities.

### Mappings

Mappings provide the glue between your 'tree' structure and the resulting `flattened` structure. Lets take a look at an example.

```swift

// we can define our structure as such:
- Provider
- Section 1
- Provider
- Section 2
- Section 3
- Section 4

// mappings will then convert this to:
- Section 1
- Section 2
- Section 3
- Section 4
```
#### `CollectionCoordinator`

Furthermore, mappings take care of the conversion from local to global indexes and more importantly `IndexPath`'s which allows for even more interesting use cases.
`CollectionCoordinator` allows for the most flexible UIs by coordinating with a `UICollectionView`.

> To find out more, checkout [ComposedUI](http://github.com/composed-swift/ComposedUI) which provides user interface implementations that work with `UICollectionView` and `UITableView`, allowing you to power an entire screen from simple reusable `Section`s.
View the [`CollectionCoordinator` README.md](./Sources/ComposedUI/CollectionView/README.md) to learn more.
51 changes: 38 additions & 13 deletions Sources/Composed/Core/Section.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,49 @@ import Foundation
import CoreGraphics

/// Represents a single section of data.
public protocol Section: class {

public protocol Section: AnyObject {
/// The number of elements in this section
var numberOfElements: Int { get }

/// The delegate that will respond to updates
var updateDelegate: SectionUpdateDelegate? { get set }

}

public extension Section {

/// Returns true if the section contains no elements, false otherwise
var isEmpty: Bool { return numberOfElements == 0 }

/// Perform multiple updates in a single batch, ensuring a single layout pass and animation is used for all updates.
///
/// Changes will be reduced in to the minimal total changes required, based on the calls made to the `updateDelegate`. If
/// no `updateDelegate` has been set the `updates` closure is called with `nil`, allowing changes to still be made.
///
/// Some updates are not correct reduced, or you may wish to avoid this batching behaviour. To enable this the `forceReloadData`
/// parameter can be set to `true`. Note that passing `true` is not supported if another batch updates that does not force reload
/// data is currently being performed.
///
/// - Parameter forceReloadData: If `true` all updates will be ignored and `reloadData` will be called after all updates are applied.
/// - Parameter updates: A closure that applies the updates.
func performBatchUpdates(forceReloadData: Bool = false, _ updates: (_ updateDelegate: SectionUpdateDelegate?) -> Void) {
if let updateDelegate = updateDelegate {
updateDelegate.section(self, willPerformBatchUpdates: {
updates(updateDelegate)
}, forceReloadData: forceReloadData)
} else {
updates(nil)
}
}
}

/// A delegate that will respond to update events from a `Section`
public protocol SectionUpdateDelegate: class {

/// Notifies the delegate before a section will process updates
/// - Parameter section: The section that will be updated
func willBeginUpdating(_ section: Section)

/// Notifies the delegate after a section has processed updates
/// - Parameter section: The section that was updated
func didEndUpdating(_ section: Section)
public protocol SectionUpdateDelegate: AnyObject {
/// Notifies the delegate that the section will perform a series of updates.
///
/// The delegate must call the `updates` closure synchronously.
///
/// - Parameter section: The section that will be updated.
/// - Parameter updates: A closure that will perform the updates.
func section(_ section: Section, willPerformBatchUpdates updates: () -> Void, forceReloadData: Bool)

/// Notifies the delegate that all sections should be invalidated, ignoring individual updates
/// - Parameter section: The section that requested the invalidation
Expand Down Expand Up @@ -82,4 +98,13 @@ public protocol SectionUpdateDelegate: class {
/// - destinationIndex: The final index where the element will be moved to
func section(_ section: Section, move sourceIndex: Int, to destinationIndex: Int)

/// Notifies the delegate that the section invalidated its header.
/// - Parameters:
/// - section: The section that invalidated its header.
func sectionDidInvalidateHeader(_ section: Section)

/// Notifies the delegate that the section invalidated its footer.
/// - Parameters:
/// - section: The section that invalidated its footer.
func sectionDidInvalidateFooter(_ section: Section)
}
Loading