{"id":13338707,"url":"https://github.com/Meniny/Ghost","last_synced_at":"2025-03-11T10:31:50.823Z","repository":{"id":56912719,"uuid":"118845191","full_name":"Meniny/Ghost","owner":"Meniny","description":"👻 A versatile HTTP(s) networking framework written in Swift.","archived":false,"fork":false,"pushed_at":"2018-05-19T10:42:06.000Z","size":2186,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-07T09:51:13.908Z","etag":null,"topics":["codable","curl","download","ios","json","macos","multipart-form","networking","stream","swift","tls","tvos","upload","urlsession","watchos"],"latest_commit_sha":null,"homepage":"https://meniny.cn/Ghost/","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/Meniny.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}},"created_at":"2018-01-25T01:30:14.000Z","updated_at":"2022-10-28T07:42:57.000Z","dependencies_parsed_at":"2022-08-21T03:20:31.377Z","dependency_job_id":null,"html_url":"https://github.com/Meniny/Ghost","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meniny%2FGhost","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meniny%2FGhost/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meniny%2FGhost/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meniny%2FGhost/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Meniny","download_url":"https://codeload.github.com/Meniny/Ghost/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243015440,"owners_count":20222082,"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":["codable","curl","download","ios","json","macos","multipart-form","networking","stream","swift","tls","tvos","upload","urlsession","watchos"],"created_at":"2024-07-29T19:17:08.647Z","updated_at":"2025-03-11T10:31:50.808Z","avatar_url":"https://github.com/Meniny.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./Assets/Ghost.jpg\" alt=\"Ghost\"\u003e\n  \u003cbr/\u003e\u003ca href=\"https://cocoapods.org/pods/Ghost\"\u003e\n  \u003cimg alt=\"Version\" src=\"https://img.shields.io/badge/version-1.4.0-brightgreen.svg\"\u003e\n  \u003cimg alt=\"Author\" src=\"https://img.shields.io/badge/author-Meniny-blue.svg\"\u003e\n  \u003cimg alt=\"Build Passing\" src=\"https://img.shields.io/badge/build-passing-brightgreen.svg\"\u003e\n  \u003cimg alt=\"Swift\" src=\"https://img.shields.io/badge/swift-4.0%2B-orange.svg\"\u003e\n  \u003cbr/\u003e\n  \u003cimg alt=\"Platforms\" src=\"https://img.shields.io/badge/platform-macOS%20%7C%20iOS%20%7C%20watchOS%20%7C%20tvOS-lightgrey.svg\"\u003e\n  \u003cimg alt=\"MIT\" src=\"https://img.shields.io/badge/license-MIT-blue.svg\"\u003e\n  \u003cbr/\u003e\n  \u003cimg alt=\"Cocoapods\" src=\"https://img.shields.io/badge/cocoapods-compatible-brightgreen.svg\"\u003e\n  \u003cimg alt=\"Carthage\" src=\"https://img.shields.io/badge/carthage-working%20on-red.svg\"\u003e\n  \u003cimg alt=\"SPM\" src=\"https://img.shields.io/badge/swift%20package%20manager-compatible-brightgreen.svg\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n# Introduction\n\n**Ghost** is a versatile HTTP(s) networking framework written in Swift.\n\n## 🌟 Features\n\n- [x] Chainable Request / Response Methods\n- [x] Asynchronous \u0026 synchronous task execution\n- [x] Basic, Bearer \u0026 Custom Authorization Handling\n- [x] `URL` / `JSON` / `Property List` Parameter Encoding\n- [x] Upload File / `Data` / `Stream` / `Multipart Form Data`\n- [x] Download File using Request / Resume Data\n- [x] Authentication with `URLCredential`\n- [x] Custom Cache Controls\n- [x] Custom Content Types\n- [x] Upload \u0026 Download Progress Closures\n- [x] `cURL` Command Debug Output\n- [x] Request \u0026 Response Interceptors\n- [x] Inference of response object type\n- [x] Network reachability\n- [x] `TLS Certificate` \u0026 `Public Key Pinning`\n- [x] Retry requests\n- [x] `Codable` protocols compatible (`JSON` / `Property List`)\n- [x] `watchOS` Compatible\n- [x] `tvOS` Compatible\n- [x] `macOS` Compatible\n\n## 📋 Requirements\n\n- iOS 8.0+\n- macOS 10.9+\n- tvOS 9.0+\n- watchOS 2.0+\n- Xcode 9.0+ with Swift 4.0+\n\n## 📲 Installation\n\nGhost is available on [CocoaPods](https://cocoapods.org):\n\n```ruby\nuse_frameworks!\npod 'Ghost'\n```\n\n## 🔧 Usage\n\n### Build a GhostRequest\n\n```swift\nimport Ghost\n\ndo {\n    let request = try GhostRequest.builder(\"YOUR_URL\")!\n                .setAccept(.json)\n                .setCache(.reloadIgnoringLocalCacheData)\n                .setMethod(.PATCH)\n                .setTimeout(20)\n                .setJSONBody([\"foo\", \"bar\"])\n                .setContentType(.json)\n                .setServiceType(.background)\n                .setCacheControls([.maxAge(500)])\n                .setURLParameters([\"foo\": \"bar\"])\n                .setAcceptEncodings([.gzip, .deflate])\n                .setBasicAuthorization(user: \"user\", password: \"password\")\n                .setHeaders([\"foo\": \"bar\"])\n                .build()\n} catch {\n    print(\"Request error: \\(error)\")\n}\n```\n\n### Request asynchronously\n\n```swift\nimport Ghost\n\nlet ghost = GhostURLSession()\n\nghost.data(URL(string: \"YOUR_URL\")!).async { (response, error) in\n    do {\n        if let object: [AnyHashable: Any] = try response?.object() {\n            print(\"Response dictionary: \\(object)\")\n        } else if let error = error {\n            print(\"Net error: \\(error)\")\n        }\n    } catch {\n        print(\"Parse error: \\(error)\")\n    }\n}\n```\n\n### Request synchronously\n\n```swift\nimport Ghost\n\nlet ghost = GhostURLSession()\n\ndo {\n    let object: [AnyHashable: Any] = try ghost.data(\"YOUR_URL\").sync().object()\n    print(\"Response dictionary: \\(object)\")\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Request from cache\n\n```swift\nimport Ghost\n\nlet ghost = GhostURLSession()\n\ndo {\n    let object: [AnyHashable: Any] = try ghost.data(\"YOUR_URL\").cached().object()\n    print(\"Response dictionary: \\(object)\")\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Track progress\n\n```swift\nimport Ghost\n\nlet ghost = GhostURLSession()\n\ndo {\n    let task = try ghost.data(\"YOUR_URL\").progress({ progress in\n        print(progress)\n    }).sync()\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n### Add interceptors for all requests\n\n```swift\nimport Ghost\n\nlet ghost = GhostURLSession()\n\nghost.addRequestInterceptor { request in\n    request.addHeader(\"foo\", value: \"bar\")\n    request.setBearerAuthorization(token: \"token\")\n    return request\n}\n```\n\n### Retry requests\n\n```swift\nimport Ghost\n\nlet ghost = GhostURLSession()\n\nghost.retryClosure = { response, _, _ in response?.statusCode == XXX }\n\ndo {\n    let task = try ghost.data(\"YOUR_URL\").retry({ response, error, retryCount in\n        return retryCount \u003c 2\n    }).sync()\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n## 🧙‍♂️ Codable\n\n### Encodable\n\n```swift\nimport Ghost\n\nlet request = GhostRequest.builder(\"YOUR_URL\")!\n            .setJSONObject(Encodable())\n            .build()\n```\n\n### Decodable\n\n```swift\nimport Ghost\n\nlet ghost = URLSession()\n\ndo {\n    let object: Decodable = try ghost.data(\"YOUR_URL\").sync().decode()\n    print(\"Response object: \\(object)\")\n} catch {\n    print(\"Error: \\(error)\")\n}\n```\n\n## 🌙 GhostHunter\n\n```swift\nlet url = URL.init(string: \"YOUR_URL\")!\ndo {\n    try GhostHunter.async(.GET, url: url, parameters: [\"name\": \"elias\"], headers: [\"Content-Type\": \"text/json\"], progress: { (pregress) in\n        print(pregress)\n    }, completion: { (response, error) in\n        do {\n            if let result: SomeCodableType = try response?.decode() {\n                print(\"GhostHunter Asynchronous: \\(result)\")\n            } else if let error = error {\n                print(\"GhostHunter Asynchronous: Ghost error: \\(error)\")\n            }\n        } catch {\n            print(\"GhostHunter: Parse error: \\(error)\")\n        }\n    }\n} catch {\n    print(\"GhostHunter: Request error: \\(error)\")\n}\n```\n\n## ❤️ Contribution\n\nYou are welcome to fork and submit pull requests.\n\n## 🔖 License\n\n`Ghost` is open-sourced software, licensed under the `MIT` license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMeniny%2FGhost","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMeniny%2FGhost","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMeniny%2FGhost/lists"}