{"_id":"5ab565f112d24900747242a7","category":{"_id":"5ab565f112d249007472429c","version":"5ab565f112d2490074724298","project":"57c88b374434350e00509d7f","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2014-11-05T21:56:07.756Z","from_sync":false,"order":3,"slug":"support","title":"Support"},"user":"55e5ba046015ce1900eadb8e","parentDoc":null,"project":"57c88b374434350e00509d7f","version":{"_id":"5ab565f112d2490074724298","project":"57c88b374434350e00509d7f","__v":1,"createdAt":"2018-03-23T20:39:13.299Z","releaseDate":"2018-03-23T20:39:13.299Z","categories":["5ab565f112d2490074724299","5ab565f112d249007472429a","5ab565f112d249007472429b","5ab565f112d249007472429c"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"3.6.0","version":"3.6.0"},"githubsync":"","__v":0,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2016-03-14T13:37:25.980Z","link_external":false,"link_url":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":16,"body":"In this guide, we list out the necessary steps to upgrade the Primer iOS SDK integration to the latest and greatest.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Upgrading from v3.0.1 to v3.1.0\"\n}\n[/block]\nIn v3.1.0 we introduced a couple of improvements and changes to the SDK's public interface that makes integration easier and more lightweight. Here's how you can leverage these changes:\n\n####Integration\n\nIf you integrated our SDK manually we now made things a bit easier. Instead of pasting the whole simulator chunk stripping script as a `Run Script Phase`, just use the script we bundled in, with this one liner:\n`sh \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Primer.framework/integrate.sh\"`\n\n####Onboarding\n\nThe `Install`, `Signup`, and `LoggedOut` experience types are now deprecated. Please use the `NewUser` type instead of these. If you keep using any of the old values the SDK automatically maps them to the new one.\n\n####Authentication\n\nThe delegation method for uniqueness validation (and uniqueness as a feature on the Dashboard) is now deprecated. It's behaviour is matched to the new validation method, which gives you all fields of the given screen for validation.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// The old delegate method header:\\n\\n- (void)validateUniqueFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;\\n\\n// Should be updated to the new one:\\n\\n- (void)validateFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// The old delegate method header:\\n\\npublic func validateUniqueFields(fields: [String : AnyObject], completion: PMRValidationBlock)\\n\\n// Should be updated to the new one:\\n\\npublic func validateFields(fields: [String : AnyObject], completion: PMRValidationBlock)\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Upgrading from v2.4.4 to v3.0.0\"\n}\n[/block]\nIn v3.0.0 we re-architectured and rewrote our whole SDK to make it even better, more stable, and future proof. While doing so we cleaned up the public interface in a major way. Here's how you can upgrade:\n\n####Integration\n\nFirst and foremost let us note that the Primer iOS SDK is now a real Cocoa Touch Framework, thus requiring us to increase our deployment target to iOS 8. That of course means we now support integration through Carthage as well as CocoaPods.\n\nIf you use CocoaPods for integration, update the Primer line in your `Podfile` to `pod 'Primer' '~> 3.0` and run `pod update`.\n\nIf you manually integrated make sure to remove `Primer.framework` and `Primer.bundle` from your project, then download the latest release from our [GitHub repository](https://github.com/goprimer/primer-ios-sdk) and drop `Primer.framework` into your Xcode project.\n\nIf you'd like to use Carthage, please clean up your previous integration first. Then add `github \"goprimer/primer-ios-sdk\" ~> 3.0` to your `Cartfile` and add `$(SRCROOT)/Carthage/Build/iOS/Primer.framework` under \"Input Files\" for the `copy-frameworks` script Build Phase as usual.\n\n####Referencing Primer\n\nFrom now on referencing the main Primer interface is much cleaner. Instead of accessing features through the singleton with `-sharedInstance`, simply use the `Primer` class.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// All references in the format:\\n\\n[[Primer sharedInstance] doSomething];\\n\\n// Should be updated to:\\n\\n[Primer doSomething];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// All references in the format:\\n\\nPrimer.sharedInstance().doSomething()\\n\\n// Should be updated to:\\n\\nPrimer.doSomething()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n####Initializing the SDK\n\nInstead of calling any of the `-registerClientWithToken...` methods you should use `+startWithToken:`. Alternatively, you now have the option to provide your token in your app's info plist, under the `PrimerToken` key, and simply call `[Primer start]` in your application delegate's `-didFinishLaunchingWithOptions:` method.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// All client registration methods similar to this:\\n\\n[[Primer sharedInstance] registerClientWithToken::::at:::\\\"[[app:token]]\\\"];\\n\\n// Should be updated to:\\n\\n[Primer startWithToken:@\\\"[[app:token]]\\\"];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// All client registration methods similar to this:\\n\\nPrimer.sharedInstance().registerClientWithToken(\\\"TOKEN\\\")\\n\\n// Should be updated to:\\n\\nPrimer.startWithToken(\\\"TOKEN\\\")\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nNote that if you used the `PMR_REFERRER_PARAMS_KEY...` constants with the referrer completion block of the registration method, you should take a look at the [Attribution](#section-attribution) section of this guide, to see what are the new ways to acquire this information.\n\n####Onboarding\n\nThe biggest change related to presenting Primer experiences is that we now only support delayed presentation of Primer experiences. Because of this, if you previously set the `delayAutomaticLaunch` property to `NO`, and did not call the `-appLaunchComplete` method you will now have to start using one of the new presentation methods in the `-viewDidLoad` method of your first view controller to be presented to the user.\n\nIf you did call `-appLaunchComplete`, you should update your code to use `+presentExperience` or one if its more advanced versions in its place. They let you provide different settings (including the view contoller to present on for example), a completion block, or even load the experience asynchronously and present it yourself.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update this:\\n\\n- (void)viewDidLoad {\\n  ...\\n  [[Primer sharedInstance] appLaunchComplete];\\n}\\n\\n// To this:\\n\\n- (void)viewDidLoad {\\n  ...\\n  [Primer presentExperience];\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Update this:\\n\\noverride func viewDidLoad() {\\n    ...\\n    Primer.sharedInstance().appLaunchComplete()\\n}\\n\\n// To this:\\n\\noverride func viewDidLoad() {\\n    ...\\n    Primer.presentExperience()\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nSince the Primer experiences are optimized for portrait orientation, if your application supports landscape you'll have to implement the `-application:supportedInterfaceOrientationsForWindow:` method in your app delegate, and check the presentation state.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {\\n    if ([Primer isPresentingExperience]) {\\n        return UIInterfaceOrientationMaskPortrait;\\n    }\\n    return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape;\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {\\n    if Primer.isPresentingExperience() {\\n        return .Portrait\\n    }\\n    return [.Portrait, .Landscape]\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nThe name of the `-nextExperienceScreen` method got updated to `+goToNextExperienceScreen`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update this:\\n\\n[[Primer sharedInstance] nextExperienceScreen];\\n\\n// To this:\\n\\n[Primer goToNextExperienceScreen];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Update this:\\n\\nPrimer.sharedInstance().nextExperienceScreen()\\n\\n// To this:\\n\\nPrimer.goToNextExperienceScreen()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nIf you have local default files bundled in your app - `PMRDefault.json` and `PMRDefault.xcassets` - please make sure to update those by downloading a fresh copy from your [Project Settings](https://goprimer.com/dashboard#/project//edit) in Dashboard.\n\nFor more information about Onboarding please take a look at its [reference page](doc:onboarding).\n\n####Authentication\n\nThe onboarding delegation is now achievable by conforming to the `PMRExperienceDelegate` protocol (formerly `PMROnboardDelegate`). When you set the delegate please use the new `+setExperienceDelegate:` method instead of `-setOnboardDelegate:`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update this:\\n\\n[[Primer sharedInstance] setOnboardDelegate:delegate];\\n\\n// To this:\\n\\n[Primer setExperienceDelegate:delegate];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Update this:\\n\\nPrimer.sharedInstance().setOnboardDelegate(delegate)\\n\\n// To this:\\n\\nPrimer.setExperienceDelegate(delegate)\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\n While most of the delegate methods remained the same in nature, we improved their declarations, so make sure to update the headers of the methods you implemented in conforming classes.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// The old delegate method headers:\\n\\n- (void)validateUniqueInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\\n\\n- (void)loginWithInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\\n\\n- (void)signupWithInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\\n\\n- (void)authWithFacebook:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\\n\\n- (void)recoverWithInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\\n\\n// Should be updated to the new ones:\\n\\n- (void)validateUniqueFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;\\n\\n- (void)logInWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion;\\n\\n- (void)signUpWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion;\\n\\n- (void)authenticateFacebookWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion;\\n\\n- (void)recoverPasswordWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// The old delegate method headers:\\n    \\npublic func validateUniqueInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\\n    \\npublic func loginWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\\n    \\npublic func signupWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\\n\\npublic func authWithFacebook(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\\n    \\npublic func recoverWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\\n\\n// Should be updated to the new ones:\\n\\npublic func validateUniqueFields(fields: [String : AnyObject], completion: PMRValidationBlock)\\n    \\npublic func logInWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock)\\n    \\npublic func signUpWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock)\\n    \\npublic func authenticateFacebookWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock)\\n    \\npublic func recoverPasswordWithFields(fields: [String : AnyObject], completion: PMRValidationBlock)\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nAlso, we renamed and revamped the `PMRValidationResult` (formerly `PMRValidityResult`) class to make it easier to use in real life scenarios where you'd invalidate a result. Instead of setting properties directly you now have the `-invalidateWithErrorMessage:`, `-invalidateField:`, and `-invalidateField:withErrorMessage:` convenience methods at your disposal. When the results are valid you don't have to use any of these, just pass the result instance or `nil` as the completion block's parameter. Where you'd previously set the `userID` on the result now simply pass it as the second parameter of the completion block.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update validation code similar to this:\\n\\n- (void)signupWithInputs:(NSDictionary *)inputs completionBlock:(void (^)(PMRValidityResult *result))completionBlock {\\n\\n    PMRValidityResult *result = [[PMRValidityResult alloc] init];\\n    BOOL signupComplete = [self validateAndSaveUserWithInputs:inputs];\\n    \\n    if (signupComplete) {\\n        result.isValid = YES;\\n        result.userID = inputs[@\\\"email\\\"];\\n    } else {\\n        result.isValid = NO;\\n        result.errorMessage = @\\\"There was an issue signing up.\\\";\\n    }\\n    \\n    completionBlock(result);\\n}\\n\\n// To use the new syntax and features:\\n\\n- (void)signUpWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion {\\n    BOOL signupComplete = [self validateAndSaveUserWithInputs:inputs];\\n    \\n    if (signupComplete) {\\n        completion(nil, inputs[@\\\"email\\\"]);\\n    } else {\\n        PMRValidationResult *result = [[PMRValidationResult alloc] init];\\n        [result invalidateWithErrorMessage:@\\\"There was an issue signing up.\\\"];\\n\\t\\t\\t\\tcompletion(result, nil);\\n    }\\n}\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Update validation code similar to this:\\n\\nfunc signupWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!) {\\n\\n    let result = PMRValidityResult()\\n    let signupComplete: Bool = self.validateAndSaveUserWithInputs(inputs)\\n    \\n    if signupComplete {\\n        result.isValid = true\\n        result.userID = inputs[\\\"email\\\"] as! String\\n    } else {\\n        result.isValid = false\\n        result.errorMessage = \\\"There was an issue signing up.\\\"\\n    }\\n    \\n    completionBlock(result)\\n}\\n\\n// To use the new syntax and features:\\n\\nfunc signUpWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock) {\\n    let signupComplete: Bool = self.validateAndSaveUserWithInputs(inputs)\\n    \\n    if (signupComplete) {\\n        completion(nil, inputs[\\\"email\\\"])\\n    } else {\\n        let result = PMRValidityResult()\\n        result.invalidateWithErrorMessage(\\\"There was an issue signing up.\\\")\\n\\t\\t\\t\\tcompletion(result, nil);\\n    }\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nIn previous version of the SDK the Facebook access token was included in the inputs dictionary of the relevant delegate method. In the new, updated callback this is not part of the fields dictionary, to acquire the access token after upgrading please use the Facebook iOS SDK's `FBSDKAccessToken` class and its methods.\n\nFor more information about Authentication please take a look at its [reference page](doc:authentication).\n\n####User Management\n\nIn case your app does not require users to log in, and you indicated this through setting `requiresLogin` as a property in previous versions, please update your integration to use the `+setRequiresLogin:` method.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// This should be updated:\\n\\n[Primer sharedInstance].requiresLogin = NO;\\n\\n// To this:\\n\\n[Primer setRequiresLogin:NO];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// This should be updated:\\n\\nPrimer.sharedInstance().requiresLogin = false\\n\\n// To this:\\n\\nPrimer.setRequiresLogin(false)\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nIf you provide ways for users to sign up or log in outside of the Primer experiences you might already use one of the `-loginUser...` methods. You now have the option to call the new `+signUpUser...` methods in situations when you log your user in for the first time. Both the signup and login methods send Primer events specific to their action, so we recommend using the method best suited to the action the user is taking for more accurate tracking.\n\nNew in this version is the introduction of the `PMRUser` class, which wraps all of the user related information into an instance under the `+currentUser` accessor. Getting the ID, the name, properties, active ABN tests, and the last viewed variation are now all possible through this interface.\n\nWhen logging the user out, instead of `-showLogoutScreen` please simply use the new presentation methods (like `+presentExperience` or `+presentExperienceWithSettings:`).\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// This should be updated:\\n\\n[[Primer sharedInstance] logoutUser];\\n[[Primer sharedInstance] showLogoutScreen];\\n\\n// To this:\\n\\n[Primer logOutUser];\\n[Primer presentExperience];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// This should be updated:\\n\\nPrimer.sharedInstance().logoutUser()\\nPrimer.sharedInstance().showLogoutScreen()\\n\\n// To this:\\n\\nPrimer.logOutUser()\\nPrimer.presentExperience()\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nFor more information about User Management please take a look at its [reference page](doc:user-management).\n\n####Event Tracking\n\nWe also cleaned up the custom event tracking interface. So instead of using `-track:` or `-track:properties:` you should use the more descriptive `+trackEventWithName:parameters:` method.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update this:\\n\\n[[Primer sharedInstance] track:@\\\"Custom Event\\\" properties:@{@\\\"parameter\\\": @\\\"value\\\"}];\\n\\n// To this:\\n\\n[Primer trackEventWithName:@\\\"Custom Event\\\" parameters:@{@\\\"parameter\\\": @\\\"value\\\"}];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Update this:\\n\\nPrimer.sharedInstance().track(\\\"Custom Event\\\", properties:[\\\"parameter\\\": \\\"value\\\"])\\n\\n// To this:\\n\\nPrimer.trackEventWithName(\\\"Custom Event\\\", parameters:[\\\"parameter\\\": \\\"value\\\"])\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nIf you registered an observer for the Primer event notifications you should update its name from `PrimerEventFiredNotification` to `PMREventFiredNotification`. The keys used for accessing the user info dictionaries were also updated similarly, swapping the `Primer` prefix to `PMR`.\n\nFor more information about Event Tracking please take a look at its [reference page](doc:event-tracking).\n\n####Attribution\n\nJust like with users, we are introducing a new wrapper class for attribution related information, called `PMRAttribution`. While in previous versions of the SDK you'd have to gather this data from the shared instance using multiple methods and the `PMR_REFERRER_PARAMS_KEY...` constants, we now have two asynchronous methods that will return either the install or the latest (formerly called return) attribution after Primer launched itself: `+getInstallAttributionWithCompletion:` and `+getLatestAttributionWithCompletion:`.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update attribution code similar to this:\\n\\ntargeting = [[Primer sharedInstance] getInstallTargeting];\\n\\n// Or this:\\n\\n[[Primer sharedInstance] registerClientWithToken:@\\\"[[app:token]]\\\" andReferrerBlock:^(NSDictionary *params, BOOL successful) {\\n    targeting = params[PMR_REFERRER_PARAMS_KEY_TARGETING];\\n}];\\n\\n// To this:\\n\\n[Primer getInstallAttributionWithCompletion:^(PMRAttribution *attribution) {\\n    targeting = attribution.targetingName;\\n}];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Update attribution code similar to this:\\n\\ntargeting = Primer.sharedInstance().getInstallTargeting()\\n\\n// Or this:\\n\\nPrimer.sharedInstance().registerClientWithToken(\\\"[[app:token]]\\\") { (parameters: [NSObject : AnyObject]!, successful: Bool) -> Void in\\n    targeting = params[PMR_REFERRER_PARAMS_KEY_TARGETING]\\n}\\n\\n// To this:\\n\\nPrimer.getInstallAttributionWithCompletion { (PMRAttribution attribution) -> Void in\\n    targeting = attribution.targetingName\\n}\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nFor more information about Attribution please take a look at its [reference page](doc:attribution). \n\n####Logging\n\nIf you previously used `verboseLogging` in debug builds, we are happy to let you know we now have a more advanced logging system instead, with multiple levels of logs customizable through `+setLoggingLevel:`. Please take a look at the `PMRLoggingLevel` header to get more information about each of the levels and what they are used for. In tandem with this, we removed `verboseLogging` and `debugMode` from the SDK.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// Update the debug logging settings:\\n\\n[Primer sharedInstance].debugMode = YES;\\n[Primer sharedInstance].verboseLogging = YES;\\n\\n// To this:\\n\\n[Primer setLoggingLevel:PMRLoggingLevelDebug];\",\n      \"language\": \"objectivec\"\n    },\n    {\n      \"code\": \"// Update the debug logging settings:\\n\\nPrimer.sharedInstance().debugMode = YES;\\nPrimer.sharedInstance().verboseLogging = YES;\\n\\n// To this:\\n\\nPrimer.setLoggingLevel(.Debug)\",\n      \"language\": \"swift\"\n    }\n  ]\n}\n[/block]\nFor more information about logging please take a look at the relevant section in [Event Tracking](doc:event-tracking).\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Need help?\"\n}\n[/block]\nHave some questions while upgrading? Don't hesitate to [Contact Us](doc:contact-us).","excerpt":"","slug":"upgrade-guide","type":"basic","title":"Upgrade Guide"}
In this guide, we list out the necessary steps to upgrade the Primer iOS SDK integration to the latest and greatest. [block:api-header] { "type": "basic", "title": "Upgrading from v3.0.1 to v3.1.0" } [/block] In v3.1.0 we introduced a couple of improvements and changes to the SDK's public interface that makes integration easier and more lightweight. Here's how you can leverage these changes: ####Integration If you integrated our SDK manually we now made things a bit easier. Instead of pasting the whole simulator chunk stripping script as a `Run Script Phase`, just use the script we bundled in, with this one liner: `sh "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Primer.framework/integrate.sh"` ####Onboarding The `Install`, `Signup`, and `LoggedOut` experience types are now deprecated. Please use the `NewUser` type instead of these. If you keep using any of the old values the SDK automatically maps them to the new one. ####Authentication The delegation method for uniqueness validation (and uniqueness as a feature on the Dashboard) is now deprecated. It's behaviour is matched to the new validation method, which gives you all fields of the given screen for validation. [block:code] { "codes": [ { "code": "// The old delegate method header:\n\n- (void)validateUniqueFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;\n\n// Should be updated to the new one:\n\n- (void)validateFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;", "language": "objectivec" }, { "code": "// The old delegate method header:\n\npublic func validateUniqueFields(fields: [String : AnyObject], completion: PMRValidationBlock)\n\n// Should be updated to the new one:\n\npublic func validateFields(fields: [String : AnyObject], completion: PMRValidationBlock)", "language": "swift" } ] } [/block] [block:api-header] { "type": "basic", "title": "Upgrading from v2.4.4 to v3.0.0" } [/block] In v3.0.0 we re-architectured and rewrote our whole SDK to make it even better, more stable, and future proof. While doing so we cleaned up the public interface in a major way. Here's how you can upgrade: ####Integration First and foremost let us note that the Primer iOS SDK is now a real Cocoa Touch Framework, thus requiring us to increase our deployment target to iOS 8. That of course means we now support integration through Carthage as well as CocoaPods. If you use CocoaPods for integration, update the Primer line in your `Podfile` to `pod 'Primer' '~> 3.0` and run `pod update`. If you manually integrated make sure to remove `Primer.framework` and `Primer.bundle` from your project, then download the latest release from our [GitHub repository](https://github.com/goprimer/primer-ios-sdk) and drop `Primer.framework` into your Xcode project. If you'd like to use Carthage, please clean up your previous integration first. Then add `github "goprimer/primer-ios-sdk" ~> 3.0` to your `Cartfile` and add `$(SRCROOT)/Carthage/Build/iOS/Primer.framework` under "Input Files" for the `copy-frameworks` script Build Phase as usual. ####Referencing Primer From now on referencing the main Primer interface is much cleaner. Instead of accessing features through the singleton with `-sharedInstance`, simply use the `Primer` class. [block:code] { "codes": [ { "code": "// All references in the format:\n\n[[Primer sharedInstance] doSomething];\n\n// Should be updated to:\n\n[Primer doSomething];", "language": "objectivec" }, { "code": "// All references in the format:\n\nPrimer.sharedInstance().doSomething()\n\n// Should be updated to:\n\nPrimer.doSomething()", "language": "swift" } ] } [/block] ####Initializing the SDK Instead of calling any of the `-registerClientWithToken...` methods you should use `+startWithToken:`. Alternatively, you now have the option to provide your token in your app's info plist, under the `PrimerToken` key, and simply call `[Primer start]` in your application delegate's `-didFinishLaunchingWithOptions:` method. [block:code] { "codes": [ { "code": "// All client registration methods similar to this:\n\n[[Primer sharedInstance] registerClientWithToken:@\"[[app:token]]\"];\n\n// Should be updated to:\n\n[Primer startWithToken:@\"[[app:token]]\"];", "language": "objectivec" }, { "code": "// All client registration methods similar to this:\n\nPrimer.sharedInstance().registerClientWithToken(\"TOKEN\")\n\n// Should be updated to:\n\nPrimer.startWithToken(\"TOKEN\")", "language": "swift" } ] } [/block] Note that if you used the `PMR_REFERRER_PARAMS_KEY...` constants with the referrer completion block of the registration method, you should take a look at the [Attribution](#section-attribution) section of this guide, to see what are the new ways to acquire this information. ####Onboarding The biggest change related to presenting Primer experiences is that we now only support delayed presentation of Primer experiences. Because of this, if you previously set the `delayAutomaticLaunch` property to `NO`, and did not call the `-appLaunchComplete` method you will now have to start using one of the new presentation methods in the `-viewDidLoad` method of your first view controller to be presented to the user. If you did call `-appLaunchComplete`, you should update your code to use `+presentExperience` or one if its more advanced versions in its place. They let you provide different settings (including the view contoller to present on for example), a completion block, or even load the experience asynchronously and present it yourself. [block:code] { "codes": [ { "code": "// Update this:\n\n- (void)viewDidLoad {\n ...\n [[Primer sharedInstance] appLaunchComplete];\n}\n\n// To this:\n\n- (void)viewDidLoad {\n ...\n [Primer presentExperience];\n}", "language": "objectivec" }, { "code": "// Update this:\n\noverride func viewDidLoad() {\n ...\n Primer.sharedInstance().appLaunchComplete()\n}\n\n// To this:\n\noverride func viewDidLoad() {\n ...\n Primer.presentExperience()\n}", "language": "swift" } ] } [/block] Since the Primer experiences are optimized for portrait orientation, if your application supports landscape you'll have to implement the `-application:supportedInterfaceOrientationsForWindow:` method in your app delegate, and check the presentation state. [block:code] { "codes": [ { "code": "- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {\n if ([Primer isPresentingExperience]) {\n return UIInterfaceOrientationMaskPortrait;\n }\n return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscape;\n}", "language": "objectivec" }, { "code": "func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {\n if Primer.isPresentingExperience() {\n return .Portrait\n }\n return [.Portrait, .Landscape]\n}", "language": "swift" } ] } [/block] The name of the `-nextExperienceScreen` method got updated to `+goToNextExperienceScreen`. [block:code] { "codes": [ { "code": "// Update this:\n\n[[Primer sharedInstance] nextExperienceScreen];\n\n// To this:\n\n[Primer goToNextExperienceScreen];", "language": "objectivec" }, { "code": "// Update this:\n\nPrimer.sharedInstance().nextExperienceScreen()\n\n// To this:\n\nPrimer.goToNextExperienceScreen()", "language": "swift" } ] } [/block] If you have local default files bundled in your app - `PMRDefault.json` and `PMRDefault.xcassets` - please make sure to update those by downloading a fresh copy from your [Project Settings](https://goprimer.com/dashboard#/project//edit) in Dashboard. For more information about Onboarding please take a look at its [reference page](doc:onboarding). ####Authentication The onboarding delegation is now achievable by conforming to the `PMRExperienceDelegate` protocol (formerly `PMROnboardDelegate`). When you set the delegate please use the new `+setExperienceDelegate:` method instead of `-setOnboardDelegate:`. [block:code] { "codes": [ { "code": "// Update this:\n\n[[Primer sharedInstance] setOnboardDelegate:delegate];\n\n// To this:\n\n[Primer setExperienceDelegate:delegate];", "language": "objectivec" }, { "code": "// Update this:\n\nPrimer.sharedInstance().setOnboardDelegate(delegate)\n\n// To this:\n\nPrimer.setExperienceDelegate(delegate)", "language": "swift" } ] } [/block] While most of the delegate methods remained the same in nature, we improved their declarations, so make sure to update the headers of the methods you implemented in conforming classes. [block:code] { "codes": [ { "code": "// The old delegate method headers:\n\n- (void)validateUniqueInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\n\n- (void)loginWithInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\n\n- (void)signupWithInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\n\n- (void)authWithFacebook:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\n\n- (void)recoverWithInputs:(NSDictionary *)inputs completionBlock:(PMRValidityResultBlock)completionBlock;\n\n// Should be updated to the new ones:\n\n- (void)validateUniqueFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;\n\n- (void)logInWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion;\n\n- (void)signUpWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion;\n\n- (void)authenticateFacebookWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion;\n\n- (void)recoverPasswordWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRValidationBlock)completion;", "language": "objectivec" }, { "code": "// The old delegate method headers:\n \npublic func validateUniqueInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\n \npublic func loginWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\n \npublic func signupWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\n\npublic func authWithFacebook(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\n \npublic func recoverWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!)\n\n// Should be updated to the new ones:\n\npublic func validateUniqueFields(fields: [String : AnyObject], completion: PMRValidationBlock)\n \npublic func logInWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock)\n \npublic func signUpWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock)\n \npublic func authenticateFacebookWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock)\n \npublic func recoverPasswordWithFields(fields: [String : AnyObject], completion: PMRValidationBlock)", "language": "swift" } ] } [/block] Also, we renamed and revamped the `PMRValidationResult` (formerly `PMRValidityResult`) class to make it easier to use in real life scenarios where you'd invalidate a result. Instead of setting properties directly you now have the `-invalidateWithErrorMessage:`, `-invalidateField:`, and `-invalidateField:withErrorMessage:` convenience methods at your disposal. When the results are valid you don't have to use any of these, just pass the result instance or `nil` as the completion block's parameter. Where you'd previously set the `userID` on the result now simply pass it as the second parameter of the completion block. [block:code] { "codes": [ { "code": "// Update validation code similar to this:\n\n- (void)signupWithInputs:(NSDictionary *)inputs completionBlock:(void (^)(PMRValidityResult *result))completionBlock {\n\n PMRValidityResult *result = [[PMRValidityResult alloc] init];\n BOOL signupComplete = [self validateAndSaveUserWithInputs:inputs];\n \n if (signupComplete) {\n result.isValid = YES;\n result.userID = inputs[@\"email\"];\n } else {\n result.isValid = NO;\n result.errorMessage = @\"There was an issue signing up.\";\n }\n \n completionBlock(result);\n}\n\n// To use the new syntax and features:\n\n- (void)signUpWithFields:(NSDictionary<NSString *, id> *)fields completion:(PMRUserValidationBlock)completion {\n BOOL signupComplete = [self validateAndSaveUserWithInputs:inputs];\n \n if (signupComplete) {\n completion(nil, inputs[@\"email\"]);\n } else {\n PMRValidationResult *result = [[PMRValidationResult alloc] init];\n [result invalidateWithErrorMessage:@\"There was an issue signing up.\"];\n\t\t\t\tcompletion(result, nil);\n }\n}", "language": "objectivec" }, { "code": "// Update validation code similar to this:\n\nfunc signupWithInputs(inputs: [NSObject : AnyObject]!, completionBlock: PMRValidityResultBlock!) {\n\n let result = PMRValidityResult()\n let signupComplete: Bool = self.validateAndSaveUserWithInputs(inputs)\n \n if signupComplete {\n result.isValid = true\n result.userID = inputs[\"email\"] as! String\n } else {\n result.isValid = false\n result.errorMessage = \"There was an issue signing up.\"\n }\n \n completionBlock(result)\n}\n\n// To use the new syntax and features:\n\nfunc signUpWithFields(fields: [String : AnyObject], completion: PMRUserValidationBlock) {\n let signupComplete: Bool = self.validateAndSaveUserWithInputs(inputs)\n \n if (signupComplete) {\n completion(nil, inputs[\"email\"])\n } else {\n let result = PMRValidityResult()\n result.invalidateWithErrorMessage(\"There was an issue signing up.\")\n\t\t\t\tcompletion(result, nil);\n }\n}", "language": "swift" } ] } [/block] In previous version of the SDK the Facebook access token was included in the inputs dictionary of the relevant delegate method. In the new, updated callback this is not part of the fields dictionary, to acquire the access token after upgrading please use the Facebook iOS SDK's `FBSDKAccessToken` class and its methods. For more information about Authentication please take a look at its [reference page](doc:authentication). ####User Management In case your app does not require users to log in, and you indicated this through setting `requiresLogin` as a property in previous versions, please update your integration to use the `+setRequiresLogin:` method. [block:code] { "codes": [ { "code": "// This should be updated:\n\n[Primer sharedInstance].requiresLogin = NO;\n\n// To this:\n\n[Primer setRequiresLogin:NO];", "language": "objectivec" }, { "code": "// This should be updated:\n\nPrimer.sharedInstance().requiresLogin = false\n\n// To this:\n\nPrimer.setRequiresLogin(false)", "language": "swift" } ] } [/block] If you provide ways for users to sign up or log in outside of the Primer experiences you might already use one of the `-loginUser...` methods. You now have the option to call the new `+signUpUser...` methods in situations when you log your user in for the first time. Both the signup and login methods send Primer events specific to their action, so we recommend using the method best suited to the action the user is taking for more accurate tracking. New in this version is the introduction of the `PMRUser` class, which wraps all of the user related information into an instance under the `+currentUser` accessor. Getting the ID, the name, properties, active ABN tests, and the last viewed variation are now all possible through this interface. When logging the user out, instead of `-showLogoutScreen` please simply use the new presentation methods (like `+presentExperience` or `+presentExperienceWithSettings:`). [block:code] { "codes": [ { "code": "// This should be updated:\n\n[[Primer sharedInstance] logoutUser];\n[[Primer sharedInstance] showLogoutScreen];\n\n// To this:\n\n[Primer logOutUser];\n[Primer presentExperience];", "language": "objectivec" }, { "code": "// This should be updated:\n\nPrimer.sharedInstance().logoutUser()\nPrimer.sharedInstance().showLogoutScreen()\n\n// To this:\n\nPrimer.logOutUser()\nPrimer.presentExperience()", "language": "swift" } ] } [/block] For more information about User Management please take a look at its [reference page](doc:user-management). ####Event Tracking We also cleaned up the custom event tracking interface. So instead of using `-track:` or `-track:properties:` you should use the more descriptive `+trackEventWithName:parameters:` method. [block:code] { "codes": [ { "code": "// Update this:\n\n[[Primer sharedInstance] track:@\"Custom Event\" properties:@{@\"parameter\": @\"value\"}];\n\n// To this:\n\n[Primer trackEventWithName:@\"Custom Event\" parameters:@{@\"parameter\": @\"value\"}];", "language": "objectivec" }, { "code": "// Update this:\n\nPrimer.sharedInstance().track(\"Custom Event\", properties:[\"parameter\": \"value\"])\n\n// To this:\n\nPrimer.trackEventWithName(\"Custom Event\", parameters:[\"parameter\": \"value\"])", "language": "swift" } ] } [/block] If you registered an observer for the Primer event notifications you should update its name from `PrimerEventFiredNotification` to `PMREventFiredNotification`. The keys used for accessing the user info dictionaries were also updated similarly, swapping the `Primer` prefix to `PMR`. For more information about Event Tracking please take a look at its [reference page](doc:event-tracking). ####Attribution Just like with users, we are introducing a new wrapper class for attribution related information, called `PMRAttribution`. While in previous versions of the SDK you'd have to gather this data from the shared instance using multiple methods and the `PMR_REFERRER_PARAMS_KEY...` constants, we now have two asynchronous methods that will return either the install or the latest (formerly called return) attribution after Primer launched itself: `+getInstallAttributionWithCompletion:` and `+getLatestAttributionWithCompletion:`. [block:code] { "codes": [ { "code": "// Update attribution code similar to this:\n\ntargeting = [[Primer sharedInstance] getInstallTargeting];\n\n// Or this:\n\n[[Primer sharedInstance] registerClientWithToken:@\"[[app:token]]\" andReferrerBlock:^(NSDictionary *params, BOOL successful) {\n targeting = params[PMR_REFERRER_PARAMS_KEY_TARGETING];\n}];\n\n// To this:\n\n[Primer getInstallAttributionWithCompletion:^(PMRAttribution *attribution) {\n targeting = attribution.targetingName;\n}];", "language": "objectivec" }, { "code": "// Update attribution code similar to this:\n\ntargeting = Primer.sharedInstance().getInstallTargeting()\n\n// Or this:\n\nPrimer.sharedInstance().registerClientWithToken(\"[[app:token]]\") { (parameters: [NSObject : AnyObject]!, successful: Bool) -> Void in\n targeting = params[PMR_REFERRER_PARAMS_KEY_TARGETING]\n}\n\n// To this:\n\nPrimer.getInstallAttributionWithCompletion { (PMRAttribution attribution) -> Void in\n targeting = attribution.targetingName\n}", "language": "swift" } ] } [/block] For more information about Attribution please take a look at its [reference page](doc:attribution). ####Logging If you previously used `verboseLogging` in debug builds, we are happy to let you know we now have a more advanced logging system instead, with multiple levels of logs customizable through `+setLoggingLevel:`. Please take a look at the `PMRLoggingLevel` header to get more information about each of the levels and what they are used for. In tandem with this, we removed `verboseLogging` and `debugMode` from the SDK. [block:code] { "codes": [ { "code": "// Update the debug logging settings:\n\n[Primer sharedInstance].debugMode = YES;\n[Primer sharedInstance].verboseLogging = YES;\n\n// To this:\n\n[Primer setLoggingLevel:PMRLoggingLevelDebug];", "language": "objectivec" }, { "code": "// Update the debug logging settings:\n\nPrimer.sharedInstance().debugMode = YES;\nPrimer.sharedInstance().verboseLogging = YES;\n\n// To this:\n\nPrimer.setLoggingLevel(.Debug)", "language": "swift" } ] } [/block] For more information about logging please take a look at the relevant section in [Event Tracking](doc:event-tracking). [block:api-header] { "type": "basic", "title": "Need help?" } [/block] Have some questions while upgrading? Don't hesitate to [Contact Us](doc:contact-us).