Twitter Login with SwiftUI

Aubree Quiroz
8 min readDec 27, 2019

--

The tutorial was written using Swift 5, iOS 13.2

Today I am going to take you through creating the Login with Twitter functionality in SwiftUI. This tutorial also goes through the nit and grit of making OAuth 1.0 requests to the Twitter API using plain old swift. While it may be inadvisable to write OAuth requests without a helper library, I figure there must be other curious minds who want to take a wack at implementing the basics themselves.

The final project will consist of a simple login to the Twitter button that starts the OAuth 1.0 authentication flow and completes by returning credentials from the Twitter API. Please note that this tutorial assumes you understand the basics of swift and how to create a new SwiftUI project.

Get Twitter API Credentials

For the purposes of this tutorial, you will need a Twitter Consumer API Key and Consumer API Key Secret. Go to https://developer.twitter.com and create an account. Once you have created an account, open your Apps tab and “Create an App”. Fill in all the required fields and ensure “Sign in with Twitter” is enabled.

Configuring the Callback URL

Part of our twitter app configuration and xCode configuration will require a common callback URL scheme. For the purposes of this tutorial, I will be using “twittersdk”. This, however, can be any arbitrary string of your choosing (be sure to avoid using a scheme reserved for app-links like “twitter”). First, configure this in your twitter app’s configuration by adding it as a valid callback URL as shown below:

Once you have completed filling out the Twitter App settings, you will be redirected to a summary of the App details. Navigate to the tab “Keys and Tokens” and be sure to take note of the provided consumer key & consumer secret.

Next, you will want to add it to your Xcode configuration. To do so:

  • Open you Project Settings
  • Select your target in the left column (under “TARGETS”)
  • Open the “Info Tab”
  • Go down to section “URL Types” and expand the section
  • Click (+) to create a new entry
  • In the new entry, enter “twittersdk” for the field labeled “URL Schemes”. The remaining fields can remain blank or as their default values.

Going forward, any URL requests made to a URL with the prefix “twittersdk” will be recognized by our app and handled in our SceneDelegate.

Now that we have our configuration complete, let’s include our consumer key, consumer secret, and callback URL scheme as global variables in our app. Create a new file named TwitterService.swift and paste the following variables at the top (replacing your own consumer key and secret). For simplicity we will store our keys here, however, you should store these more securely for production use at a later point.

Handling our Callback URL

Upon user authentication, our web view will be redirected to our callback URL. We can pick up any URL’s opened containing our callback URL scheme by implementing the following function in our SceneDelegate.

We want to post a notification containing the URL each time our callback URL is detected. So we are going to create an easily accessible notification name that we can post to each time. Add the following to TwitterService.swift.

Finally, we will create a helper function for handling URL’s in our SceneDelegate. The function will verify that the URL detected has a scheme matching our twitter callback scheme “twittersdk”.

If the URL scheme matches, we will post a notification (named twitterCallback) containing the URL as the notification object. We call this function in the SceneDelegate(_ scene:, openURLContexts:) method. Later we will create an observer to listen for this post and subsequently parse the URL.

Safari Web View

Prior to beginning our API Requests, we need to build a SwiftUI compatible SFSafariViewController that appears upon being fed our login URL.

To do this, we will enclose the SFSafariViewController in a UIViewController that removes and adds a fresh SFSafariViewController each time a new URL is fed. Create a new file named SafariView.swift and paste the following content.

Now, we need to make our CustomSafariViewController usable in SwiftUI. We will do this using UIViewControllerRepresentable. Add the following content to our SafariView.swift file.

Now we have a SwiftUI SafariView component ready for use in our ContentView. We will return to this once our API logic is complete.

App Data Flow

Let’s create an observable object to house our changes in user credentials and prompt our login window. We will add all our API logic to this class and bind it to our ContentView. Add the following to TwitterService.swift.

To start with we are going to add 3 properties:

  1. authUrl (URL?): This will be the URL loaded into our SafariView for User Login (Step 2)
  2. showSheet (Bool): This will prompt the sheet containing our SafariView to display or hide
  3. callbackObserver (Any?): This is where we will save the observer which we listen for our notifications from scene delegate

Note that @Published notation ensures our bindings in our content view will subscribe to changes. Read more about Combine to learn more about observables in SwiftUI.

Before we get started adding our API Requests, I want to quickly outline the authorization flow logic.

OAuth 1.0 Login Flow Overview

Step 1: Request a Temporary Token

