Search code examples
iosobjective-cxcodechatjsqmessagesviewcontroller

useing JSQMessageMediaData with core data


i successfully integrated core data in my JSQ project, for my JSQMessageData i use NSManagedObject i created called CDChatMessage

@interface CDChatMessage : NSManagedObject < JSQMessageData >
@end

at my JSQMessagesViewController i use NSfetchedresultsController,
it works fine for text messages but i can't figure out how to implement media messages.

JSQMessage.h have a property that represent the Media Data

@property (copy, nonatomic, readonly) id< JSQMessageMediaData > media;

but obviously i cant assassin property of type JSQMessageMediaData to my NSManagedObject, anyone have a solution for using JSQMessageMediaData with Core Data ?

thanks.


Solution

  • Basically what I've done to solve this kind of issue is this:

    Instead of using CoreData object which conforms to JSQMessageData I use something called viewModel.

    A ViewModel is basically a normal NSObject which just unwraps all necessary information from the CoreData object and conforms to JSQMessageData protocol - providing text, senderId, and other information (and also media message if necessary)

    @interface ChatMessageViewModel : NSObject <JSQMessageData>
    
    @property (nonatomic, strong, readonly) CDChatMessage *chatMessage;
    
    // main properties
    @property (nonatomic, copy, readonly) NSString *text;
    @property (nonatomic, copy, readonly) NSString *senderId;
    @property (nonatomic, copy, readonly) NSString *watcherId;
    
    ... 
    
    @property (nonatomic, strong, readonly) JSQMessage *mediaMessage;
    
    - (instancetype)initWithChatMessage:(CDChatMessage *)chatMessage;
    
    @end
    

    .m file could look like this:

    @interface ChatMessageViewModel ()
    
    @property (nonatomic, strong, readwrite) CDChatMessage *chatMessage;
    
    // main properties
    @property (nonatomic, copy, readwrite) NSString *text;
    @property (nonatomic, copy, readwrite) NSString *senderId;
    @property (nonatomic, copy, readwrite) NSString *watcherId;
    
    ... 
    
    @property (nonatomic, strong, readwrite) JSQMessage *mediaMessage;
    
    @end
    
    
    @implementation ChatMessageViewModel
    
    - (instancetype)initWithChatMessage:(CDChatMessage *)chatMessage 
    
        if (self = [super init]) {
    
            _chatMessage = chatMessage;
    
            [self unpackViewModel];
        }
    
        return self;
    }
    
    - (void)unpackViewModel {
    
        self.senderId = self.chatMessage.senderId;
        self.text = self.chatMessage.senderId;
        self.mediaMessage = [self unpackMediaData];
    }
    
    - (JSQMessage *)unpackMediaData {
    
        // Here CDCustomPhotoMediaItem is a subclass of JSQPhotoMediaItem which just lets me create custom look of JSQ media item. 
        JSQPhotoMediaItem *photoItem = [[CDCustomPhotoMediaItem alloc] init];
    
        return [JSQMessage messageWithSenderId:self.senderId displayName:@"" media:photoItem];
    }
    

    After I fetch data using NSFetchResultsController I just take all core data objects and turn them into immutable viewModels.

    Then in cellForItemAtIndexPath I just call this:

    cell.mediaView = [viewModel.media mediaView];
    

    This approach creates nice immutable wrapper which contains only necessary chunk of information needed by the JSQ chat library. Also, you can easily write tests for such object. If you're using swift, you can use struct for this kind of purpose.

    Hope my answer helps. Please ask if you need more detailed answer. ;-)