Integration

iOS Integration

Add Reqflow to your iOS app using WKWebView for a native-feeling feedback experience.

Prerequisites

  • iOS 14.0+Minimum deployment target
  • Xcode 14+Latest recommended
  • Reqflow appCreated in the dashboard

UIKit Implementation

Create a view controller that displays the Reqflow feedback page using WKWebView.

FeedbackViewController.swift
import UIKit
import WebKit

class FeedbackViewController: UIViewController {
    private var webView: WKWebView!

    // Your app's slug from the Reqflow dashboard
    private let appId = "your-app-id"

    override func viewDidLoad() {
        super.viewDidLoad()
        title = "Feedback"

        // Configure WebView
        let config = WKWebViewConfiguration()
        webView = WKWebView(frame: view.bounds, configuration: config)
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(webView)

        loadFeedbackPage()
    }

    private func loadFeedbackPage() {
        // Get user info from your auth system
        let userId = getUserId()
        let userName = getUserName()
        let userEmail = getUserEmail()

        var components = URLComponents(
            string: "https://reqflow.com/vote/\(appId)"
        )!

        components.queryItems = [
            URLQueryItem(name: "user_id", value: userId),
            URLQueryItem(name: "user_name", value: userName),
            URLQueryItem(name: "user_email", value: userEmail)
        ]

        if let url = components.url {
            webView.load(URLRequest(url: url))
        }
    }

    private func getUserId() -> String {
        // Use device identifier or your auth system's user ID
        return UIDevice.current.identifierForVendor?.uuidString
            ?? UUID().uuidString
    }

    private func getUserName() -> String {
        return "User Name" // Get from your auth system
    }

    private func getUserEmail() -> String? {
        return nil // Optional: return user's email
    }
}

SwiftUI Implementation

For SwiftUI apps, wrap the WebView in a UIViewRepresentable.

FeedbackView.swift
import SwiftUI
import WebKit

struct FeedbackView: UIViewRepresentable {
    let appId: String
    let userId: String
    let userName: String
    var userEmail: String?

    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        loadFeedbackPage(in: webView)
        return webView
    }

    func updateUIView(_ webView: WKWebView, context: Context) {}

    private func loadFeedbackPage(in webView: WKWebView) {
        var components = URLComponents(
            string: "https://reqflow.com/vote/\(appId)"
        )!

        var queryItems = [
            URLQueryItem(name: "user_id", value: userId),
            URLQueryItem(name: "user_name", value: userName)
        ]

        if let email = userEmail {
            queryItems.append(URLQueryItem(name: "user_email", value: email))
        }

        components.queryItems = queryItems

        if let url = components.url {
            webView.load(URLRequest(url: url))
        }
    }
}

// Usage
struct ContentView: View {
    var body: some View {
        NavigationView {
            FeedbackView(
                appId: "your-app-id",
                userId: UIDevice.current.identifierForVendor?.uuidString ?? "",
                userName: "John Doe",
                userEmail: "john@example.com"
            )
            .navigationTitle("Feedback")
        }
    }
}

Opening the Feedback Screen

Add a button in your app settings or menu to present the feedback screen.

// Present modally from any view controller
@IBAction func feedbackButtonTapped(_ sender: Any) {
    let feedbackVC = FeedbackViewController()
    let navController = UINavigationController(rootViewController: feedbackVC)

    feedbackVC.navigationItem.rightBarButtonItem = UIBarButtonItem(
        barButtonSystemItem: .done,
        target: self,
        action: #selector(dismissFeedback)
    )

    present(navController, animated: true)
}

@objc func dismissFeedback() {
    dismiss(animated: true)
}

Consistent User ID

Always use identifierForVendor or your auth system's user ID. Don't generate a new UUID each time, or users will lose their votes.

Secure Authentication (Optional)

For enhanced security, you can add HMAC signature verification to prevent user impersonation. The signature should be generated by your backend and passed to the app.

SignatureHelper.swift
import CryptoKit

/// Generate HMAC signature for secure authentication (optional)
func generateSignature(userId: String, secret: String) -> (signature: String, timestamp: String) {
    let timestamp = String(Int(Date().timeIntervalSince1970))
    let payload = "\(userId):\(timestamp)"

    let key = SymmetricKey(data: Data(secret.utf8))
    let signature = HMAC<SHA256>.authenticationCode(
        for: Data(payload.utf8),
        using: key
    )
    let signatureHex = signature.map { String(format: "%02x", $0) }.joined()

    return (signatureHex, timestamp)
}

// Usage with WebView
func loadFeedbackPageWithSignature() {
    let userId = getUserId()
    let secret = "your-secret-from-backend" // Get from your backend, not hardcoded

    let (signature, timestamp) = generateSignature(userId: userId, secret: secret)

    var components = URLComponents(string: "https://reqflow.com/vote/\(appId)")!
    components.queryItems = [
        URLQueryItem(name: "user_id", value: userId),
        URLQueryItem(name: "user_name", value: getUserName()),
        URLQueryItem(name: "signature", value: signature),
        URLQueryItem(name: "timestamp", value: timestamp)
    ]

    if let url = components.url {
        webView.load(URLRequest(url: url))
    }
}

Security Note

Don't hardcode the secret in your app. Fetch it from your backend or use iOS Keychain for secure storage. The signature feature is optional and only needed if you enable it on your Reqflow server.

Best Practices

  • Loading StateAdd a UIActivityIndicatorView while the page loads
  • Error HandlingImplement WKNavigationDelegate to handle network errors
  • Dark ModeReqflow automatically adapts to the system appearance
  • Safe AreaThe WebView respects safe area insets automatically