{"__v":34,"_id":"55f9fdc8aba81f0d00a1156d","category":{"project":"55e67aaa9cc7c62b00c4a1ea","version":"55e67aab9cc7c62b00c4a1ed","_id":"58b8ca5e3265d70f001788d4","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2017-03-03T01:43:58.388Z","from_sync":false,"order":2,"slug":"message-stream","title":"Message Stream"},"parentDoc":null,"project":"55e67aaa9cc7c62b00c4a1ea","user":"55d2bd8e2463351700f67dd7","version":{"__v":10,"_id":"55e67aab9cc7c62b00c4a1ed","project":"55e67aaa9cc7c62b00c4a1ea","createdAt":"2015-09-02T04:27:23.612Z","releaseDate":"2015-09-02T04:27:23.612Z","categories":["55e67aac9cc7c62b00c4a1ee","55e67b5556007d23005fee7d","55e67b5dde6fef23009480ca","55e680efde6fef23009480db","55e6829485a9741900314e99","561c61b4ad272c0d00a892df","586c014c0abf1d0f000d04d4","58991d2ad207df0f0002186b","589b8e1fdbb7cd190026732c","58b8ca5e3265d70f001788d4"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0"},"updates":["581781545ec82a0f00a093d5","58a64245fc922d0f00ce8302"],"next":{"pages":[],"description":""},"createdAt":"2015-09-16T23:39:52.522Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":1,"body":"## Retrieving messages for a user\n\nCarnival Messages have several properties which you should familiarize yourself with before building a stream or displaying messages.\n[block:parameters]\n{\n  \"data\": {\n    \"h-0\": \"Property name\",\n    \"h-1\": \"Can be blank\",\n    \"h-2\": \"Description\",\n    \"0-0\": \"`title`\",\n    \"0-1\": \"No\",\n    \"0-2\": \"The title of the message\",\n    \"1-0\": \"`text`\",\n    \"1-1\": \"Yes\",\n    \"1-2\": \"The body of the message\",\n    \"2-0\": \"`htmlText`\",\n    \"2-1\": \"Yes\",\n    \"2-2\": \"The body of the message rendered into an HTML string\",\n    \"3-0\": \"`imageURL`\",\n    \"3-1\": \"Yes\",\n    \"3-2\": \"A URL pointing to an image\",\n    \"4-0\": \"`videoURL` (iOS)\\n`mediaURL` (Android)\",\n    \"4-1\": \"Yes\",\n    \"4-2\": \"A URL pointing to a video\",\n    \"5-0\": \"`URL` (iOS)\\n`contentURL` (Android)\",\n    \"5-1\": \"Yes\",\n    \"5-2\": \"A URL pointing to a generic page or resource\",\n    \"6-0\": \"`read`\",\n    \"6-1\": \"No\",\n    \"6-2\": \"Whether or not the message has been read\",\n    \"7-0\": \"`createdAt`\",\n    \"7-1\": \"No\",\n    \"7-2\": \"The date and time when the message was created\",\n    \"8-0\": \"`messageID`\",\n    \"8-1\": \"No\",\n    \"8-2\": \"An alphanumeric unique ID for the message\",\n    \"9-0\": \"`attributes`\",\n    \"9-1\": \"Yes\",\n    \"9-2\": \"A dictionary of string keys and values set on the message\",\n    \"h-3\": \"Plugin SDK name\",\n    \"0-3\": \"`title`\",\n    \"1-3\": \"`text`\",\n    \"2-3\": \"`html_text`\",\n    \"3-3\": \"`card_image_url`\",\n    \"4-3\": \"`card_media_url`\",\n    \"5-3\": \"`url`\",\n    \"6-3\": \"`is_read`\",\n    \"7-3\": \"`created_at`\",\n    \"8-3\": \"`id`\",\n    \"9-3\": \"`attributes`\"\n  },\n  \"cols\": 4,\n  \"rows\": 10\n}\n[/block]\nYou can use the `CarnivalMessageStream` interface to asynchronously request an array of messages for that device.  Once returned you can use these objects to construct your own Message Stream interface.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"//On iOS, using Objective-C\\n\\n[CarnivalMessageStream messages:^(NSArray *messages, NSError *error) {\\n\\t//Do something with the array of messages\\n}];\\n\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"CarnivalMessageStream.messages { (theMessages, anError) -> Void in\\n\\t//Do something with the array of messages\\n}\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"Carnival.getMessages(new Carnival.MessagesHandler() {\\n  :::at:::Override\\n  public void onSuccess(ArrayList<Message> messages) {\\n    //Do something with the array of messages\\n  }\\n\\n  @Override\\n  public void onFailure(Error error) {\\n  }\\n});\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.messages( function callback(data) {\\n    //Do something with the array of messages\\n  }, function errorHandler(err) {\\n    //Do something with the error\\n  }\\n);\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"Carnival.OnMessagesReceivedEvent += (object sender, CarnivalMessagesReceivedEvent e) => {\\n    //Do something with e.messages \\n};\\nCarnival.GetMessages();\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n## Showing full screen content\n\nAfter a user taps on message, you can can show the full screen content with the following method.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[CarnivalMessageStream presentMessageDetailForMessage:self.latestMessage];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"CarnivalMessageStream.presentDetailForMessage(message)\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"Intent i = new Intent(this, MessageActivity.class);\\ni.putExtra(Carnival.EXTRA_PARCELABLE_MESSAGE, message);\\nstartActivity(i);\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.presentMessageDetail(message)\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"Carnival.ShowMessageDetail (message);\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n## Supporting Impressions\n\nIn order to support impression analytics from your Message Stream you will need to ensure you register an impression event for relevant messages when your Message Stream is shown to the user.  \n\nCarnival supports tracking **three** types of impressions:\n\n* In App Notification Impression\n* Message Viewed in Stream Impression\n* Message Viewed in Detail Impression\n\nIf you didn't customize Carnival's default in-app notification or full-screen detail user interface, there is no need to register an impression event as these will be captured automatically.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[CarnivalMessageStream registerImpressionWithType:\\n    CarnivalImpressionTypeInAppNotificationView forMessage:message];\\n\\n[CarnivalMessageStream registerImpressionWithType:\\n    CarnivalImpressionTypeStreamView forMessage:message];\\n\\n[CarnivalMessageStream registerImpressionWithType:\\n    CarnivalImpressionTypeDetailView forMessage:message];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"CarnivalMessageStream.registerImpressionWithType(\\n    CarnivalImpressionType.InAppNotificationView, forMessage: message)\\n    \\nCarnivalMessageStream.registerImpressionWithType(\\n    CarnivalImpressionType.StreamView, forMessage: message)        \\n    \\nCarnivalMessageStream.registerImpressionWithType(\\n    CarnivalImpressionType.DetailView, forMessage: message)\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"Carnival.registerMessageImpression(CarnivalImpressionType.IMPRESSION_TYPE_IN_APP_VIEW, message);\\nCarnival.registerMessageImpression(CarnivalImpressionType.IMPRESSION_TYPE_STREAM_VIEW, message);\\nCarnival.registerMessageImpression(CarnivalImpressionType.IMPRESSION_TYPE_DETAIL_VIEW, message);\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.registerImpression(Carnival.MessageImpressionType.InAppView, message);\\nCarnival.registerImpression(Carnival.MessageImpressionType.StreamView, message);\\nCarnival.registerImpression(Carnival.MessageImpressionType.DetailView, message);\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"Carnival.RegisterImpression(message, CarnivalImpressionType.InAppNotificationView); \\nCarnival.RegisterImpression(message, CarnivalImpressionType.StreamView); \\nCarnival.RegisterImpression(message, CarnivalImpressionType.DetailView); \",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n## Marking As Read\n\nYou might also want to track which messages have been read or not with your Message Stream, so you could show a number badge or unread indicator. You can mark a collection or single message as read. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[CarnivalMessageStream markMessageAsRead:message withResponse:NULL];\\n// or\\n[CarnivalMessageStream markMessagesAsRead:@[message1, message2, message3] withResponse:NULL];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"CarnivalMessageStream.markMessageAsRead(message, withResponse: nil)\\n//or \\nCarnivalMessageStream.markMessagesAsRead(messages, withResponse: nil)\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"Carnival.setMessageRead(message, messageReadHandler);\\n// or \\nCarnival.setMessagesRead(messageList, messageReadHandler);\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.markMessageAsRead(\\n  function callback(data) {\\n    console.log('markMessageAsRead successfully returned');\\n  },\\n  function errorHandler(err) {\\n    console.log('markMessageAsRead error: ' + err);\\n  },\\n  messageJSON\\n);\\n// or\\nCarnival.markMessagesAsRead(\\n  function callback(data) {\\n    console.log('markMessagesAsRead successfully returned');\\n  },\\n  function errorHandler(err) {\\n    console.log('markMessagesAsRead error: ' + err);\\n  },\\n  data\\n);\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"Carnival.OnErrorEvent += (object sender, CarnivalErrorEventArgs e) => {\\n  Debug.Log (e.ErrorDescription);\\n};\\nCarnival.MarkMessageAsRead(message);\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n## Deleting Messages\n\nMessages can be deleted from a user's stream. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"[CarnivalMessageStream removeMessage:message withResonse:^(NSError *error) {\\n\\t//Do something with any errors\\n}];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"CarnivalMessageStream.removeMessage(message, withResponse: nil)\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"Carnival.deleteMessage(message, messageDeletedHandler);\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.removeMessage(\\n  function callback(data) {\\n    console.log('removeMessage successfully returned');\\n  },\\n  function errorHandler(err) {\\n    console.log('removeMessage returned error: ' + err);\\n  },\\n  messageJSON\\n);\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    },\n    {\n      \"code\": \"Carnival.OnErrorEvent += (object sender, CarnivalErrorEventArgs e) => {\\n  Debug.Log (e.ErrorDescription);\\n};\\nCarnival.RemoveMessage (message)\",\n      \"language\": \"csharp\",\n      \"name\": \"Unity (C#)\"\n    }\n  ]\n}\n[/block]\n## Custom In-App Message Notifications\n\nYou can customize the look and feel of In-App Message Notifications by following the instructions [here](doc:customizing-in-app-notifications). \n[block:callout]\n{\n  \"type\": \"info\",\n  \"body\": \"While testing, you can reinstall the app to get a fresh set of messages, as impressions and deletions do not persist between same-device installs.\"\n}\n[/block]\n## Custom Message Attributes\n\nYou can use a dictionary or hash of custom attributes to further customize the display of your message stream and messages. These may include: \n\n * Pinned messages\n * Categories on the Stream\n * Custom CTAs or card design\n\n## HTML Support\nSince you can include [markdown](http://marketing.carnival.io/docs/using-markdown) in messages sent via Carnival, we expose this markdown rendered into HTML with the `htmlText` property. \n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"bodyLabel.attributedText = [[NSAttributedString alloc] initWithData:[message.htmlText dataUsingEncoding:NSUTF8StringEncoding]\\n                            options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,\\n                                 NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}\\n                 documentAttributes:nil error:nil];\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"do {\\n    try bodyLabel.attributedText = NSAttributedString.init(\\n        data: message.htmlText.dataUsingEncoding(NSUnicodeStringEncoding)!,\\n        options: [\\n            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,\\n            NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding\\n        ],\\n        documentAttributes: nil);\\n} catch {\\n    \\n}\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"TextView textView = ...;\\ntextView.setText(Html.fromHtml(message.getHtmlText()));\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    }\n  ]\n}\n[/block]\nHTML Text will always be populated, even if no markdown was used in the original message.\n\n## Unread messages count\nYou can get the number of unread messages from the SDK, without the need to request a message stream and iterate over each individual message.\n\nCarnival [will deliver an in-app message in two occasions:](https://docs.carnival.io/docs/setting-up-in-app-messaging#section-in-app-notification-display) after launch with app is in foreground or if the user has the app open and the device has a valid push token. Therefore, you may want to obtain the unread message count when the user opens the app, or when it becomes active after being backgrounded.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"- (void)applicationDidBecomeActive:(UIApplication *)application\\n{\\n    [CarnivalMessageStream unreadCount:^(NSUInteger unreadCount, NSError * _Nullable error) {\\n        // Add your logic here\\n    }];\\n}\",\n      \"language\": \"objectivec\",\n      \"name\": \"iOS (Objective-C)\"\n    },\n    {\n      \"code\": \"func applicationDidBecomeActive(_ application: UIApplication) {\\n    CarnivalMessageStream.unreadCount { (count, error) in\\n        // Add your logic here\\n    }\\n}\\n\",\n      \"language\": \"swift\",\n      \"name\": \"iOS (Swift)\"\n    },\n    {\n      \"code\": \"public class MainActivity extends AppCompatActivity {\\n    @Override\\n    protected void onResume() {\\n        super.onResume();\\n        int unreadCount = Carnival.getUnreadMessageCount();\\n\\n        // Add your logic here\\n    }\\n}\\n\",\n      \"language\": \"java\",\n      \"name\": \"Android (Java)\"\n    },\n    {\n      \"code\": \"Carnival.unreadCount(\\n  function callback(unreadCount) {\\n    console.log('unreadCount succesfully returned: ' + unreadCount);\\n  },\\n  function errorHandler(err) {\\n    console.log('unreadCount error: ' + err);\\n  }\\n);\\n\\n// Additionally, we expose an event named 'unreadcountdidchange'. You can attach a listener to perform an action every time the unread count changes.\\n\\ndocument.addEventListener('unreadcountdidchange', function(event) {\\n  console.log('unreadCountChanged: ' + event.detail.unreadCount);\\n});\",\n      \"language\": \"javascript\",\n      \"name\": \"Cordova (JavaScript)\"\n    }\n  ]\n}\n[/block]","excerpt":"","slug":"developing-a-custom-message-stream","type":"basic","title":"Building a Message Stream"}

Building a Message Stream


## Retrieving messages for a user Carnival Messages have several properties which you should familiarize yourself with before building a stream or displaying messages. [block:parameters] { "data": { "h-0": "Property name", "h-1": "Can be blank", "h-2": "Description", "0-0": "`title`", "0-1": "No", "0-2": "The title of the message", "1-0": "`text`", "1-1": "Yes", "1-2": "The body of the message", "2-0": "`htmlText`", "2-1": "Yes", "2-2": "The body of the message rendered into an HTML string", "3-0": "`imageURL`", "3-1": "Yes", "3-2": "A URL pointing to an image", "4-0": "`videoURL` (iOS)\n`mediaURL` (Android)", "4-1": "Yes", "4-2": "A URL pointing to a video", "5-0": "`URL` (iOS)\n`contentURL` (Android)", "5-1": "Yes", "5-2": "A URL pointing to a generic page or resource", "6-0": "`read`", "6-1": "No", "6-2": "Whether or not the message has been read", "7-0": "`createdAt`", "7-1": "No", "7-2": "The date and time when the message was created", "8-0": "`messageID`", "8-1": "No", "8-2": "An alphanumeric unique ID for the message", "9-0": "`attributes`", "9-1": "Yes", "9-2": "A dictionary of string keys and values set on the message", "h-3": "Plugin SDK name", "0-3": "`title`", "1-3": "`text`", "2-3": "`html_text`", "3-3": "`card_image_url`", "4-3": "`card_media_url`", "5-3": "`url`", "6-3": "`is_read`", "7-3": "`created_at`", "8-3": "`id`", "9-3": "`attributes`" }, "cols": 4, "rows": 10 } [/block] You can use the `CarnivalMessageStream` interface to asynchronously request an array of messages for that device. Once returned you can use these objects to construct your own Message Stream interface. [block:code] { "codes": [ { "code": "//On iOS, using Objective-C\n\n[CarnivalMessageStream messages:^(NSArray *messages, NSError *error) {\n\t//Do something with the array of messages\n}];\n", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "CarnivalMessageStream.messages { (theMessages, anError) -> Void in\n\t//Do something with the array of messages\n}", "language": "swift", "name": "iOS (Swift)" }, { "code": "Carnival.getMessages(new Carnival.MessagesHandler() {\n @Override\n public void onSuccess(ArrayList<Message> messages) {\n //Do something with the array of messages\n }\n\n @Override\n public void onFailure(Error error) {\n }\n});", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.messages( function callback(data) {\n //Do something with the array of messages\n }, function errorHandler(err) {\n //Do something with the error\n }\n);", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "Carnival.OnMessagesReceivedEvent += (object sender, CarnivalMessagesReceivedEvent e) => {\n //Do something with e.messages \n};\nCarnival.GetMessages();", "language": "csharp", "name": "Unity (C#)" } ] } [/block] ## Showing full screen content After a user taps on message, you can can show the full screen content with the following method. [block:code] { "codes": [ { "code": "[CarnivalMessageStream presentMessageDetailForMessage:self.latestMessage];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "CarnivalMessageStream.presentDetailForMessage(message)", "language": "swift", "name": "iOS (Swift)" }, { "code": "Intent i = new Intent(this, MessageActivity.class);\ni.putExtra(Carnival.EXTRA_PARCELABLE_MESSAGE, message);\nstartActivity(i);", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.presentMessageDetail(message)", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "Carnival.ShowMessageDetail (message);", "language": "csharp", "name": "Unity (C#)" } ] } [/block] ## Supporting Impressions In order to support impression analytics from your Message Stream you will need to ensure you register an impression event for relevant messages when your Message Stream is shown to the user. Carnival supports tracking **three** types of impressions: * In App Notification Impression * Message Viewed in Stream Impression * Message Viewed in Detail Impression If you didn't customize Carnival's default in-app notification or full-screen detail user interface, there is no need to register an impression event as these will be captured automatically. [block:code] { "codes": [ { "code": "[CarnivalMessageStream registerImpressionWithType:\n CarnivalImpressionTypeInAppNotificationView forMessage:message];\n\n[CarnivalMessageStream registerImpressionWithType:\n CarnivalImpressionTypeStreamView forMessage:message];\n\n[CarnivalMessageStream registerImpressionWithType:\n CarnivalImpressionTypeDetailView forMessage:message];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "CarnivalMessageStream.registerImpressionWithType(\n CarnivalImpressionType.InAppNotificationView, forMessage: message)\n \nCarnivalMessageStream.registerImpressionWithType(\n CarnivalImpressionType.StreamView, forMessage: message) \n \nCarnivalMessageStream.registerImpressionWithType(\n CarnivalImpressionType.DetailView, forMessage: message)", "language": "swift", "name": "iOS (Swift)" }, { "code": "Carnival.registerMessageImpression(CarnivalImpressionType.IMPRESSION_TYPE_IN_APP_VIEW, message);\nCarnival.registerMessageImpression(CarnivalImpressionType.IMPRESSION_TYPE_STREAM_VIEW, message);\nCarnival.registerMessageImpression(CarnivalImpressionType.IMPRESSION_TYPE_DETAIL_VIEW, message);", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.registerImpression(Carnival.MessageImpressionType.InAppView, message);\nCarnival.registerImpression(Carnival.MessageImpressionType.StreamView, message);\nCarnival.registerImpression(Carnival.MessageImpressionType.DetailView, message);", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "Carnival.RegisterImpression(message, CarnivalImpressionType.InAppNotificationView); \nCarnival.RegisterImpression(message, CarnivalImpressionType.StreamView); \nCarnival.RegisterImpression(message, CarnivalImpressionType.DetailView); ", "language": "csharp", "name": "Unity (C#)" } ] } [/block] ## Marking As Read You might also want to track which messages have been read or not with your Message Stream, so you could show a number badge or unread indicator. You can mark a collection or single message as read. [block:code] { "codes": [ { "code": "[CarnivalMessageStream markMessageAsRead:message withResponse:NULL];\n// or\n[CarnivalMessageStream markMessagesAsRead:@[message1, message2, message3] withResponse:NULL];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "CarnivalMessageStream.markMessageAsRead(message, withResponse: nil)\n//or \nCarnivalMessageStream.markMessagesAsRead(messages, withResponse: nil)", "language": "swift", "name": "iOS (Swift)" }, { "code": "Carnival.setMessageRead(message, messageReadHandler);\n// or \nCarnival.setMessagesRead(messageList, messageReadHandler);", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.markMessageAsRead(\n function callback(data) {\n console.log('markMessageAsRead successfully returned');\n },\n function errorHandler(err) {\n console.log('markMessageAsRead error: ' + err);\n },\n messageJSON\n);\n// or\nCarnival.markMessagesAsRead(\n function callback(data) {\n console.log('markMessagesAsRead successfully returned');\n },\n function errorHandler(err) {\n console.log('markMessagesAsRead error: ' + err);\n },\n data\n);", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "Carnival.OnErrorEvent += (object sender, CarnivalErrorEventArgs e) => {\n Debug.Log (e.ErrorDescription);\n};\nCarnival.MarkMessageAsRead(message);", "language": "csharp", "name": "Unity (C#)" } ] } [/block] ## Deleting Messages Messages can be deleted from a user's stream. [block:code] { "codes": [ { "code": "[CarnivalMessageStream removeMessage:message withResonse:^(NSError *error) {\n\t//Do something with any errors\n}];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "CarnivalMessageStream.removeMessage(message, withResponse: nil)", "language": "swift", "name": "iOS (Swift)" }, { "code": "Carnival.deleteMessage(message, messageDeletedHandler);", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.removeMessage(\n function callback(data) {\n console.log('removeMessage successfully returned');\n },\n function errorHandler(err) {\n console.log('removeMessage returned error: ' + err);\n },\n messageJSON\n);", "language": "javascript", "name": "Cordova (JavaScript)" }, { "code": "Carnival.OnErrorEvent += (object sender, CarnivalErrorEventArgs e) => {\n Debug.Log (e.ErrorDescription);\n};\nCarnival.RemoveMessage (message)", "language": "csharp", "name": "Unity (C#)" } ] } [/block] ## Custom In-App Message Notifications You can customize the look and feel of In-App Message Notifications by following the instructions [here](doc:customizing-in-app-notifications). [block:callout] { "type": "info", "body": "While testing, you can reinstall the app to get a fresh set of messages, as impressions and deletions do not persist between same-device installs." } [/block] ## Custom Message Attributes You can use a dictionary or hash of custom attributes to further customize the display of your message stream and messages. These may include: * Pinned messages * Categories on the Stream * Custom CTAs or card design ## HTML Support Since you can include [markdown](http://marketing.carnival.io/docs/using-markdown) in messages sent via Carnival, we expose this markdown rendered into HTML with the `htmlText` property. [block:code] { "codes": [ { "code": "bodyLabel.attributedText = [[NSAttributedString alloc] initWithData:[message.htmlText dataUsingEncoding:NSUTF8StringEncoding]\n options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,\n NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}\n documentAttributes:nil error:nil];", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "do {\n try bodyLabel.attributedText = NSAttributedString.init(\n data: message.htmlText.dataUsingEncoding(NSUnicodeStringEncoding)!,\n options: [\n NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,\n NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding\n ],\n documentAttributes: nil);\n} catch {\n \n}", "language": "swift", "name": "iOS (Swift)" }, { "code": "TextView textView = ...;\ntextView.setText(Html.fromHtml(message.getHtmlText()));", "language": "java", "name": "Android (Java)" } ] } [/block] HTML Text will always be populated, even if no markdown was used in the original message. ## Unread messages count You can get the number of unread messages from the SDK, without the need to request a message stream and iterate over each individual message. Carnival [will deliver an in-app message in two occasions:](https://docs.carnival.io/docs/setting-up-in-app-messaging#section-in-app-notification-display) after launch with app is in foreground or if the user has the app open and the device has a valid push token. Therefore, you may want to obtain the unread message count when the user opens the app, or when it becomes active after being backgrounded. [block:code] { "codes": [ { "code": "- (void)applicationDidBecomeActive:(UIApplication *)application\n{\n [CarnivalMessageStream unreadCount:^(NSUInteger unreadCount, NSError * _Nullable error) {\n // Add your logic here\n }];\n}", "language": "objectivec", "name": "iOS (Objective-C)" }, { "code": "func applicationDidBecomeActive(_ application: UIApplication) {\n CarnivalMessageStream.unreadCount { (count, error) in\n // Add your logic here\n }\n}\n", "language": "swift", "name": "iOS (Swift)" }, { "code": "public class MainActivity extends AppCompatActivity {\n @Override\n protected void onResume() {\n super.onResume();\n int unreadCount = Carnival.getUnreadMessageCount();\n\n // Add your logic here\n }\n}\n", "language": "java", "name": "Android (Java)" }, { "code": "Carnival.unreadCount(\n function callback(unreadCount) {\n console.log('unreadCount succesfully returned: ' + unreadCount);\n },\n function errorHandler(err) {\n console.log('unreadCount error: ' + err);\n }\n);\n\n// Additionally, we expose an event named 'unreadcountdidchange'. You can attach a listener to perform an action every time the unread count changes.\n\ndocument.addEventListener('unreadcountdidchange', function(event) {\n console.log('unreadCountChanged: ' + event.detail.unreadCount);\n});", "language": "javascript", "name": "Cordova (JavaScript)" } ] } [/block]