UI components
PubNub Chat Components for iOS provide easy-to-use building blocks to create chat applications for various use cases with different functionalities and customizable looks.
Available components include:
Component theming
Default ThemeTemplate
provides structural and appearance configurations for each component and its sub-components. Light and dark modes for each theme are supported automatically through adaptive elements based on the current system-wide appearance settings.
Most appearance properties use Combine to asynchronously update the corresponding bound View elements. You can access the shared ThemeTemplate
through the themeProvider
property of any ChatProvider
instance.
ChannelList
UIViewController
that displays a list of stored Channel
objects. It can represent all channels of the application, only channels joined by the current user, all channels available to be joined, or any other type of NSFetchedResultsController
made against stored channel data.
Parameters
You can initialize the ChannelList
view model using the following parameters.
Name | Type | Default value | Description |
---|---|---|---|
provider | ChatProvider | n/a | The ChatProvider instance to be used inside this component. |
fetchedEntities | NSFetchedResultsController | n/a | The data type and query options used to populate the component. |
componentTheme | ChannelListComponentTheme? | The ChannelList theme sourced from ChatProvider . | The configurable theme used for the component. If you provide a custom object, you must maintain it outside of ChatProvider . |
View model
The view model is used to configure both the main UIViewController
for the component, the channel data, and lay out any sub-components.
To override core functionality, you can either subclass the view model or implement override blocks to replace specific key functionalities.
The default ChannelListComponentViewModel
is configured to display the default channel memberships of the current user, sorted by the type and name of the channel.
Component population
ChatProvider
can be extended to return a configured NSFetchedResultsController
, and this object ensures the component always displays the most recent data. NSFetchedResultsController
can contain any type of stored data related to a channel, and can be sorted and sectioned based on any channel property.
Default example
By default, the list is populated by any channel of which the current user is a member. The list is divided into multiple sections based on the channel type, and each section is sorted by the name of the channel.
open func channelMembershipsFrom(
userId: String, sectionedByType: Bool
) -> NSFetchedResultsController<ManagedEntities.Member> {
let request = ManagedEntities.Member.membershipsBy(userId: userId)
request.sortDescriptors = [
NSSortDescriptor(key: "channel.type", ascending: false),
NSSortDescriptor(key: "channel.name", ascending: true)
]
request.relationshipKeyPathsForPrefetching = ["channel"]
return fetchedResultsControllerProvider(
fetchRequest: request,
sectionKeyPath: sectionedByType ? "channel.type": nil,
cacheName: nil
)
}
You can create a view model composed of the above default NSFetchedResultsController
and ChannelListComponentTheme
using this ChatProvider
extension method:
open func senderMembershipsChannelListComponentViewModel(
sectionedByType: Bool = true,
customTheme: ChannelListComponentTheme? = nil
) -> ChannelListComponentViewModel<ModelData, ManagedEntities> {
return ChannelListComponentViewModel(
provider: self,
fetchedEntities: channelMembershipsFrom(userId: senderID, sectionedByType: sectionedByType),
customTheme: customTheme ?? self.themeProvider.template.channelListComponent
)
}
Properties
The ChannelList
view model provides outlets for the following properties after initialization:
Name | Type | Default value | Description |
---|---|---|---|
provider | ChatProvider | n/a | A ChatProvider instance to be used inside this component. |
fetchedEntities | NSFetchedResultsController | n/a | The data type and query options used to populate the component. |
componentTheme | ChannelListComponentTheme | The ChannelList theme sourced from ChatProvider . | The configurable theme used for the component. |
leftBarButtonNavigationItems | (UIViewController, ChannelListComponentViewModel) -> [UIBarButtonItem] | nil | Views that appear on the left side of the navigation bar. |
rightBarButtonNavigationItems | (UIViewController, ChannelListComponentViewModel) -> [UIBarButtonItem] | nil | Views that appear on the right side of the navigation bar. |
customNavigationTitleView | (ChannelListComponentViewModel) -> UIView? | nil | The custom view that's used inside the navigation bar title. |
customNavigationTitleString | (ChannelListComponentViewModel) -> AnyPublisher<String?, Never> | nil | The custom String publisher that's used inside the navigation bar title if a view isn't provided. |
customChannelCellViewModel | (ChannelListComponentViewModel, Channel) -> ChannelCellComponentViewModel | nil | The cell view model that's be used instead of the default model found inside the theme. |
configureFetchedResultsController | (NSFetchedResultsController) -> Void | nil | An outlet to customize the channel data query before the data is fetched. |
You can configure the message items inside the list with the data found inside ManagedChannelViewModel
:
public protocol ManagedChannelViewModel {
associatedtype Entity: ManagedChatChannel
associatedtype MemberViewModel: ManagedMemberViewModel & Hashable
associatedtype MessageViewModel: ManagedMessageViewModel & Hashable
var pubnubId: String { get }
var managedObjectId: NSManagedObjectID { get }
var channelNamePublisher: AnyPublisher<String, Never> { get }
var channelDetailsPublisher: AnyPublisher<String?, Never> { get }
var channelAvatarUrlPublisher: AnyPublisher<URL?, Never> { get }
var channelTypePublisher: AnyPublisher<String, Never> { get }
var channelCustomPublisher: AnyPublisher<Data, Never> { get }
var membershipPublisher: AnyPublisher<Set<MemberViewModel>, Never> { get }
var memberCountPublisher: AnyPublisher<Int, Never> { get }
var presentMemberCountPublisher: AnyPublisher<Int, Never> { get }
var messagesPublisher: AnyPublisher<Set<MessageViewModel>, Never> { get }
var oldestMessagePublisher: AnyPublisher<MessageViewModel?, Never> { get }
}
Theming
You can configure the ChannelListComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
controllerType | ComponentViewController | No | Default class type that is used when creating new UIViewController instances. |
backgroundColor | UIColor | Yes | View Controller’s background color. |
navigationTitleView | BasicComponentTheme? | Yes | Theming for UIView found on the UINavigationItem component. |
collectionViewTheme | CollectionViewComponentTheme | Yes | Theming for UICollectionView found on the UIViewController component. |
sectionHeaderLabel | LabelComponentTheme | Yes | Theming for UILabel you can attach to the UICollectionReusableView section Header-type. |
cellTheme | MemberListCellComponentTheme | Yes | Theming for UICollectionViewCell found on the UIViewController component. |
You can configure the CollectionViewComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
viewType | UICollectionView.Type | No | Default class type that is used when creating new UICollectionView instances. |
layoutType | CollectionViewLayoutComponent.Type | No | Default class type that is used when creating new UICollectionViewLayout instances. |
headerType | UICollectionReusableView.Type | No | Default class type that is used when creating new UICollectionView section headers. |
footerType | UICollectionReusableView.Type | No | Default class type that is used when creating new UICollectionView section footers. |
backgroundColor | UIColor | Yes | View background color. |
scrollViewTheme | ScrollViewComponentTheme | Yes | Theming for the UIScrollView properties of UICollectionView . |
refreshControlTheme | RefreshControlTheme? | Yes | Theming for the UIRefreshControl properties of UICollectionView . |
prefetchIndexThreshold | Int? | Yes | List index that when scrolled past triggers a remote sync request to fetch additional data to be stored into the local database. |
isPrefetchingEnabled | Bool | No | A Boolean value that indicates whether cell and data pre-fetching are enabled. This allows for more efficient pre-fetching, but isn't required. |
You can configure the ChannelListCellComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
cellType | CollectionViewCellComponent.Type | No | Default class type that is used when creating new UICollectionViewCell instances. |
backgroundColor | UIColor? | Yes | View's background color. |
highlightColor | UIColor? | Yes | View's background color during highlighted state. |
selectedColor | UIColor? | Yes | View's background color during selected state. |
itemTheme | BasicComponentTheme | Yes | Theming for the sub-views of the channel list cell. |
You can configure the BasicComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
imageView | ImageComponentTheme | Yes | Theming for the User avatar UIImageView |
primaryLabel | LabelComponentTheme | Yes | Theming for the primary UILabelView that can be displayed on the channel list cell. |
secondaryLabel | LabelComponentTheme | Yes | Theming for the secondary UILabelView that can be displayed on the channel list cell. |
tertiaryLabel | LabelComponentTheme | Yes | Theming for the tertiary UILabelView that can be displayed on the channel list cell. |
quaternaryLabel | LabelComponentTheme | Yes | Theming for the quaternary UILabelView that can be displayed on the channel list cell. |
You can configure the ImageComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
customType | ImageComponentView.Type | No | Default class type that is used when creating new UIImageView instances. |
localImage | UIImage? | Yes | Locally sourced UIImage you can use as a placeholder while fetching remote images. |
cornerRadius | CGFloat | Yes | The radius to use when drawing rounded corners for the layer’s background. |
margin | UIEdgeInsets | Yes | The default spacing to use when laying out content in the view. |
You can configure the LabelComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
textFont | UIFont | Yes | Font of the text. |
textColor | UIColor | Yes | Color of the text. |
textAlignment | NSTextAlignment | Yes | Technique for aligning the text. |
adjustsFontForContentSizeCategory | Bool | Yes | Boolean that determines whether the text's font size is reduced to fit inside the bounds of the UILabelView . |
Default
The default implementation is available through ChannelListComponentTheme.pubnubDefaultGroupChannelList
which is composed of the following values:
ChannelListComponentTheme(
controllerType: CollectionViewComponent.self,
backgroundColor: .clear,
navigationTitleView: BasicComponentTheme(
imageView: ImageComponentTheme(
customType: PubNubAvatarComponentView.self,
localImage: AppearanceTemplate.Image.avatar,
diameterSize: 42,
margin: .init(top: .zero, left: .zero, bottom: .zero, right: 5.0)
),
primaryLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.title2.bold,
textColor: AppearanceTemplate.Color.label,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
)
),
collectionViewTheme: CollectionViewComponentTheme(
viewType: UICollectionView.self,
layoutType: TableCollectionViewLayout.self,
headerType: ReusableLabelViewComponent.self,
footerType: ReusableLabelViewComponent.self,
backgroundColor: .systemBackground,
isPrefetchingEnabled: true,
scrollingBounces: false,
alwaysBounceVertical: false,
showsHorizontalScrollIndicator: false,
keyboardDismissMode: .interactive,
automaticallyAdjustsScrollIndicatorInsets: true,
contentInsetAdjustmentBehavior: .always
),
headerLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.title1.bold,
textColor: AppearanceTemplate.Color.label,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
),
itemTheme: ChannelListCellComponentTheme(
customType: CollectionViewContainerCellComponent.self,
viewModelType: ChannelCellViewModel.self,
backgroundColor: .clear,
highlightColor: .clear,
selectedColor: .systemGray4,
itemTheme: BasicComponentTheme(
imageView: ImageComponentTheme(
customType: PubNubAvatarComponentView.self,
localImage: AppearanceTemplate.Image.avatar,
diameterSize: 30,
margin: .init(top: .zero, left: 10.0, bottom: .zero, right: 5.0)
),
primaryLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.caption1,
textColor: AppearanceTemplate.Color.label,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
),
secondaryLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.caption2,
textColor: AppearanceTemplate.Color.secondaryLabel,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
)
)
)
)
Actions
This component supports the following UI interactions:
Name | Type | Default value | Description |
---|---|---|---|
componentDidLoad | (UIViewController, ChannelListComponentViewModel) -> Void | nil | An action called when the component view is loaded. |
componentWillAppear | (UIViewController, ChannelListComponentViewModel) -> Void | nil | An action called when the component view appears on the screen. |
componentWillDisappear | (UIViewController, ChannelListComponentViewModel) -> Void | nil | An action called when the component view disappears from the screen. |
didSelectChannel | (UIViewController, ChannelListComponentViewModel, ChatChannel) -> Void | Default MessageList component | An action called when a user taps a list item. |
Example
The following example shows how an action can be configured after creating a view model. didSelectChannel
creates a default MessageList
component from the selected channel and displays it from the current ChannelList
component.
let channelViewModel = chatProvider
.senderMembershipsChannelListComponentViewModel()
channelViewModel.didSelectChannel = { (controller, viewModel, channel) in
// Prepare Message List View
guard let component = try? viewModel.provider
.messageListComponentViewModel(for: channel.id)
.configuredComponentView() else { return }
// Display it from the current controller in the primary content view
controller.show(component, sender: nil)
}
MemberList
UIViewController
that displays a list of stored Member
and User
objects. It can represent all members of a specific channel, only other users that are currently present in the application, a sectioned list of present and non-present members, or any other type of NSFetchedResultsController
made against stored member data.
Parameters
You can initialize the MemberList
view model using the following parameters.
Name | Type | Default value | Description |
---|---|---|---|
provider | ChatProvider | n/a | A ChatProvider instance to be used inside this component. |
selectedChannelId | String | n/a | The channel identifier used during a member query. |
fetchedEntities | NSFetchedResultsController | n/a | The data type and query options used to populate the component. |
componentTheme | MemberListComponentTheme? | The MemberList theme sourced from ChatProvider . | The configurable theme used for the component. If you provide a custom object, you must maintain it outside of ChatProvider . |
View model
The view model is used to configure both the main UIViewController
for the component, the member data, and lay out any sub-components.
To override core functionality, you can either subclass the view model or implement override blocks to replace specific key functionalities.
The default MemberListComponentViewModel
is configured to display the users that are members of a specific channel, sorted by their presence status and name.
Component population
ChatProvider
can be extended to return a configured NSFetchedResultsController
, and this object ensures the component always displays the most recent data. NSFetchedResultsController
can contain any type of stored data related to a member, and can be sorted and sectioned based on any member property.
Default example
By default, the list is populated by any user that's a member of the provided channel. The list is sorted based on the presence status and the name of the user.
open func userMembersFrom(
channelId: String,
excludingSender: Bool = false
) -> NSFetchedResultsController<ManagedEntities.Member> {
let request = ManagedEntities.Member.membersBy(
channelID: channelId,
excludingUserId: excludingSender ? senderID : nil,
onlyPresent: true
)
request.sortDescriptors = [
NSSortDescriptor(key: "user.name", ascending: true)
]
request.relationshipKeyPathsForPrefetching = ["user"]
return fetchedResultsControllerProvider(
fetchRequest: request,
sectionKeyPath: nil,
cacheName: nil
)
}
You can create a view model composed of the above default NSFetchedResultsController
and MemberListComponentTheme
using this ChatProvider
extension method:
open func memberListComponentViewModel(
channelId: String,
customTheme: MemberListComponentTheme? = nil
) -> MemberListComponentViewModel<ModelData, ManagedEntities> {
return MemberListComponentViewModel(
provider: self,
fetchedEntities: userMembersFrom(channelId: channelId),
customTheme: customTheme ?? self.themeProvider.template.memberListComponent
)
}
Properties
The MemberList
view model provides outlets for the following properties after initialization:
Name | Type | Default value | Description |
---|---|---|---|
provider | ChatProvider | n/a | A ChatProvider instance to be used inside this component. |
fetchedEntities | NSFetchedResultsController | n/a | The data type and query options used to populate the component. |
componentTheme | ChannelListComponentTheme | The ChannelList theme sourced from ChatProvider . | The configurable theme used for the component. |
selectedChannelId | String | n/a | The channel identifier used during the member query. |
leftBarButtonNavigationItems | (UIViewController, MemberListComponentViewModel) -> [UIBarButtonItem] | nil | Views that appear on the left side of the navigation bar. |
rightBarButtonNavigationItems | (UIViewController, MemberListComponentViewModel) -> [UIBarButtonItem] | nil | Views that appear on the right side of the navigation bar. |
customNavigationTitleView | (MemberListComponentViewModel) -> UIView? | nil | The custom view that's used inside the navigation bar title. |
customNavigationTitleString | (MemberListComponentViewModel) -> AnyPublisher<String?, Never> | nil | The custom String publisher that's used inside the navigation bar title if a view isn't provided. |
customMemberCellViewModel | (MemberListComponentViewModel, Member) -> MemberCellComponentViewModel | nil | The cell view model that's used instead of the default model found inside the theme. |
configureFetchedResultsController | (NSFetchedResultsController) -> Void | nil | The outlet to customize the member data query before the data is fetched. |
You can configure the message items inside the list with the data found inside ManagedMemberViewModel
:
public protocol ManagedMemberViewModel {
associatedtype Entity: ManagedChatMember
associatedtype ChannelViewModel: ManagedChannelViewModel
associatedtype UserViewModel: ManagedUserViewModel
var managedObjectId: NSManagedObjectID { get }
var isPresentPublisher: AnyPublisher<Bool, Never> { get }
var channelViewModel: ChannelViewModel { get }
var userViewModel: UserViewModel { get }
}
Theming
You can configure the MemberListComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
controllerType | ComponentViewController | No | Default class type that is used when creating new UIViewController instances. |
backgroundColor | UIColor | Yes | View Controller’s background color. |
navigationTitleView | BasicComponentTheme? | Yes | Theming for UIView found on the UINavigationItem component. |
collectionViewTheme | CollectionViewComponentTheme | Yes | Theming for UICollectionView found on the UIViewController component. |
sectionHeaderLabel | LabelComponentTheme | Yes | Theming for UILabel you can attach to the UICollectionReusableView section Header-type. |
cellTheme | MemberListCellComponentTheme | Yes | Theming for UICollectionViewCell found on the UIViewController component. |
You can configure the CollectionViewComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
viewType | UICollectionView.Type | No | Default class type that is used when creating new UICollectionView instances. |
layoutType | CollectionViewLayoutComponent.Type | No | Default class type that is used when creating new UICollectionViewLayout instances. |
headerType | UICollectionReusableView.Type | No | Default class type that is used when creating new UICollectionView section headers. |
footerType | UICollectionReusableView.Type | No | Default class type that is used when creating new UICollectionView section footers. |
backgroundColor | UIColor | Yes | View background color. |
scrollViewTheme | ScrollViewComponentTheme | Yes | Theming for UIScrollView properties of UICollectionView . |
refreshControlTheme | RefreshControlTheme? | Yes | Theming for UIRefreshControl properties of UICollectionView . |
prefetchIndexThreshold | Int? | Yes | List index that when scrolled past triggers a remote sync request to fetch additional data to be stored into the local database. |
isPrefetchingEnabled | Bool | No | A Boolean value that indicates whether cell and data pre-fetching are enabled. This allows for more efficient pre-fetching, but isn't required. |
You can configure the MemberListCellComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
cellType | CollectionViewCellComponent.Type | No | Default class type that is used when creating new UICollectionViewCell instances. |
backgroundColor | UIColor? | Yes | View's background color. |
highlightColor | UIColor? | Yes | View's background color during highlighted state. |
selectedColor | UIColor? | Yes | View's background color during selected state. |
itemTheme | BasicComponentTheme | Yes | Theming for the sub-views of the member list cell.. |
You can configure the BasicComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
imageView | ImageComponentTheme | Yes | Theming for the User avatar UIImageView |
primaryLabel | LabelComponentTheme | Yes | Theming for the primary UILabelView you can display on the member list cell. |
secondaryLabel | LabelComponentTheme | Yes | Theming for the secondary UILabelView you can display on the member list cell. |
tertiaryLabel | LabelComponentTheme | Yes | Theming for the tertiary UILabelView you can display on the member list cell. |
quaternaryLabel | LabelComponentTheme | Yes | Theming for the quaternary UILabelView you can display on the member list cell. |
You can configure the ImageComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
customType | ImageComponentView.Type | No | Default class type that is used when creating new UIImageView instances. |
localImage | UIImage? | Yes | Locally sourced UIImage you can use as a placeholder while fetching remote images. |
cornerRadius | CGFloat | Yes | The radius to use when drawing rounded corners for the layer’s background. |
margin | UIEdgeInsets | Yes | The default spacing to use when laying out content in the view. |
You can configure the LabelComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
textFont | UIFont | Yes | Font of the text. |
textColor | UIColor | Yes | Color of the text. |
textAlignment | NSTextAlignment | Yes | Technique for aligning the text. |
adjustsFontForContentSizeCategory | Bool | Yes | Boolean that determines whether the text's font size is reduced to fit inside the bounds of UILabelView . |
Default
The default implementation is available through MemberListComponentTheme.pubnubDefaultGroupMemberList
which is composed of the following values:
MemberListComponentTheme(
controllerType: CollectionViewComponent.self,
backgroundColor: .clear,
navigationTitleView: BasicComponentTheme(
imageView: ImageComponentTheme(
customType: PubNubAvatarComponentView.self,
localImage: AppearanceTemplate.Image.avatar,
diameterSize: 42,
margin: .init(top: .zero, left: .zero, bottom: .zero, right: 5.0)
),
primaryLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.title2.bold,
textColor: AppearanceTemplate.Color.label,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
)
),
collectionViewTheme: CollectionViewComponentTheme(
viewType: UICollectionView.self,
layoutType: TableCollectionViewLayout.self,
headerType: ReusableLabelViewComponent.self,
footerType: ReusableLabelViewComponent.self,
backgroundColor: .systemBackground,
isPrefetchingEnabled: true,
scrollingBounces: false,
alwaysBounceVertical: false,
showsHorizontalScrollIndicator: false,
keyboardDismissMode: .interactive,
automaticallyAdjustsScrollIndicatorInsets: true,
contentInsetAdjustmentBehavior: .always
),
headerLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.title1.bold,
textColor: AppearanceTemplate.Color.label,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
),
itemTheme: MemberListCellComponentTheme(
customType: CollectionViewContainerCellComponent.self,
viewModelType: MemberCellViewModel.self,
backgroundColor: .clear,
highlightColor: .clear,
selectedColor: .clear,
itemTheme: BasicComponentTheme(
imageView: ImageComponentTheme(
customType: PubNubAvatarComponentView.self,
localImage: AppearanceTemplate.Image.avatar,
diameterSize: 30,
margin: .init(top: .zero, left: 10.0, bottom: .zero, right: 5.0)
),
primaryLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.caption1,
textColor: AppearanceTemplate.Color.label,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
),
secondaryLabel: LabelComponentTheme(
customType: PubNubLabelComponentView.self,
textFont: AppearanceTemplate.Font.caption2,
textColor: AppearanceTemplate.Color.secondaryLabel,
adjustsFontForContentSizeCategory: true,
textAlignment: .natural,
textMargin: .zero
)
)
)
)
Actions
This component supports the following UI interactions:
Name | Type | Default value | Description |
---|---|---|---|
componentDidLoad | (UIViewController, MemberListComponentViewModel) -> Void | nil | An action called when the component view is loaded. |
componentWillAppear | (UIViewController, MemberListComponentViewModel) -> Void | nil | An action called when the component view appears on the screen. |
componentWillDisappear | (UIViewController, MemberListComponentViewModel) -> Void | nil | An action called when the component view disappears from the screen. |
didSelectMember | (UIViewController, MemberListComponentViewModel, ChatMember) -> Void | nil | An action called when a user taps a list item. |
Example
The following example shows how an action can be configured after creating a view model.
componentDidLoad
is called when the component view first loads. The following example shows how to call PubNub HereNow presence and automatically store the response in the local database.
let memberViewModel = provider
.memberListComponentViewModel(channelId: "stored-channel-id")
memberViewModel.componentDidLoad = { controller, viewModel in
viewModel.provider.dataProvider.syncHereNow(
.init(channels: [viewModel.selectedChannel.pubnubId]),
completion: nil
)
}
MessageList
UIViewController
that displays a list of stored message objects. It's primarily used to fetch historical messages for a channel, including user names, avatars, times of sending, and rich message types (links, images).
Parameters
You can initialize the MemberList
view model using the following parameters.
Name | Type | Default value | Description |
---|---|---|---|
provider | ChatProvider | n/a | A ChatProvider instance to be used inside this component. |
author | User | n/a | The User that represents the current application user. |
selectedChannel | Channel | n/a | The channel of the displayed messages. |
fetchedEntities | NSFetchedResultsController | n/a | The data type and query options used to populate the component. |
componentTheme | MessageListComponentTheme? | The MessageList theme sourced from ChatProvider . | The configurable theme used for the component. If you provide a custom object, you must maintain it outside of ChatProvider . |
View model
The view model is used to configure both the main UIViewController
for the component, the member data, and lay out any sub-components.
To override core functionality, you can either subclass the view model or implement override blocks to replace specific key functionalities.
The default MessageListComponentViewModel
is configured to display the messages of a particular channel sorted by the time a given message was sent.
Component population
ChatProvider
can be extended to return a configured NSFetchedResultsController
, and this object ensures the component always displays the most recent data. NSFetchedResultsController
can contain any type of stored data related to a message, and can be sorted and sectioned based on any message property.
Default example
By default, the list is populated by any channel of which the current user is a member. The list will be divided into multiple sections based on the channel type, and each section is sorted by the name of the channel.
open func messagesFrom(
pubnubChannelId: String
) -> NSFetchedResultsController<ManagedEntities.Message> {
let request = ManagedEntities.Message.messagesBy(pubnubChannelId: pubnubChannelId)
request.sortDescriptors = [
NSSortDescriptor(key: "dateCreated", ascending: true)
]
request.relationshipKeyPathsForPrefetching = ["sender"]
return fetchedResultsControllerProvider(
fetchRequest: request,
sectionKeyPath: nil,
cacheName: nil
)
}
You can create a view model composed of the above default NSFetchedResultsController
and MessageListComponentTheme
using this ChatProvider
extension method:
open func messageListComponentViewModel(
for pubnubChannelId: String,
customTheme: MessageListComponentTheme? = nil
) throws -> MessageListComponentViewModel<ModelData, ManagedEntities> {
let sender = try fetchSender()
guard let channel = try fetchChannel(byPubNubId: pubnubChannelId) else {
throw ChatError.missingRequiredData
}
return MessageListComponentViewModel(
provider: self,
sender: sender,
selectedChannel: channel,
fetchedMessages: messagesFrom(pubnubChannelId: pubnubChannelId),
componentTheme: customTheme ?? themeProvider.template.messageListComponent
)
}
Channel ID storage
You must store the channel matching pubnubChannelId
locally before calling the messageListComponentViewModel()
method.
Properties
You can configure the MessageList
view model using the following parameters.
Name | Type | Default | Description |
---|---|---|---|
provider | ChatProvider | n/a | A ChatProvider instance to be used inside this component. |
author | User | The User that represents the current application user. | |
selectedChannel | Channel | n/a | The channel of the displayed message. |
fetchedEntities | NSFetchedResultsController | n/a | The data type and query options used to populate the component. |
componentTheme | MessageListComponentTheme | The MessageList theme sourced from ChatProvider . | The configurable theme used for the component. |
leftBarButtonNavigationItems | (UIViewController, MessageListComponentViewModel) -> [UIBarButtonItem] | nil | Views that appear on the left side of the navigation bar. |
rightBarButtonNavigationItems | (UIViewController, MessageListComponentViewModel) -> [UIBarButtonItem] | nil | Views that appear on the right side of the navigation bar. |
customNavigationTitleView | (MessageListComponentViewModel) -> UIView? | nil | The custom view that's used inside the navigation bar title. |
customNavigationTitleString | (MessageListComponentViewModel) -> AnyPublisher<String?, Never> | nil | The custom String publisher that's used inside the navigation bar title if a view isn't provided. |
configureFetchedResultsController | (NSFetchedResultsController) -> Void | nil | The outlet to customize the message data query before the data is fetched. |
You can configure the message items inside the list with the data found inside ManagedChannelViewModel
:
public protocol ManagedMessageViewModel {
associatedtype Entity: ManagedChatMessage
associatedtype ChannelViewModel: ManagedChannelViewModel
associatedtype UserViewModel: ManagedUserViewModel
var pubnubId: Timetoken { get }
var managedObjectId: NSManagedObjectID { get }
var messageContentPublisher: AnyPublisher<MessageContent, Error> { get }
var messageCustomPublisher: AnyPublisher<Data, Never> { get }
var messageTextContentPublisher: AnyPublisher<String?, Never>? { get }
var messageLinkContentPublisher: AnyPublisher<URL?, Never>? { get }
var messageImageContentPublisher: AnyPublisher<URL?, Never>? { get }
var messageJSONContentPublisher: AnyPublisher<AnyJSON?, Never>? { get }
var messageCustomPublisher: AnyPublisher<Data, Never> { get }
var messageDateCreatedPublisher: AnyPublisher<Date, Never> { get }
var userViewModel: UserViewModel { get }
var channelViewModel: ChannelViewModel { get }
}
Theming
You can configure the MessageListComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
controllerType | ComponentViewController.Type | No | Default class type that is used when creating new UIViewController instances. |
backgroundColor | UIColor? | Yes | View Controller’s background color. |
collectionViewTheme | CollectionViewComponentTheme | Yes | Theming for UICollectionView found on the UIViewController component. |
incomingItemTheme | MessageListCellComponentTheme | Yes | Theming for messages that are sent from users other than the current user. |
authorItemTheme | MessageListCellComponentTheme? | Yes | Theming for messages that are sent from the current user. |
typingIndicatorCellTheme | TypingIndicatorCellTheme | Yes | Theming for UICollectionViewCell that's displayed when another member is typing. |
You can configure the MessageListCellComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
textMessageContentCellType | CollectionViewCellComponent.Type | No | Default class for the UICollectionViewCell specific to text based messages. |
richMessageContentCellTypes | [MessageContentType: CollectionViewCellComponent.Type] | No | Dictionary that maps UICollectionViewCell class types to known rich message content types. |
backgroundColor | UIColor? | Yes | View's background color. |
highlightColor | UIColor? | Yes | View's background color during highlighted state. |
selectedColor | UIColor? | Yes | View's background color during selected state. |
alignment | UICollectionViewCell.Alignment | Yes | Vertical list edge the message is anchored to. |
maxWidthPercentage | CGFloat | Yes | Maximum width a Cell spans inside the message list frame. |
bubbleContainerTheme | BubbleComponentTheme | Yes | Theming for the bubble view container you can wrap around message content. |
contentTextTheme | TextViewComponentTheme | Yes | Theming for UITextView that displays message text content. |
contentLinkTheme | LinkViewComponentTheme | Yes | Theming for LPLinkView that displays message URL content. |
itemTheme | BasicComponentTheme | Yes | Theming for the accessory views around the message content. |
dateFormatter | DateFormatter | Yes | DataFormatter used when displaying message dates. |
You can configure the TypingIndicatorCellTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
cellType | CollectionViewCellComponent.Type | No | Default class for UICollectionViewCell when creating typing indicator cells. |
contentViewType | UIView | No | Default class for contentView inside the UICollectionViewCell typing indicator. |
backgroundColor | UIColor? | Yes | View's background color. |
highlightColor | UIColor? | Yes | View's background color during highlighted state. |
selectedColor | UIColor? | Yes | View's background color during selected state. |
alignment | UICollectionViewCell.Alignment | Yes | The vertical list edge contentViewType is anchored to. |
primaryContentColor | UIColor? | Yes | Primary color by the contentViewType view. |
secondaryContentColor | UIColor? | Yes | Secondary color used by the contentViewType view. |
animationEnabled | Bool | No | Whether the typing indicator is animated. |
pulsing | Bool | No | Whether the typing indicator animation pulses. |
bounces | Bool | No | Whether the typing indicator animation bounces. |
bounceDelay | TimeInterval | No | Delay between each distinct sub-view's bounce animation. |
bounceOffset | CGFloat | No | Distance each distinct sub-view bounces during its animation. |
fades | Bool | No | Whether the typing indicator animation fades as it animates. |
Default
The default implementation is available through MessageInputComponentTheme.pubnubGroupChat
which is composed of the following values:
MessageListComponentTheme(
controllerType: CollectionViewComponent.self,
backgroundColor: .systemBackground,
messageInputComponent: .pubnubGroupChat,
collectionViewTheme: CollectionViewComponentTheme(
layoutType: ChatLayout.self,
backgroundColor: .secondarySystemBackground,
scrollViewTheme: .enabled,
refreshControlTheme: .init(
pullToRefreshTitle: .init(string: "Pull to Refresh"),
pullToRefreshTintColor: nil
),
prefetchIndexThreshold: 35,
isPrefetchingEnabled: false
),
incomingItemTheme: MessageListCellComponentTheme(
textMessageContentCellType: MessageListItemCell.self,
richMessageContentCellTypes: [
.link: MessageLinkContentCell.self
],
backgroundColor: .clear,
highlightColor: .clear,
selectedColor: .clear,
alignment: .leading,
maxWidthPercentage: 0.65,
bubbleContainerTheme: BubbleComponentTheme(
alignment: .leading,
containerType: .tailed,
backgroundColor: .systemBlue,
tailSize: 5,
layoutMargin: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
),
contentTextTheme: TextViewComponentTheme(
customType: UITextView.self,
backgroundColor: .clear,
textColor: .black,
textFont: AppearanceTemplate.Font.body,
usesStandardTextScaling: false,
dataDetectorTypes: .all,
linkTextAttributes: [:],
isEditable: false,
isExclusiveTouch: false,
scrollView: .disabled,
textContainerInset: .zero
),
contentLinkTheme: LinkViewComponentTheme(
cacheProvider: InMemoryLinkMetadataCache.shared,
layoutMargin: UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 16)
),
itemTheme: .pubnubGroupChannelList,
dateFormatter: .messageInline
),
authorItemTheme: nil,
typingIndicatorCellTheme: AnimatedTypingIndicatorCellTheme(
cellType: IMessageTypingIndicatorCell.self,
contentViewType: IMessageTypingBubbleView.self,
backgroundColor: .clear,
highlightColor: .clear,
selectedColor: .clear,
primaryContentColor: .systemGray5,
secondaryContentColor: .systemGray2,
animationEnabled: true,
pulsing: true,
bounces: true,
bounceDelay: 0.33,
bounceOffset: 0.25,
fades: true
)
)
Actions
This component supports the following UI interactions:
Name | Type | Default value | Description |
---|---|---|---|
componentDidLoad | (UIViewController, MemberListComponentViewModel) -> Void | nil | Action called when the component view is loaded into memory. Maps to viewDidLoad() of the component view. |
componentWillAppear | (UIViewController, MemberListComponentViewModel) -> Void | nil | Action called when the component view is about to be added to a view hierarchy. Maps to viewWillAppear(_:) of the component view. |
componentWillDisappear | (UIViewController, MemberListComponentViewModel) -> Void | nil | Action called when the component view is removed from the view hierarchy. Maps to viewWillDisappear(_:) of the component view. |
MessageInput
UIView
that can be used to display an interactive UITextView
with an optional, customizable UIButton
used for sending the String
input.
Component dependency
Unlike the other components that can be used as standalone view controllers, the message input should be treated as a sub-component. It's added as a sub-view or accessoryInput
view of the UIViewController
of another component.
Parameters
MessageInputComponent
is the base class for the component. It can be sub-classed to add additional functionality or to include a third-party input bar. You can initialize the component through MessageInputComponentViewModel
.
Name | Type | Default value | Description |
---|---|---|---|
provider | ChatProvider | n/a | ChatProvider instance to be used inside this component. |
selectedChannel | Channel | n/a | Channel used when sending messages. |
componentTheme | MessageInputComponentTheme? | The MessageInput theme sourced from ChatProvider . | The configurable theme used for the component. If you provide a custom object, you must maintain it outside of ChatProvider . |
View model
The view model is used to configure both the main UIView
for the component, the configuration of any sub-views, and the interaction between the MessageInputComponent
and MessageListComponent
.
To override core functionality, you can either subclass the view model or implement override blocks to replace specific key functionalities.
The default MessageInputComponentViewModel
is configured based on the configurations found inside MessageInputComponentTheme
.
Properties
The MessageInput
view model provides outlets for the following properties after initialization:
Name | Type | Default | Description |
---|---|---|---|
provider | ChatProvider | n/a | ChatProvider instance to be used inside this component. |
selectedChannel | Channel | n/a | Channel used when sending messages. |
componentTheme | @Published MessageInputComponentTheme | The MessageInput theme sourced from ChatProvider . | The configurable theme used for the component. |
typingMemberIds | @Published Set<String> | [] | Set of member PubNub identifiers that are currently typing on the channel. An empty Set means no one is currently typing. |
Theming
You can configure the MessageInputComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
viewType | MessageInputComponent | No | Default class type that is used when creating new view instances. |
backgroundColor | UIColor? | Yes | View’s background color. |
placeholderText | String? | Yes | Text that appears whens the user hasn't entered the text. |
placeholderTextColor | UIColor? | Yes | Placeholder text's color. |
placeholderTextFont | UIFont? | Yes | Placeholder text's font. |
textInputTheme | InputTextViewComponentTheme | Yes | Theming for UITextView used as the input view. |
sendButtonTheme | ButtonComponentTheme | Yes | Theming for UIButton responsible for sending message input. |
typingIndicatorService | TypingIndicatorService | No | Service that coordinates typing indicator events across all channels. |
publishTypingIndicator | Bool | Yes | Whether typing indicator signals are sent to other chat users. |
displayTypingIndicator | Bool | Yes | Whether typing indicator signals are displayed from other chat users. |
You can configure the ButtonComponentTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
backgroundColor | UIColor? | Yes | View’s background color. |
tintColor | UIColor? | Yes | Tint color to apply to the button title and image. |
title | ButtonTitleStateTheme | Yes | Title during normal button state. |
titleHighlighted | ButtonTitleStateTheme | Yes | Title during highlighted button state. |
titleFont | UIFont? | Yes | Font of the button text. |
image | ButtonImageStateTheme | Yes | Image during normal button state. |
imageHighlighted | ButtonImageStateTheme | Yes | Image during highlighted button state. |
You can configure the ButtonTitleStateTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
title | String? | Yes | Title that the button displays. |
attributedTitle | NSAttributedString? | Yes | Styled text that the button displays. |
titleColor | UIColor? | Yes | Color of the text. |
titleShadowColor | UIColor? | Yes | Shadow color of the text. |
You can configure the ButtonImageStateTheme
theme using the following parameters.
Name | Type | Auto update on change | Description |
---|---|---|---|
image | UIImage? | Yes | Image used for a button state. |
backgroundImage | UIImage? | Yes | Background image used for a button state. |
Default
The default implementation is available through MessageInputComponentTheme.pubnubGroupChat
which is composed of the following values:
MessageInputComponentTheme(
viewType: MessageInputBarComponent.self,
backgroundColor: .secondarySystemBackground,
placeholderText: "Type Message",
placeholderTextColor: .systemGray2,
placeholderTextFont: AppearanceTemplate.Font.body,
textInputTheme: InputTextViewComponentTheme(
customType: UITextView.self,
backgroundColor: .tertiarySystemBackground,
textColor: .systemGray2,
textFont: AppearanceTemplate.Font.body,
usesStandardTextScaling: true,
dataDetectorTypes: .all,
linkTextAttributes: [:],
isEditable: true,
isExclusiveTouch: false,
scrollView: .enabled,
textContainerInset: .zero,
typingAttributes: [:],
autocapitalizationType: .sentences,
autocorrectionType: .default,
spellCheckingType: .default,
smartQuotesType: .default,
smartDashesType: .default,
smartInsertDeleteType: .default,
keyboardType: .default,
keyboardAppearance: .default,
textContentType: nil
),
sendButton: ButtonComponentTheme(
backgroundColor: .clear,
tintColor: .systemBlue,
title: .empty,
titleHighlighted: .empty,
titleFont: AppearanceTemplate.Font.caption1,
image: ButtonImageStateTheme(
image: UIImage(systemName: "paperplane.fill")?.rotate(degree: 45)?.withTintColor(.systemBlue),
backgroundImage: nil
),
imageHighlighted: .empty
),
typingIndicatorService: .shared,
publishTypingIndicator: true,
displayTypingIndicator: true
)
Actions
This component supports the following UI interactions.
Name | Type | Default value | Description |
---|---|---|---|
messageWillSend | (MessageInputComponentViewModel, ChatMessage) -> ChatMessage | nil | Closure that's called before sending a message. Allows you to mutate the message that is sent. |
messageDidSend | (MessageInputComponentViewModel, ChatMessage, Future<ChatMessage, Error>) -> Void | nil | Closure that's called after the message was sent. You can use Future to track the transmission of the message and handle any Error that might be returned. |
Example
Let's see how to configure an action after creating a view model.
messageWillSend
is called when the user is attempting to send a new message. The following example shows how to store the message before sending so it appears on the MessageList
component faster:
let messageInputViewModel = try provider
.messageInputComponentViewModel(channelId: "stored-channel-id")
messageInputViewModel.messageWillSend = { viewModel, message in
viewModel.provider.dataProvider.load(messages: [message])
return message
}