{
    "componentChunkName": "component---node-modules-lekoarts-gatsby-theme-minimal-blog-core-src-templates-post-query-tsx",
    "path": "/hybrid-mobile-implementation-in-native-app-with-web-views",
    "result": {"data":{"post":{"slug":"/hybrid-mobile-implementation-in-native-app-with-web-views","title":"Hybrid Mobile Implementation in Native App with Web Views","date":"16.08.2022","tags":[{"name":"Adobe Target","slug":"adobe-target"},{"name":"Hybrid Implementation","slug":"hybrid-implementation"},{"name":"Mobile AEP SDK","slug":"mobile-aep-sdk"}],"description":"Adobe Target, Hybrid Implementation, Mobile AEP SDK","canonicalUrl":null,"body":"var _excluded = [\"components\"];\n\nfunction _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {\n  \"title\": \"Hybrid Mobile Implementation in Native App with Web Views\",\n  \"date\": \"2022-08-16T00:00:00.000Z\",\n  \"description\": \"Adobe Target, Hybrid Implementation, Mobile AEP SDK\",\n  \"tags\": [\"Adobe Target\", \"Hybrid Implementation\", \"Mobile AEP SDK\"],\n  \"banner\": \"./hybrid-mobile-implementation.jpg\"\n};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, _excluded);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", null, \"Implementing AEP SDK in a Native App with Web Views\"), mdx(\"p\", null, \"This article shares best practices implementing Adobe Target in the mobile app that uses native code with web views. We will use a \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/vadymus/aep-sdk-app\"\n  }, \"sample iOS app with AEP SDK and Adobe Target integration written in Swift\"), \" from the GitHub repository.  \"), mdx(\"p\", null, \"In real world, your enterprise app is likely using the web views in your mobile app. The Web View is a container that loads a web page by URL. The container is somewhat your browser window without controls. In iOS, it works as a Safari browser when processing the web pages.\"), mdx(\"h2\", null, \"Syncing Native Code with Web Views\"), mdx(\"p\", null, \"The challenge when implementing Adobe Target in the native app with the web views is that the mobile AEP SDK has already generated all necessary identifiers required for Adobe solutions to work seamlessly, but they are not yet visible to the web views because those are not on the native platform environment. Therefore, we need to create a bridge to pass some SDK identifiers to the web views so the visitor identity persists into the web environment. The failure to do this properly will result in a duplicate visit, which will affect your reporting.\"), mdx(\"p\", null, \"Fortunately, AEP SDK provides a convenient method to generate Adobe parameters required for the web views to consume and persist the same visitor. \"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"Identity.appendTo(url: URL(string: url), completion: {appendedURL, error in\\n  print(\\\"appendedURL \\\\(String(describing: appendedURL))\\\")\\n  // load the url with ECID on the main thread\\n  DispatchQueue.main.async {\\n    let request = NSMutableURLRequest(url: appendedURL!)\\n    self.webView.load(request as URLRequest)\\n  }\\n});\\n\")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Method \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Identity.appendTo\"), \" is documented \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://aep-sdks.gitbook.io/docs/foundation-extensions/mobile-core/identity/identity-api-reference\"\n  }, \"here\"), \". \")), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"See example of use for \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Identity.appendTo\"), \" \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://github.com/vadymus/aep-sdk-app/blob/master/AGS300-iOS-Demo/OrderViewController.swift#L37\"\n  }, \"here\"), \".\")), mdx(\"p\", null, \"With AEP SDK method \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Identity.appendTo\"), \" we would transform this URL:\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"https://vadymus.github.io/ateng/at-order-confirmation/index.html?a=1&b=2\")), mdx(\"p\", null, \"to the following URL:\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"https://vadymus.github.io/ateng/at-order-confirmation/index.html?a=1&b=2&adobe_mc=TS%3D1660667205%7CMCMID%3D69624092487065093697422606480535692677%7CMCORGID%3DEB9CAE8B56E003697F000101%40AdobeOrg\")), mdx(\"p\", null, \"As you can see, there is \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"adobe_mc\"), \" parameter appended to the URL. This parameter contains encoded values for:\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"TS=1660667205|\"), \" - current timestamp (this ensures web view does not get expired values)\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"MCMID=69624092487065093697422606480535692677|\"), \" - ECID, or Experience Cloud ID (aka MID or Marketing Cloud ID) required for Adobe cross-solution visitor identification\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"MCORGID=EB9CAE8B56E003697F000101@AdobeOrg\"), \" - Adobe Organization ID\")), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Identity.getUrlVariables\"), \" is an alternative AEP SDK method that returns an appropriately formed string that contains the Experience Cloud Identity Service URL variables.\"), mdx(\"blockquote\", null, mdx(\"p\", {\n    parentName: \"blockquote\"\n  }, \"Method \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Identity.getUrlVariables\"), \" is documented \", mdx(\"a\", {\n    parentName: \"p\",\n    \"href\": \"https://aep-sdks.gitbook.io/docs/foundation-extensions/mobile-core/identity/identity-api-reference#geturlvariables\"\n  }, \"here\"))), mdx(\"h2\", null, \"Passing Target Session ID for Same-Session Experience\"), mdx(\"p\", null, \"One extra step is needed to make Target user journey work seamlessly across the native and web views.\\nThis includes extracting and passing Target Session ID from AEP SDK to the web views of mobile app. \"), mdx(\"p\", null, \"Method \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Target.getSessionId\"), \" extracts Session ID that can be passed to the web view URL as an mboxSession parameter.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"Target.getSessionId { (id, err) in\\n    // read Target sessionId\\n}\\n\")), mdx(\"h2\", null, \"Testing in the Native App\"), mdx(\"p\", null, \"To test inactive Target Activities in the mobile app, we normally generate a Preview Link for one or more Activities as shown below.\"), mdx(\"p\", null, mdx(\"span\", {\n    parentName: \"p\",\n    \"className\": \"gatsby-resp-image-wrapper\",\n    \"style\": {\n      \"position\": \"relative\",\n      \"display\": \"block\",\n      \"marginLeft\": \"auto\",\n      \"marginRight\": \"auto\",\n      \"maxWidth\": \"960px\"\n    }\n  }, \"\\n      \", mdx(\"span\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-background-image\",\n    \"style\": {\n      \"paddingBottom\": \"40.416666666666664%\",\n      \"position\": \"relative\",\n      \"bottom\": \"0\",\n      \"left\": \"0\",\n      \"backgroundImage\": \"url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAABYlAAAWJQFJUiTwAAABWElEQVQoz1WRS4/UMBCE8///B3vjADcQR67LCGnEzPI4gjYTP+JXbGcm+VCb9ayIVHJ3bFVVVw8pJWKMyFlKodbaUEpt/Ssq61qxPvH282/MHFhSwjnHPM+EEFo9SOG9b1iWhd7L2cUE8rjkhYuaefj0i8k4fPAYpbBaY7TCWcuglUJr3SBKAjs7nPtHKuikTSQGuGXSPOO/nbBPP9Gn7+jTE+b8g0E7h1IKNU2N1FqLcZHZSQyxuRV34zjex/IxNULzeODP41eevxwZD0eeD0eGeD6Tl3QfT8aWbPKykHO+OxNR6Xs0pVaCcwStKMFTQ2gY8vt3bNmTS/0vN1kE7NS1cLvd6J+QXa/XVq/rSq6V9eV+B4b68QNbjtT12hz0rcrjfd/bZoVQIL3c9VoMGGNexGHbNgb95oFoRrSxLadpmrhcLlhjSUvCWI13r1uX0fvYfVnyv+MvolJj7elAm9cAAAAASUVORK5CYII=')\",\n      \"backgroundSize\": \"cover\",\n      \"display\": \"block\"\n    }\n  }), \"\\n  \", mdx(\"img\", {\n    parentName: \"span\",\n    \"className\": \"gatsby-resp-image-image\",\n    \"alt\": \"Generate Preview Link\",\n    \"title\": \"Generate Preview Link\",\n    \"src\": \"/static/673ed5abbe7dca2910482284ca65a2cc/7d769/generate-preview-link.png\",\n    \"srcSet\": [\"/static/673ed5abbe7dca2910482284ca65a2cc/5243c/generate-preview-link.png 240w\", \"/static/673ed5abbe7dca2910482284ca65a2cc/ab158/generate-preview-link.png 480w\", \"/static/673ed5abbe7dca2910482284ca65a2cc/7d769/generate-preview-link.png 960w\", \"/static/673ed5abbe7dca2910482284ca65a2cc/87339/generate-preview-link.png 1440w\", \"/static/673ed5abbe7dca2910482284ca65a2cc/88b03/generate-preview-link.png 1920w\", \"/static/673ed5abbe7dca2910482284ca65a2cc/6ff97/generate-preview-link.png 2064w\"],\n    \"sizes\": \"(max-width: 960px) 100vw, 960px\",\n    \"style\": {\n      \"width\": \"100%\",\n      \"height\": \"100%\",\n      \"margin\": \"0\",\n      \"verticalAlign\": \"middle\",\n      \"position\": \"absolute\",\n      \"top\": \"0\",\n      \"left\": \"0\"\n    },\n    \"loading\": \"lazy\",\n    \"decoding\": \"async\"\n  }), \"\\n    \")), mdx(\"p\", null, \"The generated Preview Link can be copied into Safari browser on your mobile phone to open the app and preview Target experiences:\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"com.adobe.targetmobile://?at_preview_token=mhFIzJSF7JWb-RsnakpBql4JwKgiGZF7qQ22otjykOMPNORdPe8Q54vdz_TPx_r8\")), mdx(\"p\", null, \"The iOS method \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"application(_:open:options:)\"), \" can capture URL parameters of the generated preview token. However, this token can preview mobile experiences only. Web preview link is generated differently. In the next section we will generate and append the link for web view preview.  \"), mdx(\"h2\", null, \"Testing in the Web Views\"), mdx(\"p\", null, \"Web preview links are generated on the Activity detail page by clicking \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, \"Adobe QA\"), \" link. This will open a popup to copy each experience preview link similar to below.\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"?at_preview_token=mhFIzJSF7JWb-RsnakpBqi_s83Sl64hZp928VWpkwvI&at_preview_index=1_1&at_preview_listed_activities_only=true\")), mdx(\"p\", null, \"Web preview links contain additional \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"at_preview_index\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"at_preview_listed_activities_only\"), \" parameters. Copy those to construct mobile friendly preview link with web link parameters. Example can look like this: \"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"com.adobe.targetmobile://?at_preview_token=mhFIzJSF7JWb-RsnakpBqhBwj-TiIlZsRTx_1QQuiXLIJFdpSLeEZwKGPUyy57O_&at_preview_index=1_1&at_preview_listed_activities_only=true\")), mdx(\"p\", null, \"After opening the link in iOS Safari browser, your app will capture the URL in your AppDelegate class similar to below.\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {\\n  print(\\\"url= \\\\(String(describing: url.absoluteString))\\\")\\n  //...\\n\")), mdx(\"p\", null, \"Now that we have captured all necessary parameters in the app, we can pass them to the web when necessary:\"), mdx(\"pre\", null, mdx(\"code\", {\n    parentName: \"pre\"\n  }, \"Identity.appendTo(url: URL(string: url), completion: {appendedURL, error in\\n  let urlWithWebPreviewLink = appendedURL + \\\"&\\\" + myPreviewLinkFromAppDelegate\\n\")), mdx(\"p\", null, \"Final output for the web view link may look like this:\"), mdx(\"p\", null, mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"https://vadymus.github.io/ateng/at-order-confirmation/index.html?a=1&b=2&adobe_mc=TS%3D1660667205%7CMCMID%3D69624092487065093697422606480535692677%7CMCORGID%3DEB9CAE8B56E003697F000101%40AdobeOrg&at_preview_token=mhFIzJSF7JWb-RsnakpBqi_s83Sl64hZp928VWpkwvI&at_preview_index=1_1&at_preview_listed_activities_only=true\")));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Implementing AEP SDK in a Native App with Web Views This article shares best practices implementing Adobe Target in the mobile app that uses…","timeToRead":2,"banner":null}},"pageContext":{"slug":"/hybrid-mobile-implementation-in-native-app-with-web-views","formatString":"DD.MM.YYYY"}},
    "staticQueryHashes": ["2744905544","3090400250","318001574"]}