{"_id":"59fcb11ab449e700101d2d7d","project":"55843604fd8d910d007b9502","version":{"_id":"558444ceafccfd0d00fcb2bb","forked_from":"55843604fd8d910d007b9505","project":"55843604fd8d910d007b9502","__v":71,"createdAt":"2015-06-19T16:35:26.435Z","releaseDate":"2015-06-19T16:35:26.435Z","categories":["558444cfafccfd0d00fcb2bc","558444cfafccfd0d00fcb2bd","55ad4ce733616a0d00599d2e","55ad4cef6aadf20d0015b764","55ad4cf36aadf20d0015b765","55ad4cfb24cf160d0013584f","55ad4d0024cf160d00135850","55ad4d0a24cf160d00135851","55ad4d0d24cf160d00135852","55ad4d126aadf20d0015b766","55ad4d1624cf160d00135853","55ad4d1933616a0d00599d2f","55ad4d2233616a0d00599d30","55ad4d2e24cf160d00135854","55d35b6bf77e6d0d00b1b092","55d3649a0168850d0073f14a","55d366d40168850d0073f15a","55d37fcff77e6d0d00b1b13f","55d383e50168850d0073f1e1","55d3ac26c336ec0d007c2251","55d3c51cb2330119009c31db","55d3c59bfe37111900e536f3","55d3c5a7fe37111900e536f4","55d3c5b4fe37111900e536f5","55d3c5d4fe37111900e536f6","55d3c5d6b2330119009c31df","55d3c5d71f478b170077c164","55d3c687b2330119009c31e4","55d3c6a4fe37111900e536f9","55d3c6befe37111900e536fa","55d3c6e8d2c66f0d00497f93","55d49dcfd7c16b2d007de905","55d4ca8f5082980d0009c79b","55d4cab9c95a3d2f0069ad3d","55d4d279c95a3d2f0069ad60","55d4d9355082980d0009c7e1","55d4f6b5988e130d000b3eb1","55d64dc8e60a2f0d00b88ecb","5627ca43fcbbc621004ec07d","56c64a0d8f98b50d0012c37c","56f1b8b13eb62a34003ea041","56f1b9df4476fb2200795e8c","57f6907dca5e5d1700039ae9","591dd06ca266c423002ec4ca","59234825e465c11900922518","5936f82eaa591e0027638d57","59972f54fd7078001992c136","599c6da8f180820025f14909","59b054613c3e1b0019cf27d9","59b1ceca2d6231003ad73e5f","59b1cf1857911600382e0dc4","59b1cf2730f3d60010c30ef7","59b1cf385d4b89003035441a","59b1cf5857911600382e0dc6","59bc2c4e26ac9b0010a8b753","59bc2ce20b3eb30010657b70","59f0c793ba3bc90030f413ab","59f0cd62f5ecda00325294b9","59fb55a8e8d0f600101aedc3","59fcb05c067f8d0028613f86","5a2af4a1bc5fba00283909c1","5a83673b0e56010012138c12","5a972f2e77b85a0070e4ebe2","5aa300224ed4b40012c53e1d","5acd20095efd8d000359bb3c","5ad50889c05179000306021e","5af0927a8779670003daff34","5b55a46b282b25000319669e","5ba4b423a1a8660003a6b4dd","5ba969b5bdd51d0003cf152c","5bb87f6ea7fe7b0003b86e02","5bbf51fee658fb000339f503"],"is_deprecated":false,"is_hidden":false,"is_beta":true,"is_stable":true,"codename":"","version_clean":"3.0.0","version":"3"},"category":{"_id":"59fb55a8e8d0f600101aedc3","project":"55843604fd8d910d007b9502","version":"558444ceafccfd0d00fcb2bb","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2017-11-02T17:28:08.726Z","from_sync":false,"order":5,"slug":"site-intercept-ios-sdk","title":"Mobile iOS Intercept SDK"},"user":"5919f13aff66b00f00f1948c","githubsync":"","__v":1,"parentDoc":null,"updates":["5a7bbc15770d5e0012199685"],"next":{"pages":[],"description":""},"createdAt":"2017-11-03T18:10:34.472Z","link_external":false,"link_url":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":0,"body":"This tutorial shows how to create an iOS app that presents a survey to the user using the Mobile App SDK. Several scenarios that show more advanced usage of App Feedback follow the basic survey presentation app.\n\n## Contents\n\n- [Prerequisites](#prerequisites)\n- [Create an App in Xcode](#create-an-app-in-xcode)\n- [Install the Qualtrics Framework](#install-the-qualtrics-framework)\n- [Add the App Feedback Code](#add-the-app-feedback-code)\n- [Run the App](#run-the-app)\n- [Sample Scenarios](#sample-scenarios)\n- [Where to Go from Here](#where-to-go-from-here)\n\nFor reference information about the iOS APIs, see [iOS API Reference](doc:si-ios-sdk-reference).\n[block:api-header]\n{\n  \"title\": \"Prerequisites\"\n}\n[/block]\n## App Feedback: Creative and Intercept\n\nYou need to create two items using the App Feedback Portal before you can use the Mobile App SDK: a Mobile App Prompt creative, and an intercept (targeted at mobile apps). For more information refer to the documentation on [building mobile app intercepts and creatives](https://www.qualtrics.com/support/website-app-feedback/common-use-cases/mobile-app-feedback-project/). If you want to present a survey to the user, you should have created a survey and your intercept should present that survey.\n\nOnce you have created and published your intercept, you need to save its ID. See [Finding your Intercept IDs](doc:intercept-id-for-site-intercept). You'll use this ID later in code when you initialize the SDK. You will also need your brand ID and zone ID.\n\n**Supported Versions**\n- You need to have the latest version of Xcode installed. The examples in this guide were built with Xcode version 9.2.\n- iOS 9.0+\n- Swift 3.2 or higher\n**Note**: Only iOS apps built with Objective-C or Swift are supported\n[block:api-header]\n{\n  \"title\": \"Create an App in Xcode\"\n}\n[/block]\n## Create a new Xcode project\n\nOn the **File** menu in Xcode, choose **New** then **Project**. You will see the following dialog box:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/d60fd47-Screen_Shot_2017-11-16_at_2.23.27_PM.png\",\n        \"Screen Shot 2017-11-16 at 2.23.27 PM.png\",\n        1466,\n        1068,\n        \"#eceded\"\n      ]\n    }\n  ]\n}\n[/block]\nChoose Single View App and click **Next**. You will see the following dialog:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/c17d399-Screen_Shot_2017-11-21_at_11.37.13_AM.png\",\n        \"Screen Shot 2017-11-21 at 11.37.13 AM.png\",\n        1448,\n        1038,\n        \"#eaeaea\"\n      ]\n    }\n  ]\n}\n[/block]\nName your app and choose a team. Your team is registered with iTunes Connect and your developer account. You can also create a private app if you do not have an Apple Developer account. For more information, see [Adding Your Account to Xcode](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppStoreDistributionTutorial/AddingYourAccounttoXcode/AddingYourAccounttoXcode.html).\n\n[block:api-header]\n{\n  \"title\": \"Install the Qualtrics Framework\"\n}\n[/block]\nYou can either use CocoaPods to set up the Qualtrics framework or manually download and install it. If you want to install the framework manually, you can skip the CocoaPods setup and move on to [Installing the Framework without CocoaPods](#section-installing-the-framework-without-cocoapods)\n\n## Set up CocoaPods\n\nFor more information on CocoaPods, see [CocoaPods](https://cocoapods.org).\n\nNext type the following in Terminal:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"sudo gem install cocoapods\",\n      \"language\": \"shell\",\n      \"name\": \"Terminal\"\n    }\n  ]\n}\n[/block]\n(You will be prompted to enter your password.)\n\nNext you need to update the repository spec for CocoaPods by typing:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"pod setup\",\n      \"language\": \"shell\",\n      \"name\": \"Terminal\"\n    }\n  ]\n}\n[/block]\n\n## Prepare the Project for CocoaPods\n\nIn the same directory as your new project, type the following in Terminal:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"pod init\",\n      \"language\": \"shell\",\n      \"name\": \"Terminal\"\n    }\n  ]\n}\n[/block]\n## Set up the CocoaPods Podfile\n\nYou need to edit the Podfile. It is in the root of your project directory. In the Podfile, add the following line under **use_frameworks!**:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"pod ‘Qualtrics’\",\n      \"language\": \"ruby\",\n      \"name\": \"Podfile\"\n    }\n  ]\n}\n[/block]\nSave and close your Podfile. Your Podfile should look similar to the following example:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"# Uncomment the next line to define a global platform for your project\\n# platform :ios, '9.0'\\n\\ntarget 'TestApp' do\\n  # Uncomment the next line if you're using Swift or would like to use dynamic frameworks\\n  # use_frameworks!\\n        pod 'Qualtrics'\\n\\n  # Pods for TestApp\\n\\nend\",\n      \"language\": \"ruby\",\n      \"name\": \"Podfile\"\n    }\n  ]\n}\n[/block]\nClose the project file in Xcode. Then, from Terminal ensure that you are in the project directory and type:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"pod install\",\n      \"language\": \"shell\",\n      \"name\": \"Terminal\"\n    }\n  ]\n}\n[/block]\nThis will install the Qualtrics framework, create a new workspace, and modify your project to include the framework.\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Workspace File\",\n  \"body\": \"After CocoaPods creates the workspace file, you will only open the workspace file and no longer open the project file.\"\n}\n[/block]\nIn Xcode, select **Open...** and choose the new workspace file. It should have the extension .xcworkspace.\n\n## Installing the Framework without Cocoapods\n\nThis step is optional. If you would like to avoid installing and using Cocoapods, you can install the framework manually. Select and download the latest framework as a zip file from [iOS Releases](doc:si-ios-releases).\n\nUnzip the file to find a **LICENSE** file and a **Qualtrics.framework** folder. Open Xcode and select the project in the navigator pane. From there, select the app under **Targets**  and choose **General** to view the General settings window.\n\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/2cbb7a2-unnamed-2.png\",\n        \"unnamed-2.png\",\n        455,\n        259,\n        \"#ececec\"\n      ]\n    }\n  ]\n}\n[/block]\nAt the bottom of the settings page is a section labeled **Embedded Binaries**. Click the **+** button in this section. In the dialog that appears (shown below), click on the **Add Other…** button to display an open file dialog.\n\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/4b8321b-unnamed-1.png\",\n        \"unnamed-1.png\",\n        386,\n        442,\n        \"#ebebeb\"\n      ]\n    }\n  ]\n}\n[/block]\nNavigate to the location where the SDK was unzipped, select the Qualtrics framework, and click on the **Open** button. In the options dialog that appears, check the **Copy items if needed** option and click the **Finish** button. The SDK will appear under both the **Embedded Binaries** and the **Linked Frameworks and Libraries** sections, as shown below:\n\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/7c3d61f-unnamed-4.png\",\n        \"unnamed-4.png\",\n        614,\n        286,\n        \"#dcdddb\"\n      ]\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"title\": \"Add the App Feedback Code\"\n}\n[/block]\n## Set up the App Delegate\n\nClick on AppDelegate.m or AppDelegate.swift (depending on your language) to open the view controller's source file in the editor.\n\nAdd the following import to the top of the file depending on the language:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \":::at:::import Qualtrics;\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"import Qualtrics\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n## Initialize the Qualtrics Singleton\n\nAdd the following code to the **applicationDidBecomeActive** method:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[Qualtrics.shared initializeWithBrandId:@\\\"BRAND ID\\\" zoneId:@\\\"PROJECT ID\\\" interceptId:@\\\"INTERCEPT ID\\\"];\\n\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"Qualtrics.shared.initialize(brandId: \\\"BRAND ID\\\", zoneId: \\\"PROJECT ID\\\", interceptId: \\\"INTERCEPT ID\\\")\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nReplace **BRAND ID**, **PROJECT ID**, and **INTERCEPT ID** with the intercept ID you obtained earlier.\n\nThe code should look like the following:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)applicationDidBecomeActive:(UIApplication *)application \\n{\\n    // Override point for customization after application launch.\\n    [Qualtrics.shared initializeWithBrandId:@\\\"BRAND ID\\\" zoneId:@\\\"PROJECT ID\\\" interceptId:@\\\"INTERCEPT ID\\\"];\\n}\\n\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func applicationDidBecomeActive(_ application: UIApplication) \\n{\\n        // Override point for customization after application launch.\\n        Qualtrics.shared.initialize(brandId: \\\"BRAND ID\\\", zoneId: \\\"PROJECT ID\\\", interceptId: \\\"INTERCEPT ID\\\")\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n## Set Up the Main View Controller\n\nClick on ViewController.m or ViewController.swift (depending on your language) to open the view controller's source file in the editor.\n\nAdd the following import to the top of the file depending on the language:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"@import Qualtrics;\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"import Qualtrics\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n## User Interface Setup\n\nAdd a button so the user can click to show the survey. Click on the file **ViewController.m** (for Objective C) or **ViewController.swift** (for Swift) and add the following code for the corresponding language to the file:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (IBAction)buttonTap:(UIButton *)sender\\n{\\n        [Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\\n            if ([targetingResult passed]) {\\n                [Qualtrics.shared displayWithViewController:self];\\n            }\\n        }];\\n\\n}\\n\",\n      \"language\": \"objectivec\",\n      \"name\": \"Objective C\"\n    },\n    {\n      \"code\": \"    @IBAction func buttonTapped(_ sender:UIButton)\\n    {\\n        Qualtrics.shared.evaluateTargetingLogic { (result) in\\n            if(result.passed())\\n            {\\n                let displayed = Qualtrics.shared.display(viewController: self)\\n            }\\n        }\\n    }\\n\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n### evaluateTargetingLogicWithCompletion Aside\n\nThe **evaluateTargetingLogicWithCompletion** is the most important method in the Mobile App SDK. It causes the framework to evaluate the intercept's logic asynchronously. Once the framework has completed its evaluation, the app executes the completion block. The **passed** property (Objective C) or method (Swift) signals to the app that the result of the intercept's logic is true, and the app should display the survey.\n\nThe business logic is separate from the app and can be changed without recompiling and redistributing the app. The logic could be changed to never display the survey should the need arise. Or surveys could be displayed more frequently. \n\n### Add a Button\n\nNow open the file called **Main Storyboard** and type \"button\" in the filter text field at the bottom right of the Xcode main window. Drag the button from the object library to the view in the center of the screen as shown below:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/f3d826f-Screen_Shot_2017-11-21_at_1.48.33_PM.png\",\n        \"Screen Shot 2017-11-21 at 1.48.33 PM.png\",\n        1728,\n        1852,\n        \"#edeef0\"\n      ]\n    }\n  ]\n}\n[/block]\nRename the button to \"Evaluate Intercept\" in the properties inspector.\n\n## Hook Up the Button\n\nTurn on the assistant editor by choosing **Show Assistant Editor** from **Assistant Editor** from the **View** menu.\n\nThis will show two files: the **Main Storyboard** file next to the source file. The source file (either ViewController.swift or ViewController.m) should be displayed on the right.\n\nWhile holding down **control**, drag from the button to the button action code that you just added to the source file. The entire method or function will highlight when you can release the mouse button.\n[block:api-header]\n{\n  \"title\": \"Run the App\"\n}\n[/block]\nNow choose **Run** from the **Product** menu to run the app.\n\nYou might receive the following message with an Objective C app:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"dyld: Library not loaded: @rpath/libswiftCore.dylib\",\n      \"language\": \"text\",\n      \"name\": \"Debugger\"\n    }\n  ]\n}\n[/block]\nIf so, you need to embed the Swift libraries. In your project's build settings, make sure **All** is selected and search on Swift. Turn on **Always Embed Swift Standard Libraries** as shown below:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/fbf96ce-Screen_Shot_2017-11-16_at_2.19.15_PM.png\",\n        \"Screen Shot 2017-11-16 at 2.19.15 PM.png\",\n        1698,\n        682,\n        \"#0a6fdc\"\n      ]\n    }\n  ]\n}\n[/block]\nThe following screenshot shows the app:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/3089eed-0759db7-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-10_at_13.07.04.png\",\n        \"0759db7-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-10_at_13.07.04.png\",\n        1262,\n        2228,\n        \"#040404\"\n      ]\n    }\n  ]\n}\n[/block]\nWhen you tap on the button, you should see an offer to take a survey:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/85ac9de-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-17_at_10.07.10.png\",\n        \"Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-17_at_10.07.10.png\",\n        1262,\n        2228,\n        \"#d7d9da\"\n      ]\n    }\n  ]\n}\n[/block]\nIf you tap on **Give Feedback** you should see your survey:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/6f73638-ff9cd9a-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.22.45.png\",\n        \"ff9cd9a-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.22.45.png\",\n        1262,\n        2228,\n        \"#373737\"\n      ]\n    }\n  ]\n}\n[/block]\nWhen you are finished with the survey, you should see this dialog:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/254a2ca-e5d5c1b-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.23.07.png\",\n        \"e5d5c1b-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.23.07.png\",\n        1262,\n        2228,\n        \"#101011\"\n      ]\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"title\": \"Sample Scenarios\"\n}\n[/block]\nThis section describes several alternative scenarios you can try with the base app you created in the previous sections.\n\nThe following scenarios are discussed:\n\n- [Using intercept logic to solicit app reviews](#section-using-intercept-logic-to-solicit-app-reviews). You can use an intercept's logic to determine whether the user should be asked to review the app. This lets you adjust the frequency of the requests at any time on the server rather than needing to add additional logic to the app, recompiling it, and resubmitting to the app store.\n- [View targeting](#section-view-targeting). If you use a property to save the name of the current view controller, you can create logic on the server that displays different surveys depending on which view controller the user is accessing. You could use this scenario to display different surveys for newly created UI such as a new settings screen or a new login screen.\n- [Using embedded data](#section-using-embedded-data). You can also use the properties collection to alter a survey's text. In this case, the user's first name is sent back to the server via a custom property, which is displayed to the user with piped text on the survey.\n- [Controlling the survey presentation](#section-controlling-the-survey-presentation). If you would like to create your own means of presenting a survey rather than calling the **displayWithCurrentView** method, you can use this scenario. The scenario creates a navigation controller with an embedded **WKWebView** to display the survey.\n- [Bypassing the give feedback prompt](#section-bypassing-the-give-feedback-prompt) You can use this scenario to go directly to the survey instead of asking first whether your users want to give feedback. This scenario is ideal for having a **Give Feedback** button in your app that you want to go straight to the survey.\n\n\n\n# Using Intercept Logic to Solicit App Reviews\n\nBecause the App Feedback solution allows you to control when interactions with users occur, you can use it for more besides presenting surveys. One example is you can control, from the server, when requests to review the app appear. The following example shows how to request a review only when intercept evaluation passes.\n\nFirst, add a line to import the Store Kit framework to the top of the ViewController.m or .swift file:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import \\\"ViewController.h\\\"\\n@import Qualtrics;\\n// Add the following line:\\n@import StoreKit;\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Add this line:\\nimport StoreKit\\nimport Qualtrics\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nThen create a **viewDidAppear** method like the following:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)viewDidAppear:(BOOL)animated\\n{\\n    [super viewDidAppear:animated];\\n    \\n    [Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\\n        if(targetingResult.passed)\\n        {\\n            [SKStoreReviewController requestReview];\\n        }\\n    }];\\n}\\n\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"override func viewDidAppear(_ animated: Bool) \\n{\\n\\tQualtrics.shared.evaluateTargetingLogic { (targetingResult) in\\n\\t\\tif targetingResult.passed()\\n\\t\\t{\\n\\t\\t\\tSKStoreReviewController.requestReview()\\n\\t\\t}\\n\\t}\\n}\\n\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nWhen the intercept passes evaluation, the user will be presented with a request to review the app.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/0997285-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-11_at_11.16.35.png\",\n        \"Simulator Screen Shot - iPhone 8 Plus - 2018-01-11 at 11.16.35.png\",\n        1242,\n        2208,\n        \"#9b9b9c\"\n      ]\n    }\n  ]\n}\n[/block]\n# View Targeting\n\nBy keeping the intercept logic on the server rather than in the app, you can change when intercepts appear easily and without rebuilding and redistributing your app. You can use the **registerViewVisitWithViewName** (Objective C) or **registerViewVisit** (Swift) method to return data to the server about which how many total and unique views the user has seen. These statistics can be used to enable more complex logic around displaying surveys and other actions.\n\nIn this example, the view controller sends its name to the server to be used in the logic for displaying a survey. First, when the view is loaded, it sends its name to the server. All view controllers would have a similar method, which could be moved to a base class. All classes could then be subclassed from this class.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"-(void)viewDidAppear:(BOOL)animated\\n{\\n  [super viewDidAppear:animated];\\n  \\n  [Qualtrics.shared.properties setStringWithString:NSStringFromClass([self class]) for:@\\\"viewController\\\"]; // #1\\n  [Qualtrics.shared registerViewVisitWithViewName:NSStringFromClass([self class])];; // #2\\n\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"    override func viewDidLoad() {\\n        super.viewDidLoad()\\n        \\n        Qualtrics.shared.properties.setString(string: String(describing: type(of:self)), for: \\\"viewController\\\")\\n        Qualtrics.shared.registerViewVisit(viewName: String(describing: type(of: self)))\\n    }\\n\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nThe first line registers the view controller's name  (in this case the name is **ViewController**), and the second line increments the unique views (if this is the first time a view named **viewController** has been registered) and total views.\n\nThen, in **viewDidAppear** you would evaluate the intercept logic, as shown below:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)viewDidAppear:(BOOL)animated\\n{\\n    [super viewDidAppear:animated];\\n    \\n    [Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\\n        if(targetingResult.passed)\\n        {\\n          [[Qualtrics shared] displayWithViewController: self];\\n        }\\n    }];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"    override func viewDidAppear(_ animated: Bool) {\\n        Qualtrics.shared.evaluateTargetingLogic { (targetingResult) in\\n            if targetingResult.passed()\\n            {\\n                Qualtrics.shared.display(viewController: self)\\n            }\\n        }\\n    }\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n(You could also evaluate the intercept logic elsewhere if it makes more sense for your app.)\n\nOn the server, you would add an action that is only true if the desired view is loaded. First, add a new action to your intercept by clicking on **Add Intercept Display Logic**. You will then see the action chooser:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/8f18686-Screen_Shot_2018-01-12_at_10.48.37_AM.png\",\n        \"Screen Shot 2018-01-12 at 10.48.37 AM.png\",\n        2172,\n        1464,\n        \"#f0f1f2\"\n      ]\n    }\n  ]\n}\n[/block]\nAdd the logic shown above. The custom property is **viewController** and in the case of the sample, its value is **ViewController**. \n\nYou could create new action sets (click on **Add Another Action Set**) for different view controllers (**SettingsViewController**, **LoginViewController**, for instance) and present different surveys for each. You can also use the **View Count** **Total Views** and **Unique Views** values for additional conditional processing. The **Total Views** value is incremented every time your app calls **registerViewVisitWithViewName**. The **Unique Views** value is incremented every time your app calls **registerViewVisitWithViewName** with a unique name.\n\n# Using Embedded Data\n\nBy using the **properties** collection, you can push data back to the server, which can pass it on to your survey as embedded data. For more information about using embedded data with intercepts, see [Setting up intercepts](https://www.qualtrics.com/support/website-app-feedback/common-use-cases/mobile-app-feedback-project/#SettingUptheIntercept). For more information about embedded data and surveys, see [Embedded Data](https://www.qualtrics.com/support/survey-platform/survey-module/survey-flow/standard-elements/embedded-data/).\n\nIn the example below, the app will send the user's first name back to the server to be used as [piped text](https://www.qualtrics.com/support/survey-platform/survey-module/editing-questions/piped-text/piped-text-overview/) in the survey. If the app requires the user to create an account, the user's first name should be readily available.\n\nIn the Experience Management (XM) portal, locate your specific Website / App Feedback project and open the intercept and choose the embedded data from the **Options** dropdown on the action set.\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/d07a54e-Screen_Shot_2018-01-09_at_2.31.35_PM.png\",\n        \"Screen Shot 2018-01-09 at 2.31.35 PM.png\",\n        2004,\n        906,\n        \"#edeff0\"\n      ],\n      \"caption\": \"Choose Embedded Data...\"\n    }\n  ]\n}\n[/block]\nNow choose **Custom Property** from **Value** and use type `firstName` in both the fields as shown below:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/a083f76-Screen_Shot_2018-01-09_at_1.54.11_PM.png\",\n        \"Screen Shot 2018-01-09 at 1.54.11 PM.png\",\n        1970,\n        588,\n        \"#f3f3cc\"\n      ],\n      \"caption\": \"Create a custom property called firstName.\"\n    }\n  ]\n}\n[/block]\nNow you need to choose **Insight Platform** from the top of the screen to edit the survey to include the **firstName** value in the survey. Open your survey project and choose a question. \n\nYou will modify the question text so it includes an embedded data field. Choose **Piped Text...** and then choose **Embedded Data Field** and type **firstName** in the text field as shown below:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/7ba83b8-Embedded_fill-resaved-01.png\",\n        \"Embedded fill-resaved-01.png\",\n        1275,\n        861,\n        \"#f6f6f9\"\n      ]\n    }\n  ]\n}\n[/block]\nThe question should look like this after you added the embedded text:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/b5b73f0-Embedded_filled_in-2-resaved-01.png\",\n        \"Embedded filled in-2-resaved-01.png\",\n        1275,\n        705,\n        \"#f5f5fa\"\n      ]\n    }\n  ]\n}\n[/block]\nIn the view controller source file, add the following code to the **viewDidLoad** method.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[Qualtrics.shared.properties setStringWithString:@\\\"John\\\" for:@\\\"firstName\\\"] ;\",\n      \"language\": \"objectivec\",\n      \"name\": \"Objective C\"\n    },\n    {\n      \"code\": \"Qualtrics.shared.properties.setString(string: \\\"John\\\", for: \\\"firstName\\\")\",\n      \"language\": \"swift\",\n      \"name\": \"Swift\"\n    }\n  ]\n}\n[/block]\nWhen you run the app and display the survey, you should see the **firstName** filled in as shown below:\n[block:image]\n{\n  \"images\": [\n    {\n      \"image\": [\n        \"https://files.readme.io/e613e90-d9deca4-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-01_at_15.44.47.png\",\n        \"d9deca4-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-01_at_15.44.47.png\",\n        1262,\n        2228,\n        \"#f3f3f3\"\n      ]\n    }\n  ]\n}\n[/block]\n# Controlling the Survey Presentation\n\nRather than calling the Qualtrics **display** method, you can design and build your own survey invitation prompt and then call the **getSurveyUrl** method to present the survey in a **WKWebView** (in the handler for your \"Take survey\" action).\n\nThe first step is to include the WebKit framework in ViewController.m (or .swift).\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import \\\"ViewController.h\\\"\\n// Add the following line:\\n@import WebKit;\\n@import Qualtrics;\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"import UIKit\\n// add the following line:\\nimport WebKit\\nimport Qualtrics\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nThen you need to implement your custom invitation in the callback function of evaluateTargetingLogic. An example of how to do so is shown below with a simple custom survey invitation dialog:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"    [[Qualtrics shared] evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\\n        if ([targetingResult passed])\\n        {\\n          \\n          \\t// Remove the [Qualtrics.shared display]; line and add the below code\\n            // In the custom invitation scenario, be sure to record the impressions and clicks\\n            [targetingResult recordImpression];\\n            \\n            UIAlertController* alert = [UIAlertController alertControllerWithTitle:@\\\"Give Feedback\\\"\\n                        message:@\\\"Would you like to take a brief survey?\\\"\\n                        preferredStyle:UIAlertControllerStyleAlert];\\n            \\n            UIAlertAction* defaultAction = [UIAlertAction\\n                actionWithTitle:@\\\"OK\\\"\\n                style:UIAlertActionStyleDefault\\n                handler:^(UIAlertAction * action) {\\n                    [self presentSurvey:targetingResult];\\n                }];\\n            UIAlertAction *cancelAction = [UIAlertAction\\n                               actionWithTitle:@\\\"Cancel\\\"\\n                               style:UIAlertActionStyleCancel\\n                               handler:nil];\\n            [alert addAction:defaultAction];\\n            [alert addAction:cancelAction];\\n            [self presentViewController:alert animated:YES completion:nil];\\n    \\n    }];\\n\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"        Qualtrics.shared.evaluateTargetingLogic { (targetingResult) in\\n            if targetingResult.passed()\\n            {\\n\\n                // Remove the [Qualtrics.shared display]; line and add the below code\\n                // In the custom invitation scenario, be sure to record the impressions and clicks\\n                targetingResult.recordImpression()\\n                \\n                let alert = UIAlertController(title: \\\"Give Feedback\\\", message: \\\"Would you like to take a brief survey?\\\", preferredStyle: .alert)\\n                let defaultAction = UIAlertAction(title: \\\"OK\\\", style: UIAlertActionStyle.default, handler: { (action) in\\n                    self.presentSurvey(targetingResult: TargetingResult)\\n                })\\n                let cancelAction = UIAlertAction(title: \\\"Cancel\\\", style: .cancel, handler:nil)\\n                \\n                alert.addAction(defaultAction)\\n                alert.addAction(cancelAction)\\n                self.present(alert, animated: true, completion: nil)\\n                \\n            }\\n        }\\n\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nAdd two new methods as shown below:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)presentSurvey:(TargetingResult *)targetingResult\\n{\\n    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:[targetingResult getSurveyUrl]];\\n    WKWebView *webView = [[WKWebView alloc] init];\\n    UIViewController *surveyViewController = [[UIViewController alloc] init];\\n    surveyViewController.view = webView;\\n    \\n    UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:surveyViewController];\\n    surveyViewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismiss)];\\n    \\n    \\n    [Qualtrics.shared recordClick];\\n    \\n    [webView loadRequest:request];\\n    \\n    [self presentViewController:navigationController animated:YES completion:nil];\\n}\\n\\n- (void)dismiss\\n{\\n    [self dismissViewControllerAnimated:YES completion:nil];\\n}\\n\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"    func presentSurvey(targetingResult: TargetingResult)\\n    {\\n        let request = NSURLRequest(url: URL(string: targetingResult.getSurveyUrl())!)\\n        let webView = WKWebView();\\n        let surveyViewController = UIViewController()\\n        surveyViewController.view = webView;\\n        \\n        let navigationController = UINavigationController(rootViewController: surveyViewController)\\n        surveyViewController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissSurvey))\\n        \\n        targetingResult.recordClick()\\n        \\n        webView.load(request as URLRequest)\\n        \\n        self.present(navigationController, animated: true, completion: nil)        \\n    }\\n    \\n    @objc func dismissSurvey()\\n    {\\n        self.dismiss(animated: true, completion: nil)\\n    }\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nThe new code presents the user with an alert asking whether it is okay to show a survey. \n\nThe **presentSurvey** method handles the presentation of the survey. The **presentSurvey** method creates a web view inside a navigation controller, adds a \"Done\" button (to allow exiting the survey), and displays the survey at the URL from the **targetingResult.getSurveyUrl()** method.\n\n# Bypassing the Give Feedback Prompt\n\nTypically, you would want to bypass the give feedback prompt when your users tap on a **Give Feedback** button. If you were to use the **display** method, they would be asked whether they want to give feedback after they had already chosen to give feedback. To avoid that, you can use the following code example instead of calling the **display** method.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\\n    if(targetingResult.passed)\\n    {\\n        NSString *url = targetingResult.getSurveyUrl;\\n        \\n        // Record an impression\\n        [targetingResult recordImpression];\\n        \\n        // Record a click\\n        [targetingResult recordClick];\\n        \\n        // Run this operation on the UI thread\\n        dispatch_async(dispatch_get_main_queue(), ^{\\n            // Create a QualtricsSurveyViewController activity, passing it the URL provided by the TargetingResult object\\n            // Any valid URL can be displayed using the QualtricsSurveyViewController, not only survey URLs\\n            QualtricsSurveyViewController *surveyViewController = [[QualtricsSurveyViewController alloc] initWithUrl:url];\\n            // Set the QualtricsSurveyViewController to present on top of the current view\\n            surveyViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;\\n            // Present the QualtricsSurveyViewController\\n            [self presentViewController:surveyViewController animated:YES completion:nil];\\n        });\\n    }\\n    \\n}];\\n\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"Qualtrics.shared.evaluateTargetingLogic(completion: {(targetingResult: TargetingResult) -> Void in\\n    if targetingResult.passed(), let url = targetingResult.getSurveyUrl() {\\n        // Record an impression\\n        targetingResult.recordImpression()\\n    \\n        // Record a click\\n        targetingResult.recordClick()\\n    \\n        // Run this operation on the UI thread\\n        DispatchQueue.main.async {\\n            // Create a QualtricsSurveyViewController view controller, passing it the URL provided by the TargetingResult object\\n            // Any valid URL can be displayed using the QualtricsSurveyViewController, not only survey URLs\\n            let surveyViewController = QualtricsSurveyViewController(url: url)\\n        \\n            // Set the QualtricsSurveyViewController to present on top of the current view\\n            surveyViewController.modalPresentationStyle = .overCurrentContext\\n    \\n            // Present the QualtricsSurveyViewController\\n            self.present(surveyViewController, animated: true, completion: nil)\\n        }\\n    }\\n})\\n\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"title\": \"Where to Go from Here\"\n}\n[/block]\nFor information about troubleshooting, see [Troubleshooting](doc:troubleshooting), and for the App Feedback iOS API reference, see [App Feedback iOS API Reference](doc:si-ios-sdk-reference). For information about iOS SDK releases, see [iOS Releases](doc:si-ios-releases).\n\nTo get started developing Android apps with the Mobile App SDK, see [Getting Started with the Mobile App SDK on Android](doc:getting-started-si-with-android). For reference documentation on the App Feedback APIs for Android, see [App Feedback Android API Reference](doc:si-android-sdk-reference). For information about the Android SDK releases, see [Android Releases](doc:si-android-releases).","excerpt":"","slug":"getting-started-si-with-ios","type":"basic","title":"Getting Started with the Mobile App SDK on iOS"}

Getting Started with the Mobile App SDK on iOS


This tutorial shows how to create an iOS app that presents a survey to the user using the Mobile App SDK. Several scenarios that show more advanced usage of App Feedback follow the basic survey presentation app. ## Contents - [Prerequisites](#prerequisites) - [Create an App in Xcode](#create-an-app-in-xcode) - [Install the Qualtrics Framework](#install-the-qualtrics-framework) - [Add the App Feedback Code](#add-the-app-feedback-code) - [Run the App](#run-the-app) - [Sample Scenarios](#sample-scenarios) - [Where to Go from Here](#where-to-go-from-here) For reference information about the iOS APIs, see [iOS API Reference](doc:si-ios-sdk-reference). [block:api-header] { "title": "Prerequisites" } [/block] ## App Feedback: Creative and Intercept You need to create two items using the App Feedback Portal before you can use the Mobile App SDK: a Mobile App Prompt creative, and an intercept (targeted at mobile apps). For more information refer to the documentation on [building mobile app intercepts and creatives](https://www.qualtrics.com/support/website-app-feedback/common-use-cases/mobile-app-feedback-project/). If you want to present a survey to the user, you should have created a survey and your intercept should present that survey. Once you have created and published your intercept, you need to save its ID. See [Finding your Intercept IDs](doc:intercept-id-for-site-intercept). You'll use this ID later in code when you initialize the SDK. You will also need your brand ID and zone ID. **Supported Versions** - You need to have the latest version of Xcode installed. The examples in this guide were built with Xcode version 9.2. - iOS 9.0+ - Swift 3.2 or higher **Note**: Only iOS apps built with Objective-C or Swift are supported [block:api-header] { "title": "Create an App in Xcode" } [/block] ## Create a new Xcode project On the **File** menu in Xcode, choose **New** then **Project**. You will see the following dialog box: [block:image] { "images": [ { "image": [ "https://files.readme.io/d60fd47-Screen_Shot_2017-11-16_at_2.23.27_PM.png", "Screen Shot 2017-11-16 at 2.23.27 PM.png", 1466, 1068, "#eceded" ] } ] } [/block] Choose Single View App and click **Next**. You will see the following dialog: [block:image] { "images": [ { "image": [ "https://files.readme.io/c17d399-Screen_Shot_2017-11-21_at_11.37.13_AM.png", "Screen Shot 2017-11-21 at 11.37.13 AM.png", 1448, 1038, "#eaeaea" ] } ] } [/block] Name your app and choose a team. Your team is registered with iTunes Connect and your developer account. You can also create a private app if you do not have an Apple Developer account. For more information, see [Adding Your Account to Xcode](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppStoreDistributionTutorial/AddingYourAccounttoXcode/AddingYourAccounttoXcode.html). [block:api-header] { "title": "Install the Qualtrics Framework" } [/block] You can either use CocoaPods to set up the Qualtrics framework or manually download and install it. If you want to install the framework manually, you can skip the CocoaPods setup and move on to [Installing the Framework without CocoaPods](#section-installing-the-framework-without-cocoapods) ## Set up CocoaPods For more information on CocoaPods, see [CocoaPods](https://cocoapods.org). Next type the following in Terminal: [block:code] { "codes": [ { "code": "sudo gem install cocoapods", "language": "shell", "name": "Terminal" } ] } [/block] (You will be prompted to enter your password.) Next you need to update the repository spec for CocoaPods by typing: [block:code] { "codes": [ { "code": "pod setup", "language": "shell", "name": "Terminal" } ] } [/block] ## Prepare the Project for CocoaPods In the same directory as your new project, type the following in Terminal: [block:code] { "codes": [ { "code": "pod init", "language": "shell", "name": "Terminal" } ] } [/block] ## Set up the CocoaPods Podfile You need to edit the Podfile. It is in the root of your project directory. In the Podfile, add the following line under **use_frameworks!**: [block:code] { "codes": [ { "code": "pod ‘Qualtrics’", "language": "ruby", "name": "Podfile" } ] } [/block] Save and close your Podfile. Your Podfile should look similar to the following example: [block:code] { "codes": [ { "code": "# Uncomment the next line to define a global platform for your project\n# platform :ios, '9.0'\n\ntarget 'TestApp' do\n # Uncomment the next line if you're using Swift or would like to use dynamic frameworks\n # use_frameworks!\n pod 'Qualtrics'\n\n # Pods for TestApp\n\nend", "language": "ruby", "name": "Podfile" } ] } [/block] Close the project file in Xcode. Then, from Terminal ensure that you are in the project directory and type: [block:code] { "codes": [ { "code": "pod install", "language": "shell", "name": "Terminal" } ] } [/block] This will install the Qualtrics framework, create a new workspace, and modify your project to include the framework. [block:callout] { "type": "warning", "title": "Workspace File", "body": "After CocoaPods creates the workspace file, you will only open the workspace file and no longer open the project file." } [/block] In Xcode, select **Open...** and choose the new workspace file. It should have the extension .xcworkspace. ## Installing the Framework without Cocoapods This step is optional. If you would like to avoid installing and using Cocoapods, you can install the framework manually. Select and download the latest framework as a zip file from [iOS Releases](doc:si-ios-releases). Unzip the file to find a **LICENSE** file and a **Qualtrics.framework** folder. Open Xcode and select the project in the navigator pane. From there, select the app under **Targets** and choose **General** to view the General settings window. [block:image] { "images": [ { "image": [ "https://files.readme.io/2cbb7a2-unnamed-2.png", "unnamed-2.png", 455, 259, "#ececec" ] } ] } [/block] At the bottom of the settings page is a section labeled **Embedded Binaries**. Click the **+** button in this section. In the dialog that appears (shown below), click on the **Add Other…** button to display an open file dialog. [block:image] { "images": [ { "image": [ "https://files.readme.io/4b8321b-unnamed-1.png", "unnamed-1.png", 386, 442, "#ebebeb" ] } ] } [/block] Navigate to the location where the SDK was unzipped, select the Qualtrics framework, and click on the **Open** button. In the options dialog that appears, check the **Copy items if needed** option and click the **Finish** button. The SDK will appear under both the **Embedded Binaries** and the **Linked Frameworks and Libraries** sections, as shown below: [block:image] { "images": [ { "image": [ "https://files.readme.io/7c3d61f-unnamed-4.png", "unnamed-4.png", 614, 286, "#dcdddb" ] } ] } [/block] [block:api-header] { "title": "Add the App Feedback Code" } [/block] ## Set up the App Delegate Click on AppDelegate.m or AppDelegate.swift (depending on your language) to open the view controller's source file in the editor. Add the following import to the top of the file depending on the language: [block:code] { "codes": [ { "code": "@import Qualtrics;", "language": "objectivec" }, { "code": "import Qualtrics", "language": "swift" } ] } [/block] ## Initialize the Qualtrics Singleton Add the following code to the **applicationDidBecomeActive** method: [block:code] { "codes": [ { "code": "[Qualtrics.shared initializeWithBrandId:@\"BRAND ID\" zoneId:@\"PROJECT ID\" interceptId:@\"INTERCEPT ID\"];\n", "language": "objectivec" }, { "code": "Qualtrics.shared.initialize(brandId: \"BRAND ID\", zoneId: \"PROJECT ID\", interceptId: \"INTERCEPT ID\")", "language": "swift" } ] } [/block] Replace **BRAND ID**, **PROJECT ID**, and **INTERCEPT ID** with the intercept ID you obtained earlier. The code should look like the following: [block:code] { "codes": [ { "code": "- (void)applicationDidBecomeActive:(UIApplication *)application \n{\n // Override point for customization after application launch.\n [Qualtrics.shared initializeWithBrandId:@\"BRAND ID\" zoneId:@\"PROJECT ID\" interceptId:@\"INTERCEPT ID\"];\n}\n", "language": "objectivec" }, { "code": "func applicationDidBecomeActive(_ application: UIApplication) \n{\n // Override point for customization after application launch.\n Qualtrics.shared.initialize(brandId: \"BRAND ID\", zoneId: \"PROJECT ID\", interceptId: \"INTERCEPT ID\")\n}", "language": "swift" } ] } [/block] ## Set Up the Main View Controller Click on ViewController.m or ViewController.swift (depending on your language) to open the view controller's source file in the editor. Add the following import to the top of the file depending on the language: [block:code] { "codes": [ { "code": "@import Qualtrics;", "language": "objectivec" }, { "code": "import Qualtrics", "language": "swift" } ] } [/block] ## User Interface Setup Add a button so the user can click to show the survey. Click on the file **ViewController.m** (for Objective C) or **ViewController.swift** (for Swift) and add the following code for the corresponding language to the file: [block:code] { "codes": [ { "code": "- (IBAction)buttonTap:(UIButton *)sender\n{\n [Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\n if ([targetingResult passed]) {\n [Qualtrics.shared displayWithViewController:self];\n }\n }];\n\n}\n", "language": "objectivec", "name": "Objective C" }, { "code": " @IBAction func buttonTapped(_ sender:UIButton)\n {\n Qualtrics.shared.evaluateTargetingLogic { (result) in\n if(result.passed())\n {\n let displayed = Qualtrics.shared.display(viewController: self)\n }\n }\n }\n", "language": "swift" } ] } [/block] ### evaluateTargetingLogicWithCompletion Aside The **evaluateTargetingLogicWithCompletion** is the most important method in the Mobile App SDK. It causes the framework to evaluate the intercept's logic asynchronously. Once the framework has completed its evaluation, the app executes the completion block. The **passed** property (Objective C) or method (Swift) signals to the app that the result of the intercept's logic is true, and the app should display the survey. The business logic is separate from the app and can be changed without recompiling and redistributing the app. The logic could be changed to never display the survey should the need arise. Or surveys could be displayed more frequently. ### Add a Button Now open the file called **Main Storyboard** and type "button" in the filter text field at the bottom right of the Xcode main window. Drag the button from the object library to the view in the center of the screen as shown below: [block:image] { "images": [ { "image": [ "https://files.readme.io/f3d826f-Screen_Shot_2017-11-21_at_1.48.33_PM.png", "Screen Shot 2017-11-21 at 1.48.33 PM.png", 1728, 1852, "#edeef0" ] } ] } [/block] Rename the button to "Evaluate Intercept" in the properties inspector. ## Hook Up the Button Turn on the assistant editor by choosing **Show Assistant Editor** from **Assistant Editor** from the **View** menu. This will show two files: the **Main Storyboard** file next to the source file. The source file (either ViewController.swift or ViewController.m) should be displayed on the right. While holding down **control**, drag from the button to the button action code that you just added to the source file. The entire method or function will highlight when you can release the mouse button. [block:api-header] { "title": "Run the App" } [/block] Now choose **Run** from the **Product** menu to run the app. You might receive the following message with an Objective C app: [block:code] { "codes": [ { "code": "dyld: Library not loaded: @rpath/libswiftCore.dylib", "language": "text", "name": "Debugger" } ] } [/block] If so, you need to embed the Swift libraries. In your project's build settings, make sure **All** is selected and search on Swift. Turn on **Always Embed Swift Standard Libraries** as shown below: [block:image] { "images": [ { "image": [ "https://files.readme.io/fbf96ce-Screen_Shot_2017-11-16_at_2.19.15_PM.png", "Screen Shot 2017-11-16 at 2.19.15 PM.png", 1698, 682, "#0a6fdc" ] } ] } [/block] The following screenshot shows the app: [block:image] { "images": [ { "image": [ "https://files.readme.io/3089eed-0759db7-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-10_at_13.07.04.png", "0759db7-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-10_at_13.07.04.png", 1262, 2228, "#040404" ] } ] } [/block] When you tap on the button, you should see an offer to take a survey: [block:image] { "images": [ { "image": [ "https://files.readme.io/85ac9de-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-17_at_10.07.10.png", "Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-17_at_10.07.10.png", 1262, 2228, "#d7d9da" ] } ] } [/block] If you tap on **Give Feedback** you should see your survey: [block:image] { "images": [ { "image": [ "https://files.readme.io/6f73638-ff9cd9a-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.22.45.png", "ff9cd9a-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.22.45.png", 1262, 2228, "#373737" ] } ] } [/block] When you are finished with the survey, you should see this dialog: [block:image] { "images": [ { "image": [ "https://files.readme.io/254a2ca-e5d5c1b-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.23.07.png", "e5d5c1b-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2017-12-26_at_09.23.07.png", 1262, 2228, "#101011" ] } ] } [/block] [block:api-header] { "title": "Sample Scenarios" } [/block] This section describes several alternative scenarios you can try with the base app you created in the previous sections. The following scenarios are discussed: - [Using intercept logic to solicit app reviews](#section-using-intercept-logic-to-solicit-app-reviews). You can use an intercept's logic to determine whether the user should be asked to review the app. This lets you adjust the frequency of the requests at any time on the server rather than needing to add additional logic to the app, recompiling it, and resubmitting to the app store. - [View targeting](#section-view-targeting). If you use a property to save the name of the current view controller, you can create logic on the server that displays different surveys depending on which view controller the user is accessing. You could use this scenario to display different surveys for newly created UI such as a new settings screen or a new login screen. - [Using embedded data](#section-using-embedded-data). You can also use the properties collection to alter a survey's text. In this case, the user's first name is sent back to the server via a custom property, which is displayed to the user with piped text on the survey. - [Controlling the survey presentation](#section-controlling-the-survey-presentation). If you would like to create your own means of presenting a survey rather than calling the **displayWithCurrentView** method, you can use this scenario. The scenario creates a navigation controller with an embedded **WKWebView** to display the survey. - [Bypassing the give feedback prompt](#section-bypassing-the-give-feedback-prompt) You can use this scenario to go directly to the survey instead of asking first whether your users want to give feedback. This scenario is ideal for having a **Give Feedback** button in your app that you want to go straight to the survey. # Using Intercept Logic to Solicit App Reviews Because the App Feedback solution allows you to control when interactions with users occur, you can use it for more besides presenting surveys. One example is you can control, from the server, when requests to review the app appear. The following example shows how to request a review only when intercept evaluation passes. First, add a line to import the Store Kit framework to the top of the ViewController.m or .swift file: [block:code] { "codes": [ { "code": "#import \"ViewController.h\"\n@import Qualtrics;\n// Add the following line:\n@import StoreKit;", "language": "objectivec" }, { "code": "// Add this line:\nimport StoreKit\nimport Qualtrics", "language": "swift" } ] } [/block] Then create a **viewDidAppear** method like the following: [block:code] { "codes": [ { "code": "- (void)viewDidAppear:(BOOL)animated\n{\n [super viewDidAppear:animated];\n \n [Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\n if(targetingResult.passed)\n {\n [SKStoreReviewController requestReview];\n }\n }];\n}\n", "language": "objectivec" }, { "code": "override func viewDidAppear(_ animated: Bool) \n{\n\tQualtrics.shared.evaluateTargetingLogic { (targetingResult) in\n\t\tif targetingResult.passed()\n\t\t{\n\t\t\tSKStoreReviewController.requestReview()\n\t\t}\n\t}\n}\n", "language": "swift" } ] } [/block] When the intercept passes evaluation, the user will be presented with a request to review the app. [block:image] { "images": [ { "image": [ "https://files.readme.io/0997285-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-11_at_11.16.35.png", "Simulator Screen Shot - iPhone 8 Plus - 2018-01-11 at 11.16.35.png", 1242, 2208, "#9b9b9c" ] } ] } [/block] # View Targeting By keeping the intercept logic on the server rather than in the app, you can change when intercepts appear easily and without rebuilding and redistributing your app. You can use the **registerViewVisitWithViewName** (Objective C) or **registerViewVisit** (Swift) method to return data to the server about which how many total and unique views the user has seen. These statistics can be used to enable more complex logic around displaying surveys and other actions. In this example, the view controller sends its name to the server to be used in the logic for displaying a survey. First, when the view is loaded, it sends its name to the server. All view controllers would have a similar method, which could be moved to a base class. All classes could then be subclassed from this class. [block:code] { "codes": [ { "code": "-(void)viewDidAppear:(BOOL)animated\n{\n [super viewDidAppear:animated];\n \n [Qualtrics.shared.properties setStringWithString:NSStringFromClass([self class]) for:@\"viewController\"]; // #1\n [Qualtrics.shared registerViewVisitWithViewName:NSStringFromClass([self class])];; // #2\n\n}", "language": "objectivec" }, { "code": " override func viewDidLoad() {\n super.viewDidLoad()\n \n Qualtrics.shared.properties.setString(string: String(describing: type(of:self)), for: \"viewController\")\n Qualtrics.shared.registerViewVisit(viewName: String(describing: type(of: self)))\n }\n", "language": "swift" } ] } [/block] The first line registers the view controller's name (in this case the name is **ViewController**), and the second line increments the unique views (if this is the first time a view named **viewController** has been registered) and total views. Then, in **viewDidAppear** you would evaluate the intercept logic, as shown below: [block:code] { "codes": [ { "code": "- (void)viewDidAppear:(BOOL)animated\n{\n [super viewDidAppear:animated];\n \n [Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\n if(targetingResult.passed)\n {\n [[Qualtrics shared] displayWithViewController: self];\n }\n }];\n}", "language": "objectivec" }, { "code": " override func viewDidAppear(_ animated: Bool) {\n Qualtrics.shared.evaluateTargetingLogic { (targetingResult) in\n if targetingResult.passed()\n {\n Qualtrics.shared.display(viewController: self)\n }\n }\n }", "language": "swift" } ] } [/block] (You could also evaluate the intercept logic elsewhere if it makes more sense for your app.) On the server, you would add an action that is only true if the desired view is loaded. First, add a new action to your intercept by clicking on **Add Intercept Display Logic**. You will then see the action chooser: [block:image] { "images": [ { "image": [ "https://files.readme.io/8f18686-Screen_Shot_2018-01-12_at_10.48.37_AM.png", "Screen Shot 2018-01-12 at 10.48.37 AM.png", 2172, 1464, "#f0f1f2" ] } ] } [/block] Add the logic shown above. The custom property is **viewController** and in the case of the sample, its value is **ViewController**. You could create new action sets (click on **Add Another Action Set**) for different view controllers (**SettingsViewController**, **LoginViewController**, for instance) and present different surveys for each. You can also use the **View Count** **Total Views** and **Unique Views** values for additional conditional processing. The **Total Views** value is incremented every time your app calls **registerViewVisitWithViewName**. The **Unique Views** value is incremented every time your app calls **registerViewVisitWithViewName** with a unique name. # Using Embedded Data By using the **properties** collection, you can push data back to the server, which can pass it on to your survey as embedded data. For more information about using embedded data with intercepts, see [Setting up intercepts](https://www.qualtrics.com/support/website-app-feedback/common-use-cases/mobile-app-feedback-project/#SettingUptheIntercept). For more information about embedded data and surveys, see [Embedded Data](https://www.qualtrics.com/support/survey-platform/survey-module/survey-flow/standard-elements/embedded-data/). In the example below, the app will send the user's first name back to the server to be used as [piped text](https://www.qualtrics.com/support/survey-platform/survey-module/editing-questions/piped-text/piped-text-overview/) in the survey. If the app requires the user to create an account, the user's first name should be readily available. In the Experience Management (XM) portal, locate your specific Website / App Feedback project and open the intercept and choose the embedded data from the **Options** dropdown on the action set. [block:image] { "images": [ { "image": [ "https://files.readme.io/d07a54e-Screen_Shot_2018-01-09_at_2.31.35_PM.png", "Screen Shot 2018-01-09 at 2.31.35 PM.png", 2004, 906, "#edeff0" ], "caption": "Choose Embedded Data..." } ] } [/block] Now choose **Custom Property** from **Value** and use type `firstName` in both the fields as shown below: [block:image] { "images": [ { "image": [ "https://files.readme.io/a083f76-Screen_Shot_2018-01-09_at_1.54.11_PM.png", "Screen Shot 2018-01-09 at 1.54.11 PM.png", 1970, 588, "#f3f3cc" ], "caption": "Create a custom property called firstName." } ] } [/block] Now you need to choose **Insight Platform** from the top of the screen to edit the survey to include the **firstName** value in the survey. Open your survey project and choose a question. You will modify the question text so it includes an embedded data field. Choose **Piped Text...** and then choose **Embedded Data Field** and type **firstName** in the text field as shown below: [block:image] { "images": [ { "image": [ "https://files.readme.io/7ba83b8-Embedded_fill-resaved-01.png", "Embedded fill-resaved-01.png", 1275, 861, "#f6f6f9" ] } ] } [/block] The question should look like this after you added the embedded text: [block:image] { "images": [ { "image": [ "https://files.readme.io/b5b73f0-Embedded_filled_in-2-resaved-01.png", "Embedded filled in-2-resaved-01.png", 1275, 705, "#f5f5fa" ] } ] } [/block] In the view controller source file, add the following code to the **viewDidLoad** method. [block:code] { "codes": [ { "code": "[Qualtrics.shared.properties setStringWithString:@\"John\" for:@\"firstName\"] ;", "language": "objectivec", "name": "Objective C" }, { "code": "Qualtrics.shared.properties.setString(string: \"John\", for: \"firstName\")", "language": "swift", "name": "Swift" } ] } [/block] When you run the app and display the survey, you should see the **firstName** filled in as shown below: [block:image] { "images": [ { "image": [ "https://files.readme.io/e613e90-d9deca4-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-01_at_15.44.47.png", "d9deca4-Simulator_Screen_Shot_-_iPhone_8_Plus_-_2018-01-01_at_15.44.47.png", 1262, 2228, "#f3f3f3" ] } ] } [/block] # Controlling the Survey Presentation Rather than calling the Qualtrics **display** method, you can design and build your own survey invitation prompt and then call the **getSurveyUrl** method to present the survey in a **WKWebView** (in the handler for your "Take survey" action). The first step is to include the WebKit framework in ViewController.m (or .swift). [block:code] { "codes": [ { "code": "#import \"ViewController.h\"\n// Add the following line:\n@import WebKit;\n@import Qualtrics;", "language": "objectivec" }, { "code": "import UIKit\n// add the following line:\nimport WebKit\nimport Qualtrics", "language": "swift" } ] } [/block] Then you need to implement your custom invitation in the callback function of evaluateTargetingLogic. An example of how to do so is shown below with a simple custom survey invitation dialog: [block:code] { "codes": [ { "code": " [[Qualtrics shared] evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\n if ([targetingResult passed])\n {\n \n \t// Remove the [Qualtrics.shared display]; line and add the below code\n // In the custom invitation scenario, be sure to record the impressions and clicks\n [targetingResult recordImpression];\n \n UIAlertController* alert = [UIAlertController alertControllerWithTitle:@\"Give Feedback\"\n message:@\"Would you like to take a brief survey?\"\n preferredStyle:UIAlertControllerStyleAlert];\n \n UIAlertAction* defaultAction = [UIAlertAction\n actionWithTitle:@\"OK\"\n style:UIAlertActionStyleDefault\n handler:^(UIAlertAction * action) {\n [self presentSurvey:targetingResult];\n }];\n UIAlertAction *cancelAction = [UIAlertAction\n actionWithTitle:@\"Cancel\"\n style:UIAlertActionStyleCancel\n handler:nil];\n [alert addAction:defaultAction];\n [alert addAction:cancelAction];\n [self presentViewController:alert animated:YES completion:nil];\n \n }];\n", "language": "objectivec" }, { "code": " Qualtrics.shared.evaluateTargetingLogic { (targetingResult) in\n if targetingResult.passed()\n {\n\n // Remove the [Qualtrics.shared display]; line and add the below code\n // In the custom invitation scenario, be sure to record the impressions and clicks\n targetingResult.recordImpression()\n \n let alert = UIAlertController(title: \"Give Feedback\", message: \"Would you like to take a brief survey?\", preferredStyle: .alert)\n let defaultAction = UIAlertAction(title: \"OK\", style: UIAlertActionStyle.default, handler: { (action) in\n self.presentSurvey(targetingResult: TargetingResult)\n })\n let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel, handler:nil)\n \n alert.addAction(defaultAction)\n alert.addAction(cancelAction)\n self.present(alert, animated: true, completion: nil)\n \n }\n }\n", "language": "swift" } ] } [/block] Add two new methods as shown below: [block:code] { "codes": [ { "code": "- (void)presentSurvey:(TargetingResult *)targetingResult\n{\n NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:[targetingResult getSurveyUrl]];\n WKWebView *webView = [[WKWebView alloc] init];\n UIViewController *surveyViewController = [[UIViewController alloc] init];\n surveyViewController.view = webView;\n \n UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:surveyViewController];\n surveyViewController.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(dismiss)];\n \n \n [Qualtrics.shared recordClick];\n \n [webView loadRequest:request];\n \n [self presentViewController:navigationController animated:YES completion:nil];\n}\n\n- (void)dismiss\n{\n [self dismissViewControllerAnimated:YES completion:nil];\n}\n", "language": "objectivec" }, { "code": " func presentSurvey(targetingResult: TargetingResult)\n {\n let request = NSURLRequest(url: URL(string: targetingResult.getSurveyUrl())!)\n let webView = WKWebView();\n let surveyViewController = UIViewController()\n surveyViewController.view = webView;\n \n let navigationController = UINavigationController(rootViewController: surveyViewController)\n surveyViewController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissSurvey))\n \n targetingResult.recordClick()\n \n webView.load(request as URLRequest)\n \n self.present(navigationController, animated: true, completion: nil) \n }\n \n @objc func dismissSurvey()\n {\n self.dismiss(animated: true, completion: nil)\n }", "language": "swift" } ] } [/block] The new code presents the user with an alert asking whether it is okay to show a survey. The **presentSurvey** method handles the presentation of the survey. The **presentSurvey** method creates a web view inside a navigation controller, adds a "Done" button (to allow exiting the survey), and displays the survey at the URL from the **targetingResult.getSurveyUrl()** method. # Bypassing the Give Feedback Prompt Typically, you would want to bypass the give feedback prompt when your users tap on a **Give Feedback** button. If you were to use the **display** method, they would be asked whether they want to give feedback after they had already chosen to give feedback. To avoid that, you can use the following code example instead of calling the **display** method. [block:code] { "codes": [ { "code": "[Qualtrics.shared evaluateTargetingLogicWithCompletion:^(TargetingResult * _Nonnull targetingResult) {\n if(targetingResult.passed)\n {\n NSString *url = targetingResult.getSurveyUrl;\n \n // Record an impression\n [targetingResult recordImpression];\n \n // Record a click\n [targetingResult recordClick];\n \n // Run this operation on the UI thread\n dispatch_async(dispatch_get_main_queue(), ^{\n // Create a QualtricsSurveyViewController activity, passing it the URL provided by the TargetingResult object\n // Any valid URL can be displayed using the QualtricsSurveyViewController, not only survey URLs\n QualtricsSurveyViewController *surveyViewController = [[QualtricsSurveyViewController alloc] initWithUrl:url];\n // Set the QualtricsSurveyViewController to present on top of the current view\n surveyViewController.modalPresentationStyle = UIModalPresentationOverCurrentContext;\n // Present the QualtricsSurveyViewController\n [self presentViewController:surveyViewController animated:YES completion:nil];\n });\n }\n \n}];\n", "language": "objectivec" }, { "code": "Qualtrics.shared.evaluateTargetingLogic(completion: {(targetingResult: TargetingResult) -> Void in\n if targetingResult.passed(), let url = targetingResult.getSurveyUrl() {\n // Record an impression\n targetingResult.recordImpression()\n \n // Record a click\n targetingResult.recordClick()\n \n // Run this operation on the UI thread\n DispatchQueue.main.async {\n // Create a QualtricsSurveyViewController view controller, passing it the URL provided by the TargetingResult object\n // Any valid URL can be displayed using the QualtricsSurveyViewController, not only survey URLs\n let surveyViewController = QualtricsSurveyViewController(url: url)\n \n // Set the QualtricsSurveyViewController to present on top of the current view\n surveyViewController.modalPresentationStyle = .overCurrentContext\n \n // Present the QualtricsSurveyViewController\n self.present(surveyViewController, animated: true, completion: nil)\n }\n }\n})\n", "language": "swift" } ] } [/block] [block:api-header] { "title": "Where to Go from Here" } [/block] For information about troubleshooting, see [Troubleshooting](doc:troubleshooting), and for the App Feedback iOS API reference, see [App Feedback iOS API Reference](doc:si-ios-sdk-reference). For information about iOS SDK releases, see [iOS Releases](doc:si-ios-releases). To get started developing Android apps with the Mobile App SDK, see [Getting Started with the Mobile App SDK on Android](doc:getting-started-si-with-android). For reference documentation on the App Feedback APIs for Android, see [App Feedback Android API Reference](doc:si-android-sdk-reference). For information about the Android SDK releases, see [Android Releases](doc:si-android-releases).