{"_id":"5d729af8b7dc640024d47409","project":"55dd9f2e0efd5821000d54d9","version":{"_id":"55dd9f4dab0e4d210045aae9","__v":45,"project":"55dd9f2e0efd5821000d54d9","createdAt":"2015-08-26T11:13:17.024Z","releaseDate":"2015-08-26T11:13:17.024Z","categories":["55dd9f4dab0e4d210045aaea","55ddb5fa9067202b00ddff6f","55e0472c6bad670d0081f213","55e04764a44fae0d00214671","55e047a9a44fae0d00214672","55e047b258c5460d0076a9a7","55e95e337fc27b2d00d32cf2","55e979bda7ca823900ad549a","55edb8c18dcb210d0056900b","55f0365c8563861700a33765","55f03677d58f9b1900acf996","55f036938eeefc23001ea5de","55f036a38563861700a33767","55f036c08563861700a33769","55f036d02911b72100482cd7","55f036e92911b72100482cd9","55f036fa8563861700a3376b","55f0370ee507711900e58c69","55f0371df6101b1900c70700","55f0374f2911b72100482cdb","55f0375e2911b72100482cdc","560eb0f659cb8d0d0015cd52","560eb25239fad419002ae1e0","561fb64d4d67490d00804b2a","562b9f775a39cd0d009aff22","562ba0505a39cd0d009aff23","562ba149d56bc30d00f0cb18","562ba595f68a5f0d007b1f3b","562ba78fd56bc30d00f0cb1b","562ba8b95a39cd0d009aff27","562baadf6562140d001501d2","562bab37f68a5f0d007b1f3d","562bc1bf9ebc950d000f7523","562bc99ced4bea0d00c11dfa","562bd29c1b98640d00714520","562bd5875a39cd0d009aff60","562bdfabff2da50d002c0aaf","562be0bd5a39cd0d009aff75","57a0b476d8313e1900454439","5b19051beece890003020163","5b34ded01cb20f000391ad6d","5b3a325acffe770003fd29e5","5b3c737a7f7b890003365501","5b3c929b367036000391b11e","5b7c1e210dc2e20003871521"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"2.0.0","version":"2"},"category":{"_id":"5b34ded01cb20f000391ad6d","project":"55dd9f2e0efd5821000d54d9","version":"55dd9f4dab0e4d210045aae9","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2018-06-28T13:12:48.898Z","from_sync":false,"order":1,"slug":"push-notifications","title":"Push notifications"},"user":"55114030a226890d00911658","__v":0,"parentDoc":null,"updates":[],"next":{"pages":[],"description":""},"createdAt":"2019-09-06T17:44:24.839Z","link_external":false,"link_url":"","sync_unique":"","hidden":false,"api":{"settings":"","results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":10,"body":"iOS SDK allows your app users to receive push notifications from your Engagement Cloud account. Engagement Cloud collects your user's email addresses which is then used to identify them so you can send push notifications.\n\nOur iOS SDK uses the [Apple Push Notification Service (APNS)](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) to send push notifications to your contacts. \n\nTo set up push notifications for native iOS apps, complete the following tasks:\n\n1. [Install the iOS SDK](#section-installing-the-ios-sdk)\n2. [Configure the iOS SDK](#section-configuring-the-ios-sdk)\n3. [Initialise the iOS SDK](#section-initialising-the-ios-sdk)\n\n# Installing the iOS SDK\n\nIn order to install the SDK, we will use CMPComapiFoundation, a dependency manager for iOS/MacOS projects. To add the iOS SDK to your Xcode project with CMPComapiFoundation, do the following: \n\n1. Add the iOS SDK\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"# other podfile info\\n\\ntarget '*Your-Target*'\\nuse_frameworks!\\n\\npod 'CMPComapiFoundation'\\n\\nend\",\n      \"language\": \"objectivec\",\n      \"name\": \"Objective-C\"\n    }\n  ]\n}\n[/block]\n2. Install the iOS SDK\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"$ pod install\",\n      \"language\": \"shell\"\n    }\n  ]\n}\n[/block]\n3. Import the iOS SDK in your Objective-C or Swift file\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import <CMPComapiFoundation/CMPComapiFoundation.h>\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n# Initialise\nTo initialise the Comapi SDK, you will need a few pre-requisites listed below:\n\n  * A configured API Space\n  * An authentication provider that can generate a JWT (JSON Web Token) that matches the auth scheme configured for your Api Space\n  * The generated JWT must include the provided nonce as a claim in the generated JWT\n\nIn order for the client to be able to start a session, the config's authenticationDelegate object must conform to the protocol's method:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"NSString *id = <Portal's authentication tab ID claim value>;\\nNSString *issuer = <Portal's authentication tab issuer value>;\\nNSString *audience = <Portal's authentication tab audience value>;\\nNSString *secret = <Portal's authentication tab secret value>;\\n  \\n- (void)client:(CMPComapiClient *)client didReceiveAuthenticationChallenge:(CMPAuthenticationChallenge *)challenge completion:(void (^)(NSString * _Nullable))continueWithToken {\\n  \\t// request a JWT token from your provider (backend server)\\n  \\t// example call\\n  \\t[YourProviderServer getTokenForNonce:challenge.nonce id:id issuer:issuer audience:audience secret:secret completion:^(NSString * token, NSError * error) {\\n    \\t\\t// call continueWithToken block with generated token\\n        if (token && !error) {\\n            continueWithToken(token);\\n        }\\n    }];\\n}\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nA JWT is used to securely transmit information between parties as a JSON object; it's digitally signed, therefore the information can be verified and trusted.  For more information on JWT, click [here](https://jwt.io/introduction/).\n\nThe JWT needs to include claims from the authentication panel in the dashboard. For further guidance, please click [here](https://developer.dotdigital.com/v2/docs/creating-a-push-notification-profile#in-engagement-cloud).\n\nHere's an example implementation of a token generator in Objective-C using JWT:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"#import \\\"CMPAuthenticationManager.h\\\"\\n#import <JWT/JWT.h>\\n\\n:::at:::implementation CMPAuthenticationManager\\n\\n+ (NSString *)generateTokenForNonce:(NSString *)nonce profileID:(NSString *)profileID issuer:(NSString *)issuer audience:(NSString *)audience secret:(NSString *)secret {\\n    NSDate *now = [NSDate date];\\n    NSDate *exp = [NSCalendar.currentCalendar dateByAddingUnit:NSCalendarUnitDay value:30 toDate:now options:0];\\n    \\n    NSDictionary *headers = @{@\\\"typ\\\" : @\\\"JWT\\\"};\\n    NSDictionary *payload = @{@\\\"nonce\\\" : nonce,\\n                               @\\\"sub\\\" : profileID,\\n                               @\\\"iss\\\" : issuer,\\n                               @\\\"aud\\\" : audience,\\n                               @\\\"iat\\\" : [NSNumber numberWithDouble:now.timeIntervalSince1970],\\n                               @\\\"exp\\\" : [NSNumber numberWithDouble:exp.timeIntervalSince1970]};\\n    \\n    NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];\\n    id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@\\\"HS256\\\"];\\n    \\n    NSString *token = [JWTBuilder encodePayload:payload].headers(headers).secretData(secretData).algorithm(algorithm).encode;\\n    return token;\\n}\\n\\n@end\\n  \\n/* Note that this should preferably be generated by your backend, the app should only retreive the token through an HTTP call */\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n# Configuring the iOS SDK\n\nBefore you can configure the iOS SDK, you need the following:\n\n* The value of the [API space ID field in Engagement Cloud](doc:creating-a-push-notification-profile#section-finding-your-api-space-id)\n* A class that creates a JWT token\n* Your app must be set up so that it [asks the user for permission to send push notifications](https://developer.apple.com/documentation/usernotifications/asking_permission_to_use_notifications).\n\nTo configure the iOS SDK:\n\n1. Create a new instance of the `ComapiConfig` class and store it in a variable\n\n2. As the first argument of the `ComapiConfig` instance, pass your API Space ID\n\n3. As the second argument of the `ComapiConfig` instance, pass an instance of [a class that creates a JWT token](#section-ios-jwt-code-sample)\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"// create a config object with your api-space-id and an object conforming to CMPAuthenticationDelegate protocol;\\nCMPComapiConfig *config = [[CMPComapiConfig alloc] initWithApiSpaceID:@\\\"<API_SPACE_ID>\\\" authenticationDelegat:<CMPAuthenticationDelegate_Conforming_Object>];\\n\\nCMPComapiClient *client = [CMPComapi initialiseWithConfig:config];\\n// we can use the client object now\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n# Retrieving the client\n\nThe client can be retrieved either as a separate object using:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"CMPComapiClient *client = [CMPComapi initialiseWithConfig:config];\\n// client instance ready to use\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nor as a singleton:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[CMPComapi initialiseSharedInstanceWithConfig:config];\\n\\nCMPComapiClient *client = [Comapi shared];\\n// shared client ready to use\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n# Start session\n\nCalling Comapi services requires an active session. Once you successfully initialise and retrieve a client object, call:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.session startSessionWithCompletion:^{\\n  // session successfully created\\n} failure:^(NSError * _Nullable error) {\\n  // error ocurred\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nA session will also be automatically created once you call and of the API services, so the above step is not required.\n\nTo end the current session, call:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.session endSessionWithCompletion:^(CMPResult<NSNumber *> * result) {\\n    if (result.error) {\\n      // error occurred\\n    } else {\\n      BOOL success = [result.object boolValue];\\n      if (success) {\\n        // session successfully ended\\n      }     \\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n# Configuring your app to ask users' permission to send them push notifications\n\nTo receive push notifications, app users must give their permission.\n\nCall the `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` [method](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application?language=swift) to get the `deviceToken` string and pass it to the `setPushToken()` method on the `ComapiClient` object. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {\\n    NSMutableString *token = [NSMutableString string];\\n    \\n    const char *data = [deviceToken bytes];\\n    for (NSUInteger i = 0; i < [deviceToken length]; i++) {\\n        [token appendFormat:@\\\"%.2hhx\\\", data[i]];\\n    }\\n  \\n    if (token) {\\n        [client setPushToken:token completion:^(CMPResult<NSNumber *> * result) {\\n          \\tBOOL success = [result.object boolValue];\\n            if (result.error || !success) {\\n                // error occurred\\n            } else {\\n                // configuration successful\\n            }\\n        }];\\n    }\\n    \\n    // rest of you push notification code\\n}\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n# Displaying push notifications when the app is in the foreground\n\nPush notifications are displayed only while the app is in the background. These notification are sent to the notification center and launch your app when users tap them.\n\nIf you want to display the push notification message while the app is in the foreground, do one of the following, depending on the iOS version that your app is running on:\n\n**For iOS 9** \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {\\n    if (application.applicationState == UIApplicationStateActive) {\\n        \\n    }\\n}\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n**For iOS 10 and above** \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {\\n    \\n    completionHandler();\\n}\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n# References\n[block:callout]\n{\n  \"type\": \"success\",\n  \"body\": \"Now ensure your app passes an email address to the SDK for the app user to ensure they get a contact created in Engagement Cloud by [following these instructions](setting-up-push-notifications-new#section-pass-app-user-information-to-the-sdk)\",\n  \"title\": \"Next steps\"\n}\n[/block]\n## Configuring logs\n\nYou can set internal file logs and console logs to the following levels:\n\n* `verbose`\n* `debug`\n* `info`\n* `warning`\n* `error`\n\n## Websocket\n\nWhenever ComapiClient is configured and authenticated, a new session is started which opens a WebSocket connection. This allows for a two-way real-time communication between the client and the server. For more information on WebSocket visit the official RFC site. When open, the socket provides live updates for the profile and conversations which you are a participant of. You can subscribe for these updates to update your views and models.\n\n## Add event listener\n\nRegister the delegate for the incoming events with the CMPComapiClient object. The delegate should conform to the CMPEventListener protocol.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client addEventDelegate:self];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nTo receive the event, implement the following method from CMPEventListener protocol:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)client:(CMPComapiClient *)client didReceiveEvent:(CMPEvent *)event {\\n  \\tswitch (event.type) {\\n        case CMPEventTypeConversationMessageSent:\\n      \\t\\t\\t// message sent event received\\n            break;\\n        case CMPEventTypeConversationParticipantTypingOff:\\n      \\t\\t\\t// participant typing off event received\\n            break;\\n        case CMPEventTypeConversationParticipantTyping:\\n      \\t\\t\\t// participant typing event received\\n            break;\\n        case CMPEventTypeConversationMessageRead:\\n      \\t\\t\\t// message read event received\\n            break;\\n      \\t// case .other events:\\n        default:\\n      \\t\\t\\t// unknown event type\\n            break;\\n    }\\n}\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nPlease note that you can register multiple listeners in your code to handle events in different parts of your application. The events are broadcasted to all registered delegates.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client removeEventDelegate:self];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nRegister the delegate for the incoming events with the CMPComapiClient object. The delegate should conform to the CMPEventListener protocol.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client removeEventDelegate:self];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n## Available events\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Event type\",\n    \"h-1\": \"Event subtype\",\n    \"0-0\": \"profile\",\n    \"0-1\": \"update\",\n    \"0-2\": \"Sent when a user's profile is updated.\",\n    \"1-0\": \"conversation\",\n    \"1-1\": \"participantAdded\",\n    \"1-2\": \"Sent when a participant is added to a conversation. When a conversation is created, this event will also fire with the owner's profileId.\",\n    \"2-0\": \"conversation\",\n    \"2-1\": \"participantUpdated\",\n    \"2-2\": \"Sent when a participant role is updated in a conversation.\",\n    \"3-0\": \"conversation\",\n    \"3-1\": \"participantRemoved\",\n    \"3-2\": \"Sent when a participant is removed from a conversation.\",\n    \"4-0\": \"conversation\",\n    \"4-1\": \"delete\",\n    \"4-2\": \"Sent when a conversation is deleted.\",\n    \"5-0\": \"conversation\",\n    \"5-1\": \"update\",\n    \"5-2\": \"Sent when a conversation's details were updated.\",\n    \"6-0\": \"conversation\",\n    \"6-1\": \"undelete\",\n    \"6-2\": \"Sent when a conversation is restored.\",\n    \"7-0\": \"conversation\",\n    \"7-1\": \"create\",\n    \"7-2\": \"Sent when a new conversation was created.\",\n    \"8-0\": \"conversation\",\n    \"8-1\": \"participantTyping\",\n    \"8-2\": \"Sent when one of the participants is typing a new message.\",\n    \"9-0\": \"conversation\",\n    \"9-1\": \"participantTypingOff\",\n    \"9-2\": \"Sent when the other participant stops typing.\",\n    \"10-0\": \"conversationMessage\",\n    \"10-1\": \"delivered\",\n    \"10-2\": \"Sent when on of the participants updated the message status to 'delivered'.\",\n    \"11-0\": \"conversationMessage\",\n    \"11-1\": \"read\",\n    \"11-2\": \"Sent when one of the participants updated the message status to 'read'.\",\n    \"12-0\": \"conversationMessage\",\n    \"12-1\": \"sent\",\n    \"12-2\": \"Sent when a new message appeared in a conversation. This event will also be delivered to the message sender.\"\n  },\n  \"cols\": 3,\n  \"rows\": 13\n}\n[/block]\nFor all events you can access id, apiSpace and name and context properties. For other events, there are other properties you can access, as follows:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"profile.profileId\",\n      \"language\": \"text\",\n      \"name\": \"profile\"\n    }\n  ]\n}\n[/block]\n##Conversation\nBelow are listed properties specific for the conversation event subtypes.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"conversation.conversationId\\nconversation.payload.profileId\\nconversation.payload.role\",\n      \"language\": \"text\",\n      \"name\": \"participantAdded\"\n    },\n    {\n      \"code\": \"conversation.conversationId\\nconversation.payload.profileId\\nconversation.payload.role\",\n      \"language\": \"text\",\n      \"name\": \"participantUpdated\"\n    },\n    {\n      \"code\": \"conversation.conversationId\\nconversation.payload.profileId\\nconversation.payload.role\",\n      \"language\": \"text\",\n      \"name\": \"participantRemoved\"\n    },\n    {\n      \"code\": \"conversation.conversationId\\nconversation.payload.date\",\n      \"language\": \"text\",\n      \"name\": \"delete\"\n    },\n    {\n      \"code\": \"conversation.profileId\\nconversation.payload.roles\\nconversation.payload.isPublic\\nconversation.payload.participants\",\n      \"language\": \"text\",\n      \"name\": \"undelete\"\n    },\n    {\n      \"code\": \"conversation.conversationId\\nconversation.payload.description\\nconversation.payload.roles\",\n      \"language\": \"text\",\n      \"name\": \"update\"\n    },\n    {\n      \"code\": \"conversation.profileId\\nconversation.payload.roles\\nconversation.payload.isPublic\\nconversation.payload.participants\",\n      \"language\": \"text\",\n      \"name\": \"create\"\n    },\n    {\n      \"code\": \"conversation.account\\nconversation.payload.profileId\\ncovenrsation.payload.conversationId\",\n      \"language\": \"text\",\n      \"name\": \"participantTyping\"\n    },\n    {\n      \"code\": \"conversation.account\\nconversation.payload.profileId\\ncovenrsation.payload.conversationId\",\n      \"language\": \"text\",\n      \"name\": \"participantTypingOff\"\n    }\n  ]\n}\n[/block]\nConversationMessage\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"conversationMessage.messageId\\nconversationMessage.conversationId\\nconversationMessage.profileId\\nconversationMessage.timestamp\",\n      \"language\": \"text\",\n      \"name\": \"delivered\"\n    },\n    {\n      \"code\": \"conversationMessage.messageId\\nconversationMessage.conversationId\\nconversationMessage.profileId\\nconversationMessage.timestamp\",\n      \"language\": \"text\",\n      \"name\": \"sent\"\n    },\n    {\n      \"code\": \"conversationMessage.messageId\\nconversationMessage.metadata\\nconversationMessage.parts\\nconversationMessage.alert\",\n      \"language\": \"text\",\n      \"name\": \"read\"\n    }\n  ]\n}\n[/block]\n## Client APIs\nYou can use **CMPComapi** client instance obtained in Initialisation to access SDK APIs.\n\n##Session\nGet the current **profileID**:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"BOOL isSuccessfullyCreated = [client isSessionSuccessfullyCreated];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nCheck if the session was successfully created:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"BOOL isSuccessfullyCreated = [client isSessionSuccessfullyCreated];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n##Services\nAccessing services is done by calling methods from the proper group:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"client.services.profile\\nclient.services.session\\nclient.services.messaging\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nMost services use CMPResult object as a return value in completion blocks:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"@interface CMPResult<__covariant T> : NSObject\\n\\n@property (nonatomic, nullable) T object;\\n@property (nonatomic, nullable) NSError *error;\\n@property (nonatomic, nullable) NSString *eTag;\\n@property (nonatomic) NSInteger code;\\n\\n- (instancetype)init NS_UNAVAILABLE;\\n\\n- (instancetype)initWithObject:(nullable T)object error:(nullable NSError *)error eTag:(nullable NSString *)eTag code:(NSInteger)code;\\n\\n@end\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nYou can get the ETage value from it, as well as an HTTP status code and potential error.\n\n##Profile services\n\nGet profile:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.profile getProfileForProfileID:@\\\"<PROFILE-ID>\\\" completion:^(CMPResult<CMPProfile *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\",\n      \"name\": null\n    }\n  ]\n}\n[/block]\nQuery profiles with query builder:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.profile queryProfilesWithQueryElements:@[] completion:^(CMPResult<NSArray<CMPProfile *> *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nPatch profile:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.profile patchProfileForProfileID:@\\\"<PROFILE-ID>\\\" attributes:@{@\\\"email\\\" : @\\\"new@email.com\\\"} eTag:nil completion:^(CMPResult<CMPProfile *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nUpdate profile:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.profile updateProfileForProfileID:@\\\"<PROFILE-ID>\\\" attributes:@{@\\\"email\\\" : @\\\"new@email.com\\\"} eTag:nil completion:^(CMPResult<CMPProfile *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n##Messaging services\n\nCreate conversation:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"CMPRoleAttributes *ownerAttributes = [[CMPRoleAttributes alloc] initWithCanSend:YES canAddParticipants:YES canRemoveParticipants:YES];\\n\\n    CMPRoleAttributes *participantAttributes = [[CMPRoleAttributes alloc] initWithCanSend:YES canAddParticipants:NO canRemoveParticipants:NO];\\n\\n    CMPRoles *roles = [[CMPRoles alloc] initWithOwnerAttributes:ownerAttributes participantAttributes:participantAttributes];\\n\\nCMPConversationParticipant *owner = [[CMPConversationParticipant alloc] initWithID:profileID role:@\\\"owner\\\"];\\n\\nCMPNewConversation *newConversation = [[CMPNewConversation alloc] initWithID:@\\\"{id}\\\" name:@\\\"{name}\\\" description:@\\\"{description}\\\" roles:roles participants:@[owner] isPublic:@(NO)];\\n\\n[client.services.messaging addConversationWithConversation:weakSelf.conversation completion:^(CMPResult<CMPConversation *> *result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n## Query \n\nQuery all conversations:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[_client.services.messaging getConversationsWithProfileID:_client.profileID isPublic:YES completion:^(CMPResult<NSArray<CMPConversation *> *> * _Nonnull) {\\n    if (result.error) {\\n      \\t// error occurred\\n    } else {\\n      \\t// success\\n    }              \\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nQuery specific conversation:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.messaging getConversationWithConversationID:@\\\"{id}\\\" completion:^(CMPResult<CMPConversation *> *result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nUpdate conversation:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"CMPConversationUpdate *update = [[CMPConversationUpdate alloc] initWithID:@\\\"{id}\\\" name:@\\\"{name}\\\" description:@\\\"{description}\\\" roles:roles isPublic:@(NO)];\\n\\n[client.services.messaging updateConversationWithConversationID:@\\\"{id}\\\" conversation:update eTag:nil completion:^(CMPResult<CMPConversation *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nDelete conversation:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.messaging deleteConversationWithConversationID:@\\\"{id}\\\" eTag:nil completion:^(CMPResult<NSNumber *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n## Participants\n\nGet participants:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.messaging getParticipantsWithConversationID:@\\\"{id}\\\" completion:^(CMPResult<NSArray<CMPConversationParticipant *> *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nAdd participants:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"NSArray<CMPConversationParticipant *> *participants = @[[[CMPConversationParticipant alloc] initWithID:@\\\"@{id1}\\\" role:@\\\"{role}\\\"], [[CMPConversationParticipant alloc] initWithID:@\\\"@{id2}\\\" role:@\\\"{role}\\\"]];\\n\\n[client.services.messaging addParticipantsWithConversationID:@\\\"{id}\\\" participants:participants completion:^(CMPResult<NSNumber *> * result) {\\n    if (result.error) {\\n    \\t\\t// error occurred\\n    } else {\\n      \\t// success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nRemove participants:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.messaging removeParticipantsWithConversationID:@\\\"{id}\\\" participants:participants completion:^(CMPResult<NSNumber *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n##Messages\n\nQuery messages:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.messaging getMessagesWithConversationID:@\\\"{id}\\\" limit:100 from:0 completion:^(CMPResult<CMPGetMessagesResult *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nCreate message:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"CMPMessagePart *part = [[CMPMessagePart alloc] initWithName:@\\\"{name}\\\" type:@\\\"{type}\\\" url:nil data:@\\\"{data}\\\" size:@(123)];\\n\\nCMPMessageAlert *alert = [[CMPMessageAlert alloc] initWithPlatforms:[[CMPMessageAlertPlatforms alloc] initWithApns:@{} fcm:@{}]];\\n    \\nCMPSendableMessage *message = [[CMPSendableMessage alloc] initWithMetadata:@{} parts:@[part] alert:alert];\\n\\n[client.services.messaging sendMessage:message toConversationWithID:@\\\"{id}\\\" completion:^(CMPResult<CMPSendMessagesResult *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\nUpdate messages status:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.messaging updateStatusForMessagesWithIDs:@[@\\\"{id1}\\\", @\\\"{id2}\\\"] status:@\\\"read\\\" conversationID:@\\\"{id}\\\" timestamp:[NSDate date] completion:^(CMPResult<NSNumber *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]\n##Events\n\nQuery events:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[client.services.messaging queryEventsWithConversationID:@\\\"{id}\\\" limit:0 from:100 completion:^(CMPResult<NSArray<CMPEvent *> *> * result) {\\n    if (result.error) {\\n        // error occurred\\n    } else {\\n        // success\\n    }\\n}];\",\n      \"language\": \"objectivec\"\n    }\n  ]\n}\n[/block]","excerpt":"A tutorial for adding the iOS SDK code to your app to allow your app users to receive push notifications from your Engagement Cloud account.","slug":"ios-sdk-new","type":"basic","title":"Using the iOS SDK"}

Using the iOS SDK

A tutorial for adding the iOS SDK code to your app to allow your app users to receive push notifications from your Engagement Cloud account.

iOS SDK allows your app users to receive push notifications from your Engagement Cloud account. Engagement Cloud collects your user's email addresses which is then used to identify them so you can send push notifications. Our iOS SDK uses the [Apple Push Notification Service (APNS)](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) to send push notifications to your contacts. To set up push notifications for native iOS apps, complete the following tasks: 1. [Install the iOS SDK](#section-installing-the-ios-sdk) 2. [Configure the iOS SDK](#section-configuring-the-ios-sdk) 3. [Initialise the iOS SDK](#section-initialising-the-ios-sdk) # Installing the iOS SDK In order to install the SDK, we will use CMPComapiFoundation, a dependency manager for iOS/MacOS projects. To add the iOS SDK to your Xcode project with CMPComapiFoundation, do the following: 1. Add the iOS SDK [block:code] { "codes": [ { "code": "# other podfile info\n\ntarget '*Your-Target*'\nuse_frameworks!\n\npod 'CMPComapiFoundation'\n\nend", "language": "objectivec", "name": "Objective-C" } ] } [/block] 2. Install the iOS SDK [block:code] { "codes": [ { "code": "$ pod install", "language": "shell" } ] } [/block] 3. Import the iOS SDK in your Objective-C or Swift file [block:code] { "codes": [ { "code": "#import <CMPComapiFoundation/CMPComapiFoundation.h>", "language": "objectivec" } ] } [/block] # Initialise To initialise the Comapi SDK, you will need a few pre-requisites listed below: * A configured API Space * An authentication provider that can generate a JWT (JSON Web Token) that matches the auth scheme configured for your Api Space * The generated JWT must include the provided nonce as a claim in the generated JWT In order for the client to be able to start a session, the config's authenticationDelegate object must conform to the protocol's method: [block:code] { "codes": [ { "code": "NSString *id = <Portal's authentication tab ID claim value>;\nNSString *issuer = <Portal's authentication tab issuer value>;\nNSString *audience = <Portal's authentication tab audience value>;\nNSString *secret = <Portal's authentication tab secret value>;\n \n- (void)client:(CMPComapiClient *)client didReceiveAuthenticationChallenge:(CMPAuthenticationChallenge *)challenge completion:(void (^)(NSString * _Nullable))continueWithToken {\n \t// request a JWT token from your provider (backend server)\n \t// example call\n \t[YourProviderServer getTokenForNonce:challenge.nonce id:id issuer:issuer audience:audience secret:secret completion:^(NSString * token, NSError * error) {\n \t\t// call continueWithToken block with generated token\n if (token && !error) {\n continueWithToken(token);\n }\n }];\n}", "language": "objectivec" } ] } [/block] A JWT is used to securely transmit information between parties as a JSON object; it's digitally signed, therefore the information can be verified and trusted. For more information on JWT, click [here](https://jwt.io/introduction/). The JWT needs to include claims from the authentication panel in the dashboard. For further guidance, please click [here](https://developer.dotdigital.com/v2/docs/creating-a-push-notification-profile#in-engagement-cloud). Here's an example implementation of a token generator in Objective-C using JWT: [block:code] { "codes": [ { "code": "#import \"CMPAuthenticationManager.h\"\n#import <JWT/JWT.h>\n\n@implementation CMPAuthenticationManager\n\n+ (NSString *)generateTokenForNonce:(NSString *)nonce profileID:(NSString *)profileID issuer:(NSString *)issuer audience:(NSString *)audience secret:(NSString *)secret {\n NSDate *now = [NSDate date];\n NSDate *exp = [NSCalendar.currentCalendar dateByAddingUnit:NSCalendarUnitDay value:30 toDate:now options:0];\n \n NSDictionary *headers = @{@\"typ\" : @\"JWT\"};\n NSDictionary *payload = @{@\"nonce\" : nonce,\n @\"sub\" : profileID,\n @\"iss\" : issuer,\n @\"aud\" : audience,\n @\"iat\" : [NSNumber numberWithDouble:now.timeIntervalSince1970],\n @\"exp\" : [NSNumber numberWithDouble:exp.timeIntervalSince1970]};\n \n NSData *secretData = [secret dataUsingEncoding:NSUTF8StringEncoding];\n id<JWTAlgorithm> algorithm = [JWTAlgorithmFactory algorithmByName:@\"HS256\"];\n \n NSString *token = [JWTBuilder encodePayload:payload].headers(headers).secretData(secretData).algorithm(algorithm).encode;\n return token;\n}\n\n@end\n \n/* Note that this should preferably be generated by your backend, the app should only retreive the token through an HTTP call */", "language": "objectivec" } ] } [/block] # Configuring the iOS SDK Before you can configure the iOS SDK, you need the following: * The value of the [API space ID field in Engagement Cloud](doc:creating-a-push-notification-profile#section-finding-your-api-space-id) * A class that creates a JWT token * Your app must be set up so that it [asks the user for permission to send push notifications](https://developer.apple.com/documentation/usernotifications/asking_permission_to_use_notifications). To configure the iOS SDK: 1. Create a new instance of the `ComapiConfig` class and store it in a variable 2. As the first argument of the `ComapiConfig` instance, pass your API Space ID 3. As the second argument of the `ComapiConfig` instance, pass an instance of [a class that creates a JWT token](#section-ios-jwt-code-sample) [block:code] { "codes": [ { "code": "// create a config object with your api-space-id and an object conforming to CMPAuthenticationDelegate protocol;\nCMPComapiConfig *config = [[CMPComapiConfig alloc] initWithApiSpaceID:@\"<API_SPACE_ID>\" authenticationDelegat:<CMPAuthenticationDelegate_Conforming_Object>];\n\nCMPComapiClient *client = [CMPComapi initialiseWithConfig:config];\n// we can use the client object now", "language": "objectivec" } ] } [/block] # Retrieving the client The client can be retrieved either as a separate object using: [block:code] { "codes": [ { "code": "CMPComapiClient *client = [CMPComapi initialiseWithConfig:config];\n// client instance ready to use", "language": "objectivec" } ] } [/block] or as a singleton: [block:code] { "codes": [ { "code": "[CMPComapi initialiseSharedInstanceWithConfig:config];\n\nCMPComapiClient *client = [Comapi shared];\n// shared client ready to use", "language": "objectivec" } ] } [/block] # Start session Calling Comapi services requires an active session. Once you successfully initialise and retrieve a client object, call: [block:code] { "codes": [ { "code": "[client.services.session startSessionWithCompletion:^{\n // session successfully created\n} failure:^(NSError * _Nullable error) {\n // error ocurred\n}];", "language": "objectivec" } ] } [/block] A session will also be automatically created once you call and of the API services, so the above step is not required. To end the current session, call: [block:code] { "codes": [ { "code": "[client.services.session endSessionWithCompletion:^(CMPResult<NSNumber *> * result) {\n if (result.error) {\n // error occurred\n } else {\n BOOL success = [result.object boolValue];\n if (success) {\n // session successfully ended\n } \n }\n}];", "language": "objectivec" } ] } [/block] # Configuring your app to ask users' permission to send them push notifications To receive push notifications, app users must give their permission. Call the `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)` [method](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application?language=swift) to get the `deviceToken` string and pass it to the `setPushToken()` method on the `ComapiClient` object. [block:code] { "codes": [ { "code": "- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {\n NSMutableString *token = [NSMutableString string];\n \n const char *data = [deviceToken bytes];\n for (NSUInteger i = 0; i < [deviceToken length]; i++) {\n [token appendFormat:@\"%.2hhx\", data[i]];\n }\n \n if (token) {\n [client setPushToken:token completion:^(CMPResult<NSNumber *> * result) {\n \tBOOL success = [result.object boolValue];\n if (result.error || !success) {\n // error occurred\n } else {\n // configuration successful\n }\n }];\n }\n \n // rest of you push notification code\n}", "language": "objectivec" } ] } [/block] # Displaying push notifications when the app is in the foreground Push notifications are displayed only while the app is in the background. These notification are sent to the notification center and launch your app when users tap them. If you want to display the push notification message while the app is in the foreground, do one of the following, depending on the iOS version that your app is running on: **For iOS 9** [block:code] { "codes": [ { "code": "- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {\n if (application.applicationState == UIApplicationStateActive) {\n \n }\n}", "language": "objectivec" } ] } [/block] **For iOS 10 and above** [block:code] { "codes": [ { "code": "- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {\n \n completionHandler();\n}", "language": "objectivec" } ] } [/block] # References [block:callout] { "type": "success", "body": "Now ensure your app passes an email address to the SDK for the app user to ensure they get a contact created in Engagement Cloud by [following these instructions](setting-up-push-notifications-new#section-pass-app-user-information-to-the-sdk)", "title": "Next steps" } [/block] ## Configuring logs You can set internal file logs and console logs to the following levels: * `verbose` * `debug` * `info` * `warning` * `error` ## Websocket Whenever ComapiClient is configured and authenticated, a new session is started which opens a WebSocket connection. This allows for a two-way real-time communication between the client and the server. For more information on WebSocket visit the official RFC site. When open, the socket provides live updates for the profile and conversations which you are a participant of. You can subscribe for these updates to update your views and models. ## Add event listener Register the delegate for the incoming events with the CMPComapiClient object. The delegate should conform to the CMPEventListener protocol. [block:code] { "codes": [ { "code": "[client addEventDelegate:self];", "language": "objectivec" } ] } [/block] To receive the event, implement the following method from CMPEventListener protocol: [block:code] { "codes": [ { "code": "- (void)client:(CMPComapiClient *)client didReceiveEvent:(CMPEvent *)event {\n \tswitch (event.type) {\n case CMPEventTypeConversationMessageSent:\n \t\t\t// message sent event received\n break;\n case CMPEventTypeConversationParticipantTypingOff:\n \t\t\t// participant typing off event received\n break;\n case CMPEventTypeConversationParticipantTyping:\n \t\t\t// participant typing event received\n break;\n case CMPEventTypeConversationMessageRead:\n \t\t\t// message read event received\n break;\n \t// case .other events:\n default:\n \t\t\t// unknown event type\n break;\n }\n}", "language": "objectivec" } ] } [/block] Please note that you can register multiple listeners in your code to handle events in different parts of your application. The events are broadcasted to all registered delegates. [block:code] { "codes": [ { "code": "[client removeEventDelegate:self];", "language": "objectivec" } ] } [/block] Register the delegate for the incoming events with the CMPComapiClient object. The delegate should conform to the CMPEventListener protocol. [block:code] { "codes": [ { "code": "[client removeEventDelegate:self];", "language": "objectivec" } ] } [/block] ## Available events [block:parameters] { "data": { "h-0": "Event type", "h-1": "Event subtype", "0-0": "profile", "0-1": "update", "0-2": "Sent when a user's profile is updated.", "1-0": "conversation", "1-1": "participantAdded", "1-2": "Sent when a participant is added to a conversation. When a conversation is created, this event will also fire with the owner's profileId.", "2-0": "conversation", "2-1": "participantUpdated", "2-2": "Sent when a participant role is updated in a conversation.", "3-0": "conversation", "3-1": "participantRemoved", "3-2": "Sent when a participant is removed from a conversation.", "4-0": "conversation", "4-1": "delete", "4-2": "Sent when a conversation is deleted.", "5-0": "conversation", "5-1": "update", "5-2": "Sent when a conversation's details were updated.", "6-0": "conversation", "6-1": "undelete", "6-2": "Sent when a conversation is restored.", "7-0": "conversation", "7-1": "create", "7-2": "Sent when a new conversation was created.", "8-0": "conversation", "8-1": "participantTyping", "8-2": "Sent when one of the participants is typing a new message.", "9-0": "conversation", "9-1": "participantTypingOff", "9-2": "Sent when the other participant stops typing.", "10-0": "conversationMessage", "10-1": "delivered", "10-2": "Sent when on of the participants updated the message status to 'delivered'.", "11-0": "conversationMessage", "11-1": "read", "11-2": "Sent when one of the participants updated the message status to 'read'.", "12-0": "conversationMessage", "12-1": "sent", "12-2": "Sent when a new message appeared in a conversation. This event will also be delivered to the message sender." }, "cols": 3, "rows": 13 } [/block] For all events you can access id, apiSpace and name and context properties. For other events, there are other properties you can access, as follows: [block:code] { "codes": [ { "code": "profile.profileId", "language": "text", "name": "profile" } ] } [/block] ##Conversation Below are listed properties specific for the conversation event subtypes. [block:code] { "codes": [ { "code": "conversation.conversationId\nconversation.payload.profileId\nconversation.payload.role", "language": "text", "name": "participantAdded" }, { "code": "conversation.conversationId\nconversation.payload.profileId\nconversation.payload.role", "language": "text", "name": "participantUpdated" }, { "code": "conversation.conversationId\nconversation.payload.profileId\nconversation.payload.role", "language": "text", "name": "participantRemoved" }, { "code": "conversation.conversationId\nconversation.payload.date", "language": "text", "name": "delete" }, { "code": "conversation.profileId\nconversation.payload.roles\nconversation.payload.isPublic\nconversation.payload.participants", "language": "text", "name": "undelete" }, { "code": "conversation.conversationId\nconversation.payload.description\nconversation.payload.roles", "language": "text", "name": "update" }, { "code": "conversation.profileId\nconversation.payload.roles\nconversation.payload.isPublic\nconversation.payload.participants", "language": "text", "name": "create" }, { "code": "conversation.account\nconversation.payload.profileId\ncovenrsation.payload.conversationId", "language": "text", "name": "participantTyping" }, { "code": "conversation.account\nconversation.payload.profileId\ncovenrsation.payload.conversationId", "language": "text", "name": "participantTypingOff" } ] } [/block] ConversationMessage [block:code] { "codes": [ { "code": "conversationMessage.messageId\nconversationMessage.conversationId\nconversationMessage.profileId\nconversationMessage.timestamp", "language": "text", "name": "delivered" }, { "code": "conversationMessage.messageId\nconversationMessage.conversationId\nconversationMessage.profileId\nconversationMessage.timestamp", "language": "text", "name": "sent" }, { "code": "conversationMessage.messageId\nconversationMessage.metadata\nconversationMessage.parts\nconversationMessage.alert", "language": "text", "name": "read" } ] } [/block] ## Client APIs You can use **CMPComapi** client instance obtained in Initialisation to access SDK APIs. ##Session Get the current **profileID**: [block:code] { "codes": [ { "code": "BOOL isSuccessfullyCreated = [client isSessionSuccessfullyCreated];", "language": "objectivec" } ] } [/block] Check if the session was successfully created: [block:code] { "codes": [ { "code": "BOOL isSuccessfullyCreated = [client isSessionSuccessfullyCreated];", "language": "objectivec" } ] } [/block] ##Services Accessing services is done by calling methods from the proper group: [block:code] { "codes": [ { "code": "client.services.profile\nclient.services.session\nclient.services.messaging", "language": "objectivec" } ] } [/block] Most services use CMPResult object as a return value in completion blocks: [block:code] { "codes": [ { "code": "@interface CMPResult<__covariant T> : NSObject\n\n@property (nonatomic, nullable) T object;\n@property (nonatomic, nullable) NSError *error;\n@property (nonatomic, nullable) NSString *eTag;\n@property (nonatomic) NSInteger code;\n\n- (instancetype)init NS_UNAVAILABLE;\n\n- (instancetype)initWithObject:(nullable T)object error:(nullable NSError *)error eTag:(nullable NSString *)eTag code:(NSInteger)code;\n\n@end", "language": "objectivec" } ] } [/block] You can get the ETage value from it, as well as an HTTP status code and potential error. ##Profile services Get profile: [block:code] { "codes": [ { "code": "[client.services.profile getProfileForProfileID:@\"<PROFILE-ID>\" completion:^(CMPResult<CMPProfile *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec", "name": null } ] } [/block] Query profiles with query builder: [block:code] { "codes": [ { "code": "[client.services.profile queryProfilesWithQueryElements:@[] completion:^(CMPResult<NSArray<CMPProfile *> *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] Patch profile: [block:code] { "codes": [ { "code": "[client.services.profile patchProfileForProfileID:@\"<PROFILE-ID>\" attributes:@{@\"email\" : @\"new@email.com\"} eTag:nil completion:^(CMPResult<CMPProfile *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] Update profile: [block:code] { "codes": [ { "code": "[client.services.profile updateProfileForProfileID:@\"<PROFILE-ID>\" attributes:@{@\"email\" : @\"new@email.com\"} eTag:nil completion:^(CMPResult<CMPProfile *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] ##Messaging services Create conversation: [block:code] { "codes": [ { "code": "CMPRoleAttributes *ownerAttributes = [[CMPRoleAttributes alloc] initWithCanSend:YES canAddParticipants:YES canRemoveParticipants:YES];\n\n CMPRoleAttributes *participantAttributes = [[CMPRoleAttributes alloc] initWithCanSend:YES canAddParticipants:NO canRemoveParticipants:NO];\n\n CMPRoles *roles = [[CMPRoles alloc] initWithOwnerAttributes:ownerAttributes participantAttributes:participantAttributes];\n\nCMPConversationParticipant *owner = [[CMPConversationParticipant alloc] initWithID:profileID role:@\"owner\"];\n\nCMPNewConversation *newConversation = [[CMPNewConversation alloc] initWithID:@\"{id}\" name:@\"{name}\" description:@\"{description}\" roles:roles participants:@[owner] isPublic:@(NO)];\n\n[client.services.messaging addConversationWithConversation:weakSelf.conversation completion:^(CMPResult<CMPConversation *> *result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] ## Query Query all conversations: [block:code] { "codes": [ { "code": "[_client.services.messaging getConversationsWithProfileID:_client.profileID isPublic:YES completion:^(CMPResult<NSArray<CMPConversation *> *> * _Nonnull) {\n if (result.error) {\n \t// error occurred\n } else {\n \t// success\n } \n}];", "language": "objectivec" } ] } [/block] Query specific conversation: [block:code] { "codes": [ { "code": "[client.services.messaging getConversationWithConversationID:@\"{id}\" completion:^(CMPResult<CMPConversation *> *result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] Update conversation: [block:code] { "codes": [ { "code": "CMPConversationUpdate *update = [[CMPConversationUpdate alloc] initWithID:@\"{id}\" name:@\"{name}\" description:@\"{description}\" roles:roles isPublic:@(NO)];\n\n[client.services.messaging updateConversationWithConversationID:@\"{id}\" conversation:update eTag:nil completion:^(CMPResult<CMPConversation *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] Delete conversation: [block:code] { "codes": [ { "code": "[client.services.messaging deleteConversationWithConversationID:@\"{id}\" eTag:nil completion:^(CMPResult<NSNumber *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] ## Participants Get participants: [block:code] { "codes": [ { "code": "[client.services.messaging getParticipantsWithConversationID:@\"{id}\" completion:^(CMPResult<NSArray<CMPConversationParticipant *> *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] Add participants: [block:code] { "codes": [ { "code": "NSArray<CMPConversationParticipant *> *participants = @[[[CMPConversationParticipant alloc] initWithID:@\"@{id1}\" role:@\"{role}\"], [[CMPConversationParticipant alloc] initWithID:@\"@{id2}\" role:@\"{role}\"]];\n\n[client.services.messaging addParticipantsWithConversationID:@\"{id}\" participants:participants completion:^(CMPResult<NSNumber *> * result) {\n if (result.error) {\n \t\t// error occurred\n } else {\n \t// success\n }\n}];", "language": "objectivec" } ] } [/block] Remove participants: [block:code] { "codes": [ { "code": "[client.services.messaging removeParticipantsWithConversationID:@\"{id}\" participants:participants completion:^(CMPResult<NSNumber *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] ##Messages Query messages: [block:code] { "codes": [ { "code": "[client.services.messaging getMessagesWithConversationID:@\"{id}\" limit:100 from:0 completion:^(CMPResult<CMPGetMessagesResult *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] Create message: [block:code] { "codes": [ { "code": "CMPMessagePart *part = [[CMPMessagePart alloc] initWithName:@\"{name}\" type:@\"{type}\" url:nil data:@\"{data}\" size:@(123)];\n\nCMPMessageAlert *alert = [[CMPMessageAlert alloc] initWithPlatforms:[[CMPMessageAlertPlatforms alloc] initWithApns:@{} fcm:@{}]];\n \nCMPSendableMessage *message = [[CMPSendableMessage alloc] initWithMetadata:@{} parts:@[part] alert:alert];\n\n[client.services.messaging sendMessage:message toConversationWithID:@\"{id}\" completion:^(CMPResult<CMPSendMessagesResult *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] Update messages status: [block:code] { "codes": [ { "code": "[client.services.messaging updateStatusForMessagesWithIDs:@[@\"{id1}\", @\"{id2}\"] status:@\"read\" conversationID:@\"{id}\" timestamp:[NSDate date] completion:^(CMPResult<NSNumber *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block] ##Events Query events: [block:code] { "codes": [ { "code": "[client.services.messaging queryEventsWithConversationID:@\"{id}\" limit:0 from:100 completion:^(CMPResult<NSArray<CMPEvent *> *> * result) {\n if (result.error) {\n // error occurred\n } else {\n // success\n }\n}];", "language": "objectivec" } ] } [/block]