{"id":22416100,"url":"https://github.com/appunite/stefan","last_synced_at":"2025-05-08T21:21:44.698Z","repository":{"id":28855960,"uuid":"116941941","full_name":"appunite/Stefan","owner":"appunite","description":"A guy that helps you manage collections and placeholders in easy way.","archived":false,"fork":false,"pushed_at":"2024-10-30T08:39:14.000Z","size":3757,"stargazers_count":47,"open_issues_count":0,"forks_count":1,"subscribers_count":18,"default_branch":"master","last_synced_at":"2025-04-11T20:49:07.459Z","etag":null,"topics":["error","placeholder","states","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/appunite.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-01-10T10:20:52.000Z","updated_at":"2023-10-08T22:22:09.000Z","dependencies_parsed_at":"2024-01-02T09:27:51.507Z","dependency_job_id":"f7680f03-a268-49c3-a12c-b59445ca5679","html_url":"https://github.com/appunite/Stefan","commit_stats":{"total_commits":84,"total_committers":8,"mean_commits":10.5,"dds":"0.19047619047619047","last_synced_commit":"61cae818efba2c23e94f544ac2c289d35becae78"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appunite%2FStefan","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appunite%2FStefan/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appunite%2FStefan/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appunite%2FStefan/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/appunite","download_url":"https://codeload.github.com/appunite/Stefan/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253150085,"owners_count":21861831,"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":["error","placeholder","states","swift","uicollectionview","uitableview"],"created_at":"2024-12-05T15:14:37.619Z","updated_at":"2025-05-08T21:21:44.679Z","avatar_url":"https://github.com/appunite.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Carthage compatible](https://img.shields.io/badge/Carthage-Compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Cocoapods](https://img.shields.io/cocoapods/v/Stefan.svg?style=flat)](https://cocoapods.org/pods/Stefan)\n[![Platform](https://img.shields.io/cocoapods/p/Stefan.svg?style=flat)](https://cocoapods.org/pods/Stefan)\n[![License](https://img.shields.io/cocoapods/l/Stefan.svg?style=flat)](https://cocoapods.org/pods/Stefan)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"resources/stefan_logo.png\" alt=\"Stefan logo\"/\u003e\n\u003c/p\u003e\n\n## Why?\n\nAs mobile developers we all have to handle displaying collections of data. But is it always as simple as it sounds? \n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"resources/stefan_spaghetti.png\" alt=\"Stefan spaghetti\"/\u003e\n\u003c/p\u003e\n\nLooks like spaghetti? It is a common situation that application displays data / placeholders (which may be different depending for example on loading state) and some of us end up with defining states and tangling everything together. \n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"resources/stefan_overview.png\" alt=\"Stefan overview\"/\u003e\n\u003c/p\u003e\n\n**Stefan** is a framework that helps you to manage states in your collection views. Basically it is a middleman between data source and the view itself. Of course it supports UITableView and UICollectionView out of box, but it is up to you to decide what is your **ReloadableView** and what is **PlaceholderView**. It contains most commonly used states that might occur when loading data collection. \n\n### Basic setup \n\n- Define `Stefan` object, the best solution is to define one `Stefan` per one `ReloadableView`.\n- Connect Stefan's `reloadableView` and `placeholderPresenter` (which both might be `nil`).\n\u003e Remember: class that implements LoadableStatePlaceholderPresentable must have a **weak** reference to a placeholderView.\n- Call `stefan.load(newState: ...)` \n\nExample of setup:\n\n```swift\nprivate func setupStefan() {\n\tviewModel.stefan.reloadableView = self.tableView\n\tviewModel.stefan.placeholderPresenter = self\n\tviewModel.stefan.load(newState: .noContent) // initial state\n}\n```\n\nFor reading current items just call \n\n```swift\ntry stefan.state.items()\n```\n\n### Few words about placeholder\n\n- We have tried to make it as flexible as possible so you can provide a custom placeholder view which should conform to `LoadableStatePlaceholderView` and also implement `ItemsLoadableStateBindable` protocol. To display custom placeholder view just implement your own version of function `customPlaceholderView` that is handled by Stefan's `placeholderPresenter`. \n\nExample for customization: \n\n```swift\npublic func customPlaceholderView() -\u003e LoadableStatePlaceholderView {\n\tlet view = LoadingPlaceholderView.instanceFromNib()\n    view.dataSource = self.viewModel\n    return view\n}\n```\n\n- There is also a default placeholder view built from title, subtitle and activity indicator which might also be useful for you. Feel free to see how it works in the example project. \n\n### Closures\n\nTo make Stefan fully generic we provided delegate by common closures pattern. Following delegate closures are available: \n\n```swift\npublic var shouldReload: ((ReloadableView!) -\u003e Bool)\n    \npublic var didChangeState: ((ItemsLoadableState\u003cItemType\u003e) -\u003e Void)\n    \npublic var shouldDisplayPlaceholder: ((ItemsLoadableState\u003cItemType\u003e) -\u003e Bool)\n```\n\n### Differing \n\nStefan provides default implementation of differring function which result should be `ItemReloadingResult`. This result tells Stefan what should be reloaded (or maybe there is nothing to do for him). You can provide your own implementation by defining \n\n`stefan.statesDiffer = ...` \n\nPlease remember that this is a weak object so you have to handle retaining it. \n\nStefan has a `Differ` dependency which is needed for performing fast operations on collections while comparing. When initializing Stefan you are able to provide reloading type which might be `animated` or `basic`. By default animations are disabled, but when you'll enable it Stefan will handle collection animation automatically by himself.\n\n### RxStefan\n\nWe've added some **RxSwift** extensions for **Stefan** please follow [this repo](https://github.com/appunite/RxStefan).\n\n\n### Carthage\n\nAdd the following entry in your Cartfile:\n\n```\ngithub \"appunite/Stefan\"\n```\n\nThen run `carthage update`.\n\n\n### Cocoapods\n\nAdd the following entry in your Podfile\n\n```\npod 'Stefan'\n```\n\nThen run `pod install`.\n\n### Known issues\n\n• Nothing yet\n\n### Contribution\n\nProject is created and maintened by **Piotr Bernad** and **Szymon Mrozek**.\n\nWe could use your help with reporting or fixing bugs. We would also like to hear from you about feature suggestions. If you have an idea how to make Stefan better you are welcome to send us a Pull Request.\n\n### License\n\nStefan is released under an MIT license. See [License.md](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappunite%2Fstefan","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fappunite%2Fstefan","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappunite%2Fstefan/lists"}