{"id":1630,"url":"https://github.com/freshOS/ws-deprecated","last_synced_at":"2025-08-02T04:31:56.375Z","repository":{"id":56933595,"uuid":"46144496","full_name":"freshOS/ws-deprecated","owner":"freshOS","description":"⚠️ Deprecated - (in favour of Networking) :cloud: Elegantly connect to a JSON api. (Alamofire + Promises + JSON Parsing)","archived":false,"fork":false,"pushed_at":"2020-06-07T16:27:12.000Z","size":31084,"stargazers_count":353,"open_issues_count":14,"forks_count":32,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-07-09T14:31:07.641Z","etag":null,"topics":["alamofire","freshos","http","httpclient","ios","json","micro-framework","networking","promise","rest","rest-api","swift"],"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/freshOS.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"open_collective":"freshos","github":"s4cha"}},"created_at":"2015-11-13T20:06:07.000Z","updated_at":"2024-05-29T15:36:47.000Z","dependencies_parsed_at":"2022-08-21T00:40:24.656Z","dependency_job_id":null,"html_url":"https://github.com/freshOS/ws-deprecated","commit_stats":null,"previous_names":["freshos/ws"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/freshOS/ws-deprecated","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freshOS%2Fws-deprecated","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freshOS%2Fws-deprecated/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freshOS%2Fws-deprecated/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freshOS%2Fws-deprecated/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/freshOS","download_url":"https://codeload.github.com/freshOS/ws-deprecated/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/freshOS%2Fws-deprecated/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265759006,"owners_count":23823831,"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":["alamofire","freshos","http","httpclient","ios","json","micro-framework","networking","promise","rest","rest-api","swift"],"created_at":"2024-01-05T20:15:51.830Z","updated_at":"2025-08-02T04:31:55.688Z","avatar_url":"https://github.com/freshOS.png","language":"Swift","readme":"![ws](https://raw.githubusercontent.com/freshOS/ws/master/banner.png)\n\n# ⚠ Important Notice: Farewell ws... hello Networking !\n[Networking](https://github.com/freshOS/Networking) is the next generation of the [ws](https://github.com/freshOS/ws) project. Think of it as ws 2.0 built for iOS13.\nIt uses Combine native Apple's framework over [Then](https://github.com/freshOS/Then) Promise Library, removes [Arrow](https://github.com/freshOS/Arrow) dependency to favour Codable (Arrow can still be adapted easily though) and removes the [Alamofire](https://github.com/Alamofire/Alamofire) dependency in favour of a simpler purely native [URLSession](https://developer.apple.com/documentation/foundation/urlsession) implementation. In essence, less dependencies and more native stuff with an almost identical api. If your app supports iOS13 and up, it is strongly advised to migrate to Networking. WS will be \"maintained\" for backwards compatibility reasons but consider it deprected starting iOS13.\n\n# ws\n\n[![Language: Swift 5](https://img.shields.io/badge/language-swift5-f48041.svg?style=flat)](https://developer.apple.com/swift)\n![Platform: iOS 8+](https://img.shields.io/badge/platform-iOS%208%2B-blue.svg?style=flat)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![Cocoapods compatible](https://img.shields.io/badge/Cocoapods-compatible-4BC51D.svg?style=flat)](https://cocoapods.org)\n[![License: MIT](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/freshOS/ws/blob/master/LICENSE)\n[![Build Status](https://app.bitrise.io/app/a6d157138f9ee86d/status.svg?token=W7-x9K5U976xiFrI8XqcJw\u0026branch=master)](https://app.bitrise.io/app/a6d157138f9ee86d)\n[![codebeat badge](https://codebeat.co/badges/78d86c16-aa61-4a5e-8342-1aea8d437453)](https://codebeat.co/projects/github-com-freshos-ws)\n![Release version](https://img.shields.io/github/release/freshos/ws.svg)\n\n[Reason](#why) - [Example](#usage) - [Installation](#installation)\n\n```swift\nlet ws = WS(\"http://jsonplaceholder.typicode.com\")\n\nws.get(\"/users\").then { json in\n    // Get back some json \\o/\n}\n```\nBecause JSON apis are used in **99% of iOS Apps**, this should be  **simple**.  \nWe developers should **focus on our app logic** rather than *boilerplate code* .  \n*Less* code is *better* code\n## Try it!\n\nws is part of [freshOS](http://freshos.org) iOS toolset. Try it in an example App ! \u003ca class=\"github-button\" href=\"https://github.com/freshOS/StarterProject/archive/master.zip\" data-icon=\"octicon-cloud-download\" data-style=\"mega\" aria-label=\"Download freshOS/StarterProject on GitHub\"\u003eDownload Starter Project\u003c/a\u003e\n\n\n## How\nBy providing a lightweight client that **automates boilerplate code everyone has to write**.  \nBy exposing a **delightfully simple** api to get the job done simply, clearly, quickly.  \nGetting swift models from a JSON api is now *a problem of the past*\n\n## What\n- [x] Build concise Apis\n- [x] Automatically maps your models\n- [x] Built-in network logger\n- [x] Stands on the shoulder of giants (Alamofire \u0026 Promises)\n- [x] Pure Swift, Simple \u0026 Lightweight\n\n## Usage\n\n### Bare JSON\n\n```swift\nimport ws // Import ws at the top of your file\nimport Arrow // Import Arrow to get access to the JSON type\n\nclass ViewController: UIViewController {\n\n    // Set webservice base URL\n    let ws = WS(\"http://jsonplaceholder.typicode.com\")\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n       // Get back some json instantly \\o/\n       ws.get(\"/users\").then { (json:JSON) in\n           print(json)\n       }\n    }\n}\n```\n\n### Set up Model parsing\nCreate a `User+JSON.swift` file and map the JSON keys to your model properties\n```swift\nimport Arrow\n\nextension User: ArrowParsable {\n\n    mutating func deserialize(_ json: JSON) {\n        identifier \u003c-- json[\"id\"]\n        username \u003c-- json[\"username\"]\n        email \u003c-- json[\"email\"]\n    }\n}\n```\n*Note: `ws` uses `Arrow` for JSON Parsing\nhttps://github.com/freshOS/Arrow*\n\n### Choose what you want back\n\nHere you are going to create a function that wraps your request.\nThere are different ways of writing that function depending on what you want back. An empty block, the JSON, the model or the array of models.\n\n```swift\nfunc voidCall() -\u003e Promise\u003cVoid\u003e {\n    return ws.get(\"/users\")\n}\n\nfunc jsonCall() -\u003e Promise\u003cJSON\u003e {\n    return ws.get(\"/users\")\n}\n\nfunc singleModelCall() -\u003e Promise\u003cUser\u003e {\n    return ws.get(\"/users/3\")\n}\n\nfunc modelArrayCall() -\u003e Promise\u003c[User]\u003e {\n    return ws.get(\"/users\")\n}\n```\nAs you can notice, only by changing the return type,\nws *automatically* knows what to do, for instance, try to parse the response into `User` models.\n\nThis enables us to stay concise without having to write extra code. \\o/\n\n*Note: `ws` uses `then` for Promises\nhttps://github.com/freshOS/then*\n\n### Get it!\n\n```swift\nvoidCall().then {\n    print(\"done\")\n}\n\njsonCall().then { json in\n    print(json)\n}\n\nsingleModelCall().then { user in\n    print(user) // Strongly typed User \\o/\n}\n\nmodelArrayCall().then { users in\n    print(users) // Strongly typed [User] \\o/\n}\n```\n\n## Settings\n\nWant to log all network calls and responses?\n```swift\nws.logLevels = .debug\n```\n\nWant to hide network activity indicator?\n\n```swift\nws.showsNetworkActivityIndicator = false\n```\n\nWant to override the default session manager to customize trust policies?\n\n```swift\nimport Alamofire\n\nws.sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(\n  policies: [\"myspecialhostname.com\" : .disableEvaluation]\n))\n```   \n\n## Api Example\nHere is a Typical CRUD example for Articles :\n\n```swift\nextension Article {\n\n    static func list() -\u003e Promise\u003c[Article]\u003e {\n        return ws.get(\"/articles\")\n    }\n\n    func save() -\u003e Promise\u003cArticle\u003e {\n        return ws.post(\"/articles\", params: [\"name\":name])\n    }\n\n    func fetch() -\u003e Promise\u003cArticle\u003e {\n        return ws.get(\"/articles/\\(id)\")\n    }\n\n    func update() -\u003e Promise\u003cVoid\u003e {\n        return ws.put(\"/articles/\\(id)\", params: [\"name\":name])\n    }\n\n    func delete() -\u003e Promise\u003cVoid\u003e {\n        return ws.delete(\"/articles/\\(id)\")\n    }\n\n}\n```\n\nHere is how we use it in code :\n```swift\n// List Articles\nArticle.list().then { articles in\n\n}\n\n// Create Article\nvar newArticle = Article(name:\"Cool story\")\nnewArticle.save().then { createdArticle in\n\n}\n\n// Fetch Article\nvar existingArticle = Article(id:42)\nexistingArticle.fetch().then { fetchedArticle in\n\n}\n\n// Edit Article\nexistingArticle.name = \"My new name\"\nexistingArticle.update().then {\n\n}\n\n// Delete Article\nexistingArticle.delete().then {\n\n}\n```\n\n\n### HTTP Status code\n\nWhen a request fails, we often want to know the reason thanks to the HTTP status code.\nHere is how to get it :\n\n```swift\nws.get(\"/users\").then {\n    // Do something\n}.onError { e in\n    if let wsError = e as? WSError {\n        print(wsError.status)\n        print(wsError.status.rawValue) // RawValue for Int status\n    }\n}\n```\nYou can find the full `WSError` enum here -\u003e https://github.com/freshOS/ws/blob/master/ws/WSError.swift\n\n## Bonus - Load More pattern\n\nVery often we deal we lists and the ability to `load more` items.\nHere we are going to see an example implementation of this pattern using `ws`.\nThis is not included because the logic itself depends on your backend implementation.\nThis will give you an example for you to roll out your own version.\n\n### Implementation\n\n\n```swift\nimport ws\nimport then\nimport Arrow\n\n\nclass LoadMoreRequest\u003cT:ArrowParsable\u003e {\n\n    var limit = 12\n\n    private var params = [String:Any]()\n    private var offset = 0\n    private var call: WSRequest!\n    private var canLoadMore = true\n    private var aCallback:((_ ts: [T]) -\u003e [T])? = nil\n\n    init(_ aCall: WSRequest) {\n        call = aCall\n    }\n\n    func resetOffset() {\n        offset = 0\n        canLoadMore = true\n    }\n\n    func hasMoreItemsToload() -\u003e Bool {\n        return canLoadMore\n    }\n\n    func fetchNext() -\u003e Promise\u003c[T]\u003e {\n        params = call.params\n        params[\"limit\"] = limit\n        params[\"offset\"] = offset\n        call.params = params\n        offset += limit\n        return call.fetch()\n                .registerThen(parseModels)\n                .resolveOnMainThread()\n    }\n\n    private func parseModels(_ json: JSON) -\u003e [T] {\n        let mapper = WSModelJSONParser\u003cT\u003e()\n        let models = mapper.toModels(json)\n        if models.count \u003c limit {\n            canLoadMore = false\n        }\n        return models\n    }\n}\n```\nAs you can see, we have a strongly typed request.  \nThe limit is adjustable.  \nIt encapsulates a WSRequest.  \nIt handles the offset logic and also wether or not there are more items to load.\n\nAnd that's all we need!\n\nNow, this is how  we build a `LoadMoreRequest`\n\n```swift\nfunc loadMoreUsersRequest() -\u003e LoadMoreRequest\u003cUser\u003e {\n    return LoadMoreRequest(ws.getRequest(\"/users\"))\n}\n```\n\n### Usage\nAnd here is how we use it in our controllers :\n\n```swift\nclass ViewController: UIViewController {\n\n    // Get a request\n    let request = api.loadMoreUsersRequest()\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        request.limit = 5 // Set a limit if needed\n    }\n\n    func refresh() {\n      // Resets the request, usually plugged with\n      // the pull to refresh feature of a tableview\n      request.resetOffset()\n    }\n\n    func loadMore() {\n      // Get the next round of users\n      request.fetchNext().then { users in\n          print(users)\n      }\n    }\n\n    func shouldDisplayLoadMoreSpinner() -\u003e Bool {\n      // This asks the requests if there are more items to come\n      // This is useful to know if we show the \"load more\" spinner\n      return request.hasMoreItemsToload()\n    }\n}\n```\n\nHere you go you now have a simple way to deal with load more requests in your App 🎉\n\n\n## Bonus - Simplifying restful routes usage\n\nWhen working with a `RESTFUL` api, we can have fun and go a little further.\n\nBy introducing a `RestResource` protocol\n```swift\npublic protocol RestResource {\n    static func restName() -\u003e String\n    func restId() -\u003e String\n}\n```\nWe can have a function that builds our `REST` URL\n```swift\npublic func restURL\u003cT:RestResource\u003e(_ r:T) -\u003e String {\n    return \"/\\(T.restName())/\\(r.restId())\"\n}\n```\n\nWe conform our `User` Model to the protocol\n```swift\nextension User:RestResource {\n    static func restName() -\u003e String { return \"users\" }\n    func restId() -\u003e String { return \"\\(identifier)\" }\n}\n```\n\n\nAnd we can implement a version of `get` that takes our a `RestResource`\n\n```swift\npublic func get\u003cT:ArrowParsable \u0026 RestResource\u003e(_ restResource:T, params:[String:Any] = [String:Any]()) -\u003e Promise\u003cT\u003e {               \n    return get(restURL(restResource), params: params)\n}\n```\nthen\n\n```swift\nws.get(\"/users/\\(user.identifier)\")\n```\nCan be written like :\n```swift\nws.get(user)\n```\nOf course, the same logic can be applied to the all the other ws functions (`post`, `put` `delete` etc) ! 🎉\n## Installation\n\n### Swift Package Manager (SPM)\nDue to the challenge of supporting all package manager at once, SPM support is availlable on a separate branch `spm-only`.\n\n### Carthage\nIn your Cartfile\n```\ngithub \"freshOS/ws\"\n```\n- Run `carthage update`\n- Drag and drop `ws.framework` from `Carthage/Build/iOS` to `Linked Frameworks and Libraries` (“General” settings tab)\n- Go to  `Project` \u003e `Target` \u003e `Build Phases` + `New run Script Phase`\n\n`/usr/local/bin/carthage copy-frameworks`\n\nAdd input files\n```\n$(SRCROOT)/Carthage/Build/iOS/ws.framework\n$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework\n$(SRCROOT)/Carthage/Build/iOS/Arrow.framework\n$(SRCROOT)/Carthage/Build/iOS/then.framework\n```\n\nThis links ws and its dependencies.\n\n### Manually\n\nCarthage is pretty useful since it takes care of pulling dependencies such as Arrow, then and Alamofire.\nWhat's cool is that it really is transparent. What I mean is that you could just use carthage on the side to pull and build dependencies and manually link frameworks to your Xcode project.\n\nWithout Carthage, I'd see 2 solutions :\n1 - Copy paste all the source code : ws / then / Arrow / Alamofire which doesn't sound like a lot of fun ;)\n2 - Manually link the frameworks (ws + dependencies) by A grabbing .frameworks them on each repo, or B use Carthage to build them\n\n### Cocoapods\n\n```\ntarget 'MyApp'\npod 'ws'\nuse_frameworks!\n```\n\n## Swift Version\nSwift 2 -\u003e version [**1.3.0**](https://github.com/freshOS/ws/releases/tag/1.3.0)  \nSwift 3 -\u003e version [**2.0.4**](https://github.com/freshOS/ws/releases/tag/2.0.4)  \nSwift 4 -\u003e version [**3.0.0**](https://github.com/freshOS/ws/releases/tag/3.0.0)  \nSwift 4.1 -\u003e version  [**3.1.0**](https://github.com/freshOS/ws/releases/tag/3.1.0)  \nSwift 4.2 -\u003e version [**3.2.0**](https://github.com/freshOS/ws/releases/tag/3.2.0)  \nSwift 5.0 -\u003e version [**5.0.0**](https://github.com/freshOS/ws/releases/tag/5.0.0)  \nSwift 5.1 -\u003e version [**5.1.0**](https://github.com/freshOS/ws/releases/tag/5.1.0)  \nSwift 5.1.3 -\u003e version [**5.1.1**](https://github.com/freshOS/ws/releases/tag/5.1.1)  \n\n\n### Backers\nLike the project? Offer coffee or support us with a monthly donation and help us continue our activities :)\n\n\u003ca href=\"https://opencollective.com/freshos/backer/0/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/0/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/1/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/1/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/2/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/2/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/3/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/3/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/4/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/4/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/5/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/5/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/6/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/6/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/7/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/7/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/8/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/8/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/9/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/9/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/10/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/10/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/11/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/11/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/12/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/12/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/13/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/13/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/14/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/14/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/15/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/15/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/16/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/16/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/17/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/17/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/18/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/18/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/19/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/19/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/20/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/20/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/21/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/21/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/22/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/22/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/23/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/23/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/24/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/24/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/25/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/25/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/26/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/26/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/27/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/27/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/28/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/28/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/backer/29/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/backer/29/avatar.svg\"\u003e\u003c/a\u003e\n\n### Sponsors\nBecome a sponsor and get your logo on our README on Github with a link to your site :)\n\n\u003ca href=\"https://opencollective.com/freshos/sponsor/0/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/0/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/1/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/1/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/2/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/2/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/3/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/3/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/4/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/4/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/5/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/5/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/6/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/6/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/7/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/7/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/8/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/8/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/9/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/9/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/10/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/10/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/11/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/11/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/12/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/12/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/13/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/13/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/14/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/14/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/15/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/15/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/16/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/16/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/17/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/17/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/18/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/18/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/19/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/19/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/20/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/20/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/21/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/21/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/22/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/22/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/23/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/23/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/24/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/24/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/25/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/25/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/26/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/26/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/27/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/27/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/28/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/28/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/freshos/sponsor/29/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/freshos/sponsor/29/avatar.svg\"\u003e\u003c/a\u003e\n","funding_links":["https://opencollective.com/freshos","https://github.com/sponsors/s4cha","https://opencollective.com/freshos/backer/0/website","https://opencollective.com/freshos/backer/1/website","https://opencollective.com/freshos/backer/2/website","https://opencollective.com/freshos/backer/3/website","https://opencollective.com/freshos/backer/4/website","https://opencollective.com/freshos/backer/5/website","https://opencollective.com/freshos/backer/6/website","https://opencollective.com/freshos/backer/7/website","https://opencollective.com/freshos/backer/8/website","https://opencollective.com/freshos/backer/9/website","https://opencollective.com/freshos/backer/10/website","https://opencollective.com/freshos/backer/11/website","https://opencollective.com/freshos/backer/12/website","https://opencollective.com/freshos/backer/13/website","https://opencollective.com/freshos/backer/14/website","https://opencollective.com/freshos/backer/15/website","https://opencollective.com/freshos/backer/16/website","https://opencollective.com/freshos/backer/17/website","https://opencollective.com/freshos/backer/18/website","https://opencollective.com/freshos/backer/19/website","https://opencollective.com/freshos/backer/20/website","https://opencollective.com/freshos/backer/21/website","https://opencollective.com/freshos/backer/22/website","https://opencollective.com/freshos/backer/23/website","https://opencollective.com/freshos/backer/24/website","https://opencollective.com/freshos/backer/25/website","https://opencollective.com/freshos/backer/26/website","https://opencollective.com/freshos/backer/27/website","https://opencollective.com/freshos/backer/28/website","https://opencollective.com/freshos/backer/29/website"],"categories":["Networking","Swift"],"sub_categories":["Video"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FfreshOS%2Fws-deprecated","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FfreshOS%2Fws-deprecated","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FfreshOS%2Fws-deprecated/lists"}