# Non-SDK Integration Guide for iOS
source: https://developer.mastercard.com/open-finance-us/documentation/connect/integrating/non-sdk/non-sdk-ios/index.md

This page outlines the non-SDK solution and steps needed to load the Data Connect URL in your iOS Mobile App (either inside a WebView or Secure Container).

This documentation also includes Universal Link and deep link configuration, as well as details on customizing the `WebViewClient` and secure container (`SFSafariViewController`) for enhanced control and handling of web interactions.

These are the steps which occur when launching:

1. Launch your [Data Connect URL](https://developer.mastercard.com/open-finance-us/documentation/connect/generate-2-connect-url-apis/index.md) (generated with `isHostedInMobileApp` flag as true and with the `redirectUri` provided) ---either in a web view or secure web container inside your mobile app.

2. After successful FI OAuth account addition, control returns to your app where you dismiss the already opened secure container or web view.

Secure containers are mandatory in some situations (for example, Chase does not allow their consent URL to be launched from within insecure webviews).

Below we provide details of the following:

* **Implementation A** --- Data Connect URL launched in Web View, FI OAuth session opened in Secure Container (`SFSafariViewController`) as we don't support WebViews for OAuth process.

  * Scenario 1: FI app is installed --- the FI app will launch and the OAuth session will occur in the FI's app.

  * Scenario 2: FI app is not installed --- the FI OAuth login page will open in new instance `SFSafariViewController` as a pop up.

* **Implementation B** --- Data Connect URL launched in new separate Secure Container (`SFSafariViewController`), no WebViews are involved.

  * Scenario 1: FI app is installed --- the FI app will launch and the OAuth session will occur in the FI app.

  * Scenario 2: FI app is not installed --- the FI OAuth login page will open in same `SFSafariViewController`.

Tip: In order to support [App To App Authentication](https://developer.mastercard.com/open-finance-us/documentation/connect/integrating/index.md#app-to-app-authentication) make sure you pass a suitable `redirectUri` parameter in your Generating Data Connect URL call.

## iOS project setup {#ios-project-setup}

Make sure you have set up the following in your XCode project.

Under your Project target, select the Signing and Capabilities section:

* Under this section click on **+Capability** to add a new capability, and select **Associated Domains** . Add your domain here. Under the **Signing** section, make sure you have set the development team correctly (or leave the team value as _*None*).

* This will create entitlement file `YourProject.entitlements`:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>com.apple.developer.associated-domains</key>
  <array>
    <string>applinks:*.example.com</string>
  </array>
</dict>
</plist>
```

For further details on app to app setup see here.

## Implementation A (Data Connect URL in WebView) {#implementation-a-data-connect-url-in-webview}

In this implementation, the Data Connect URL (which must be generated with `isHostedInMobileApp` flag as true and `redirectUri` set) is launched in a WebView, then a secure container (`SFSafariViewController`) used for the OAuth popup.

### WebView Initialization {#webview-initialization}

In your view controller, create a `WKWebView` (named `webView` in the following example) to load Data Connect URL (Generated with with `isHostedInMobileApp` flag and `redirectUri` parameters) in it

Then, add setup steps for the `WKWebView` in the `viewDidAppear` function within which `WKPreferences`, `WKWebViewConfiguration` and delegates (`uiDelegate` and `navigationDelegate`) are added to the view controller.

```swift
import UIKit
import WebKit
import SafariServices
import Foundation

class ViewController: UIViewController, SFSafariViewControllerDelegate {
  var webView: WKWebView!

  override func viewDidAppear(_ animated: Bool) {
    setupWebView()
    loadUrlInWebView()
  }

  // Add this function if not added already in basic XCode project installation steps.
  func setupWebView() {
    let preferences = WKPreferences()
    preferences.javaScriptCanOpenWindowsAutomatically = true

    let configuration = WKWebViewConfiguration()
    configuration.preferences = preferences

    webView = WKWebView(frame: view.bounds, configuration: configuration)
    webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    webView.uiDelegate = self
    webView.navigationDelegate = self

    view.addSubview(webView)
}
```

### Loading your Data Connect URL inside the WebView {#loading-your-data-connect-url-inside-the-webview}

Call the `loadUrlInWebView()` function to load your domain URL in the `webView`.

```swift
func loadUrlInWebView(){
  if let url = URL(string: "Generated Data Connect URL with isHostedInMobileApp flag  as true and redirectUri parameters") {
    let urlRequest = URLRequest(url: url)
    webView.load(urlRequest)
  }
}
```

### Handling OAuth Login URL and App to App {#handling-oauth-login-url-and-app-to-app}

Add a `WKUIDelegate` extension which listens for the FI OAuth Event in Data Connect and opens a Secure Container or FI App.

Add a `SafariVIewController` delegate to listen for manual closing of secure container.

```swift
func closeChildWebViewOrPopUp(){
  if(self.presentedViewController != nil){
    self.dismiss(animated: true)
  }
}


extension WKWebContainerViewController: SFSafariViewControllerDelegate {
  public func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
    controller.dismiss(animated: true, completion: nil)
    self.closeChildWebViewOrPopUp()
  }
}

extension ViewController: WKUIDelegate {
  func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    var popupWebView = WKWebView(frame: view.bounds, configuration: configuration)
    popupWebView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    popupWebView!.navigationDelegate = self
    popupWebView!.uiDelegate = self
    popupWebView?.isHidden = true

    if let url = navigationAction.request.url {
      OpenUniversalLink.inAnyNativeWay(url: url) { success in
        // Creation of OpenUniversalLink class is in Step no. 3
        if !success {
          let sfSafariViewController:SFSafariViewController = SFSafariViewController(url:url)
          sfSafariViewController.delegate = self
          self.present(sfSafariViewController, animated: true, completion: nil)
        }
      }
    }
    return popupWebView!
  }
}
```

Add the following code in `SceneDelegate` class for control to close already opened secure container.

```swift
// Scene delegate functions to Close Secure Container / Webview Pop Up

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

  func scene(_ scene: UIScene, continue userActivity: NSUserActivity){
    self.closeSecureContainerPopUp(url: userActivity.webpageURL)
  }

  func closeSecureContainerPopUp(url:URL?) {

    if(shouldClosePopUp(currentURLString: url.absoluteString)){
      var viewController:ViewController = ViewController()

      if ((self.window?.rootViewController?.children.count)! > 0) {
        viewController = self.window?.rootViewController?.children.last as! ViewController
      }
      else if (self.window?.rootViewController is ViewController) {
        viewController = self.window?.rootViewController as! ViewController
      }

      DispatchQueue.main.asyncAfter(deadline: .now() + 0.005) {
        viewController.closeChildWebViewOrPopUp()
      }
    }
  }
}


