Git Product home page Git Product logo

twift's Introduction

Twift

Twitter API v2 badge Documentation Coverage Run Tests Build Demo App

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

Disambiguating List

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 the getUser endpoint, this contains a User)
  • includes, which includes any expansions you request (e.g. for the getUser endpoint, you can optionally request an expansion on pinnedTweetId; this would result in the includes property containing a Tweet)
  • 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

twift's People

Contributors

daneden avatar emin-grbo avatar hallee avatar qusea avatar tunous avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

twift's Issues

'List' is ambiguous for type lookup in this context

There seems to be a collision for the List type, whenever I use it I get the following error.

Maybe I am missing some simple solution to this?
I know that if there is a name collision with other types you could say Twift.List, but here, all List types seem to be from Twift, but registered as different?

Do advise :) Thank you!

image

Unable to call into polls data

I'm trying to capture Poll data and having issues accessing the options portion. I'm accurately tagging posts with polls, but can't pull in the actual data.

I'm also pulling in poll data in the expansion

expansions: [.authorId(userFields: [\.profileImageUrl, \.verified]), .mediaKeys(mediaFields: [\.id, \.url, \.width, \.height, \.previewImageUrl]), .pollIds(pollFields: [\.id, \.options, \.endDatetime, \.votingStatus])],

Any thoughts?

  /// Contains objects describing each choice in the referenced poll.
  public let options: [Option]

Here's a rough sample of the code I'm just trying to get up and running

               if let pollKeys = tweet.attachments?.pollIds {
                        ForEach(pollKeys, id: \.self) { key in
                            if let media = includes?.polls {
                                ForEach(media){ poll in
                                    if poll.id == key {
                                        ForEach(poll.option, id: \.position) { item in
                                            Text(item.label)
                                        }
                                    }
                                }
                            }

                        }
                    }

Fetching tweets in a list + mediaKeys error

While working on an app I encountered an issue while trying to retrieve URLs for images users posted in a tweet.

Specifically .mediaKeys(mediaFields: [\.url]) expansion in the array is the one that causes the issue.

The response we receive prints and UnknownError which actually contains a model which seems ok?! 🤷‍♂️

The same request without the .mediaKeys value returns 👌.

Below is a method I used to retrieve the list tweets.

    func fetchTwetsInList(listId: String) async -> TwitterAPIDataIncludesAndMeta<[Tweet], Tweet.Includes, Meta>? {
        do {
            let response = try await client?.getListTweets(listId,
                                                           fields: [\.text],
                                                           expansions: [.mediaKeys(mediaFields: [\.url]),
                                                                        .authorId(userFields: [\.profileImageUrl]),
                                                                        .referencedTweetsId],
                                                           paginationToken: nil,
                                                           maxResults: 50)
            return response
        } catch {
            print(error)
        }
        return nil
    }

Note to Contributors: I’m on parental leave!

Hi there! If you came here to contribute to Twift, that’s awesome. I’m excited about this project and it seems like others are too; it definitely has some missing pieces, so contributions are welcome!

I might be slow to respond to new issues and PRs because my wife and I are (at the time of writing) expecting our first child in a few days’ time! Please be patient but keep your contributions and suggestions coming. I’ll be slow to respond for at least April–July 2022.

Add OAuth 2.0 User Authentication Support

The Bookmarks API requires OAuth 2.0 support, and it seems like other APIs will follow suit in the future.

Twift v0 was primarily designed for use with OAuth 1.0a which, in retrospect, was probably a mistake. The library will need to support OAuth 2.0 user authentication—maybe even deprecate 1.0a in order to simplify the library and its usage.

#16 will add OAuth 2.0 user auth support and mark OAuth 1.0a auth as deprecated, maybe even remove it, but I discovered that Twitter's v1.1 API methods (namely the media methods—the only 1.1 methods in this library) don’t support OAuth 2.0 auth at all. So I have a choice:

  1. Only support OAuth 2.0 and wait until Twitter releases v2 media endpoints
  2. Support both OAuth 2.0 and 1.0a which requires more maintenance and probably leads to a confusing engineering experience when using the library since some methods will not support one or the other auth type
  3. Only support OAuth 1.0a and as a result, limit the API endpoints that can be used.

Option (1) above seems the best route forward.

Client ID missing from OAuth2User convenience init?

This in the signature for the convenience init but there is no client id param:

