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
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)
}To be completed
You can find the full documentation in this repo's Wiki (auto-generated by SwiftDoc).
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.ListTwift's methods generally return TwitterAPI[...] objects containing up to four properties:
data, which contains the main object(s) you requested (e.g. for thegetUserendpoint, this contains aUser)includes, which includes any expansions you request (e.g. for thegetUserendpoint, you can optionally request an expansion onpinnedTweetId; this would result in theincludesproperty 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.
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