{"id":20373245,"url":"https://github.com/amirsaam/patreonapi-swift","last_synced_at":"2026-06-05T08:31:30.905Z","repository":{"id":65784782,"uuid":"596330177","full_name":"amirsaam/PatreonAPI-Swift","owner":"amirsaam","description":"A Swift library for interacting with Patreon API","archived":false,"fork":false,"pushed_at":"2024-07-15T13:29:38.000Z","size":40,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-15T17:36:33.087Z","etag":null,"topics":["api","api-client","development","open-source","patreon","patreon-api","rest-api","swift","swift-package"],"latest_commit_sha":null,"homepage":"https://docs.patreon.com","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/amirsaam.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}},"created_at":"2023-02-02T00:13:11.000Z","updated_at":"2024-11-22T23:34:17.000Z","dependencies_parsed_at":"2025-01-15T05:44:33.067Z","dependency_job_id":"5bb3a3c9-d9e6-4a78-a742-07722f733bf3","html_url":"https://github.com/amirsaam/PatreonAPI-Swift","commit_stats":null,"previous_names":["amirsaam/patreonapi-swift"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amirsaam%2FPatreonAPI-Swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amirsaam%2FPatreonAPI-Swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amirsaam%2FPatreonAPI-Swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amirsaam%2FPatreonAPI-Swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amirsaam","download_url":"https://codeload.github.com/amirsaam/PatreonAPI-Swift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241921837,"owners_count":20042763,"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":["api","api-client","development","open-source","patreon","patreon-api","rest-api","swift","swift-package"],"created_at":"2024-11-15T01:17:23.935Z","updated_at":"2025-03-04T20:44:13.896Z","avatar_url":"https://github.com/amirsaam.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PatreonAPI-Swift\n\u003e The missing library for interacting with Patreon's API in Swift.\n\n[![Swift Version][swift-image]][swift-url]\n[![License][license-image]][license-url]\n\nSince I needed to show the Patreon campaign in one of our projects and there was no Swift Package or base code for Patreon's API in Swift, I needed to write my own and I think there was need for it in the community, so I just made it a package in order to make fellow developers' life easier since the API is a total nightmare!\n\n## Installation\n\nAdd this project on your `Package.swift`\n\n```swift\nimport PackageDescription\n\nlet package = Package(\n    dependencies: [\n        .package(url: \"https://github.com/amirsaam/PatreonAPI-Swift.git\", from: \"0.9.00\"),\n    ]\n)\n```\n\n## Usage example\n\n\nSurely import the package\n```swift\nimport PatreonAPI\n```\n\nInitialise the Class\n```swift\nlet patreonAPI = PatreonAPI(clientID: \"YOUR CLIENT ID\",\n                            clientSecret: \"YOUR CLIENT SECRET\",\n                            creatorAccessToken: \"YOUR CREATOR'S ACCESS TOKEN\",\n                            creatorRefreshToken: \"YOUR CREATOR'S REFRESH TOKEN\",\n                            redirectURI: \"YOUR CLIENT REDIRECT URL\",\n                            campaignID: \"YOUR CAMPAIGN ID\")\n```\n\nUse built-in Functions\n```swift\n// For Authentication: \npatreonAPI.doOAuth() // Uses Client ID and Redirect URI from Initialiser\nvar oauthData = await patreonAPI.getOAuthTokens(callbackCode: \"CALLBACK CODE FROM doOAuth()\") // Uses Client ID, Client Secret and Redirect URI from Initialiser\noauthData = await patreonAPI.refreshOAuthTokens(userRefreshToken: oauthData?.refresh_token ?? \"\") // Uses Client Secret and Redirect URI from Initialiser\n\n// For Retrieving Data:\nlet userIdentity = await patreonAPI.getUserIdentity(userAccessToken: oauthData?.access_token ?? \"\")\nlet userCampaigns = await patreonAPI.getUserOwnedCampaigns(userAccessToken: oauthData?.access_token ?? \"\")\nlet creatorCampaign = await patreonAPI.getDataForCampaign() // Uses Creator Access Token and Campaign ID from Initialiser\nlet creatorCampaignMembersList = await patreonAPI.getMembersForCampaign() // Uses Creator Access Token and Campaign ID from Initialiser\nlet specificMemberFromCreatorCampaign = await patreonAPI.getMemberForCampaignByID(memberID: \"MEMBER OF CAMPAIGN ID\") // Uses Creator Access Token from Initialiser. `memberID` should be retrieved from `membersList`\n```\n\nHandling `doOAuth()`:\n- First you need to handle the redirect, rather storing the returning data to your online database or redirect them to your app with url scheme that first needs to be redirected to your website because Patreon doesn't support App URL Scheme.\n- If you choosed URL Scheme, after redirect to it the function will return a URL with some queries (read more in Patreon docs)\n- You can parse the url manually or you can use such code as below for automatic parse:\n```swift\nextension URL {\n    func params() -\u003e [String : Any] {\n        var dict = [String : Any]()\n        if let components = URLComponents(url: self, resolvingAgainstBaseURL: false) {\n            if let queryItems = components.queryItems {\n                for item in queryItems {\n                    dict[item.name] = item.value!\n                }\n            }\n            return dict\n        } else {\n            return [:]\n        }\n    }\n}\n```\nthat can be used like this for the future usages:\n```swift\n.onOpenURL { url in\n  let callback = url.params()\n  if callback.isEmpty {\n      // handle callback being unsuccessful\n  } else {\n      let patreonCallbackCode = callback[\"code\"] as! String\n      let patreonCallbackState = callback[\"state\"] as! String\n  }\n}\n```\nthen you can pass `patreonCallbackCode` to the `getOAuthTokens` function.\n\n## A Problem in Patreon's API\n\nUnfortunately, when I was trying to retrieve all data about Creator's campaign, I have encountered a bizarre situation where returning data with same name with no any distinguishing ability could have various structures and I needed to just use them with native Swift experience with decoding structs.\nSo I made something called [CodableAny](https://github.com/amirsaam/CodableAny) (that is in this package's dependencies) for fixing my own issue.\nBasically when we call `getDataForCampaign` function, we will return:\n```swift\npublic struct PatreonCampaignInfo: Codable {\n    public let data: CampaignData\n    public let included: [CampaignIncludedAny]\n    public let links: SelfLink\n}\n```\nand `CampaignIncludedAny` is:\n```swift\npublic struct CampaignIncludedAny: Codable {\n    public let attributes: [String: CodableAny]\n    public let id: String\n    public let type: String\n}\n```\nSo we retrived any data that is in the `Included` part of the API call in one network call, but it is not decoded so an extra local step is required! In your ViewModel for Patreon you can use such thing to decode that `Included` part based on if it's a Tier data or a Benefit data:\n```swift\n@Published var campaignTiers: [CampaignIncludedTier] = []\n@Published var campaignBenefits: [CampaignIncludedBenefit] = []\n@Published var patreonCampaign: PatreonCampaignInfo? {\n    didSet {\n        if let campaign = patreonCampaign {\n            campaignTiers = extractCampaignTiers(from: campaign.included)\n            campaignBenefits = extractCampaignBenefits(from: campaign.included)\n        } else {\n            campaignTiers = []\n            campaignBenefits = []\n        }\n    }\n}\n\nfunc extractCampaignTiers(from campaign: [CampaignIncludedAny]) -\u003e [CampaignIncludedTier] {\n    var decodedArray = [CampaignIncludedTier]()\n    for campaignIncluded in campaign {\n        if campaignIncluded.type == \"tier\" {\n            let decoded = try? JSONDecoder().decode(CampaignIncludedTier.self, from: try JSONEncoder().encode(campaignIncluded))\n            if let decoded = decoded {\n                decodedArray.append(decoded)\n            }\n        }\n    }\n    return decodedArray\n}\n\nfunc extractCampaignBenefits(from campaign: [CampaignIncludedAny]) -\u003e [CampaignIncludedBenefit] {\n    var decodedArray = [CampaignIncludedBenefit]()\n    for campaignIncluded in campaign {\n        if campaignIncluded.type == \"benefit\" {\n            let decoded = try? JSONDecoder().decode(CampaignIncludedBenefit.self, from: try JSONEncoder().encode(campaignIncluded))\n            if let decoded = decoded {\n                decodedArray.append(decoded)\n            }\n        }\n    }\n    return decodedArray\n}\n```\nFor more information about decoding structs please go to [this directory](https://github.com/amirsaam/PatreonAPI-Swift/tree/master/Sources/PatreonAPI/Patreon%20Data%20Structs)\n\n\n## To Do\n\n- [ ] Add Missing API Calls(?)\n- [ ] Write Xcode Tests\n- [ ] Make Pod\n- [ ] Deploy Github Action\n\n## Dependencies \n\n- [Alamofire](https://github.com/Alamofire/Alamofire)\n- [Semaphore](https://github.com/groue/Semaphore)\n- [CodableAny](https://github.com/amirsaam/CodableAny)\n\n## Meta\n\nAmir Mohammadi – [@amirsaam](https://twitter.com/amirsaam) – amirsaam [at] me [dot] com\n\nDistributed under the MIT license. See ``LICENSE`` for more information.\n\n[https://github.com/amirsaam/PatreonAPI-Swift/](https://github.com/amirsaam/PatreonAPI-Swift)\n\n[swift-image]:https://img.shields.io/badge/swift-5.0-orange.svg\n[swift-url]: https://swift.org/\n[license-image]: https://img.shields.io/badge/License-MIT-blue.svg\n[license-url]: LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famirsaam%2Fpatreonapi-swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famirsaam%2Fpatreonapi-swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famirsaam%2Fpatreonapi-swift/lists"}