Skip to content

iOS and Android Integration

Open the Canviq feedback board natively in your app using SFSafariViewController (iOS) or Chrome Custom Tabs (Android). Users get the full Canviq experience — including magic link auth — without leaving your app's navigation.


Why In-App Browser?

Unlike a plain WKWebView, SFSafariViewController and Chrome Custom Tabs:

  • Share the Safari/Chrome cookie jar, so Canviq sessions persist between app launches
  • Are trusted by iOS/Android for magic link email flows (unlike WebView)
  • Require zero additional backend code for basic use

Basic Setup

iOS (Swift)

import SafariServices

func openFeedbackBoard() {
    let url = URL(string: "https://\(orgSlug).canviq.app/en")!
    let safari = SFSafariViewController(url: url)
    safari.preferredBarTintColor = UIColor(named: "AppBackground")
    safari.preferredControlTintColor = UIColor(named: "AccentColor")
    present(safari, animated: true)
}

Android (Kotlin)

import androidx.browser.customtabs.CustomTabsIntent

fun openFeedbackBoard() {
    val url = Uri.parse("https://$orgSlug.canviq.app/en")
    val intent = CustomTabsIntent.Builder()
        .setShowTitle(true)
        .setDefaultColorSchemeParams(
            CustomTabColorSchemeParams.Builder()
                .setToolbarColor(ContextCompat.getColor(this, R.color.app_background))
                .build()
        )
        .build()
    intent.launchUrl(this, url)
}

Post-Auth Deep Link (Universal Links / App Links)

After a user authenticates via magic link, Canviq can redirect them back into your native app. This requires two things:

  1. Your app handles Universal Links (iOS) or App Links (Android)
  2. Canviq has your domain registered as a trusted redirect origin

Step 1 — Register Your Trusted Redirect Origin

Go to Settings → General → Trusted Redirect Origins and add your app's domain:

https://yourapp.com

!!! info "Currently manual" The Settings UI for trusted redirect origins is coming soon. Until it ships, contact [email protected] to register your domain.

You can also register via API once you have an org API key with org:write scope:

curl -X PATCH https://canviq.app/api/organizations/{org_id} \
  -H "Authorization: Bearer $CANVIQ_API_KEY" \
  -d '{
    "trusted_redirect_origins": ["https://yourapp.com"]
  }'

In your apple-app-site-association file (served at https://yourapp.com/.well-known/apple-app-site-association):

{
  "applinks": {
    "details": [
      {
        "appIDs": ["TEAMID.com.yourcompany.yourapp"],
        "components": [
          { "/": "/feedback*", "comment": "Canviq post-auth redirect" }
        ]
      }
    ]
  }
}

Enable Associated Domains in your Xcode project:

applinks:yourapp.com

In your assetlinks.json file (served at https://yourapp.com/.well-known/assetlinks.json):

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.yourapp",
      "sha256_cert_fingerprints": ["AB:CD:EF:..."]
    }
  }
]

Enable in your AndroidManifest.xml:

<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="https" android:host="yourapp.com" android:pathPrefix="/feedback" />
</intent-filter>

Step 4 — Pass the Redirect Target When Opening

When you open Canviq in the in-app browser, include the destination your app should receive:

func openFeedbackBoard(destination: String = "/feedback") {
    // URL-encode the destination
    let encoded = destination.addingPercentEncoding(
        withAllowedCharacters: .urlQueryAllowed
    ) ?? ""
    let url = URL(string: "https://\(orgSlug).canviq.app/en?redirect=\(encoded)")!
    let safari = SFSafariViewController(url: url)
    present(safari, animated: true)
}

// Usage: open board, after auth redirect to /feedback?screen=submit
openFeedbackBoard(destination: "https://yourapp.com/feedback?screen=submit")
fun openFeedbackBoard(destination: String = "https://yourapp.com/feedback") {
    val encoded = Uri.encode(destination)
    val url = Uri.parse("https://$orgSlug.canviq.app/en?redirect=$encoded")
    val intent = CustomTabsIntent.Builder().build()
    intent.launchUrl(this, url)
}

After the user authenticates, Canviq redirects to https://yourapp.com/feedback?screen=submit. iOS/Android intercepts it, opens your app, and you can re-open the Canviq board in SFSafariViewController — the user is already authenticated.


Handling the Incoming URL in Your App

iOS

// In SceneDelegate or AppDelegate
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url,
          url.host == "yourapp.com",
          url.path.hasPrefix("/feedback") else { return }

    // Parse the screen parameter and re-open Canviq
    let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
    let screen = components?.queryItems?.first(where: { $0.name == "screen" })?.value

    openFeedbackBoard(destination: screen == "submit"
        ? "https://yourapp.com/feedback?screen=submit"
        : "https://yourapp.com/feedback"
    )
}

Android

// In the Activity that handles the intent
override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    val uri = intent?.data ?: return
    if (uri.host == "yourapp.com" && uri.path?.startsWith("/feedback") == true) {
        val screen = uri.getQueryParameter("screen")
        openFeedbackBoard(
            destination = if (screen == "submit") "https://yourapp.com/feedback?screen=submit"
                          else "https://yourapp.com/feedback"
        )
    }
}

Troubleshooting

Symptom Likely cause Fix
Universal Link not opening app apple-app-site-association not served correctly Verify file is served at /.well-known/apple-app-site-association with Content-Type: application/json
Redirect loop after auth Your domain not in trusted redirect origins Register it in Settings → General or contact support
User not authenticated in SFSafariViewController WebView used instead of SFSafariViewController Cookies are not shared with WebView — use SFSafariViewController only
Magic link not arriving Email client opens link in default browser, not SFSafariViewController This is expected — user will be authenticated in Safari, not in-app. See note below

!!! note "Email clients and in-app browsers" Magic links opened in a default email app open in the system browser (Safari/Chrome), not in your in-app SFSafariViewController. If the user then opens your app, the in-app browser starts a new session. The Universal Link redirect pattern above mitigates this — Canviq sends the user back to your app after auth regardless of which browser they used.