{"id":13877678,"url":"https://github.com/eBay/NMessenger","last_synced_at":"2025-07-16T13:32:27.059Z","repository":{"id":62448528,"uuid":"65587921","full_name":"eBay/NMessenger","owner":"eBay","description":"A fast, lightweight messenger component built on AsyncDisplaykit and written in Swift","archived":true,"fork":false,"pushed_at":"2018-05-12T11:34:41.000Z","size":5112,"stargazers_count":2421,"open_issues_count":93,"forks_count":270,"subscribers_count":76,"default_branch":"master","last_synced_at":"2025-06-29T22:18:05.471Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eBay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-08-12T22:52:16.000Z","updated_at":"2025-05-26T23:58:15.000Z","dependencies_parsed_at":"2022-11-01T23:17:57.504Z","dependency_job_id":null,"html_url":"https://github.com/eBay/NMessenger","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/eBay/NMessenger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2FNMessenger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2FNMessenger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2FNMessenger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2FNMessenger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eBay","download_url":"https://codeload.github.com/eBay/NMessenger/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eBay%2FNMessenger/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265514181,"owners_count":23780234,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-06T08:01:18.385Z","updated_at":"2025-07-16T13:32:25.524Z","avatar_url":"https://github.com/eBay.png","language":"Swift","funding_links":[],"categories":["Others","Messaging"],"sub_categories":[],"readme":"# ![./NMessenger](https://github.com/eBay/NMessenger/blob/master/Assets/nmessenger.png)\n[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/eBay/NMessenger/blob/master/LICENSE)\n\nNMessenger is a fast, lightweight messenger component built on [AsyncDisplaykit](https://github.com/facebook/AsyncDisplayKit) and written in [Swift](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/). Developers can inherently achieve 60FPS scrolling and smooth transitions with rich content components.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/NMessenger-Overview.png\" alt=\"NMessenger\" width=\"300\"/\u003e\n\u003c/p\u003e\n\n## Features\n\nBuilt-in support for:\n- [Text](https://github.com/eBay/NMessenger#posting-messages)\n- [Images (network and local)](https://github.com/eBay/NMessenger#posting-messages)\n- [Collection Views](https://github.com/eBay/NMessenger#posting-messages)\n- [Rich Content](https://github.com/eBay/NMessenger#posting-messages)\n- [Typing Indicators](https://github.com/eBay/NMessenger#typing-indicators)\n- [Avatars](https://github.com/eBay/NMessenger#avatars)\n- [Custom and Layer Masked Bubbles](https://github.com/eBay/NMessenger#message-bubbles)\n- [Bubble Configurations](https://github.com/eBay/NMessenger#bubble-configuration)\n- [Extendable Components](https://github.com/eBay/NMessenger#content-nodes-and-custom-components)\n- [Async Head Prefetching](https://github.com/eBay/NMessenger#head-prefetching)\n- [Message Groups](https://github.com/eBay/NMessenger#message-groups)\n- [Adding, Removing, and Updating Messages (with animations)](https://github.com/eBay/NMessenger#adding-removing-and-updating)\n\n## Version Information\n* 1.0.0\n\n## Requirements\n* iOS 8.2+\n\n## Installation for [Cocoapods](https://cocoapods.org)\n\n```ruby\n# For latest release in cocoapods - 1.0.80 (Swift 3, ASDK 2.X)\npod 'NMessenger'\n\n# For ASDK 1.9\npod 'NMessenger', '1.0.79'\n\n# For Swift 2.3 support\npod 'NMessenger', '1.0.76'\n```\n\n## Notes\n### For iOS 10 Support\nAdd `NSPhotoLibraryUsageDescription` and `NSCameraUsageDescription` to your App Info.plist to specify the reason for accessing photo library and camera. See [Cocoa Keys](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html) for more details.\n\n### Landscape Mode\n- **Landscape mode is not currently supported.** While it may be supported in a future release, we have disabled device rotation for `NMessengerViewController` to prevent issues.\n\n## Getting Started\n\n### NMessengerViewController\nNMessenger comes with a prebuilt NMessengerViewController that has a few supported features.\n- Posting messages\n- Input bar with a custom placeholder\n- Camera and photo library access\n- Typing indicator support\n\n\n#### Posting Messages\nSend a text message.\n```swift\nfunc sendText(text: String, isIncomingMessage:Bool) -\u003e GeneralMessengerCell\n```\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/Text-Message.png\" alt=\"Text Message\" width=\"400\"/\u003e\n\u003c/p\u003e\n---\n\nSend a message with an image.\n```swift\nfunc sendImage(image: UIImage, isIncomingMessage:Bool) -\u003e GeneralMessengerCell\n```\nSend a message with a network image. (Uses AsyncDisplayKit with PINCache to lazyload and cache network images)\n```swift\nfunc sendNetworkImage(imageURL: String, isIncomingMessage:Bool) -\u003e GeneralMessengerCell\n```\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/Image-Message.png\" alt=\"Image Message\" width=\"400\"/\u003e\n\u003c/p\u003e\n---\n\nA message with a collection view can be created directly from an array of views or nodes. *Note: Nodes take advantage of ASDK's async rendering capabilities and will make this component scroll more smoothly.*\n\n```swift\nfunc sendCollectionViewWithViews(views: [UIView], numberOfRows:CGFloat, isIncomingMessage:Bool) -\u003e GeneralMessengerCell\nfunc sendCollectionViewWithNodes(nodes: [ASDisplayNode], numberOfRows:CGFloat, isIncomingMessage:Bool) -\u003e GeneralMessengerCell\n```\n\nOne line CollectionView\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/CollectionSingle-Message.png\" alt=\"One line CollectionView\" width=\"400\"/\u003e\n\u003c/p\u003e\n\nMulti line CollectionView\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/CollectionMulti-Message.png\" alt=\"Multi line CollectionView\" width=\"400\"/\u003e\n\u003c/p\u003e\n---\n\nSend a message with a custom view or node. *Note: Nodes take advantage of ASDK's async rendering capabilities and will make this component scroll more smoothly.*\n\n```swift\nfunc sendCustomView(view: UIView, isIncomingMessage:Bool) -\u003e GeneralMessengerCell\nfunc sendCustomNode(node: ASDisplayNode, isIncomingMessage:Bool) -\u003e GeneralMessengerCell\n```\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/Custom-Message.png\" alt=\"Custom Message\" width=\"400\"/\u003e\n\u003c/p\u003e\n\nThese functions are meant to be overridden for network calls and other controller logic.\n\n#### Typing Indicators\nTyping indicators signify that incoming messages are being typed. This will be the last message in the messenger by default.\n```swift\n/** Adds an incoming typing indicator to the messenger */\nfunc showTypingIndicator(avatar: ASDisplayNode) -\u003e GeneralMessengerCell\n\n/** Removes a typing indicator from the messenger */\nfunc removeTypingIndicator(indicator: GeneralMessengerCell)\n```\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/TypingIndicator.png\" alt=\"Typing Indicator\" width=\"400\"/\u003e\n\u003c/p\u003e\n\n#### Custom InputBar\nTo use a custom input bar, you must subclass `InputBarView`. `InputBarView` conforms to `InputBarViewProtocol`:\n\n```swift\n@objc public protocol InputBarViewProtocol\n{\n    /* Superview of textInputView - can hold send button and/or attachment button*/\n    var textInputAreaView: UIView! {get set}\n    /* UITextView where the user will input the text*/\n    var textInputView: UITextView! {get set}\n    //NMessengerViewController where to input is sent to\n    var controller:NMessengerViewController! {get set}\n}\n```\n\nBoth `textInputAreaView` and `textInputView` must be created in order for `NMessengerViewController` to have the correct behavior. `controller` is set by the initializer in `InputBarView` base class. \n\nIn order to use your custom InputBar, override `func getInputBar()-\u003eInputBarView` in `NMessengerViewController`.\n\n### NMessenger\n\nNMessenger can be added to any view. \n```swift\nself.messengerView = NMessenger(frame: CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height))\nmessengerView.delegate = self\nself.view.addSubview(self.messengerView)\n```\nWith NMessenger, there is no need to manage a data source. Simply add a message and forget about it.\n\nChanging and updating a message relies on a reference. These references can be passed through a delegate method or stored locally in a class. \n\n```swift\nself.delegate.deleteMessageBtnClick(self)\n.\n.\n.\nfunc deleteMessageBtnClick(message: GeneralMessageCell) {\n  self.messengerView.removeMessage(message, .None)\n}\n```\n\n### Message Bubbles\nNMessenger comes with a few prebuilt bubble types. `bubble` can also easily be subclassed to create a new bubble component. \n\n`SimpleBubble`\n\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/SimpleBubble.png\" alt=\"Simple Bubble\" width=\"200\"/\u003e\n\n`DefaultBubble`\n\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/DefaultBubble.png\" alt=\"Default Bubble\" width=\"200\"/\u003e\n\n`StackedBubble`\n\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/StackedBubble.png\" alt=\"Stacked Bubble\" width=\"200\"/\u003e\n\n`ImageBubble` - Can be used with any [9 Patch Image](https://github.com/chrislondon/9-Patch-Image-for-Websites/wiki/What-Are-9-Patch-Images)\n\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/ImageBubble.png\" alt=\"Image Bubble\" width=\"200\"/\u003e\n\nBy setting `hasLayerMask = true`, bubbles will mask their content. This is relevant for images and other rich content components. \n\n### Bubble Configuration\nIn order to configure custom bubbles for the messenger, you must create a new class that implements `BubbleConfigurationProtocol`.\n```swift\n/** Configures a bubble for a ContentNode. Implement this to create your own bubble configuration */\nprotocol BubbleConfigurationProtocol {\n    var isMasked: Bool {get set}\n    \n    /** Create and return a UI color representing an incoming message */\n    func getIncomingColor() -\u003e UIColor\n    \n    /** Create and return a UI color representing an outgoing message */\n    func getOutgoingColor() -\u003e UIColor\n    \n    /** Create and return a bubble for the ContentNode */\n    func getBubble() -\u003e Bubble\n    \n    /** Create and return a bubble that is used by the Message group for Message nodes after the first. This is typically used to \"stack\" messages */\n    func getSecondaryBubble() -\u003e Bubble\n}\n```\nThis protocol is meant to provide a new instance of the bubble class for primary (messageNode) and secondary (messageGroup) bubble types. By changing `var sharedBubbleConfiguration: BubbleConfigurationProtocol` in `NMessengerViewController`, you can set the configuration for all newly added messages. \n\n### Content Nodes and Custom Components\n#### Content Nodes\nA Content Node holds message content in a `MessageNode` (everything inside of the bubble).\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/ContentNode.png\" alt=\"Content Node\" width=\"500\"/\u003e\n\u003c/p\u003e\n\nSubclassing `ContentNode` gives you the ability to define your own content. This is particularly useful for creating rich content components that are not in our stock message kit. Alternatively, you can initialize a 'CustomContentNode' with your own view or node. \n\nContent Nodes can also be given a `BubbleConfigurationProtocol` to customize their bubble. \n\n#### GeneralMessengerCell\n`GeneralMessengerCell` can be subclassed to make any type of component. All messenger cells extend this object.\n\n#### Timestamps\nTimestamps can be easily added with the `MessageSentIndicator` class. \n```swift\nlet messageTimestamp = MessageSentIndicator()\nmessageTimestamp.messageSentText = \"NOW\"\n\n//NMessengerViewController\nself.addMessageToMessenger(messageTimestamp)\n\n//NMessenger\nmessengerView.addMessage(messageTimestamp, scrollsToMessage: false)\n```\n\n### Avatars\nCustom avatars can be set with an AsyncDisplayKit `ASImageNode`.\n\n```swift\nlet nAvatar = ASImageNode()\nnAvatar.image = UIImage(named: \"nAvatar\")\n.\n.\n.\nmessageNode.avatarNode = nAvatar\n```\n\n### Head Prefetching\nMany messengers prefetch at the head. This is not trivial with a UITableView or AysncDisplayKit features. NMessenger supports head prefetching out of the box.\n\nTo use the head prefetch feature, set `var doesBatchFetch: Bool = true` on NMessenger. NMessengerDelegate will also need to be implemented and set by your controller.\n\n```swift\n@objc protocol NMessengerDelegate {\n    /**\n     Triggered when a load batch content should be called. This method is called on a background thread.\n     Make sure to add prefetched content with *endBatchFetchWithMessages(messages: [GeneralMessengerCell])**/\n    optional func batchFetchContent()\n    /** Returns a newly created loading Indicator that should be used at the top of the messenger */\n    optional func batchFetchLoadingIndicator()-\u003eGeneralMessengerCell\n}\n```\n\nAll batch fetch network calls should be done in `batchFetchContent`. Make sure to add your message cells with `endBatchFetchWithMessages(messages: [GeneralMessengerCell])` to end the batch fetch. Calling this function will remove the loading indicator and batch fetch lock.\n\n### Message Groups\nMessage Groups can be used to stack messages and animate avatars. Like `MessageNode`, `MessageGroup` extends `GeneralMessageCell`. The difference, however is that `MessageGroup` holds a table of `MessageNode`s rather than a `ContentNode`.\n```\n================\n| MessageGroup | -\u003e ===============    ===============\n================    | MessageNode | -\u003e | ContentNode | (Primary Bubble)\n                    ---------------    ===============\n                    | MessageNode | -\u003e | ContentNode | (Secondary Bubble)\n                    ---------------    ===============\n                    | MessageNode | -\u003e | ContentNode | (Secondary Bubble)\n                    ---------------    ===============\n                    | MessageNode | -\u003e | ContentNode | (Secondary Bubble)\n                    ===============    ===============\n```\nAdditionally, `MessageGroup` determines the bubble type of the `MessageNode`'s content based on the position in the table. The first message's content will have a primary bubble, the rest will have a secondary bubble. Typically, the avatar will be disabled on any `MessagesNode` in the group, but kept for the `MessageGroup`.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/MessageGroup.png\" alt=\"Message Group\" width=\"300\"/\u003e\n\u003c/p\u003e\n\n#### Adding, Removing, and Updating\n\nMessage Groups include a few slick animations for Adding, Removing, and Updating MessageNodes in the group. These can be called directly from `MessageGroup` or Added and Removed from `NMessenger`. *Note: These are not surfaced in the `NMessengerViewController` yet*\n\nIt is recommended that they are removed from `NMessenger` because of the possibility that the `MessageNode` removed was the last message in the group. If this happens, the `MessageGroup` will remain after the `MessageGroup` was removed. `NMessenger` makes sure that in this case the `MessageGroup` is removed from the messenger.\n\n##### Adding\nTo add a `MessageNode`. \n```swift\nmessageGroup.addMessageToGroup(message: GeneralMessengerCell, completion: (()-\u003eVoid)?)\n```\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/Mg-Add.gif\" alt=\"Message Group Add Animation\" width=\"200\"/\u003e\n\u003c/p\u003e\n\n##### Removing\nTo remove a `MessageNode`.\n```swift\nmessageGroup.removeMessageFromGroup(message: GeneralMessengerCell, completion: (()-\u003eVoid)?)\n```\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/Mg-Delete.gif\" alt=\"Message Group Remove Animation\" width=\"200\"/\u003e\n\u003c/p\u003e\n\n##### Updating\nTo update an existing `MessageNode` with a new `MessageNode`.\n```swift\nmessageGroup.replaceMessage(message: GeneralMessengerCell, withMessage newMessage: GeneralMessengerCell, completion: (()-\u003eVoid)?)\n```\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://github.com/eBay/NMessenger/blob/master/Assets/Mg-Replace.gif\" alt=\"Message Group Update Animation\" width=\"200\"/\u003e\n\u003c/p\u003e\n\n## Authors\n- Aaron Tainter\n- David Schechter\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FeBay%2FNMessenger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FeBay%2FNMessenger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FeBay%2FNMessenger/lists"}