public init(accessToken: String, refreshToken: String? = nil, expiresIn: TimeInterval = 7200, scope: Set<OAuth2Scope>) {

which is needed to refresh the token here:

let clientId = oauthUser.clientId else {

I am using a similar init in my linux fork (Hoping to PR back once i've had time to clean it up) that im maintaining, would be nice to delete this code. Unless I'm missing something?

Missing User/Poll/Geo fields

I have just started fetching some lists, and in this specific example I am trying to fetch tweets from one specific playlist.
The call works, and I do get the tweets, however, to render them I would need the author profile image, which is specified as a field under User.Fields
In the current implementation of this method we can only use Set<Tweet.Field>

This is the method currently on the package:

public func getListTweets(_ listId: List.ID,
                            fields: Set<Tweet.Field>,
                            expansions: [Tweet.Expansions],
                            paginationToken: String? = nil,
                            maxResults: Int = 100

And this is the Twitter API explorer
image

Submitting to appstore requires the ability to report post

I was rejected from the app store since I did not have a way for content to be reported. Just a heads up for other users that maybe using Twift.

Guideline 1.2 - Safety - User Generated Content

We found in our review that your app includes user-generated content but does not have all the required precautions. Apps with user-generated content must take specific steps to moderate content and prevent abusive behavior.

Next Steps

To resolve this issue, please revise your app to implement the following precautions:

  • Require that users agree to terms (EULA) and these terms must make it clear that there is no tolerance for objectionable content or abusive users

  • A mechanism for users to flag objectionable content

Resources

Learn more about our policies for user-generated content in App Store Review Guideline 1.2.

MediaTypes decoding errors

A slight continuation of the previous issue: #23

I managed to fetch "some" tweets but on a regular basis, I am still getting the same decode error.

I used the following method while hitting the breakpoint at decodeOrThrow.

static func decodeOrReport<T: Codable>(data: Data, withExpected model: T) {
        do {
            let _ = try JSONDecoder().decode(T.self , from: data)
        } catch  DecodingError.keyNotFound( let  key,  let  context) {
            print("———— FAILED TO DECODE \n\n\n\n")
            print("could not find key \(key) in JSON: \(context.debugDescription)")
        } catch  DecodingError.valueNotFound( let  type,  let  context) {
            print("———— FAILED TO DECODE \n\n\n\n")
            print("could not find type \(type) in JSON: \(context.debugDescription)")
        } catch  DecodingError.typeMismatch( let  type,  let  context) {
            print("———— FAILED TO DECODE \n\n\n\n")
            print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
        } catch  DecodingError.dataCorrupted( let  context) {
            print("———— FAILED TO DECODE \n\n\n\n")
            print("data found to be corrupted in JSON: \(context.debugDescription)")
        } catch   let  error  as  NSError {
            print("———— FAILED TO DECODE \n\n\n\n")
            print(String(data: data, encoding: String.Encoding.utf8) ??  "NIL")
            print("\n\n\n —————————— ")
            NSLog("Error in read(from:ofType:) domain = \(error.domain), description= \(error.localizedDescription)")
        }
        return
    }

And response/print was:

 DecodingError
   keyNotFound : 2 elements
    - .0 : CodingKeys(stringValue: "mediaKey", intValue: nil)
     .1 : Context
       codingPath : 3 elements
        - 0 : CodingKeys(stringValue: "includes", intValue: nil)
        - 1 : CodingKeys(stringValue: "media", intValue: nil)
         2 : _JSONKey(stringValue: "Index 0", intValue: 0)
          - stringValue : "Index 0"
           intValue : Optional<Int>
            - some : 0
      - debugDescription : "No value associated with key CodingKeys(stringValue: \"mediaKey\", intValue: nil) (\"mediaKey\")."
      - underlyingError : nil

Not sure if this can be the "pain" point? 🤷‍♂️

public typealias ID = String
  
/// Unique identifier of the expanded media content.
public let mediaKey: ID
  
/// A convenience accessor for the `mediaKey` property mapped to the more standard `id` key path
public var id: ID { mediaKey }

Error getting users bookmarks

I have a user that when launching the app post sign in they're unable to get their bookmarks. I can see the error being returned is the following. Any leads on why this would be failing? I added the tweet in question as a bookmark and no issues.

UnknownError(Optional("{\"data\":[{\"text\":\"OK, hear me out, this was actually a visionary and inspired action.\\nhttps://t.co/OeMH8DVg2V\",\"edit_history_tweet_ids\":[\"1581146439098716160\"],\"entities\":{\"urls\":[{\"start\":68,\"end\":91,\"url\":\"https://t.co/OeMH8DVg2V\",\"expanded_url\":\"https://twitter.com/damiengayle/status/1580864210741133312/video/1\",\"display_url\":\"pic.twitter.com/OeMH8DVg2V\",\"media_key\":\"7_1580864119145697282\"}]},\"created_at\":\"2022-10-15T04:54:22.000Z\",\"public_metrics\":{\"retweet_count\":4932,\"reply_count\":1624,\"like_count\":19660,\"quote_count\":1946},\"lang\":\"en\",\"id\":\"1581146439098716160\",\"author_id\":\"881615056437297152\",\"attachments\":{\"media_keys\":[\"7_1580864119145697282\"]},\"source\":\"Twitter Web App\",\"conversation_id\":\"1581146439098716160\",\"possibly_sensitive\":false,\"reply_settings\":\"everyone\"},{\"text\":\"Their apps are non-native and poorly d<…>

Using Previews

Good morning,

Am I missing something here? I am trying to feed a User array into my preview, but for the life of me I cannot get this thing to work. Below is a snippet, the error is "Type Twift has no member User", however this a nearly exact copy of the results received from the api call. Thoughts.

struct FollowingView_Previews: PreviewProvider {
static var previews: some View {
let users: [User] = [Twift.User(id: "1445771133337235470", name: "Oppenheimer", username: "OppenheimerFilm", createdAt: Date(), protected: false, withheld: WithheldInformation, location: "nowhere", pinnedTweetId: "", url: "", description: "", verified: "", entities: "", profileImageUrl: "", publicMetrics: "")]
FollowingView(users: users)
}
}

The is no env.example file

The instructions for this framework say to use env.example as an example for the environment variables, but there is no such file. What I need to know is what the scheme:// looks like to put in the Twitter App Info URL field.

Refresh Token Issue

Not sure if my implementation is not correct or if in the SPM itself, but still having the same refresh token issues 🤷‍♂️
Posting here just in case as these days I am quite in a time-crunch so will surely forget 😅

Issue:
After access token has expired, it either does not refresh properly or new token is not propagated.

User lookup by usernames Error response issue

I am not 100% sure if my setup is wrong, but if one searches for multiple users, ie, the request has an array of parameters....if one of them produces an error, the whole response is nil 🤷‍♂️

Here is the example of such a response on twitter developer portal:
image

@daneden
You can see in the screenshot what is the setup. Basically, find a character that produces an error ("e" is one of them), and send several other strings along with it in a single response.

Refresh token/access possible issue

I have an issue where I am not sure If I am doing anything wrong or if there is an issue with 2.0 token.

When I initiate the app and login by authorizing the user like so:

let oauthUser = try await Twift.Authentication().authenticateUser(
     clientId: Constants.clientID,
     redirectUri: URL(string: Constants.urlScheme)!,
     scope: Set(OAuth2Scope.allCases),
     presentationContextProvider: nil)
client = Twift(.oauth2UserAuth(oauthUser))
UserDefaults.encodeUser(user: oauthUser)
self.oauthUser = oauthUser
completion(true)

Everything works fine (UserDefaults storing is temporary)

But after launching the app for example...few hours later, same build in xcode, I use the path:

if let oauthUser = UserDefaults.decodeUser() {
                client = Twift(.oauth2UserAuth(oauthUser))
                completion(true)

However, the call I make after this, using the said client, fails to get a response other than Unauthorised

When I follow the call, refresh is not used as authToken is valid, and by checking expiresIn myself, I saw that is true.

So, seems that the client is not initiated/authorized then?
The only difference in two flows is that first one uses authenticateUser and second one does not, but I am storing that user properly (I read it before and after storing and it seems 👌)

Here is the call that gets invalid response"
https://api.twitter.com/2/users/me?user.fields=description,profile_image_url
with token:
Bearer akNhMEZxTklrSU1ZV19TVS1qM2FSenRmdmdlWm9OZ0x6dTd6WktzUWlzZmE0OjE2NTQxMTE1MDg1NTE6MTowOmF0OjE

Update:
If I perform the force refresh, everything is fine 🤷‍♂️.
client?.refreshOAuth2AccessToken()
So maybe the Twift(.oauth2UserAuth(oauthUser)) just needs to be async?

Hope I am not talking out my ass here, just typing this as I debug 😂

adding to project

sorry for the beginner question but i dont understand from the readme file how to embed this framework into my xcode project please help

Add Tests

A valid question asked by early contributors: How do we test new methods?

We need to add a testing suite to this project to make contributions easier.

Problem uploading image

Apologies if this has been discussed already, but I couldn't find any issues regarding image upload.

I'm trying the straight demo app, with no changes (except obviously for the Twitter API credentials). After successfully logging in, I'm trying to upload an image. Choosing a photo works. But tapping Upload image yields a problem.

I've traced it to the initializeUpload method in Twift+Media.swift line 120.

This code is throwing an exception

  fileprivate func initializeUpload(data: Data, mimeType: String) async throws -> MediaInitResponse {
    guard case .userAccessTokens(let clientCredentials, let userCredentials) = self.authenticationType else {
      throw TwiftError.WrongAuthenticationType(needs: .userAccessTokens)
    }

Is this an OAuth 1.0/2.0 issue? Tweeting straight text does work, from the same login and app, and the code to simply post a text tweet doesn't have the same check for userAccessTokens.

Thanks, -- Atul

Support for prefersEphemeralWebBrowserSession

iOS 13 introduces the new prefersEphemeralWebBrowserSession property on ASWebAuthenticationSession.

Setting this property to true indicates you don't want to share any cookies or browsing data between this authentication session and the user's normal browser session in Safari. More importantly, to us, it skips the '"App" Wants to use "domain" to Sign In' prompt which many of our users have complained about.

Can you provide an optional on authenticateUser to set this as true or false?

[Feature Request]Multiple accounts

I've been playing around with the ability to add multiple accounts, but I'm not seeing that as a possibility. Am I missing something?

Add/fix media methods

The biggest blocker to general release of Twift is media upload methods. Unfortunately, they're proving extremely difficult to get working.

I’m using the same method to OAuth sign requests as all the other API calls, with the exception that I’m ignoring query and form parameters since the documentation says that’s how media upload POST calls should be signed:

Because the method uses multipart POST, OAuth is handled differently. POST or query string parameters are not used when calculating an OAuth signature basestring or signature. Only the oauth_* parameters are used.

Despite all other API calls working with the signing method, and despite following the above instructions, all my attempts to call the /1.1/media/upload.json?command=INIT&media_type=image/jpeg&total_bytes=123456 endpoint result in the same unhelpful error:

{
  "errors": [
    {
      "code": 32,
      "message": "Could not authenticate you."
    }
  ]
}

I’ve tried everything I can think of:

  • URL percent encoding the query parameters
  • Passing the parameters as the request's HTTP body
  • Doing the above and URL percent encoding them
  • Ignoring the documentation's advice and passing the parameters to the OAuth signing method
  • Deliberately malforming the request method (e.g. request.method = "POOP") to try and elicit a different response

This is my white whale.

Cannot Find .env file for Demo App

I have put the .env file in the root directory of the Demo App, but I am still getting the "No .env file found in...." Any thoughts? Thanks

Flesh out media methods

  • Verify the upload method works with different kinds of media
    • gif
    • png
    • jpeg
    • mp4
    • mov
  • Add method(s) for appending metadata (alt text)
  • Add method(s) for periodically checking the processing status
  • Add documentation!

Refresh token issues for oAuth2

I've been running into an issue with refresh tokens. I have no issues logging in for the user, but I'm finding if I come back several hours later I'm unauthorized and have to initiate the login process all over again.

This is what I'm running at start of the update in my AppDelegate. Do I need to refreshOAuth2AccessToken at initiateLogin?

func initiateLogin(forceReconnect: Bool = false, completion: @escaping (Bool)->Void) {
     Task {
         try await container.client?.refreshOAuth2AccessToken() // I don't know if this will do anything, but I'm trying.
         await loginUser(forceReconnect: forceReconnect, completion: { user in

             if let encoded = try? self.encoder.encode(user) {
                 self.defaults.set(encoded, forKey: ConstantStrings.authKeyChainString)
                 self.initClient(withCredentials: user)
                 completion(true)
             }
       })
     }
   }


     func loginUser(forceReconnect: Bool = false, completion: @escaping (OAuth2User)->Void) async {
     do {
       // flow with existing user
         if let savedUser = defaults.object(forKey: ConstantStrings.authKeyChainString) as? Data {
             if let oauthUser = try? decoder.decode(OAuth2User.self, from: savedUser), !forceReconnect {
                 completion(oauthUser)
             } else {
                 // flow with new user
                 let oauthUser = try await Twift.Authentication().authenticateUser(
                   clientId: TWITTER_clientId,
                   redirectUri: URL(string: TWITTER_CALL_BACKURL)!,
                   scope: Set(OAuth2Scope.allCases),
                   presentationContextProvider: nil)
                 completion(oauthUser)
               }
         }
     } catch {
       print(error.localizedDescription)
     }
   }


   func initClient(withCredentials oauthUser: OAuth2User) {
       container.client = Twift(oauth2User: oauthUser, onTokenRefresh: { newUser in
           if let encoded = try? self.encoder.encode(newUser) {
               self.defaults.set(encoded, forKey: ConstantStrings.authKeyChainString)
           }
       })
   }

[Bug] UI API called on a background thread in Demo App

In the Demo App, an AsyncButton calls

Twift.Authentication().authenticateUser(clientId: String,
                               redirectUri: URL,
                               scope: Set<OAuth2Scope>,
                               presentationContextProvider: ASWebAuthenticationPresentationContextProviding? = nil
  ) async throws -> OAuth2User

when pressed, which results in a UI hang and the following warning:
Main Thread Checker: UI API called on a background thread: -[UIWindow init]

It looks like presentationAnchor(for session: ASWebAuthenticationSession) is being called from a background thread, instead of the main thread.

I'm using similar code in my app and seeing the same issue; does this need to be fixed within Twift or is there a workaround?

Thanks.

Linux Support

Swift Server folks might want to use this package, and most likely will be deploying via linux. Swift linux does not have the Foundation and CryptoKit frameworks. But there are implementations for linux called FoundationNetworking and Crypto which have the same API.

Adding support should be as easy as adding

#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

and

Importing Crypto over CryptoKit

How to go about saving user's credentials?

Hey everyone, curious how you are implementing the saveUserCredentials function for the onTokenRefresh. I am storing the token in keychain, but when I extract the token and place it in a OAuth2User accessToken, I get an "UnAuthorized" from TwitterAPIError. Below is a snippet of what I am doing, please ignore how this is setup as I am just trying to get it to work initially. I can save and read from keychain and it provides me with the token when I print it out, but can't seem to save and authorize a user once they allow the app to use Twitter. Thoughts?

func getFollowing(results: Int){
Task{
do{
let getStoredToken = read(service: "access-token", account: "twitter")!
let storedAccessToken = String(data: getStoredToken, encoding: .utf8)!
let oauthUser = OAuth2User(accessToken: storedAccessToken, clientId: TWITTER_CLIENT_ID, scope: Set(OAuth2Scope.allCases))

            print(storedAccessToken)
            
            if(storedAccessToken.isEmpty){
                let oauthUser = try await Twift.Authentication().authenticateUser(
                  clientId: TWITTER_CLIENT_ID,
                  redirectUri: URL(string: TWITTER_CALLBACK_URL)!,
                  scope: Set(OAuth2Scope.allCases)
                )
                print(oauthUser.accessToken)
            }
            
            var twitterClient = await Twift(oauth2User: oauthUser, onTokenRefresh: save)
          
            save(oauthUser: oauthUser)
            
            let result = try await twitterClient.getMe(fields: allUserFields)
            let following = try await twitterClient.getFollowing((result.data.id), maxResults: results)
            users = following.data
        }   catch{
            print(String(describing: error))
        }
    }
    
}

func save(oauthUser: OAuth2User) {
let data = Data(oauthUser.accessToken.utf8)
let service = "access-token"
let account = "twitter"

    // Create query
    let query = [
        kSecValueData: data,
        kSecClass: kSecClassGenericPassword,
        kSecAttrService: service,
        kSecAttrAccount: account,
    ] as CFDictionary
    
    // Add data in query to keychain
    let status = SecItemAdd(query, nil)
    
    if status == errSecDuplicateItem {
            // Item already exist, thus update it.
            let query = [
                kSecAttrService: service,
                kSecAttrAccount: account,
                kSecClass: kSecClassGenericPassword,
            ] as CFDictionary

            let attributesToUpdate = [kSecValueData: data] as CFDictionary

            // Update existing item
            SecItemUpdate(query, attributesToUpdate)
        }
}

func read(service: String, account: String) -> Data? {
    
    let query = [
        kSecAttrService: service,
        kSecAttrAccount: account,
        kSecClass: kSecClassGenericPassword,
        kSecReturnData: true
    ] as CFDictionary
    
    var result: AnyObject?
    SecItemCopyMatching(query, &result)
    
    return (result as? Data)
}

Authentication always redirects to native Twitter app.

Following scenario:

  1. no user is logged in with the current web browser session.
  2. native Twitter app is installed
  3. TWITTER_CALLBACK_URL is correct set in Developer Portal
  4. same TWITTER_CALLBACK_URL is set in Twift configuration

Result:
After successful authentication native Twitter app gets launched and becomes foreground app.

Any ideas on how to fix this issue ?

Build error

When I build the source code , I find it missed the secrets file in the Twift_SwiftUI floder, and Build Error.
anything else missed? who can tell me?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.