useing JSQMessageMediaData with core data - ios

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.

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. ;-)

Related

Adding an object to NSMutableArray - weird behaviour

I am facing a very weird issue. I create an object and I add it to a NSMutableArray but when I try to read it after I insert it, some subclasses of the object change to some weird classes like
PINGIFAnimatedImageManager
Here is the code I use to create the object and insert it to the NSMutableArray:
CustomContentGridRow *row = [[CustomContentGridRow alloc]init];
row.child1 = [dataManager getMapLocation]; // This is the MapLocation object that will change to this weird PINGIFAnimatedImageManager
row.useFullWidth=1;
row.index=0;
[arrCustomChilds addObject:row];
This is the CustomContentGridRow class:
#interface CustomContentGridRow : NSObject
#property (nonatomic, assign) MapLocation *child1;
#property (nonatomic, assign) MapLocation *child2;
#property (nonatomic, assign) int useFullWidth;
#property (nonatomic, assign) int index;
#end
So when I put a breakpoint at this line [arrCustomChilds addObject:hotelRow];, when I read the row object I get the expected results. But when I place a breakpoint after the above code to read the arrCustomChilds the class of child1 changes to some weird classes. Also, sometimes it won't change to another class but it will give nil values.
Any idea why this is happening?
You should change property modifier from "assign" to "strong" for class objects. Otherwise undefined behaviour can happen.
In Xcode -> Product -> Scheme - edit Scheme. Check the settings of RUN mode. If it is Release change to Debug.
This will give you the correct values
Change below properties
#property (nonatomic, strong) MapLocation *child1;
#property (nonatomic, strong) MapLocation *child2;
assign to strong
Your interface should be:
#interface CustomContentGridRow : NSObject
#property (nonatomic, strong) MapLocation *child1;
#property (nonatomic, strong) MapLocation *child2;
#property (nonatomic, assign) int useFullWidth;
#property (nonatomic, assign) int index;
#end
the class objects should be strong not assign

What will happen if my class conforms to two protocols having same property?

Let's say I have two protocols
#protocol Playlist<NSObject>
#property(nonatomic, copy) NSString *title;
#property(nonatomic, assign) NSUInteger trackCount;
#end
and another as
#protocol Album<NSObject>
#property(nonatomic, copy) NSString *name;
#property(nonatomic, assign) NSUInteger trackCount;
#end
and there is a class which conforms to these protocols
.h file
#interface MusicLibrary <Playlist, Album>
#end
.m file
#implementation MusicLibrary
#synthesize title;
#synthesize name;
#synthesize trackCount;
#end
Which trackCount property will it refer to? Can I use trackCount twice?
It surely do not give any compile time error.
It looks to me that you are modeling your data wrong. The way you have it setup a musicLibrary is BOTH a playlist and an album. I think a more correct model would have a MusicLibrary containing many playlist and many albums. Something like:
#property (nonatomic, strong) NSArray<Album>* albums;
#property (nonatomic, strong) NSArray<Playlist>* playlists;

iOS - Core Data - Saving an object that contains an array of Objects

