Twift
Twift is an asynchronous Swift library for the Twitter v2 API.
- No external dependencies
- Fully async
- Full Swift type definitions/wrappers around Twitter's API objects
Quick Start Guide
New Twift
instances must be initiated with either OAuth 2.0 user authentication or an App-Only Bearer Token:
// OAuth 2.0 User Authentication
let oauthUser: OAuth2User = OAUTH2_USER
let userAuthenticatedClient = Twift(oauth2User: oauthUser, onTokenRefresh: saveUserCredentials)
// App-Only Bearer Token
let appOnlyClient = Twift(appOnlyBearerToken: BEARER_TOKEN)
You can authenticate users with Twift.Authentication().authenticateUser()
:
var client: Twift?
do {
let oauthUser = try await Twift.Authentication().authenticateUser(
clientId: TWITTER_CLIENT_ID,
redirectUri: URL(string: TWITTER_CALLBACK_URL)!,
scope: Set(OAuth2Scope.allCases)
)
client = Twift(oauth2User: oauthUser, onTokenRefresh: saveUserCredentials)
// It's recommended that you store your user auth tokens via Keychain or another secure storage method.
// OAuth2User can be encoded to a data object for storage and later retrieval.
saveUserCredentials(oauthUser) // Saves the data to Keychain, for example
} catch {
print(error.localizedDescription)
}
Once initiated, you can begin calling methods appropriate for the authentication type:
do {
// User objects always return id, name, and username properties,
// but additional properties can be requested by passing a `fields` parameter
let authenticatedUser = try await userAuthenticatedClient.getMe(fields: [\.profilePhotoUrl, \.description])
// Non-standard properties are optional and require unwrapping
if let description = authenticatedUser.description {
print(description)
}
} catch {
print(error.localizedDescription)
}
Posting Tweets supports text and polls. Media methods are OAuth 1.0a only and the API may change significantly when Twitter's v2 API adds new media endpoints.
do {
let textOnlyTweet = MutableTweet(text: "This is a test Tweet")
try await twitterClient.postTweet(textOnlyTweet)
let poll = try MutablePoll(options: ["Soft g", "Hard g"])
let tweetWithPoll = MutableTweet(text: "How do you pronounce 'gif'?", poll: poll)
try await twitterClient.postTweet(tweetWithPoll)
} catch {
print(error)
}
Requirements
To be completed
Documentation
You can find the full documentation in this repo's Wiki (auto-generated by SwiftDoc).
Quick Tips
List
Disambiguating If you use Twift with SwiftUI, sooner or later you might run into the problem of needing to disambiguate Twift.List
from SwiftUI.List
. It is recommended that you assign a typealias in a file that doesn't import SwiftUI to disambiguate between the types:
import struct Twift.List
typealias TwitterList = Twift.List
Typical Method Return Types
Twift's methods generally return TwitterAPI[...]
objects containing up to four properties:
data
, which contains the main object(s) you requested (e.g. for thegetUser
endpoint, this contains aUser
)includes
, which includes any expansions you request (e.g. for thegetUser
endpoint, you can optionally request an expansion onpinnedTweetId
; this would result in theincludes
property containing aTweet
)meta
, which includes information about pagination (such as next/previous page tokens and result counts)errors
, which includes an array of non-failing errors
All of the methods are throwing, and will throw either a TwiftError
, indicating a problem related to the Twift library (such as incorrect parameters passed to a method) or a TwitterAPIError
, indicating a problem sent from Twitter's API as a response to the request.
Fields and Expansions
Many of Twift's methods accept two optional parameters: fields
and expansions
. These parameters allow you to request additional fields
(properties) on requested objects, as well as expansions
on associated objects. For example:
// Returns the currently-authenticated user
let response = try? await userAuthenticatedClient.getMe(
// Asks for additional fields: the profile image URL, and the user's description/bio
fields: [\.profileImageUrl, \.description],
// Asks for expansions on associated fields; in this case, the pinned Tweet ID.
// This will result in a Tweet on the returned `TwitterAPIDataAndIncludes.includes`
expansions: [
// Asks for additional fields on the Tweet: the Tweet's timestamp, and public metrics (likes, retweets, and replies)
.pinnedTweetId([
\.createdAt,
\.publicMetrics
])
]
)
// The user object
let me = response?.data
// The user's pinned Tweet
let tweet = response?.includes?.tweets.first