{"id":13729754,"url":"https://github.com/mironal/TwitterAPIKit","last_synced_at":"2025-05-08T02:30:41.434Z","repository":{"id":39924351,"uuid":"454675964","full_name":"mironal/TwitterAPIKit","owner":"mironal","description":"Swift library for the Twitter API v1 and v2 🍷","archived":true,"fork":false,"pushed_at":"2022-11-04T00:20:07.000Z","size":981,"stargazers_count":126,"open_issues_count":13,"forks_count":27,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-19T08:50:03.441Z","etag":null,"topics":["ios","macos","swift","twitter","twitter-api"],"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/mironal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["mironal"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2022-02-02T07:09:54.000Z","updated_at":"2025-03-24T14:45:22.000Z","dependencies_parsed_at":"2022-07-06T11:21:04.080Z","dependency_job_id":null,"html_url":"https://github.com/mironal/TwitterAPIKit","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mironal%2FTwitterAPIKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mironal%2FTwitterAPIKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mironal%2FTwitterAPIKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mironal%2FTwitterAPIKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mironal","download_url":"https://codeload.github.com/mironal/TwitterAPIKit/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252986575,"owners_count":21836185,"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":["ios","macos","swift","twitter","twitter-api"],"created_at":"2024-08-03T02:01:04.732Z","updated_at":"2025-05-08T02:30:41.031Z","avatar_url":"https://github.com/mironal.png","language":"Swift","readme":"# TwitterAPIKit\n\nSwift library for the Twitter API v1 and v2.\n\n[![Swift](https://github.com/mironal/TwitterAPIKit/actions/workflows/swift.yml/badge.svg)](https://github.com/mironal/TwitterAPIKit/actions/workflows/swift.yml)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fmironal%2FTwitterAPIKit%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/mironal/TwitterAPIKit) [![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fmironal%2FTwitterAPIKit%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/mironal/TwitterAPIKit)\n[![Standard](https://img.shields.io/endpoint?url=https%3A%2F%2Ftwbadges.glitch.me%2Fbadges%2Fstandard)](https://developer.twitter.com/en/docs/twitter-api) [![v2](https://img.shields.io/endpoint?url=https%3A%2F%2Ftwbadges.glitch.me%2Fbadges%2Fv2)](https://developer.twitter.com/en/docs/twitter-api)\n[![codecov](https://codecov.io/gh/mironal/TwitterAPIKit/branch/main/graph/badge.svg?token=T2H3D6ASA0)](https://codecov.io/gh/mironal/TwitterAPIKit)\n\nPlease see this issue for the progress of the API implementation.\n\n- [API v1](https://github.com/mironal/TwitterAPIKit/issues/5)\n- [API v2](https://github.com/mironal/TwitterAPIKit/issues/6)\n\nIssue やコメントは日本語でも大丈夫です。\n\n---\n\n## Motivation\n\nUnfortunately, I couldn't find any active Twitter API library for Swift at the moment.\n\nSo, I decided to create one.\n\n## Policy\n\n- No dependencies\n\n## API Structures\n\nYou can limit the scope of available APIs depending on your application.\nThis is useful if your app only supports v1, or if you want to limit access to the API.\nCurrently, scoping according to Twitter's App permissions is not yet implemented.\n\n```swift\n\n// The most common usage.\n\n// For OAuth 1.0a\nlet client = TwitterAPIClient(.oauth10a(.init(\n            consumerKey: \"\",\n            consumerSecret: \"\",\n            oauthToken: \"\",\n            oauthTokenSecret: \"\"\n        )))\n// For OAuth 2.0 client\nlet client = TwitterAPIClient(.oauth20(.init(\n            clientID: \"\",\n            scope: [],\n            tokenType: \"\",\n            expiresIn: 0,\n            accessToken: \"\",\n            refreshToken: \"\"\n        )))\n\nclient.v1.someV1API()\nclient.v2.someV2API()\n\n// V1 only client\nlet v1Client = client.v1\nv1Client.someV1API()\n\n// V2 only client\nlet v2Client = client.v2\nv2Client.someV2API()\n\n// DM only client\nlet dmClient = client.v1.directMessage\ndmClient.someDM_APIs()\n\n// Each API can be accessed flatly or by individual resource.\n\n// Flat.\nlet client.v1.allV1_APIs()\n\n// Individual resources.\nlet client.v1.tweet.someTweetAPIs()\nlet client.v1.directMessage.someDM_APIs()\n```\n\n## How do I authenticate?\n\n[Please see \"HowDoIAuthenticate.md\"](./HowDoIAuthenticate.md)\n\nAnd the following sample project includes a sample authentication.\n\n\u003e https://github.com/mironal/TwitterAPIKit-iOS-sample\n\n## How to decode response\n\n[Please see \"HowToDecodeResponse.md\"](./HowToDecodeResponse.md)\n\n## Linux support (experimental)\n\nTwitterAPIKit can be used on Linux, but cannot be merged into the main branch because it cannot run tests.\n\nIf you want to use it on Linux, use [THIS BRANCH](https://github.com/mironal/TwitterAPIKit/pull/121).\n\n## Example\n\n### Projects\n\n- https://github.com/mironal/TwitterAPIKit-iOS-sample\n\nThis sample project contains examples of how to authenticate with `OAuth 1.0a User Access Tokens (3-legged OAuth flow)` and `OAuth 2.0 Authorization Code Flow with PKCE`.\n\n### Basic\n\n```swift\n    let consumerKey = \"\"\n    let consumerSecret = \"\"\n    let oauthToken = \"\"\n    let oauthTokenSecret = \"\"\n\n    let client = TwitterAPIClient(\n        consumerKey: consumerKey,\n        consumerSecret: consumerSecret,\n        oauthToken: oauthToken,\n        oauthTokenSecret: oauthTokenSecret\n    )\n\n    client.v1.getShowStatus(.init(id: \"status id\"))\n         // Already serialized using \"JSONSerialization.jsonObject(with:, options:)\".\n        .responseObject() { response in }\n        .responseObject(queue: .global(qos: .default)) { response in  }\n\n        // Already decoded using JSONDecoder.\n        .responseDecodable(type: Entity.self, queue: .global(qos: .default)) { response in }\n        .responseDecodable(type: Entity.self) { response in }\n\n        // Unprocessed data\n        .responseData() { response in /* Run in .main queue */ }\n        .responseData((queue: .global(qos: .default)) { response in /* Run in .global(qos: .default) queue  */ }\n\n        // !! A `prettyString` is provided for debugging purposes. !!\n        print(response.prettyString)\n\n        result.map((Success) -\u003e NewSuccess)\n        result.tryMap((Success) throws -\u003e NewSuccess)\n        result.mapError((TwitterAPIKitError) -\u003e TwitterAPIKitError\u003e)\n        result.success // Success?\n        result.error // TwitterAPIKitError?\n        response.rateLimit\n\n        // Use result\n        do {\n            let success = try response.result.get()\n            print(success)\n        } catch let error {\n            print(error)\n        }\n    }\n```\n\n### Refresh OAuth 2.0 Token\n\n```swift\nlet refresh = try await client.refreshOAuth20Token(type: .confidentialClient(clientID: \"\", clientSecret: \"\"), forceRefresh: true)\n// let refresh = try await client.refreshOAuth20Token(type: .publicClient, forceRefresh: true)\n\n// The authentication information in the Client is also updated, so there is no need to recreate a new instance of the Client.\n\nif refresh.refreshed {\n    storeToken(refresh.token)\n}\n\n// Or\n\nclient.refreshOAuth20Token(type: .publicClient, forceRefresh: true) { result in\n    do {\n        let refresh = try result.get()\n        if refresh.refreshed {\n            storeToken(refresh.token)\n        }\n    } catch {\n\n    }\n}\n\n// Notification\n\nNotificationCenter.default.addObserver(\n    self,\n    selector: #selector(didRefreshOAuth20Token(_:)),\n    name: TwitterAPIClient.didRefreshOAuth20Token,\n    object: nil\n)\n\n@objc func didRefreshOAuth20Token(_ notification: Notification) {\n    guard let token = notification.userInfo?[TwitterAPIClient.tokenUserInfoKey] as? TwitterAuthenticationMethod.OAuth20 else {\n        fatalError()\n    }\n    print(\"didRefreshOAuth20Token\", didRefreshOAuth20Token, token)\n    store(token)\n}\n```\n\n### Custom Request class\n\nThe class of each request can be inherited to create subclasses. This is why it is declared as an open class instead of a struct.\n\nThis is intended so that when new parameters are added due to changes in the Twitter API, you can handle them yourself without waiting for the library to be updated.\n\n```swift\n// example\nclass CustomListsListRequestV1: GetListsListRequestV1 {\n\n    let custom: String\n\n    override var parameters: [String: Any] {\n        var p = super.parameters\n        p[\"custom\"] = custom\n        return p\n    }\n\n    init(custom: String, user: TwitterUserIdentifierV1, reverse: Bool? = .none) {\n        self.custom = custom\n        super.init(user: user, reverse: reverse)\n    }\n}\n```\n\nIt is also possible to create an encapsulated custom request class.\n\n```swift\nclass CapsuledListsListRequestV1: GetListsListRequestV1 {\n    init() {\n        super.init(user: .userID(\"100\"), reverse: true)\n    }\n}\n```\n\n### Low level api\n\nThis method is intended to be used when the library does not yet support Twitter's new API.\n\n- You can customize the request yourself.\n- You can use `session.send(TwitterAPIRequest,completionHandler:)` to send the request.\n\n```swift\n\n    class YourCustomRequest: TwitterAPIRequest {\n        // write code...\n    }\n\n\n    let consumerKey = \"\"\n    let consumerSecret = \"\"\n    let oauthToken = \"\"\n    let oauthTokenSecret = \"\"\n\n    let client = TwitterAPIClient(\n        consumerKey: consumerKey,\n        consumerSecret: consumerSecret,\n        oauthToken: oauthToken,\n        oauthTokenSecret: oauthTokenSecret\n    )\n\n    let request = YourCustomRequest()\n    client.session.send(request)\n}\n```\n\n### Swift Concurrency (experimental)\n\n```swift\nTask {\n    let result = try await client.v1.timeline.getHomeTimeline(.init()).responseData // or responseObject or response responseDecodable(type: Hoge.self)\n\n    print(result.prettyString)\n}\n```\n\n### Stream API\n\n- [Sample code for GET /2/tweets/sample/stream](https://gist.github.com/mironal/bd511a211f6a300b32350b83350894eb)\n- [Sample code for GET /2/tweets/search/stream](https://gist.github.com/mironal/6ad38705ad729c71afec9816abccbfd4)\n\n## TODO\n\n- [ ] Support API v1 endpoint : 85% completed (Commonly used APIs are 100% supported.)\n- [x] Support API v2 endpoint: 100% completed (Except for Lab)\n- [x] Swift Concurrency (Experimental)\n- [ ] Document\n","funding_links":["https://github.com/sponsors/mironal"],"categories":["Libraries"],"sub_categories":["Swift"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmironal%2FTwitterAPIKit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmironal%2FTwitterAPIKit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmironal%2FTwitterAPIKit/lists"}