To kick off our authorization flow, we need an OAuth Token in order to build an authorization URL for the user to login at. Thus, we will start off by requesting one from the “oauth/request_token” endpoint. Upon receiving the OAuth token, we can construct the authorization URL needed for User Login in Step 2.

Step 2: User Twitter Login

Using the OAuth Token from the previous step, we can now construct an authorization URL to load in our SafariView:

https://api.twitter.com/oauth/authorize?oauth_token=<TOKEN>

At this point, we will open a sheet containing our SafariView to prompt user authentication. Once the user has authenticated, the web view will re-route to our callback URL & exit. Appended to our callback URL will be an OAuth Verifier (which we will need to parse out for the next step). We will capture the verifier in our Scene Delegate (where the app will be listening for any URL’s opened containing our URL Scheme). We will post a notification containing the callback URL (and OAuth Verifier) unparsed.

Step 3: Request an Access Token

Once the notification is received from step 2, we will parse out the OAuth Verifier. Using the OAuth tokens from step 1 and the verifier from step 2, we can request an access token by posting to the “oauth/access_token” endpoint. Upon successful completion of this request, we will have our user id, screen name, access token, and access token secret.

Note that the requested OAuth token and token secret in Step 1 are temporary credentials whereas the OAuth token and token secret in Step 3 are our access token and access secret.

Twitter API Requests

In order to build a twitter API request, we need to sign it with an OAuth signature. If you’re like me I prefer to look at the code. So to get started, copy down these helper functions that take care of the more tedious aspects of the API requests.

The most commonly used extension we will be using URL encodes string values. This will be heavily used when signing requests. Add the following at the end of TwitterService.swift.

Frequently we will need to break apart URL Query parameters into more usable values, i.e. transforming something like:

“param1=value1&param2=value2”

into a dictionary →

[ “param1”: “value1”, “param2”: “value2”]

The below extension that will help us with doing just that. You can paste this just below the previous helper in TwitterService.swift.

Building an Authorization Header

An authorization header is one of the key pieces that will be required in our API requests. Twitter API requires that our request parameters be formatted into a single string like such:

OAuth key1="value1", key2="value2"..., keyN="valueN”

Additionally, the API requires that:

  • Parameter keys and values are each URL encoded
  • Parameters are sorted ascending by the URL encoded key
  • Parameters are joined as a string delimited by “, “
  • Parameter string is preceded by “OAuth “

Add the following function to our TwitterService class in TwitterService.swift.

Building an OAuth Signature

An OAuth Signature will also be required in every API request. The below functions split out each step in building a signature. Add each to our TwitterService class in TwitterService.swift.

Step 1: Calculate a Signing Key

  • Join the Consumer Secret (URL Encoded) + “&” + OAuth Token Secret (URL Encoded)
  • If the OAuth Token Secret is unavailable (as it will be in our first request to the request_token endpoint) just return the Consumer Secret (URL Encoded) followed by an “&”

Step 2: Calculate the Signature Base String

First, we need to build a signature parameter string that joins each of our parameters as a string formatted such that:

key1=value1&key2=value2...&keyN=valueN

Where:

  • Parameter keys and values are each URL encoded
  • Parameters are sorted ascending by the URL encoded key
  • Sorted parameters are joined as a string delimited by “&”
  • The below function will create the needed parameter string

Finally, we need to join our parameter string to our request URL and HTTP Method where each URL encoded and are joined with “&”. The below function will handle building that string:

Step 3: Combining Signature Components to get our OAuth Signature

The final step to building our OAuth signature is passing the signature base string and signing key to the HMAC-SHA1 hashing algorithm. Below is a helper function to preform that algorithm. You can add the following functions to our TwitterService class in TwitterService.swift.

By incorporating the above function we can complete our OAuth signature:

Now that we have all the required Helper functions, we can easily build API Requests. Let’s build our first request: Requesting a temporary token from the “oauth/request_token” endpoint. Add the following code to our TwitterService.

The next HTTP request we will need is to the “oauth/access_token” endpoint. From this endpoint, we complete our user’s authorization by retrieving their user ID and screen name in addition to an access token and access token secret. Add the following to our TwitterService.

Combining all the Steps & Completing our TwitterService

Lastly, let’s add a property to our service to save the access token response and a single function to launch the authorization process when we click login.

Bringing it all together

Now let’s update our content view to include our TwitterService and some controls to test out the functionality.

Don’t forget to include our TwitterService object in our SceneDelegate initialization!!

And that pretty much sums it up!

--

--