func shouldClosePopUp(currentURLString: String) -> Bool {
  guard let urlComponent = URLComponents(string: currentURLString) else {
    return false
  }
  let queryItems = urlComponent.queryItems
  if let typeQuery = queryItems?.first(where: { $0.name == "redirectedFrom" }), let value = typeQuery.value{
    if(value == "maOBFIOauth"){
      return true
    }
  }
  return false
}
```

Create a new `OpenUniversalLink` class using the following code. This will open the FI app using the universal link if the app is present on the device.

```swift
// Sample Open link class
import Foundation
import UIKit
import SafariServices
public class OpenUniversalLink {
  typealias completionHandler = (_ success:Bool) -> Void
  static func inAnyNativeWay(url: URL, dontPreferEmbeddedBrowser: Bool = false,completion: @escaping completionHandler) { // OPEN AS UNIVERSAL LINK IF EXISTS else : EMBEDDED or EXTERNAL
    if #available(iOS 10.0, *) {
      // Try to open with owner universal link app
      UIApplication.shared.open(url, options: [UIApplication.OpenExternalURLOptionsKey.universalLinksOnly : true]) { (success) in
        if !success {
          completion(false)
        }
      }
    } else {
      completion(false)
    }
  }
}
```

Dismissing the web view which has opened Data Connect URL:

```swift
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
  if let url = navigationAction.request.url{
    if(url.description.contains("https://example.com/") && self.shouldCloseWebView(currentURLString: url.description)){
      self.navigationController?.popViewController(animated: true)
    }
  }
  decisionHandler(.allow)
}


// Add this function to check when to pop webview loaded with connect url

func shouldCloseWebView(currentURLString: String) -> Bool {
  guard let urlComponent = URLComponents(string: currentURLString) else {
    return false
  }
  let queryItems = urlComponent.queryItems
  if (queryItems?.first(where: { $0.name == "reason" })) != nil{
    return true
  }
  return false
}
```

## Implementation B (Data Connect in Secure Container) {#implementation-b-data-connect-in-secure-container}

In this implementation, the Data Connect URL (which must be generated with `isHostedInMobileApp` flag as true and `redirectUri` set) is launched a secure container (`SFSafariViewController`) and no WebView is used.

First, add a close function and code to launch a Safari View Controller in the existing `ViewController` to help open the FI OAuth login screen (or to open the FI app for OAuth Login):

```swift
class ViewController : UIViewController, SFSafariViewControllerDelegate {

  var sfSafariViewController:SFSafariViewController? = nil

  override func viewDidAppear(_ animated: Bool) {
    launchSafari(url: "Generated Data Connect URL with isHostedInMobielApp and redirectUri parameters")
  }

  func launchSafari(url:String){
    let sfSafariViewController:SFSafariViewController = SFSafariViewController(url: URL.init(string: url)!)
    sfSafariViewController.delegate = self
    self.present(sfSafariViewController, animated: true, completion: nil)
  }

  func closeChildWebViewOrPopUp(){
    if(self.presentedViewController != nil){
      self.dismiss(animated: true)
    }
  }

}
```

### Closing the secure web container {#closing-the-secure-web-container}

Add the following code in the `SceneDelegate` class to handle the closure of the secure web container:

```swift
// Scene delegate functions to close secure web container / webview pop up

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

 func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
   self.closeSecureContainerPopUp(url: userActivity.webpageURL)

 }

 func closeSecureContainerPopUp(url:URL?) {
  if(shouldCloseSecureContainer(currentURLString: url.absoluteString)){
   var viewController:ViewController = ViewController()

   if( (self.window?.rootViewController?.children.count)! > 0) {
     viewController = self.window?.rootViewController?.children.last as! ViewController
     } else if (self.window?.rootViewController is ViewController) {
       viewController = self.window?.rootViewController as! ViewController
     }
     DispatchQueue.main.asyncAfter(deadline: .now() + 0.005) {
       viewController.closeChildWebViewOrPopUp()
     }         
   }
  }
}


func shouldCloseSecureContainer(currentURLString: String) -> Bool {   
  guard let urlComponent = URLComponents(string: currentURLString) else {
    return false
  }
  let queryItems = urlComponent.queryItems
  if (queryItems?.first(where: { $0.name == "reason" })) != nil{
    return true
  }
  return false
}
```

