Handling Links In Rich Content

Responsys SDK provides APIs to get the latest Message Center (MC) messages. The SDK does not enforce UI restrictions on the Message Center UX design. The UI for displaying Message Center is left for the app to implement according to its theme and colors. When a Message Center message has rich content (HTML), the most common way to display the content is by using a WebView component (WKWebView in iOS). WebView provides APIs to intercept all URLs being loaded (or clicked). We will use these APIs to intercept the hyperlinks. The hyperlink itself is encoded, and the actual deep link or web link can be retrieved via this hyperlink. So, we will intercept the hyperlink, make an HTTP/GET request to this URL and get back the actual link.

Implementation

  • Define a WebView component in the message details viewer screen.

    • Import WebKit framework.

      #import <WebKit/WebKit.h> 					
      import WebKit 				
    • Configure WKWebview.

      WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
      WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];					
      let config = WKWebViewConfiguration()
      var webView = WKWebView(frame: UIScreen.main.bounds, configuration: config)			
  • Fetch Rich content of PIOMCMessage 'Message' object by calling following API.

    [[PushIOManager sharedInstance] fetchRichContentForMessage:message.messageID CompletionHandler:^(NSError *error, NSString *messageID, NSString *content) {
      dispatch_async(dispatch_get_main_queue(), ^{
          if (nil == error && content != nil) {
              // save content for future use
              // load Rich content in web view
              [webView loadHTMLString:content baseURL:nil];
          }
    }); }];				
    PushIOManager.sharedInstance().fetchRichContent(forMessage: message.messageID) { error, messageID, content in
      DispatchQueue.main.async {
      if let content = content, error == nil {
              // save content for future use
              // load Rich content in web view
              webview.loadHTMLString(content, baseURL: nil)
          }
      }
    }		

    Your class needs to Conform WKNavigationDelegate protocol.

    @interface classA : UIViewController <WKNavigationDelegate> 					
    class classA: UIViewController,WKNavigationDelegate 		
  • Set the WKWebview navigationDelegate to responding class and implement nativationAction Delegate method.

    webView.navigationDelegate =  self;	
  • Implement the methods of the WKNavigationDelegate protocol, When the user clicks on any link in the webview, webView(_ webView: navigationAction:decisionHandler) will be called.

  • Intercept the URL here and check if it is a Responsys formatted link.

    -(void) webView:(WKWebView *)webView decidePolicyForNavigationAction:(nonnull WKNavigationAction *)navigationAction decisionHandler:(nonnull void (^)(WKNavigationActionPolicy))decisionHandler {
        
      if(navigationAction.navigationType == WKNavigationTypeLinkActivated) {
          
          NSURL *url = navigationAction.request.URL;
          
          if(url != nil && ( [url.path containsString:@"/pub/acc"] || [url.path containsString:@"/pub/pacc"] )) {
              
              // parse the Responsys url by calling following method
              [self processResponsysLink:url];
              
              // cancle the default action of webview, to handle to url by app
              decisionHandler(WKNavigationActionPolicyCancel);
          } else {
            // Allow default action of webview
              decisionHandler(WKNavigationActionPolicyAllow);
          }
      } else {
         // Allow default action of webview
          decisionHandler(WKNavigationActionPolicyAllow);
      }
    }					
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    
        if navigationAction.navigationType == .linkActivated {
            if let url =  navigationAction.request.url,(url.path.contains("/pub/acc") || url.path.contains("/pub/pacc")){
              
              // parse the Responsys url by calling following method
               processResponsysLink(url: url)
    
                // cancle the default action of webview, to handle to url by app
                decisionHandler(WKNavigationActionPolicy.cancel)
                return
            }
            // Allow default action of webview
            decisionHandler(WKNavigationActionPolicy.allow)
        } else {
           // Allow default action of webview
            decisionHandler(WKNavigationActionPolicy.allow)
        }
    }			
  • If the link is a Responsys link, make an HTTP/GET request.

     -(void) processResponsysLink:(NSURL*)url {
      // create a new ephemeral session configuration
      NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
      sessionConfig.timeoutIntervalForRequest = 10;
             
      //set user agent
      sessionConfig.HTTPAdditionalHeaders = @{@"User-Agent":@"ResponsysPubWebResolver (CPU iPhone OS like Mac OS X)"};
             
      NSURLSession* session = [NSURLSession
                                      sessionWithConfiguration:sessionConfig delegate:nil delegateQueue:nil];
     NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
     request.HTTPMethod = @"GET";
             
     // set accept header to get a JSON response
     [request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
             
     // make request to Responsys to track click & resolve the Universa Link
     NSURLSessionDataTask* task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
         
          if (error == nil) {
             // resolve request failure
             NSLog(@"Request to resolve Responsys Universal Link failed: %@", [error localizedDescription]);
              
             NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
             
             if(httpResponse.statusCode >= 200 && httpResponse.statusCode <= 299) {
                           
               // parse resolved JSON
               NSError *parseError = nil;
               NSDictionary *resolvedResponsysUniversalLinkInfo = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
              if (!resolvedResponsysUniversalLinkInfo) {
                     NSLog(@"Couldn't parse resolved Responsys Universal Link info: %@; data = %@", parseError, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
               } else {
                   NSLog(@"Resolved Responsys Universal Link Info: %@", resolvedResponsysUniversalLinkInfo);
                   NSString *webLink = resolvedResponsysUniversalLinkInfo[@"webLinkUrl"];
                   NSString *deeplink = resolvedResponsysUniversalLinkInfo[@"mobileDeepLinkUrl"];
                               
                  // TODO: add your code that would use the resolved Responsys Universal Link info
              }
             }
          } else {
                 // resolve request returned error
                 NSLog(@"Request to resolve Responsys Universal Link returned error: %ld; data = %@",(long)((NSHTTPURLResponse*)response).statusCode, [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
          }
         
     }];
     [task resume];
     [session finishTasksAndInvalidate];
    } 
    func processResponsysLink(url:URL) {
            
      // create a new ephemeral session configuration
      let config  = URLSessionConfiguration.ephemeral;
      config.timeoutIntervalForRequest = 10;
      
      //set user agent
      config.httpAdditionalHeaders = ["User-Agent":"ResponsysPubWebResolver (CPU iPhone OS like Mac OS X)"];
      
      let session = URLSession.init(configuration: config)
      
     var request = URLRequest(url: url,timeoutInterval: Double.infinity)
      
      // set accept header to get a JSON response
     request.addValue("application/json", forHTTPHeaderField: "Accept")
     request.httpMethod = "GET"
          
      // make request to Responsys to track click & resolve the Universa Link
     let task = session.dataTask(with: request) { data, response, error in
         
         if (error == nil) {
             
          if let statucode:Int = (response as? HTTPURLResponse)?.statusCode,statucode >= 200 && statucode <= 209 {
                              
              guard let data = data else {
                print(String(describing: error))
                return
              }
              
              if data.count > 0 {
                  do {
                  if let resolvedResponsysUniversalLinkInfo: [String:Any] = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String:Any] {
                      print("Resolved Responsys Universal Link Info:",resolvedResponsysUniversalLinkInfo)
                      let webLink = resolvedResponsysUniversalLinkInfo["webLinkUrl"]
                      let deeplink = resolvedResponsysUniversalLinkInfo["mobileDeepLinkUrl"]
                      
                  } else {
                      print("Could not parse JSON")
                  }
                  } catch {
                      print(error.localizedDescription)
                  }
              }
              
          }
            
         } else {
             print("Request to resolve Responsys Universal Link returned error:",error?.localizedDescription)
         }
     }
      task.resume()
      session.finishTasksAndInvalidate()
    }