{"id":18991675,"url":"https://github.com/djs66256/ddpresenter","last_synced_at":"2025-10-27T00:48:26.789Z","repository":{"id":239577713,"uuid":"797587404","full_name":"djs66256/DDPresenter","owner":"djs66256","description":"A state-driven view system, with service and notification system.","archived":false,"fork":false,"pushed_at":"2025-01-01T06:08:18.000Z","size":228,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-15T12:22:24.792Z","etag":null,"topics":["iglistkit","ios","listview","state-driven","swift","uicollectionview","uitableview"],"latest_commit_sha":null,"homepage":"","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/djs66256.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,"zenodo":null}},"created_at":"2024-05-08T06:13:20.000Z","updated_at":"2025-07-01T02:28:15.000Z","dependencies_parsed_at":"2025-04-16T18:11:24.773Z","dependency_job_id":"8fea171b-09ad-40c2-a2ab-eecd60831694","html_url":"https://github.com/djs66256/DDPresenter","commit_stats":null,"previous_names":["djs66256/ddpresenter"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/djs66256/DDPresenter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djs66256%2FDDPresenter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djs66256%2FDDPresenter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djs66256%2FDDPresenter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djs66256%2FDDPresenter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/djs66256","download_url":"https://codeload.github.com/djs66256/DDPresenter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/djs66256%2FDDPresenter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":281196969,"owners_count":26459679,"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-10-26T02:00:06.575Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","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":["iglistkit","ios","listview","state-driven","swift","uicollectionview","uitableview"],"created_at":"2024-11-08T17:14:47.238Z","updated_at":"2025-10-27T00:48:26.766Z","avatar_url":"https://github.com/djs66256.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DDPresenter\n\nA state-driven view system, with service and notification system. A power tool to develop composite page and list.\n\n```ruby\npod 'DDPresenter'\n```\n\nUsing custom collection view layout with subspecs. Now, support `NECollectionViewLayout`, `CHTCollectionViewWaterfallLayout`.\n```ruby\npod 'DDPresenter', :subspecs =\u003e [\"Core\", \"NECollectionViewLayout\", \"CHTCollectionViewWaterfallLayout\"]\n```\n\n# Usage\n\n## Presenter\n\nYou should put all states here to drive view updating.\n1. You can trigger updating by define a state type:\n```swift\nstruct StateType {\n    // all your states\n}\n@StateChecker\nprivate var _state: StateType = StateType()\n@MainActor var state: StateType {\n    get {\n        _state\n    }\n    set {\n        // updating view only state changed\n        if _state != newValue {\n            setState {\n                _state = newValue\n            }\n        }\n    }\n}\n```\n2. Or just using `setState`:\n```swift\n@MainActor var text: String = smallText {\n    didSet {\n        setState {}\n    }\n}\n```\n3. Using a `ViewStatePresenter` is a better choice for complex states. And update view by:\n```swift\nfunc updateState() {\n    setState { state in\n        state.count += 1\n    }\n}\n```\n\n#### Lifecycle\n\n```text\n        +------------+\n        |   Create   |\n        +------------+\n              |\n              v\n        +------------+\n   +----|  onAttach  |\n   |    +------------+\n   |          |\n   |          v\n   |    +------------+\n   |    | onBindView |\n   |    +------------+\n   |          |\n   |          v\n   |    +------------+   \u003c---\\\n   |    |  onUpdate  |        | setState\n   |    +------------+   ----/\n   |          |\n   |          v\n   |    +------------+\n   |    |onUnbindView|\n   |    +------------+\n   |          |\n   |          v\n   |    +------------+\n   +---\u003e|  onDetach  |\n        +------------+\n              |\n              v\n        +------------+\n        |   Destroy  |\n        +------------+\n```\n\n#### Animations\n\nYou can perform animations when updating view.\n\n```\nsetState { $0.normalProgress = 1 - $0.normalProgress } context: { context in\n    context.animated = true\n    context.layoutIfNeeded = true\n    context.animator = UIViewDefaultAnimator(duration: 1)\n}\n```\n\n\u003e An animator contains the animation params. You can custom animator to apply different animation params.\n\n## Service\n\nYou can provide services in the view controller. Services can be used in the view controller and all presenter-tree in the view controller. When you `getService`, make sure the presenter has attached to root.\n\n```swift \nfunc doSomething() {\n    getService(MyBusinessService.self).doSomething()\n}\n```\n\nWhen needs add listeners, here is the best practice:\n\n```swift\noverride func onAttachToRoot(_ presenter: RootViewPresentable) {\n    super.onAttachToRoot(presenter)\n    \n    getService(MyService.self)?.addListener(self)\n}\n\noverride func onDetachFromRoot(_ presenter: RootViewPresentable) {\n    super.onDetachFromRoot(presenter)\n    \n    getService(MyService.self)?.removeListener(self)\n}\n```\n\n\u003e Putting all business logics in a service is a best practice. Service is easy to reuse, and not acting with view updating.\n\nAlso, you can add your exists Service-System (IoC) by: \n```swift\npublic struct GlobalServiceConfig {\n    /// If already have global service, `getService()` can use this to downgrade to global services.\n    public static var serviceDowngrade: ServiceProviderDowngrade? = nil\n}\n```\n\n## Notification\n\nNotifying between presenters or services, may use `delegate`, `listener`, `NSNotificationCenter`. But it will be very complex when having lots of messages. Here provide a new notification system. You can notify other presenters below the same root presenter who implementing the `NotificationProtocol`. \n\nNotify:\n```swift\nfunc notify() {\n    notifyGlobal(MyNotification.self) {\n        $0.onMyMessage()\n    }\n}\n```\n\nAnd you can select notify scope as your needs.\n```swift \npublic enum NotifyScope {\n    case global             // Notify from root to all children presenters\n    case reusable           // Notify to nearest reusable parent presenter and its children\n    case children           // Notify to its chidren\n    case childrenAndSelf    // Notify to its children and itself\n    case parents            // Notify to its parents, until to root presenter\n    case manually           // Nofity to listeners that added manually\n}\n```\n\n## UICollectionView / UITableView\n\n`UICollectionView` / `UITableView` delegate by `Proxy`. It supports many features:\n- data source diff\n- size caching and size calculating automatically\n- updating view only in the dirty part\n\nPresenters for `UICollectionView` with `UICollectionViewFlowLayout`:\n- `UICollectionViewFlowPresenter` will bind `UICollectionView`\n- `UICollectionViewFlowSectionPresenter` is section data source type\n- `UICollectionViewFlowItemPresenterHolder` is item data source type\n- `UICollectionViewFlowReusableItemPresenter` will bind `UICollectionViewCell`\n\n\u003e It will create more than one cell when animation or other situation. So seprate item to `holder` and `reusable presenter`. `holder` is unique, and `reusable presenter` will bind cell that create by UICollectionView. At last, may have more than one `resusable presenter` in a `holder`.\n\nAnd the same as `UITableView`:\n- `UITableViewPresenter` will bind `UICollectionView`\n- `UITableViewSectionPresenter` is section data source type\n- `UITableViewCellPresenterHolder` is item data source type\n- `UITableViewReusableCellPresenter` will bind `UICollectionViewCell`\n\n### Custom UICollectionViewLayout\n\nWhen custom `UICollectionViewLayout`, need different presenters listed below:\n\n| Layout         | UICollectionViewFlowLayout               | NECollectionViewLayout                    | CHTCollectionViewWaterfallLayout          |\n|----------------|------------------------------------------|-------------------------------------------|-------------------------------------------|\n| CollectionView | UICollectionViewFlowPresenter            | NECollectionViewFlowPresenter             | CHTCollectionViewWaterfallPresenter       |\n| Section        | UICollectionViewFlowSectionPresenter     | NECollectionViewFlowSectionPresenter      | CHTCollectionViewWaterfallSectionPresenter|\n| Item Holder    | UICollectionViewFlowItemPresenterHolder  | UICollectionViewReusablePresenterHolder   | UICollectionViewReusablePresenterHolder   |\n| Item Reusable  | UICollectionViewFlowReusableItemPresenter | UICollectionViewFlowReusableItemPresenter | UICollectionViewFlowReusableItemPresenter |","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjs66256%2Fddpresenter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdjs66256%2Fddpresenter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdjs66256%2Fddpresenter/lists"}