API Keys

This guide will cover setting up the Parra iOS SDK to allow you to start gathering user feedback from within your iOS app. This is done with four steps:

  1. Prepare your backend
  2. Install the SDK
  3. Authenticate your users
  4. Integrate Parra Feedback Module
  5. Customizing the Parra Feedback View (Optional)
  6. Best Practices

Prepare Your Backend

Before you are ready to integrate the Parra Feedback iOS SDK, you will need to make some server side changes to authenticate your users with the Parra API.

  1. Create an API key if you do not already have one. An API key is required to make authenticated requests to the Parra API.

  2. Set up your backend to provide access tokens to your iOS client. These access tokens will be used by the Parra Feedback iOS SDK to securely authenticate your users, without exposing your API key. You should never embed your API Key or Secret in your iOS app.

If you have not already configured your backend to issue access tokens for your users, jump over to our backend guides to do so.

Installation

The Parra Feedback SDK and Parra Core modules require a deployment target of iOS 13 or above and can be installed with Cocoapods.

  1. Make sure that you have a recent version of Cocoapods installed.
  2. If you were not already using Cocoapods, you will need to run pod init from within the same directory as your .xcodeproj file.

  3. Add the ParraFeedback Pod under your app's target in your Podfile by adding pod 'ParraFeedback', '~> 1.1.0'. If you are not exactly clear on where to put this, see the Podfile documentation. Check the Releases page on GitHub to be sure you are installing the latest version.

  4. Install the ParraFeedback Pod into your project by running pod install

  5. Make sure to open the .xcworkspace file instead of the .xcodeproj file from now on.

Authentication

In order to authenticate your users with the Parra API you must configure your backend to provide you with an access token signed by your API Key for consumption by the Parra SDK when requested. After your backend is configured to create and sign these access tokens, you will need to implement one of the available Parra.setAuthenticationProvider overloads to retrieve access tokens on behalf of your users whenever authentication is required within the Parra API. This will happen when an access token for a user has not yet been provided and when a user's access token expires. If you need to switch between multiple users, it is important that you use Parra.logout() to clear the cached access token if your user logs out. This will prevent data collected from the previous user from being attributed to the new user and trigger a refresh of the access token.

import UIKit
import ParraCore

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
              didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  let myAppAccessToken = "9B5CDA6B-7538-4A2A-9611-7308D56DFFA1"

  Parra.setAuthenticationProvider {(completion: @escaping(Result<ParraCredential, Error>) -> Void) in
    // Replace this with your Parra access token generation endpoint
    let url = URL(string: "http://localhost:8080/v1/parra/auth/token")!
    var request = URLRequest(url: url)

    request.httpMethod = "POST"
    
    // Replace this with your app's way of authenticating users
    request.setValue("Bearer \\(myAppAccessToken)", forHTTPHeaderField: "Authorization")

    URLSession.shared.dataTask(with: request) { data, _, error in
      if let error = error {
        completion(.failure(error))
      } else {
        do {
          let response = try JSONDecoder().decode([String: String].self, from: data!)

          completion(.success(ParraCredential(token: response["access_token"]!)))
        } catch let error {
          completion(.failure(error))
        }
      }
    }.resume()
  }

  return true
}

// ...
}

Authentication with Public Auth

If your application does not have a backend, you may use public auth to authenticate directly from your app. This approach is not recommended.

import UIKit
import ParraCore

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
  func application(_ application: UIApplication,
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

      let tenantId = "<tenant-id>" // TODO: - Replace me
      let publicApiKeyId = "<public-api-key-id>" // TODO: - Replace me
      let baseUrl = URL(string: "https://api.parra.io")!
      let parraPublicAuthTokenUrl = URL(string: "v1/tenants/\\(tenantId)/issuers/public/auth/token", relativeTo: baseUrl)!

      Parra.setAuthenticationProvider { (completion: @escaping (Result<ParraCredential, Error>) -> Void) in
          var request = URLRequest(url: parraPublicAuthTokenUrl)

          let json: [String: Any] = ["user_id": "cool-user-1"]  // TODO: - Replace me with the id of the user in your system
          let jsonData = try! JSONSerialization.data(withJSONObject: json)
          let authData = ("api_key:" + publicApiKeyId).data(using: .utf8)!.base64EncodedString()
          
          request.httpMethod = "POST"
          request.httpBody = jsonData
          request.setValue("Basic \\(authData)", forHTTPHeaderField: "Authorization")
          
          URLSession.shared.dataTask(with: request) { data, _, error in
              if let error = error {
                  completion(.failure(error))
              } else {
                  do {
                      let response = try JSONDecoder().decode([String: String].self, from: data!)
                      
                      completion(.success(ParraCredential(token: response["access_token"]!)))
                  } catch let error {
                      completion(.failure(error))
                  }
              }
          }.resume()
      }
              
      return true
  }

  // ...
}

Integration

Now that your users are authenticated, you are ready to request cards and present them in your app. The Parra Feedback iOS SDK gives you the flexibility to fetch cards when it is convenient and display the feedback view where it makes sense. To fetch the cards, simple call ParraFeedback.fetchFeedbackCards and in the completion handler,

