diff --git a/JSQMessages.xcodeproj/project.pbxproj b/JSQMessages.xcodeproj/project.pbxproj index 8a3395016..a0157ba66 100644 --- a/JSQMessages.xcodeproj/project.pbxproj +++ b/JSQMessages.xcodeproj/project.pbxproj @@ -93,6 +93,7 @@ 88D1B0C9190604AF00AFE162 /* JSQMessagesTypingIndicatorFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 88D1B0C8190604AF00AFE162 /* JSQMessagesTypingIndicatorFooterView.xib */; }; 88D1B0CC190606F100AFE162 /* typing.png in Resources */ = {isa = PBXBuildFile; fileRef = 88D1B0CA190606F100AFE162 /* typing.png */; }; 88D1B0CD190606F100AFE162 /* typing@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 88D1B0CB190606F100AFE162 /* typing@2x.png */; }; + 88D4ED7319B555E800C2DCF9 /* JSQDemoModelData.m in Sources */ = {isa = PBXBuildFile; fileRef = 88D4ED7219B555E800C2DCF9 /* JSQDemoModelData.m */; }; C78CEF093F584B85B4321C83 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 343577012CC24261BE4B61A1 /* libPods.a */; }; D68A9FB68FC5463D9A5B23E0 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 343577012CC24261BE4B61A1 /* libPods.a */; }; /* End PBXBuildFile section */ @@ -228,6 +229,8 @@ 88D1B0C8190604AF00AFE162 /* JSQMessagesTypingIndicatorFooterView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = JSQMessagesTypingIndicatorFooterView.xib; sourceTree = ""; }; 88D1B0CA190606F100AFE162 /* typing.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = typing.png; sourceTree = ""; }; 88D1B0CB190606F100AFE162 /* typing@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "typing@2x.png"; sourceTree = ""; }; + 88D4ED7119B555E800C2DCF9 /* JSQDemoModelData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSQDemoModelData.h; path = JSQMessagesDemo/JSQDemoModelData.h; sourceTree = SOURCE_ROOT; }; + 88D4ED7219B555E800C2DCF9 /* JSQDemoModelData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JSQDemoModelData.m; path = JSQMessagesDemo/JSQDemoModelData.m; sourceTree = SOURCE_ROOT; }; 88FFE06619B2E5CB0038C3FF /* JSQMessagesCollectionViewDelegateFlowLayout.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSQMessagesCollectionViewDelegateFlowLayout.h; sourceTree = ""; }; A0F1B1EFE54F44FEA6C4F786 /* Pods.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.xcconfig; path = Pods/Pods.xcconfig; sourceTree = ""; }; /* End PBXFileReference section */ @@ -458,6 +461,8 @@ 885D596418CBD2A600D77BB3 /* Images.xcassets */, 885D596518CBD2A600D77BB3 /* JSQAppDelegate.h */, 885D596618CBD2A600D77BB3 /* JSQAppDelegate.m */, + 88D4ED7119B555E800C2DCF9 /* JSQDemoModelData.h */, + 88D4ED7219B555E800C2DCF9 /* JSQDemoModelData.m */, 8897FBF718CBF967004F59C3 /* JSQDemoViewController.h */, 8897FBF818CBF967004F59C3 /* JSQDemoViewController.m */, 885D596718CBD2A600D77BB3 /* JSQTableViewController.h */, @@ -760,6 +765,7 @@ 8816A6A218E9250400111919 /* UIColor+JSQMessages.m in Sources */, 8816A6A518E9250400111919 /* JSQMessagesViewController.m in Sources */, 8816A6B718E9250400111919 /* JSQMessagesToolbarContentView.m in Sources */, + 88D4ED7319B555E800C2DCF9 /* JSQDemoModelData.m in Sources */, 885D596A18CBD2A600D77BB3 /* JSQAppDelegate.m in Sources */, 8816A6AE18E9250400111919 /* JSQMessagesCollectionView.m in Sources */, 8816A6AB18E9250400111919 /* JSQMessagesCollectionViewFlowLayout.m in Sources */, diff --git a/JSQMessagesDemo/JSQDemoModelData.h b/JSQMessagesDemo/JSQDemoModelData.h new file mode 100644 index 000000000..4de3f4d13 --- /dev/null +++ b/JSQMessagesDemo/JSQDemoModelData.h @@ -0,0 +1,55 @@ +// +// Created by Jesse Squires +// http://www.hexedbits.com +// +// +// Documentation +// http://cocoadocs.org/docsets/JSQMessagesViewController +// +// +// GitHub +// https://github.com/jessesquires/JSQMessagesViewController +// +// +// License +// Copyright (c) 2014 Jesse Squires +// Released under an MIT license: http://opensource.org/licenses/MIT +// + +#import + +#import "JSQMessages.h" + +/** + * This is for demo/testing purposes only. + * This object sets up some fake model data. + * Do not actually do anything like this. + */ + +static NSString * const kJSQDemoAvatarDisplayNameSquires = @"Jesse Squires"; +static NSString * const kJSQDemoAvatarDisplayNameCook = @"Tim Cook"; +static NSString * const kJSQDemoAvatarDisplayNameJobs = @"Jobs"; +static NSString * const kJSQDemoAvatarDisplayNameWoz = @"Steve Wozniak"; + +static NSString * const kJSQDemoAvatarIdSquires = @"053496-4509-289"; +static NSString * const kJSQDemoAvatarIdCook = @"468-768355-23123"; +static NSString * const kJSQDemoAvatarIdJobs = @"707-8956784-57"; +static NSString * const kJSQDemoAvatarIdWoz = @"309-41802-93823"; + +static const CGFloat kJSQDemoAvatarSize = 34.0f; + + + +@interface JSQDemoModelData : NSObject + +@property (strong, nonatomic) NSMutableArray *messages; + +@property (strong, nonatomic) NSDictionary *avatars; + +@property (strong, nonatomic) UIImageView *outgoingBubbleImageView; + +@property (strong, nonatomic) UIImageView *incomingBubbleImageView; + +@property (strong, nonatomic) NSDictionary *users; + +@end diff --git a/JSQMessagesDemo/JSQDemoModelData.m b/JSQMessagesDemo/JSQDemoModelData.m new file mode 100644 index 000000000..4555f6127 --- /dev/null +++ b/JSQMessagesDemo/JSQDemoModelData.m @@ -0,0 +1,140 @@ +// +// Created by Jesse Squires +// http://www.hexedbits.com +// +// +// Documentation +// http://cocoadocs.org/docsets/JSQMessagesViewController +// +// +// GitHub +// https://github.com/jessesquires/JSQMessagesViewController +// +// +// License +// Copyright (c) 2014 Jesse Squires +// Released under an MIT license: http://opensource.org/licenses/MIT +// + +#import "JSQDemoModelData.h" + +/** + * This is for demo/testing purposes only. + * This object sets up some fake model data. + * Do not actually do anything like this. + */ + +@implementation JSQDemoModelData + +- (instancetype)init +{ + self = [super init]; + if (self) { + /** + * Load some fake messages for demo. + * + * You should have a mutable array or orderedSet, or something. + */ + self.messages = [[NSMutableArray alloc] initWithObjects: + [[JSQMessage alloc] initWithText:@"Welcome to JSQMessages: A messaging UI framework for iOS." + senderId:kJSQDemoAvatarIdSquires + senderDisplayName:kJSQDemoAvatarDisplayNameSquires + date:[NSDate distantPast]], + + [[JSQMessage alloc] initWithText:@"It is simple, elegant, and easy to use. There are super sweet default settings, but you can customize like crazy." + senderId:kJSQDemoAvatarIdWoz + senderDisplayName:kJSQDemoAvatarDisplayNameWoz + date:[NSDate distantPast]], + + [[JSQMessage alloc] initWithText:@"It even has data detectors. You can call me tonight. My cell number is 123-456-7890. My website is www.hexedbits.com." + senderId:kJSQDemoAvatarIdSquires + senderDisplayName:kJSQDemoAvatarDisplayNameSquires + date:[NSDate distantPast]], + + [[JSQMessage alloc] initWithText:@"JSQMessagesViewController is nearly an exact replica of the iOS Messages App. And perhaps, better." + senderId:kJSQDemoAvatarIdJobs + senderDisplayName:kJSQDemoAvatarDisplayNameJobs + date:[NSDate date]], + + [[JSQMessage alloc] initWithText:@"It is unit-tested, free, and open-source." + senderId:kJSQDemoAvatarIdCook + senderDisplayName:kJSQDemoAvatarDisplayNameCook + date:[NSDate date]], + + [[JSQMessage alloc] initWithText:@"Oh, and there's sweet documentation." + senderId:kJSQDemoAvatarIdSquires + senderDisplayName:kJSQDemoAvatarDisplayNameSquires + date:[NSDate date]], + nil]; + + + /** + * Create avatar images once. + * + * Be sure to create your avatars one time and reuse them for good performance. + * + * If you are not using avatars, ignore this. + */ + + UIImage *jsqImage = [JSQMessagesAvatarFactory avatarWithUserInitials:@"JSQ" + backgroundColor:[UIColor colorWithWhite:0.85f alpha:1.0f] + textColor:[UIColor colorWithWhite:0.60f alpha:1.0f] + font:[UIFont systemFontOfSize:14.0f] + diameter:kJSQDemoAvatarSize]; + + UIImage *cookImage = [JSQMessagesAvatarFactory avatarWithImage:[UIImage imageNamed:@"demo_avatar_cook"] + diameter:kJSQDemoAvatarSize]; + + UIImage *jobsImage = [JSQMessagesAvatarFactory avatarWithImage:[UIImage imageNamed:@"demo_avatar_jobs"] + diameter:kJSQDemoAvatarSize]; + + UIImage *wozImage = [JSQMessagesAvatarFactory avatarWithImage:[UIImage imageNamed:@"demo_avatar_woz"] + diameter:kJSQDemoAvatarSize]; + self.avatars = @{ kJSQDemoAvatarIdSquires : jsqImage, + kJSQDemoAvatarIdCook : cookImage, + kJSQDemoAvatarIdJobs : jobsImage, + kJSQDemoAvatarIdWoz : wozImage }; + + + self.users = @{ kJSQDemoAvatarIdJobs : kJSQDemoAvatarDisplayNameJobs, + kJSQDemoAvatarIdCook : kJSQDemoAvatarDisplayNameCook, + kJSQDemoAvatarIdWoz : kJSQDemoAvatarDisplayNameWoz, + kJSQDemoAvatarIdSquires : kJSQDemoAvatarDisplayNameSquires }; + + + /** + * Create bubble images. + * + * Be sure to create your avatars one time and reuse them for good performance. + * + */ + self.outgoingBubbleImageView = [JSQMessagesBubbleImageFactory + outgoingMessageBubbleImageViewWithColor:[UIColor jsq_messageBubbleLightGrayColor]]; + + self.incomingBubbleImageView = [JSQMessagesBubbleImageFactory + incomingMessageBubbleImageViewWithColor:[UIColor jsq_messageBubbleGreenColor]]; + + /** + * Change to add more messages for testing + */ + NSUInteger messagesToAdd = 0; + NSArray *copyOfMessages = [self.messages copy]; + for (NSUInteger i = 0; i < messagesToAdd; i++) { + [self.messages addObjectsFromArray:copyOfMessages]; + } + + /** + * Change to YES to add a super long message for testing + * You should see "END" twice + */ + BOOL addREALLYLongMessage = NO; + if (addREALLYLongMessage) { + JSQMessage *reallyLongMessage = [JSQMessage messageWithText:@"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? END Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? END" senderId:kJSQDemoAvatarIdSquires senderDisplayName:kJSQDemoAvatarDisplayNameSquires]; + + [self.messages addObject:reallyLongMessage]; + } + } + return self; +} + +@end diff --git a/JSQMessagesDemo/JSQDemoViewController.h b/JSQMessagesDemo/JSQDemoViewController.h index d2af45cac..187f35fd5 100644 --- a/JSQMessagesDemo/JSQDemoViewController.h +++ b/JSQMessagesDemo/JSQDemoViewController.h @@ -18,8 +18,10 @@ #import "JSQMessages.h" -@class JSQDemoViewController; +#import "JSQDemoModelData.h" + +@class JSQDemoViewController; @protocol JSQDemoViewControllerDelegate @@ -34,16 +36,10 @@ @property (weak, nonatomic) id delegateModal; -@property (strong, nonatomic) NSMutableArray *messages; -@property (copy, nonatomic) NSDictionary *avatars; - -@property (strong, nonatomic) UIImageView *outgoingBubbleImageView; -@property (strong, nonatomic) UIImageView *incomingBubbleImageView; +@property (strong, nonatomic) JSQDemoModelData *demoData; - (void)receiveMessagePressed:(UIBarButtonItem *)sender; - (void)closePressed:(UIBarButtonItem *)sender; -- (void)setupTestModel; - @end diff --git a/JSQMessagesDemo/JSQDemoViewController.m b/JSQMessagesDemo/JSQDemoViewController.m index b16f3d0ce..5b6b2a08d 100644 --- a/JSQMessagesDemo/JSQDemoViewController.m +++ b/JSQMessagesDemo/JSQDemoViewController.m @@ -19,90 +19,15 @@ #import "JSQDemoViewController.h" -static NSString * const kJSQDemoAvatarNameCook = @"Tim Cook"; -static NSString * const kJSQDemoAvatarNameJobs = @"Jobs"; -static NSString * const kJSQDemoAvatarNameWoz = @"Steve Wozniak"; - - @implementation JSQDemoViewController -#pragma mark - Demo setup - -- (void)setupTestModel -{ - /** - * Load some fake messages for demo. - * - * You should have a mutable array or orderedSet, or something. - */ - self.messages = [[NSMutableArray alloc] initWithObjects: - [[JSQMessage alloc] initWithText:@"Welcome to JSQMessages: A messaging UI framework for iOS." sender:self.sender date:[NSDate distantPast]], - [[JSQMessage alloc] initWithText:@"It is simple, elegant, and easy to use. There are super sweet default settings, but you can customize like crazy." sender:kJSQDemoAvatarNameWoz date:[NSDate distantPast]], - [[JSQMessage alloc] initWithText:@"It even has data detectors. You can call me tonight. My cell number is 123-456-7890. My website is www.hexedbits.com." sender:self.sender date:[NSDate distantPast]], - [[JSQMessage alloc] initWithText:@"JSQMessagesViewController is nearly an exact replica of the iOS Messages App. And perhaps, better." sender:kJSQDemoAvatarNameJobs date:[NSDate date]], - [[JSQMessage alloc] initWithText:@"It is unit-tested, free, and open-source." sender:kJSQDemoAvatarNameCook date:[NSDate date]], - [[JSQMessage alloc] initWithText:@"Oh, and there's sweet documentation." sender:self.sender date:[NSDate date]], - nil]; - - /** - * Create avatar images once. - * - * Be sure to create your avatars one time and reuse them for good performance. - * - * If you are not using avatars, ignore this. - */ - CGFloat outgoingDiameter = self.collectionView.collectionViewLayout.outgoingAvatarViewSize.width; - - UIImage *jsqImage = [JSQMessagesAvatarFactory avatarWithUserInitials:@"JSQ" - backgroundColor:[UIColor colorWithWhite:0.85f alpha:1.0f] - textColor:[UIColor colorWithWhite:0.60f alpha:1.0f] - font:[UIFont systemFontOfSize:14.0f] - diameter:outgoingDiameter]; - - CGFloat incomingDiameter = self.collectionView.collectionViewLayout.incomingAvatarViewSize.width; - - UIImage *cookImage = [JSQMessagesAvatarFactory avatarWithImage:[UIImage imageNamed:@"demo_avatar_cook"] - diameter:incomingDiameter]; - - UIImage *jobsImage = [JSQMessagesAvatarFactory avatarWithImage:[UIImage imageNamed:@"demo_avatar_jobs"] - diameter:incomingDiameter]; - - UIImage *wozImage = [JSQMessagesAvatarFactory avatarWithImage:[UIImage imageNamed:@"demo_avatar_woz"] - diameter:incomingDiameter]; - self.avatars = @{ self.sender : jsqImage, - kJSQDemoAvatarNameCook : cookImage, - kJSQDemoAvatarNameJobs : jobsImage, - kJSQDemoAvatarNameWoz : wozImage }; - - /** - * Change to add more messages for testing - */ - NSUInteger messagesToAdd = 0; - NSArray *copyOfMessages = [self.messages copy]; - for (NSUInteger i = 0; i < messagesToAdd; i++) { - [self.messages addObjectsFromArray:copyOfMessages]; - } - - /** - * Change to YES to add a super long message for testing - * You should see "END" twice - */ - BOOL addREALLYLongMessage = NO; - if (addREALLYLongMessage) { - JSQMessage *reallyLongMessage = [JSQMessage messageWithText:@"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? END Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur? END" sender:self.sender]; - [self.messages addObject:reallyLongMessage]; - } -} - - - #pragma mark - View lifecycle /** * Override point for customization. * * Customize your view. - * Look at the properties on `JSQMessagesViewController` to see what is possible. + * Look at the properties on `JSQMessagesViewController` and `JSQMessagesCollectionView` to see what is possible. * * Customize your layout. * Look at the properties on `JSQMessagesCollectionViewFlowLayout` to see what is possible. @@ -113,9 +38,11 @@ - (void)viewDidLoad self.title = @"JSQMessages"; - self.sender = @"Jesse Squires"; + self.senderId = kJSQDemoAvatarIdSquires; + + self.senderDisplayName = kJSQDemoAvatarDisplayNameSquires; - [self setupTestModel]; + self.demoData = [[JSQDemoModelData alloc] init]; /** * Remove camera button since media messages are not yet implemented @@ -125,18 +52,7 @@ - (void)viewDidLoad * Or, you can set a custom `leftBarButtonItem` and a custom `rightBarButtonItem` */ - /** - * Create bubble images. - * - * Be sure to create your avatars one time and reuse them for good performance. - * - */ - self.outgoingBubbleImageView = [JSQMessagesBubbleImageFactory - outgoingMessageBubbleImageViewWithColor:[UIColor jsq_messageBubbleLightGrayColor]]; - - self.incomingBubbleImageView = [JSQMessagesBubbleImageFactory - incomingMessageBubbleImageViewWithColor:[UIColor jsq_messageBubbleGreenColor]]; - + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"typing"] style:UIBarButtonItemStyleBordered target:self @@ -189,17 +105,23 @@ - (void)receiveMessagePressed:(UIBarButtonItem *)sender [self scrollToBottomAnimated:YES]; - JSQMessage *copyMessage = [[self.messages lastObject] copy]; + JSQMessage *copyMessage = [[self.demoData.messages lastObject] copy]; if (!copyMessage) { - copyMessage = [JSQMessage messageWithText:@"First received!" sender:kJSQDemoAvatarNameJobs]; + copyMessage = [JSQMessage messageWithText:@"First received!" + senderId:kJSQDemoAvatarIdJobs + senderDisplayName:kJSQDemoAvatarDisplayNameJobs]; } dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - NSMutableArray *copyAvatars = [[self.avatars allKeys] mutableCopy]; - [copyAvatars removeObject:self.sender]; - copyMessage.sender = [copyAvatars objectAtIndex:arc4random_uniform((int)[copyAvatars count])]; + NSMutableArray *userIds = [[self.demoData.users allKeys] mutableCopy]; + [userIds removeObject:self.senderId]; + NSString *userId = userIds[arc4random_uniform((int)[userIds count])]; + + JSQMessage *newMessage = [JSQMessage messageWithText:copyMessage.text + senderId:userId + senderDisplayName:self.demoData.users[userId]]; /** * This you should do upon receiving a message: @@ -209,7 +131,7 @@ - (void)receiveMessagePressed:(UIBarButtonItem *)sender * 3. Call `finishReceivingMessage` */ [JSQSystemSoundPlayer jsq_playMessageReceivedSound]; - [self.messages addObject:copyMessage]; + [self.demoData.messages addObject:newMessage]; [self finishReceivingMessage]; }); } @@ -226,7 +148,8 @@ - (void)closePressed:(UIBarButtonItem *)sender - (void)didPressSendButton:(UIButton *)button withMessageText:(NSString *)text - sender:(NSString *)sender + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName date:(NSDate *)date { /** @@ -238,8 +161,12 @@ - (void)didPressSendButton:(UIButton *)button */ [JSQSystemSoundPlayer jsq_playMessageSentSound]; - JSQMessage *message = [[JSQMessage alloc] initWithText:text sender:sender date:date]; - [self.messages addObject:message]; + JSQMessage *message = [[JSQMessage alloc] initWithText:text + senderId:senderId + senderDisplayName:senderDisplayName + date:date]; + + [self.demoData.messages addObject:message]; [self finishSendingMessage]; } @@ -258,7 +185,7 @@ - (void)didPressAccessoryButton:(UIButton *)sender - (id)collectionView:(JSQMessagesCollectionView *)collectionView messageDataForItemAtIndexPath:(NSIndexPath *)indexPath { - return [self.messages objectAtIndex:indexPath.item]; + return [self.demoData.messages objectAtIndex:indexPath.item]; } - (UIImageView *)collectionView:(JSQMessagesCollectionView *)collectionView bubbleImageViewForItemAtIndexPath:(NSIndexPath *)indexPath @@ -273,15 +200,15 @@ - (UIImageView *)collectionView:(JSQMessagesCollectionView *)collectionView bubb * Otherwise, each cell would be referencing the same imageView and bubbles would disappear from cells */ - JSQMessage *message = [self.messages objectAtIndex:indexPath.item]; + JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item]; - if ([message.sender isEqualToString:self.sender]) { - return [[UIImageView alloc] initWithImage:self.outgoingBubbleImageView.image - highlightedImage:self.outgoingBubbleImageView.highlightedImage]; + if ([message.senderId isEqualToString:self.senderId]) { + return [[UIImageView alloc] initWithImage:self.demoData.outgoingBubbleImageView.image + highlightedImage:self.demoData.outgoingBubbleImageView.highlightedImage]; } - return [[UIImageView alloc] initWithImage:self.incomingBubbleImageView.image - highlightedImage:self.incomingBubbleImageView.highlightedImage]; + return [[UIImageView alloc] initWithImage:self.demoData.incomingBubbleImageView.image + highlightedImage:self.demoData.incomingBubbleImageView.highlightedImage]; } - (UIImageView *)collectionView:(JSQMessagesCollectionView *)collectionView avatarImageViewForItemAtIndexPath:(NSIndexPath *)indexPath @@ -307,9 +234,9 @@ - (UIImageView *)collectionView:(JSQMessagesCollectionView *)collectionView avat * * Override the defaults in `viewDidLoad` */ - JSQMessage *message = [self.messages objectAtIndex:indexPath.item]; + JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item]; - UIImage *avatarImage = [self.avatars objectForKey:message.sender]; + UIImage *avatarImage = [self.demoData.avatars objectForKey:message.senderId]; return [[UIImageView alloc] initWithImage:avatarImage]; } @@ -322,7 +249,7 @@ - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionVi * Show a timestamp for every 3rd message */ if (indexPath.item % 3 == 0) { - JSQMessage *message = [self.messages objectAtIndex:indexPath.item]; + JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item]; return [[JSQMessagesTimestampFormatter sharedFormatter] attributedTimestampForDate:message.date]; } @@ -331,18 +258,18 @@ - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionVi - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForMessageBubbleTopLabelAtIndexPath:(NSIndexPath *)indexPath { - JSQMessage *message = [self.messages objectAtIndex:indexPath.item]; + JSQMessage *message = [self.demoData.messages objectAtIndex:indexPath.item]; /** * iOS7-style sender name labels */ - if ([message.sender isEqualToString:self.sender]) { + if ([message.senderId isEqualToString:self.senderId]) { return nil; } if (indexPath.item - 1 > 0) { - JSQMessage *previousMessage = [self.messages objectAtIndex:indexPath.item - 1]; - if ([[previousMessage sender] isEqualToString:message.sender]) { + JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:indexPath.item - 1]; + if ([[previousMessage senderId] isEqualToString:message.senderId]) { return nil; } } @@ -350,7 +277,7 @@ - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionVi /** * Don't specify attributes to use the defaults. */ - return [[NSAttributedString alloc] initWithString:message.sender]; + return [[NSAttributedString alloc] initWithString:message.senderDisplayName]; } - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionView attributedTextForCellBottomLabelAtIndexPath:(NSIndexPath *)indexPath @@ -362,7 +289,7 @@ - (NSAttributedString *)collectionView:(JSQMessagesCollectionView *)collectionVi - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - return [self.messages count]; + return [self.demoData.messages count]; } - (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath @@ -386,9 +313,9 @@ - (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collection * Instead, override the properties you want on `self.collectionView.collectionViewLayout` from `viewDidLoad` */ - JSQMessage *msg = [self.messages objectAtIndex:indexPath.item]; + JSQMessage *msg = [self.demoData.messages objectAtIndex:indexPath.item]; - if ([msg.sender isEqualToString:self.sender]) { + if ([msg.senderId isEqualToString:self.senderId]) { cell.textView.textColor = [UIColor blackColor]; } else { @@ -433,14 +360,14 @@ - (CGFloat)collectionView:(JSQMessagesCollectionView *)collectionView /** * iOS7-style sender name labels */ - JSQMessage *currentMessage = [self.messages objectAtIndex:indexPath.item]; - if ([[currentMessage sender] isEqualToString:self.sender]) { + JSQMessage *currentMessage = [self.demoData.messages objectAtIndex:indexPath.item]; + if ([[currentMessage senderId] isEqualToString:self.senderId]) { return 0.0f; } if (indexPath.item - 1 > 0) { - JSQMessage *previousMessage = [self.messages objectAtIndex:indexPath.item - 1]; - if ([[previousMessage sender] isEqualToString:[currentMessage sender]]) { + JSQMessage *previousMessage = [self.demoData.messages objectAtIndex:indexPath.item - 1]; + if ([[previousMessage senderId] isEqualToString:[currentMessage senderId]]) { return 0.0f; } } diff --git a/JSQMessagesTests/ControllerTests/JSQMessagesViewControllerTests.m b/JSQMessagesTests/ControllerTests/JSQMessagesViewControllerTests.m index 7789e78aa..e9b6fa75a 100644 --- a/JSQMessagesTests/ControllerTests/JSQMessagesViewControllerTests.m +++ b/JSQMessagesTests/ControllerTests/JSQMessagesViewControllerTests.m @@ -53,7 +53,8 @@ - (void)testJSQMessagesViewControllerInit XCTAssertNotNil(vc.collectionView, @"Collection view should not be nil"); XCTAssertNotNil(vc.inputToolbar, @"Input toolbar should not be nil"); - XCTAssertEqualObjects(vc.sender, @"JSQDefaultSender", @"Property should be equal to default value"); + XCTAssertEqualObjects(vc.senderId, @"JSQDefaultSender", @"Property should be equal to default value"); + XCTAssertEqualObjects(vc.senderDisplayName, @"JSQDefaultSender", @"Property should be equal to default value"); XCTAssertEqual(vc.automaticallyAdjustsScrollViewInsets, YES, @"Property should be equal to default value"); XCTAssertEqualObjects(vc.incomingCellIdentifier, [JSQMessagesCollectionViewCellIncoming cellReuseIdentifier], @"Property should be equal to default value"); XCTAssertEqualObjects(vc.outgoingCellIdentifier, [JSQMessagesCollectionViewCellOutgoing cellReuseIdentifier], @"Property should be equal to default value"); diff --git a/JSQMessagesTests/ModelTests/JSQMessageTests.m b/JSQMessagesTests/ModelTests/JSQMessageTests.m index 553e6f5c8..edae0ff66 100644 --- a/JSQMessagesTests/ModelTests/JSQMessageTests.m +++ b/JSQMessagesTests/ModelTests/JSQMessageTests.m @@ -17,7 +17,8 @@ @interface JSQMessageTests : XCTestCase @property (strong, nonatomic) NSString *text; -@property (strong, nonatomic) NSString *sender; +@property (strong, nonatomic) NSString *senderId; +@property (strong, nonatomic) NSString *senderDisplayName; @property (strong, nonatomic) NSDate *date; @end @@ -31,60 +32,62 @@ - (void)setUp self.text = @"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque" @"laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi" @"architecto beatae vitae dicta sunt explicabo."; - self.sender = @"Jesse Squires"; + self.senderId = @"324543-43556-212343"; + self.senderDisplayName = @"Jesse Squires"; self.date = [NSDate date]; } - (void)tearDown { self.text = nil; - self.sender = nil; + self.senderId = nil; + self.senderDisplayName = nil; self.date = nil; [super tearDown]; } - (void)testMessageInit { - JSQMessage *msg0 = [[JSQMessage alloc] initWithText:self.text sender:self.sender date:self.date]; + JSQMessage *msg0 = [[JSQMessage alloc] initWithText:self.text senderId:self.senderId senderDisplayName:self.senderDisplayName date:self.date]; XCTAssertNotNil(msg0, @"Message should not be nil"); - JSQMessage *msg1 = [JSQMessage messageWithText:self.text sender:self.sender]; + JSQMessage *msg1 = [JSQMessage messageWithText:self.text senderId:self.senderId senderDisplayName:self.senderDisplayName]; XCTAssertNotNil(msg1, @"Message shold not be nil"); } - (void)testMessageInvalidInit { - XCTAssertThrows([JSQMessage messageWithText:nil sender:nil], @"Invalid init should throw"); - XCTAssertThrows([JSQMessage messageWithText:self.text sender:nil], @"Invalid init should throw"); - XCTAssertThrows([JSQMessage messageWithText:nil sender:self.sender], @"Invalid init should throw"); + XCTAssertThrows([JSQMessage messageWithText:nil senderId:nil senderDisplayName:nil], @"Invalid init should throw"); + XCTAssertThrows([JSQMessage messageWithText:self.text senderId:nil senderDisplayName:nil], @"Invalid init should throw"); + XCTAssertThrows([JSQMessage messageWithText:nil senderId:self.senderId senderDisplayName:self.senderDisplayName], @"Invalid init should throw"); - XCTAssertThrows([[JSQMessage alloc] initWithText:nil sender:nil date:nil], @"Invalid init should throw"); - XCTAssertThrows([[JSQMessage alloc] initWithText:self.text sender:nil date:nil], @"Invalid init should throw"); - XCTAssertThrows([[JSQMessage alloc] initWithText:nil sender:self.sender date:nil], @"Invalid init should throw"); - XCTAssertThrows([[JSQMessage alloc] initWithText:nil sender:nil date:self.date], @"Invalid init should throw"); + XCTAssertThrows([[JSQMessage alloc] initWithText:nil senderId:nil senderDisplayName:nil date:nil], @"Invalid init should throw"); + XCTAssertThrows([[JSQMessage alloc] initWithText:self.text senderId:nil senderDisplayName:nil date:nil], @"Invalid init should throw"); + XCTAssertThrows([[JSQMessage alloc] initWithText:nil senderId:self.senderId senderDisplayName:nil date:nil], @"Invalid init should throw"); + XCTAssertThrows([[JSQMessage alloc] initWithText:nil senderId:nil senderDisplayName:self.senderDisplayName date:nil], @"Invalid init should throw"); + XCTAssertThrows([[JSQMessage alloc] initWithText:nil senderId:nil senderDisplayName:nil date:self.date], @"Invalid init should throw"); } - (void)testMessageIsEqual { - JSQMessage *msg = [JSQMessage messageWithText:self.text sender:self.sender]; + JSQMessage *msg = [JSQMessage messageWithText:self.text senderId:self.senderId senderDisplayName:self.senderDisplayName]; JSQMessage *copy = [msg copy]; XCTAssertEqualObjects(msg, copy, @"Copied messages should be equal"); XCTAssertEqual([msg hash], [copy hash], @"Copied messages hashes should be equal"); - XCTAssertTrue([msg isEqualToMessage:copy], @"Copied messages should be equal"); - XCTAssertTrue([msg isEqualToMessage:msg], @"Messages should be equal to itself"); - XCTAssertFalse([msg isEqualToMessage:nil], @"Initialized message should not be equal to nil"); + XCTAssertEqualObjects(msg, copy, @"Copied messages should be equal"); + XCTAssertEqualObjects(msg, msg, @"Messages should be equal to itself"); } - (void)testMessageArchiving { - JSQMessage *msg = [JSQMessage messageWithText:self.text sender:self.sender]; + JSQMessage *msg = [JSQMessage messageWithText:self.text senderId:self.senderId senderDisplayName:self.senderDisplayName]; NSData *msgData = [NSKeyedArchiver archivedDataWithRootObject:msg]; JSQMessage *unarchivedMsg = [NSKeyedUnarchiver unarchiveObjectWithData:msgData]; - XCTAssertTrue([msg isEqualToMessage:unarchivedMsg], @"Message should be equal"); + XCTAssertEqualObjects(msg, unarchivedMsg, @"Message should be equal"); } @end diff --git a/JSQMessagesViewController/Controllers/JSQMessagesViewController.h b/JSQMessagesViewController/Controllers/JSQMessagesViewController.h index feb5eabd9..a00f57b1e 100644 --- a/JSQMessagesViewController/Controllers/JSQMessagesViewController.h +++ b/JSQMessagesViewController/Controllers/JSQMessagesViewController.h @@ -46,10 +46,22 @@ @property (weak, nonatomic, readonly) JSQMessagesInputToolbar *inputToolbar; /** - * The name of the user sending messages. This value must not be `nil`. - * The default value is `@"JSQDefaultSender"`. + * The display name of the current user who is sending messages. + * This value does not have to be unique. + * + * @discussion This value must not be `nil`. The default value is `@"JSQDefaultSender"`. + */ +@property (copy, nonatomic) NSString *senderDisplayName; + +/** + * The string identifier that uniquely identifies the current user sending messages. + * + * @discussion This property is used to determine if a message is incoming or outgoing. + * All message data objects returned by `collectionView:messageDataForItemAtIndexPath:` are + * checked against this identifier. + * This value must not be `nil`. The default value is `@"JSQDefaultSender"`. */ -@property (copy, nonatomic) NSString *sender; +@property (copy, nonatomic) NSString *senderId; /** * Specifies whether or not the view controller should automatically scroll to the most recent message @@ -151,14 +163,16 @@ * This method is called when the user taps the send button on the inputToolbar * after composing a message with the specified data. * - * @param button The send button that was pressed by the user. - * @param text The message text. - * @param sender The message sender. - * @param date The message date. + * @param button The send button that was pressed by the user. + * @param text The message text. + * @param senderId The message sender identifier. + * @param senderDisplayName The message sender display name. + * @param date The message date. */ - (void)didPressSendButton:(UIButton *)button withMessageText:(NSString *)text - sender:(NSString *)sender + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName date:(NSDate *)date; /** diff --git a/JSQMessagesViewController/Controllers/JSQMessagesViewController.m b/JSQMessagesViewController/Controllers/JSQMessagesViewController.m index 40adc2b23..309557790 100644 --- a/JSQMessagesViewController/Controllers/JSQMessagesViewController.m +++ b/JSQMessagesViewController/Controllers/JSQMessagesViewController.m @@ -126,7 +126,8 @@ - (void)jsq_configureMessagesViewController self.inputToolbar.contentView.textView.placeHolder = NSLocalizedString(@"New Message", @"Placeholder text for the message input text view"); self.inputToolbar.contentView.textView.delegate = self; - self.sender = @"JSQDefaultSender"; + self.senderId = @"JSQDefaultSender"; + self.senderDisplayName = @"JSQDefaultSender"; self.automaticallyScrollsToMostRecentMessage = YES; @@ -161,7 +162,8 @@ - (void)dealloc _toolbarHeightConstraint = nil; _toolbarBottomLayoutGuide = nil; - _sender = nil; + _senderId = nil; + _senderDisplayName = nil; _outgoingCellIdentifier = nil; _incomingCellIdentifier = nil; @@ -286,8 +288,12 @@ - (BOOL)canBecomeFirstResponder - (void)didPressSendButton:(UIButton *)button withMessageText:(NSString *)text - sender:(NSString *)sender - date:(NSDate *)date { } + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName + date:(NSDate *)date +{ + NSAssert(NO, @"Error! required method not implemented in subclass. Need to implement %s", __PRETTY_FUNCTION__); +} - (void)didPressAccessoryButton:(UIButton *)sender { } @@ -387,18 +393,20 @@ - (UICollectionViewCell *)collectionView:(JSQMessagesCollectionView *)collection id messageData = [collectionView.dataSource collectionView:collectionView messageDataForItemAtIndexPath:indexPath]; NSParameterAssert(messageData != nil); - NSString *messageSender = [messageData sender]; - NSParameterAssert(messageSender != nil); - - BOOL isOutgoingMessage = [messageSender isEqualToString:self.sender]; + NSString *messageSenderId = [messageData senderId]; + NSParameterAssert(messageSenderId != nil); - NSString *cellIdentifier = isOutgoingMessage ? self.outgoingCellIdentifier : self.incomingCellIdentifier; - JSQMessagesCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath]; - cell.delegate = collectionView; + NSString *messageSenderDisplayName = [messageData senderDisplayName]; + NSParameterAssert(messageSenderDisplayName != nil); NSString *messageText = [messageData text]; NSParameterAssert(messageText != nil); + BOOL isOutgoingMessage = [messageSenderId isEqualToString:self.senderId]; + + NSString *cellIdentifier = isOutgoingMessage ? self.outgoingCellIdentifier : self.incomingCellIdentifier; + JSQMessagesCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath]; + cell.delegate = collectionView; cell.textView.text = messageText; cell.messageBubbleImageView = [collectionView.dataSource collectionView:collectionView bubbleImageViewForItemAtIndexPath:indexPath]; cell.avatarImageView = [collectionView.dataSource collectionView:collectionView avatarImageViewForItemAtIndexPath:indexPath]; @@ -550,7 +558,8 @@ - (void)messagesInputToolbar:(JSQMessagesInputToolbar *)toolbar didPressLeftBarB else { [self didPressSendButton:sender withMessageText:[self jsq_currentlyComposedMessageText] - sender:self.sender + senderId:self.senderId + senderDisplayName:self.senderDisplayName date:[NSDate date]]; } } @@ -560,7 +569,8 @@ - (void)messagesInputToolbar:(JSQMessagesInputToolbar *)toolbar didPressRightBar if (toolbar.sendButtonOnRight) { [self didPressSendButton:sender withMessageText:[self jsq_currentlyComposedMessageText] - sender:self.sender + senderId:self.senderId + senderDisplayName:self.senderDisplayName date:[NSDate date]]; } else { diff --git a/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m b/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m index 951dfd84d..2f45c194b 100644 --- a/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m +++ b/JSQMessagesViewController/Layout/JSQMessagesCollectionViewFlowLayout.m @@ -436,9 +436,9 @@ - (CGFloat)jsq_messageBubbleTextContainerInsetsTotal - (CGSize)jsq_avatarSizeForIndexPath:(NSIndexPath *)indexPath { id messageData = [self.collectionView.dataSource collectionView:self.collectionView messageDataForItemAtIndexPath:indexPath]; - NSString *messageSender = [messageData sender]; + NSString *messageSender = [messageData senderId]; - if ([messageSender isEqualToString:[self.collectionView.dataSource sender]]) { + if ([messageSender isEqualToString:[self.collectionView.dataSource senderId]]) { return self.outgoingAvatarViewSize; } diff --git a/JSQMessagesViewController/Model/JSQMessage.h b/JSQMessagesViewController/Model/JSQMessage.h index a9b717e30..ae3c92244 100644 --- a/JSQMessagesViewController/Model/JSQMessage.h +++ b/JSQMessagesViewController/Model/JSQMessage.h @@ -21,59 +21,61 @@ #import "JSQMessageData.h" /** - * A `JSQMessage` model object represents a single user message. - * This is a concrete class that implements the `JSQMessageData` protocol. - * It contains the message text, its sender, and the date that the message was sent. + * A `JSQMessage` model object represents a single user message. An instance of `JSQMessage` is immutable. + * This is a concrete class that implements the `JSQMessageData` protocol. + * It contains the message text, senderId, senderDisplayName, and the date that the message was sent. */ @interface JSQMessage : NSObject /** - * The body text of the message. This value must not be `nil`. + * Returns the body text of the message. */ -@property (copy, nonatomic) NSString *text; +@property (copy, nonatomic, readonly) NSString *text; /** - * The name of user who sent the message. This value must not be `nil`. + * Returns the string identifier that uniquely identifies the user who sent the message. */ -@property (copy, nonatomic) NSString *sender; +@property (copy, nonatomic, readonly) NSString *senderId; /** - * The date that the message was sent. This value must not be `nil`. + * Returns the display name for the user who sent the message. This value does not have to be unique. */ -@property (copy, nonatomic) NSDate *date; +@property (copy, nonatomic, readonly) NSString *senderDisplayName; + +/** + * Returns the date that the message was sent. + */ +@property (copy, nonatomic, readonly) NSDate *date; #pragma mark - Initialization /** - * Initializes and returns a message object having the given text, sender, and current system date. + * Initializes and returns a message object having the given text, senderId, senderDisplayName, + * and current system date. * - * @param text The body text of the message. - * @param sender The name of the user who sent the message. + * @param text The body text of the message. This value must not be `nil`. + * @param senderId The unique identifier for the user who sent the message. This value must not be `nil`. + * @param senderDisplayName The display name for the user who sent the message. This value must not be `nil`. * * @return An initialized `JSQMessage` object or `nil` if the object could not be successfully initialized. */ -+ (instancetype)messageWithText:(NSString *)text sender:(NSString *)sender; ++ (instancetype)messageWithText:(NSString *)text + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName; /** - * Initializes and returns a message object having the given text, sender, and date. + * Initializes and returns a message object having the given text, senderId, senderDisplayName, and date. * - * @param text The body text of the message. - * @param sender The name of the user who sent the message. - * @param date The date that the message was sent. + * @param text The body text of the message. This value must not be `nil`. + * @param senderId The unique identifier for the user who sent the message. This value must not be `nil`. + * @param senderDisplayName The display name for the user who sent the message. This value must not be `nil`. + * @param date The date that the message was sent. This value must not be `nil`. * * @return An initialized `JSQMessage` object or `nil` if the object could not be successfully initialized. */ - (instancetype)initWithText:(NSString *)text - sender:(NSString *)sender + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName date:(NSDate *)date; -/** - * Returns a boolean value that indicates whether a given message is equal to the receiver. - * - * @param aMessage The message with which to compare the receiver. - * - * @return `YES` if aMessage is equivalent to the receiver, otherwise `NO`. - */ -- (BOOL)isEqualToMessage:(JSQMessage *)aMessage; - @end diff --git a/JSQMessagesViewController/Model/JSQMessage.m b/JSQMessagesViewController/Model/JSQMessage.m index d0dd78f47..2d1c1e900 100644 --- a/JSQMessagesViewController/Model/JSQMessage.m +++ b/JSQMessagesViewController/Model/JSQMessage.m @@ -22,43 +22,48 @@ @implementation JSQMessage #pragma mark - Initialization -+ (instancetype)messageWithText:(NSString *)text sender:(NSString *)sender ++ (instancetype)messageWithText:(NSString *)text + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName { - return [[JSQMessage alloc] initWithText:text sender:sender date:[NSDate date]]; + return [[JSQMessage alloc] initWithText:text + senderId:senderId + senderDisplayName:senderDisplayName + date:[NSDate date]]; } - (instancetype)initWithText:(NSString *)text - sender:(NSString *)sender + senderId:(NSString *)senderId + senderDisplayName:(NSString *)senderDisplayName date:(NSDate *)date { NSParameterAssert(text != nil); - NSParameterAssert(sender != nil); + NSParameterAssert(senderId != nil); + NSParameterAssert(senderDisplayName != nil); NSParameterAssert(date != nil); - self = [self init]; + self = [super init]; if (self) { - _text = text; - _sender = sender; - _date = date; + _text = [text copy]; + _senderId = [senderId copy]; + _senderDisplayName = [senderDisplayName copy]; + _date = [date copy]; } return self; } -- (instancetype)init +- (id)init { - self = [super init]; - if (self) { - _text = @""; - _sender = @""; - _date = [NSDate date]; - } - return self; + NSAssert(NO, @"%s is not a valid initializer for %@. Use %@ instead.", + __PRETTY_FUNCTION__, [self class], NSStringFromSelector(@selector(initWithText:senderId:senderDisplayName:date:))); + return nil; } - (void)dealloc { _text = nil; - _sender = nil; + _senderId = nil; + _senderDisplayName = nil; _date = nil; } @@ -67,7 +72,8 @@ - (void)dealloc - (BOOL)isEqualToMessage:(JSQMessage *)aMessage { return [self.text isEqualToString:aMessage.text] - && [self.sender isEqualToString:aMessage.sender] + && [self.senderId isEqualToString:aMessage.senderId] + && [self.senderDisplayName isEqualToString:aMessage.senderDisplayName] && ([self.date compare:aMessage.date] == NSOrderedSame); } @@ -83,17 +89,23 @@ - (BOOL)isEqual:(id)object return NO; } - return [self isEqualToMessage:(JSQMessage *)object]; + JSQMessage *aMessage = (JSQMessage *)object; + + return [self.text isEqualToString:aMessage.text] + && [self.senderId isEqualToString:aMessage.senderId] + && [self.senderDisplayName isEqualToString:aMessage.senderDisplayName] + && ([self.date compare:aMessage.date] == NSOrderedSame); } - (NSUInteger)hash { - return [self.text hash] ^ [self.sender hash] ^ [self.date hash]; + return [self.senderId hash] ^ [self.date hash]; } - (NSString *)description { - return [NSString stringWithFormat:@"<%@>[ %@, %@, %@ ]", [self class], self.sender, self.date, self.text]; + return [NSString stringWithFormat:@"<%@: senderId=%@, senderDisplayName=%@, date=%@, text=%@>", + [self class], self.senderId, self.senderDisplayName, self.date, self.text]; } #pragma mark - NSCoding @@ -103,7 +115,8 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder self = [super init]; if (self) { _text = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(text))]; - _sender = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(sender))]; + _senderId = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderId))]; + _senderDisplayName = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(senderDisplayName))]; _date = [aDecoder decodeObjectForKey:NSStringFromSelector(@selector(date))]; } return self; @@ -112,7 +125,8 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.text forKey:NSStringFromSelector(@selector(text))]; - [aCoder encodeObject:self.sender forKey:NSStringFromSelector(@selector(sender))]; + [aCoder encodeObject:self.senderId forKey:NSStringFromSelector(@selector(senderId))]; + [aCoder encodeObject:self.senderDisplayName forKey:NSStringFromSelector(@selector(senderDisplayName))]; [aCoder encodeObject:self.date forKey:NSStringFromSelector(@selector(date))]; } @@ -120,9 +134,10 @@ - (void)encodeWithCoder:(NSCoder *)aCoder - (instancetype)copyWithZone:(NSZone *)zone { - return [[[self class] allocWithZone:zone] initWithText:[self.text copy] - sender:[self.sender copy] - date:[self.date copy]]; + return [[[self class] allocWithZone:zone] initWithText:self.text + senderId:self.senderId + senderDisplayName:self.senderDisplayName + date:self.date]; } @end diff --git a/JSQMessagesViewController/Model/JSQMessageData.h b/JSQMessagesViewController/Model/JSQMessageData.h index 3cc1685b3..45f0fd92d 100644 --- a/JSQMessagesViewController/Model/JSQMessageData.h +++ b/JSQMessagesViewController/Model/JSQMessageData.h @@ -26,7 +26,7 @@ * which `JSQMessagesViewController` and `JSQMessagesCollectionView` interacts with message model objects. * * It declares the required and optional methods that a class must implement so that instances of that class - * can be displayed properly with a `JSQMessagesCollectionViewCell`. + * can be displayed properly within a `JSQMessagesCollectionViewCell`. */ @protocol JSQMessageData @@ -34,18 +34,31 @@ /** * @return The body text of the message. + * * @warning You must not return `nil` from this method. */ - (NSString *)text; /** - * @return The name of the user who sent the message. + * @return A string identifier that uniquely identifies the user who sent the message. + * + * @discussion If you need to generate a unique identifier, consider using + * `[[NSProcessInfo processInfo] globallyUniqueString]` + * + * @warning You must not return `nil` from this method. This value must be unique. + */ +- (NSString *)senderId; + +/** + * @return The display name for the user who sent the message. + * * @warning You must not return `nil` from this method. */ -- (NSString *)sender; +- (NSString *)senderDisplayName; /** * @return The date that the message was sent. + * * @warning You must not return `nil` from this method. */ - (NSDate *)date; diff --git a/JSQMessagesViewController/Model/JSQMessagesCollectionViewDataSource.h b/JSQMessagesViewController/Model/JSQMessagesCollectionViewDataSource.h index 04fede483..635437d2b 100644 --- a/JSQMessagesViewController/Model/JSQMessagesCollectionViewDataSource.h +++ b/JSQMessagesViewController/Model/JSQMessagesCollectionViewDataSource.h @@ -36,11 +36,22 @@ @required /** - * Asks the data source for the message sender, that is, the user who is sending messages. + * Asks the data source for the current sender's display name, that is, the current user who is sending messages. * - * @return An initialized string describing the sender. You must not return `nil` from this method. + * @return An initialized string describing the current sender to display in a `JSQMessagesCollectionViewCell`. + * + * @warning You must not return `nil` from this method. This value does not need to be unique. */ -- (NSString *)sender; +- (NSString *)senderDisplayName; + +/** + * Asks the data source for the current sender's unique identifier, that is, the current user who is sending messages. + * + * @return An initialized string identifier that uniquely identifies the current sender. + * + * @warning You must not return `nil` from this method. This value must be unique. + */ +- (NSString *)senderId; /** * Asks the data source for the message data that corresponds to the specified item at indexPath in the collectionView.