{"id":24618097,"url":"https://github.com/winkgroup/winkit-ios","last_synced_at":"2025-10-26T19:38:45.300Z","repository":{"id":56927568,"uuid":"90266319","full_name":"WINKgroup/winkit-ios","owner":"WINKgroup","description":"An iOS framework written in Swift used for Wink's application.","archived":false,"fork":false,"pushed_at":"2019-05-12T12:33:31.000Z","size":6890,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-11T11:32:49.552Z","etag":null,"topics":["ios-swift","mvp-pattern","swift","xcode"],"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/WINKgroup.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}},"created_at":"2017-05-04T13:24:51.000Z","updated_at":"2021-11-05T09:56:46.000Z","dependencies_parsed_at":"2022-08-20T23:30:38.558Z","dependency_job_id":null,"html_url":"https://github.com/WINKgroup/winkit-ios","commit_stats":null,"previous_names":["winkgroup/winkkit"],"tags_count":19,"template":false,"template_full_name":null,"purl":"pkg:github/WINKgroup/winkit-ios","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WINKgroup%2Fwinkit-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WINKgroup%2Fwinkit-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WINKgroup%2Fwinkit-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WINKgroup%2Fwinkit-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WINKgroup","download_url":"https://codeload.github.com/WINKgroup/winkit-ios/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WINKgroup%2Fwinkit-ios/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269502567,"owners_count":24427790,"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-08-08T02:00:09.200Z","response_time":72,"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":["ios-swift","mvp-pattern","swift","xcode"],"created_at":"2025-01-24T23:49:46.455Z","updated_at":"2025-10-26T19:38:45.218Z","avatar_url":"https://github.com/WINKgroup.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg src=\"readme-res/winkkit_logo.png\" width=\"33%\"\u003e\n\u003c/p\u003e\n\nWinkKit\n========\n\n[![CocoaPods Version](https://img.shields.io/cocoapods/v/WinkKit.svg?style=flat)](http://cocoapods.org/pods/WinkKit)\n[![License](https://img.shields.io/cocoapods/l/WinkKit.svg?style=flat)](http://cocoapods.org/pods/WinkKit)\n[![Platforms](https://img.shields.io/badge/platform-iOS-blue.svg)](http://cocoapods.org/pods/WinkKit)\n[![Swift Version](https://img.shields.io/badge/swift-4.0-orange.svg?style=flat)](https://developer.apple.com/swift)\n\n\nAn iOS framework that contains a set of classes that follow MVP pattern and solve some common problem written in Swift, used for Wink's application. Follow this guide to know how to structure a Wink iOS project.\n\n## Table of Contents\n1. [Getting Started](#Getting_Started)\n2. [Understanding Structure](#Understanding_Structure)\n3. [Using enhanced Views](#UI_Extension)\n4. [Using ViewControllers and Presenters](#Using_ViewControllers)\n5. [Using Table Views and Collection Views](#Using_TabColViews)\n6. [Utils and more](#Utils_And_More)\n\n## Getting Started \u003ca name=\"Getting_Started\"\u003e\u003c/a\u003e\n\n### Prerequisites\n\nYou need [Xcode 9](https://developer.apple.com/xcode/), Swift 4 and [CocoaPods](https://guides.cocoapods.org/using/getting-started.html) installed.\n\n### Add project/file templates to Xcode (Optional but recommended)\n\nWinkKit has been designed to help creating app with MVP pattern (you'll understand better later); to follow this pattern, it's needed to create for each view several files.\nWinkKit contains a set of Xcode templates to make project and file creation faster; download the \u003ca href=\"https://github.com/WINKgroup/Wink-Kit-Helper/blob/master/Release/Wink%20Project%20Helper.app.zip\" download target=\"_blank\" download\u003eWink Project Helper\u003c/a\u003e macOS app to install all templates.\n\nAfter template install, in Xcode, under **File \u003e New \u003e File** (or CMD+N) you can create view controllers, table view cells and collection view cells that conform to MVP like following.\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"readme-res/template_files.png\" width=\"50%\"\u003e\n\u003c/p\u003e\n\nYou can even create a whole project, see next point.\n\n## Installing\n\n### Create project with template\n\nIf you're starting from scratch, and you successfully installed the Wink Kit templates, you can create new project directly from Xcode, under **File \u003e New \u003e Project**\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"readme-res/template_project.png\" width=\"50%\"\u003e\n\u003c/p\u003e\n\nThis template creates for you the basic structure of a Wink app (that will be explained later), the `Podfile` already configured with `WinkKit` framework, a `.gitignore` and other boilerplate files/code.\n\nNow run `pod install` in the root project folder, and re-open the project from the workspace file. That's all. 🎉\n\n\n### Manual\n\nJust paste the CocoaPods dependency in your `Podfile`. Due to a cocoapods bug, ensure to paste the **post_install** function too.\n\n```ruby\n# Podfile\nuse_frameworks!\n\ntarget 'YOUR_TARGET_NAME' do\n\n    # https://github.com/WINKgroup/WinkKit\n    pod 'WinkKit'\n    \nend\n\n# This post install is needed because of a Cocoapods bug; it is needed to render WinkKit properties in InterfaceBuilder correctly.\npost_install do |installer|\n    installer.pods_project.targets.each do |target|\n        target.build_configurations.each do |config|\n            if target.name === 'AlamofireImage'\n                config.build_settings['SWIFT_VERSION'] = '3.3' # set swift 3.3 on AlamofireImage\n            end\n            config.build_settings['CONFIGURATION_BUILD_DIR'] = '$PODS_CONFIGURATION_BUILD_DIR'\n        end\n    end\nend\n\n```\n\n**N.B.**: AlamofireImage compiles only with Swift 3.3.\n\n## API Documentation\n\n### Check the classes reference [here](./docs/index.html).\n\n## Understanding Structure \u003ca name=\"Understanding_Structure\"\u003e\u003c/a\u003e\n\nBefore talking about classes of framework we'll take a look on architecture structure. \n\nIt is a MVP pattern; look at this [iOS Architectures overview](https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52) to understand differences between MVC, MVP, MVVM, VIPER.\n\nA Wink iOS project **should** be structured in the following way, expecially if the project will grow a lot:\n\n\n![Arch diagram](./readme-res/arch_diagram.jpg)\n\n\u003cbr\u003e\n\n### Presentation\n\nIt's the layer that contains all iOS Framework dedicated classes, like `UIKit` framework. We could say that this layer cannot exists without an iPhone/iPad because `UIKit` can run only there.\n\n\u003cimg src=\"readme-res/presentation_layer.png\" width=50% /\u003e\n\n* **AppDelegate**: It's the well known AppDelegate class, nothing special;\n* **Use Cases**: A group that contains all use cases. It's important to understand that a *Use Case* is what user do in one or more app screen, for example the Login.\n\t* **Login**: En example of a Use Case. It will contain all related ViewControllers, Presenter (if a use case contains more than one), DataSources etc.\n\t\t* **LoginPresenter**: A simple presenter; LoginPresenter keep the state of LoginViewController; a presenter is the class that contains logic, the ViewController does **not** contain logic. **Presenter doesn't contains UIKit classes!**, this is needed to keep presenters easy testable.\n\t\t* **LoginViewController**: In classic MVC pattern, (Massive View Controller in iOS world 😫) all logic was here, mixed with the view handling; in this framework a ViewController **owns** a presenter and delegates it for the logic. The view controller doesn't have a method `func performLogin(email: String, password: String)` for example; instead, the presenter does. The view controller will only receive user input and tell the presenter that something happened. The presenter will do work and tell the view controller that the view should change.\n* **Core**: A group that contains base classes re-usable all around project. It's a good practice to define this classes to avoid code duplication that could increase the maintanance difficulty.\n* **Resources**: All resources go here, included .xcassets, custom fonts...\n\n\u003cbr\u003e\n\n### Data\nIt's the layer that handles all data stuff, such as http calls, cache uploading/downloading to/from a backend. No `UIKit` classes in this layer!\n\n\u003cimg src=\"readme-res/data_layer.png\" width=50% /\u003e\n\n* **Cache**: A group that contains classes like SessionManager and all other stuff that saves data locally.\n* **Networking**: The group that contains the Http Client, which must be implemented with **Alamofire**. WinkKit provides [Alamofire](https://github.com/Alamofire/Alamofire) and [Alamofire Image](https://github.com/Alamofire/AlamofireImage) in the framework itself, so you don't need to add anything in the Podfile.\n\t* **ResponseSerialization**: Contains the `DataResponse` extension of Alamofire: it provides a common response for http calls that return an object instead of a json; json parsing is done in this extension (see source file for detail). Notice that this extension uses [Argo](https://github.com/thoughtbot/Argo) for json parsing. \n\t* **Resource**: an enum that maps the response of https calls.\n\t* **Error**: the class/struct that maps http errors (both client and server) \n\t* **Routers**: Routers are responsible to know api's endpoints and to create a `urlRequest` that are used by **Services** to perform http calls.\n\t* **Services**: Services perform http calls, using the request created by routers.\n\n\u003cbr\u003e\n\n# Presentation\n\n## Using enhanced Views \u003ca name=\"UI_Extension\"\u003e\u003c/a\u003e\n\nWinkKit provides common view classes that have more `@IBDesignable` in InterfaceBuilder.\n\n- WKView\n- WKImageView\n- WKButton\n- WKLabel\n- WKTextView\n\nEvery class extends the `UIKit` one; for example `WKView` extends `UIView`. To use these classes in InterfaceBuilder, drag the object from the Object library and make it extends the desired WinkKit view.\nFor example to use a `WKButton`, drag a Button from Object library, then go to Identity Inspector and set the custom class:\n\n\u003cimg src=\"readme-res/button_identity_inspector.png\" width=\"40%\"\u003e\n\n**Make sure to leave WinkKit as module**\n\nThen you can customize the button from Attributes inspector:\n\n\u003cimg src=\"readme-res/button_attributes_inspector.png\" width=\"40%\"\u003e\n\n## Using ViewControllers and Presenters \u003ca name=\"Using_ViewControllers\"\u003e\u003c/a\u003e\n\nIn a WinkKit app every view controller should extends the `WKViewController\u003cP\u003e` (or `WKTableViewController\u003cP\u003e` or `WKCollectionViewContrller\u003cP\u003e`, they have all same behaviours).\n\nA `WKViewController` wants a subclass of `WKGenericViewControllerPresenter` (which is a protocol that extends the base presenter protocol `WKPresenter`) because the view controller life-cycle is bound to this kind of presenter. A typical implementation of home page is\n\n```swift\n// HomeViewController.swift\n\nclass HomeViewController: WKViewController\u003cHomePresenter\u003e {\n\t// do only UI stuff here\n}\n\n// Since the view controller is handled by HomePresenter, it must be conform to LoginView.\nextension HomeViewController: LoginView {\n\t// implements all HomeView methods/properties\n}\n\n\n// HomePresenter.swift\n\n// Define what the view can do\nprotocol LoginView: PresentableView {\n\n}\n\nclass HomePresenter: WKGenericViewControllerPresenter {\n\n    typealias View = LoginView // need to tell the protocol which is the view handled\n    \n    weak var view: LoginView? // keep view weak to avoid retain-cycle since view controller holds a reference to this presenter\n    \n    required init() {} // framework wants empty init\n    \n    // do all logic here, such as use a Service to fetch data and tell the view to update\n}\n\n```\n**Notice that you don't need to call any method to bind the view controller and the presenter, everything is automatically done by the framework!** 🎉🎉🎉\n\nHomePresenter and HomeViewController are two different files. You can use the file template to create quickly this structure 😉.\n\n\n## Using Table Views and Collection Views \u003ca name=\"Using_TabColViews\"\u003e\u003c/a\u003e\n\nWinkKit provides both `WKTableView`, `WKCollectionView` and `WKTableViewCell`, `WKCollectionViewCell`.\nLet's talk about `WKTableView` and `WKTableViewCell` (collection view has same logic).\n\n**N.B.**: To have a better structure, all cell must have a xib: do **not** create cell in the storyboard directly.\n\nThe `WKTableView` provides two methods to register and dequeue a `WKTableViewCell` quickly by doing:\n\n```swift\ntableView.register(cell: ItemTableViewCell.self) // register the cell with a xib that has same name of the class\n\ntableView.dequeueReusableCell(ofType: ItemTableViewCell.self, for: indexPath) // dequeue a cell, already casted\n\n```\n\nCell acts like view controller: they have a presenter (in this case the plain `WKPresenter`) and they must conform to the view that the presenter handles. For this reason creating a cell is like creating a view controller:\n\n```swift\n// ItemTableViewCell.swift\n\nclass ItemTableViewCell: WKTableViewCell\u003cItemPresenter\u003e {\n\t// do only UI stuff here\n}\n\nextension ItemTableViewCell: ItemView {\n  \t// implements all ItemView methods/properties  \n}\n\n// ItemPresenter.swift\n\n/// The protocol that the cell handled by presenter must conforms to.\nprotocol ItemView: PresentableView {\n    \n}\n\n/// The presenter that will handle all logic of the view.\nclass ItemPresenter: WKPresenter {\n    \n    typealias View = ItemView\n    \n    // The view associated to this presenter. Keep weak to avoid retain-cycle\n    weak var view: ItemView?\n\t\n\t// the item that will be showed in this cell\n\tprivate var item: Item!\n\t \n    init(with item: Item) {\n    \tself.item = item\n    }\n    \n    // do all logic here\n}\n\n```\nYou can use template to quickly create all of those class/protocols 😁.\n\nUnlike view controllers, a cell must be configured after dequeued in the data source by doing something like:\n\n```swift\nfunc tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n\tlet cell = tableView.dequeueReusableCell(ofType: ItemTableViewCell.self, for: indexPath)\n  \tlet presenter = ItemPresenter() // create a presenter\n  \tcell.configure(with: presenter) // configure the cell with the presenter\n  \treturn cell\n}\n\n```\n\n### Collection View and Table View data source\n\nAs a best practice, it's better to decouple data sources from view controller. Avoid making a view controller as a data source to have all stuff better separated and re-usable. To communicate from data source to view controller, is it possible to use closures or delegation pattern. A typical implementation of a simple table view data source could be like:\n\n```swift\nclass ItemDataSource: NSObject, UITableViewDataSource {\n    \n    private var items = [Any]()\n    \n    init(tableView: WKTableView) {\n    \t// register cell here so when you need this data source you don't have to repeat this line of code\n    \ttableView.register(cell: ItemTableViewCell.self)\n    }\n    \n    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -\u003e Int {\n        return items.count\n    }\n    \n    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -\u003e UITableViewCell {\n        let cell = tableView.dequeueReusableCell(ofType: ItemTableViewCell.self, for: indexPath)\n        let presenter = ItemPresenter(with: items[indexPath.row])\n        cell.configure(with: presenter)\n        \n        return cell\n    }\n    \n}\n\n```\n\nThen in your view controller use the data source as instance variable.\n\n**Tips**: `WinkKit` provides few ready data source classes that have common methods, like inserting/deleting/reloading items or handle infinite scroll. Check `WKTableViewDataSource`, `WKCollectionViewDataSource` and `WKTableViewInfiniteDataSourceDelegate`. \n\n## Utils and more \u003ca name=\"Utils_And_More\"\u003e\u003c/a\u003e\n\nThere are other classes and extensions that can be used to achieve some behaviour:\n\n- Classes:\n\t- `Logger`: contains methods to log info and to avoid print debug info in release mode;\n\t- `OrderedSet`: it's like a `Set` but elements are ordered;\n\t- `Queue`: a simple queue class\n- Extensions:\n\t- `UIAlertController`: contains method to show quickly an alert\n\t- `Date`: contains an `init` to create a date from a string and a format and some methods to get day, hour of month\n\t- `Collection`: constains a subscript to access values even if the index is wrong (it returns an optional)\n\t- `CALayer`: contains method to add border to a layer\n\t- `UIColor`: allow color creation with a hex string\n\t- `UIWindow`: contains method to get the current top most view controller.\n\n## License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinkgroup%2Fwinkit-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwinkgroup%2Fwinkit-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinkgroup%2Fwinkit-ios/lists"}