I'm just getting started with Core data, (and I'm also trying to use Magical Record). I'm creating a pretty simple Payment tracking app.
I would like to save a Payment object that has an array of Debtors. This is what my Payment object looks like
#class Debtor;
#interface Payment : NSObject
#property (strong, nonatomic) NSString *paymentAmountString;
#property (strong, nonatomic) NSString *titleString;
#property (strong, nonatomic) NSArray *debtorsArray;
#property (strong, nonatomic) NSDate *dueDate;
#property (strong, nonatomic) NSString *notesString;
#end
And the debtorsArray is an array of Debtor objects
#interface Debtor : NSObject
#property (strong, nonatomic) NSString *nameString;
#property (strong, nonatomic) NSString *amountOwedString;
How should I go about saving this object since it contains an array. Do I need to create two different Entities, with a relationship between Payment and Debtor? How exactly do I do this, and how would I ensure that they are fetched properly?
Create only one entity for Payment. You will have to use the 'Transformable' data type for your attribute debtorsArray within this entity.
Then implement the following methods in your Debtor class:
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:self.nameString forKey:#"nameString"];
[aCoder encodeObject:self.amountOwnedString forKey:#"amountOwnedString"];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if(self = [super init]){
self.nameString = [aDecoder decodeObjectForKey:#"nameString"];
self.amountOwnedString = [aDecoder decodeObjectForKey:#"amountOwnedString"];
}
return self;
}
Entity should be fetched normally like any other fetch query.
Hope this helps.

Objective C #property accessors in custom class?

I have a custom NSObject called MAAssignment. It's basically a data type that has a number of #properties and one custom init method:
#property (nonatomic, strong) NSDate *date;
#property (nonatomic, strong) NSString *assignmentName;
#property (nonatomic, strong) NSNumber *totalPoints;
#property (nonatomic, strong) NSNumber *recievedPoints;
#property (nonatomic, strong) NSNumber *classAverage;
#property (nonatomic, strong) NSNumber *extraCredit;
#property (nonatomic, strong) NSNumber *notGraded;
- (id)initWithDate:(NSString *)date assignmentName:(NSString *)assignmentName totalPoints:(NSNumber *)totalPoints recievedPoints:(NSNumber *)recievedPoints classAverage:(NSString *)classAverage extraCredit:(NSNumber *)extraCredit notGraded:(NSNumber *)notGraded;
I create an instance of it in the viewController, hoping to populate the newly created item with some data... But I can't figure out how to access the variables of the object. I went MAAssignment *assignment = [[MAAssignment alloc] init];, then I tried [assignment setDate] or assignment.date = ddate but none of them seem to work.
Am I misunderstanding how the accessors for objects work?
You should place these variables in h. file
of MAAsignment
I'm not understanding your question very well (where are you declaring this code? What's the full code of your MAAsiignment initialization example?), however I'll try to answer anyway.
Are you sure that the #property declarations are inside the #interface in .h and not inside .m?
The #interface inside .m is a private class extensions, and allows you to declare private properties. To make them available outside, you need to put them inside the header (.h).
Please post more code to let us provide a more exhaustive answer.
What you describe is a data container object. It should work as described:
MAAssignment *anAssignment = [[MAAssignment alloc] init];
anAssignment.date = [NSDate date];
anAssignment.totalPoints = #(10);
NSLog(#"anAssignment.date = %#", anAssignment.date);
NSLog(#"anAssignment.totalPoints = %#", anAssignment.totalPoints);
Should work perfectly. Are you getting any warnings?

Access private interface property by public method

I would like to ask you some opinion about what I'm doing. I know it works, but I'm not sure is proper to do what I'm doing.
I have a superclass Building that need to expose two NSString, name and description. No one should be able to modify those variables apart their subclasses.
To get this result I have created two public method on the base class:
#interface Building : NSObject
- (NSString *)name;
- (NSString *)description;
#end
Then on each subclass I'm creating a private interface with name and description properties and let the magic happen.
#interface Barrack()
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *description;
#end
#implementation Barrack
#synthesize name, description;
...
#end
What you guys think about this?Is this a proper way to get this kind of result, anyone have better ideas about this topic?
Thanks for your opinion.
Best,
Enrico
Declare readonly properties in the interface, readwrite in the implementation class extension. No need for #synthesize. This is one of the main reason class extensions were added to Objective-C.
in Building.h
#interface Building : NSObject
#property (nonatomic, strong, readonly) NSString *name;
#property (nonatomic, strong, readonly) NSString *description;
#end
In Barrack.m
#interface Barrack ()
#property (nonatomic, strong, readwrite) NSString *name;
#property (nonatomic, strong, readwrite) NSString *description;
#end
#implementation Barrack
...
#end

Resources