ParraFeedback.fetchFeedbackCards { result in
switch result {
  case .success(let cards):
    // Store the cards
  case .failure(let error):
    print(error)
}
}

Once you have the cards, you must pass them into a Parra feedback view. You may add it directly to a UIView or use wrappers around it for both UITableView and UICollectionView implementations. You can find sample implementations in the Demo project

Present Parra Feedback View

import UIKit
import ParraFeedback

class ViewController: UIViewController {
override func viewDidLoad() {
  super.viewDidLoad()

  ParraFeedback.fetchFeedbackCards {result in
    switch result {
      case .success(let cards):
        let feedbackView = ParraFeedbackView(cardItems: cards, config: .default)
        self.view.addSubview(feedbackView)
      case .failure(let error):
        print(error)
      }
    }
}
// ...
}

When you run your app, you should now see any questions you have created in the Parra dashboard. If you have not yet created any questions, head over to the Dashboard to do so.

Present Parra Feedback View in a UITableView

First, register your cell in viewDidLoad(_:).

tableView.register(ParraFeedbackTableViewCell.self, forCellReuseIdentifier: ParraFeedbackTableViewCell.defaultCellId)

Next, dequeue your cell.

override func tableView(_ tableView: UITableView,
                  cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == Constant.indexToDisplayParraFeedback {
  let parraCell = tableView.dequeueReusableCell(withIdentifier: ParraFeedbackTableViewCell.defaultCellId, for: indexPath) as! ParraFeedbackTableViewCell

  parraCell.config = .default
  parraCell.delegate = self

  return parraCell
}

// ...your normal cells
return cell
}

Finally, call endDisplaying on the cell when you are done displaying it to trigger a sync of any answers.

override func tableView(_ tableView: UITableView,
                      didEndDisplaying cell: UITableViewCell,
                      forRowAt indexPath: IndexPath) {
  if let parraCell = cell as? ParraFeedbackTableViewCell {
      parraCell.endDisplaying()
  }
}

Present Parra Feedback View in a UICollectionView

First, register your cell in viewDidLoad(_:).

collectionView.register(ParraFeedbackCollectionViewCell.self, forCellWithReuseIdentifier: ParraFeedbackCollectionViewCell.defaultCellId)

Next, dequeue your cell.

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == Constant.indexToDisplayParraFeedback {
  let parraCell = collectionView.dequeueReusableCell(
    withReuseIdentifier: ParraFeedbackCollectionViewCell.defaultCellId,
    for: indexPath
  ) as! ParraFeedbackCollectionViewCell

  parraCell.config = .default
  parraCell.delegate = self

  return parraCell
}

// ...your other cells
return cell
}

Finally, call endDisplaying on the cell when you are done displaying it to trigger a sync of any answers.

override func collectionView(_ collectionView: UICollectionView,
                            didEndDisplaying cell: UICollectionViewCell,
                            forItemAt indexPath: IndexPath) {
  if let parraCell = cell as? ParraFeedbackCollectionViewCell {
      parraCell.endDisplaying()
  }
}

Customization

Parra Feedback views are built to be customizable to match the look and feel of your app. To facilitate this, the Parra Feedback SDK provides extensive options to customize properties like background and tint colors, content insets, corner radius and many other properties.

Applying Configurations

Customization of the ParraFeedbackView is done by either setting the config parameter when initializing the view, or setting its config instance variable. config takes an instance of ParraFeedbackViewConfig, which allows you to customize virtually every aspect of the feedback view.

feedbackView.config = ParraFeedbackViewConfig(
backgroundColor: .white,
tintColor: .systemBlue,
cornerRadius: 12,
contentInsets: UIEdgeInsets(top: 12, left: 6, bottom: 12, right: 6),
shadow: .default,
title: .titleDefault,
subtitle: .subtitleDefault,
body: ParraTextConfig(
  color: .red,
  font: .boldSystemFont(ofSize: 14.0),
  shadow: .default
)
)

ParraFeedbackViewConfig also provides a set of sensible defaults for light and dark themes that you can use as a starting point.

feedbackView.config = .default
feedbackView.config = .defaultDark

Best Practices

Best practices provide guidelines for better integration but your specific integration may differ.

  1. Fetch card in parallel with any other data you are attempting to display

  2. Only show the Parra Feedback view when their are cards available

Choose your client

Before making your first API request, you need to pick which API client you will use. In addition to good ol' cURL HTTP requests, Parra offers clients for JavaScript, Python, and PHP. In the following example, you can see how to install each client.

# cURL is most likely already installed on your machine
curl --version

Making your first API request

After picking your preferred client, you are ready to make your first call to the Parra API. Below, you can see how to send a GET request to the Conversations endpoint to get a list of all your conversations. In the cURL example, results are limited to ten conversations, the default page length for each client.

GET
/v1/conversations
curl -G https://api.parra.io/v1/conversations \
-H "Authorization: Bearer {token}" \
-d limit=10

What's next?

Great, you're now set up with an API client and have made your first request to the API. Here are a few links that might be handy as you venture further into the Parra API:

Was this page helpful?