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
Related
How do you deal with subclassing collection attributes in JSONModel?
Let's say I have these two endpoints with different responses of the same "product object".
domain.com/api/1.0/getProductList
domain.com/api/1.0/getProductDetails/productId
I wrote some example code below to show you my issue:
// ProductListModel
#interface ProductListModel : JSONModel
#property (nonatomic, strong) NSNumber *productId;
#property (nonatomic, strong) NSNumber *userId;
#property (nonatomic, strong) NSArray<OrderListModel> *orders;
#end
// ProductDetailModel
#interface ProductDetailModel : ProductListModel
#property (nonatomic, strong) NSURL *productImageUrl;
#property (nonatomic, strong) NSArray<OrderDetailModel> *orders;
#end
// OrderListModel
#protocol OrderListModel <NSObject>
#end
#interface OrderListModel : JSONModel
#property (nonatomic, strong) NSNumber *orderId;
#property (nonatomic, strong) NSNumber *price;
#end
// OrderDetailModel
#protocol OrderDetailModel <NSObject>
#end
#interface OrderDetailModel : OrderListModel
#property (nonatomic, strong) NSURL *orderImageUrl;
#end
The ProductDetailModel wants the same inherited attributes as ProductListModel, but it wants the orders array in the subclassed type.
However this leads to a compiler warning:
Property type 'NSArray<OrderDetailModel> *' is incompatible with type
'NSArray<OrderListModel> *' inherited from 'ProductListModel'
I found this related SO post but I'd rather not monkey patch the JSONModel library.
Edit #1:
This has been discussed in the #574, and #229 github issues before but requires a "type" string to determine what class to instantiate. This requires a change on the backend API.
Is there a way to do this without changing the backend API?
You can't override the property in the subclass to have a different type as it will violate the Liskov substitution principle - #Paulw11
For future readers, here's how the updated example code would look like:
// ProductBaseModel
#interface ProductBaseModel : JSONModel
#property (nonatomic, strong) NSNumber *productId;
#property (nonatomic, strong) NSNumber *userId;
#end
// ProductListModel
#interface ProductListModel : ProductBaseModel
#property (nonatomic, strong) NSArray<OrderListModel> *orders;
#end
// ProductDetailModel
#interface ProductDetailModel : ProductBaseModel
#property (nonatomic, strong) NSURL *productImageUrl;
#property (nonatomic, strong) NSArray<OrderDetailModel> *orders;
#end
// OrderListModel
#protocol OrderListModel <NSObject>
#end
#interface OrderListModel : JSONModel
#property (nonatomic, strong) NSNumber *orderId;
#property (nonatomic, strong) NSNumber *price;
#end
// OrderDetailModel
#protocol OrderDetailModel <NSObject>
#end
#interface OrderDetailModel : OrderListModel
#property (nonatomic, strong) NSURL *orderImageUrl;
#end
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;
I have one problem in converting JSON array to model. I am using JSONModel library.
#protocol PTTemplateModel <NSObject>
#end
#protocol PTProfileTemplateModel <PTTemplateModel>
#end
#protocol PTCategoryTemplateModel <PTTemplateModel>
#end
#interface PTTemplateModel : JSONModel
#property (nonatomic, assign) TemplateType type;
#property (nonatomic, copy) NSString* templateID;
#end
#interface PTProfileTemplateModel : PTTemplateModel
#property (nonatomic, copy) NSString* logoURL;
#property (nonatomic, copy) NSString* title;
#end
#interface PTCategoryTemplateModel : PTTemplateModel
#property (nonatomic, strong) NSString* category;
#end
#interface PTModel : JSONModel
#property (nonatomic, copy) NSString* title;
#property (nonatomic, strong) NSArray< PTTemplateModel>* templates; // PTTemplateModel
Here templates array can have both PTProfileTemplateModel and PTCategoryTemplateModel.
JSON Input:
{"title":"Core","templates":[{"type":0,"templateID":"","logoURL":"", "title":"data"},{"type":1,"templateID":"","category":"DB"}]}
What I need is according to type I have to get CategoryTemplate or ProfileTemplate. But after conversion I am getting just PTTemplateModel type.
I know that I have specified protocol type as PTTemplateModel. But how do I get different type of model according to given data.
I tried:
#property (nonatomic, strong) NSArray< PTTemplateModel>* templates;
#property (nonatomic, strong) NSArray<PTProfileTemplateModel, PTCategoryTemplateModel>* templates;
#property (nonatomic, strong) NSArray< PTTemplateModel , PTProfileTemplateModel, PTCategoryTemplateModel>* templates;
None of them works.
Any suggestions?
Why not try BWJSONMatcher, it helps you work with json data in a very very neat way:
#interface PTModel : NSObject<BWJSONValueObject>
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSArray *templates;
#end
#interface PTTemplateModel : NSObject
#property (nonatomic, assign) TemplateType type;
#property (nonatomic, strong) NSString *templateID;
#property (nonatomic, strong) NSString *logoURL;
#property (nonatomic, strong) NSString *title;
#property (nonatomic, strong) NSString *category;
#end
In the implementation of PTModel, implement the function typeInProperty: declared in protocol BWJSONValueObject:
- (Class)typeInProperty:(NSString *)property {
if ([property isEqualToString:#"templates"]) {
return [PTTemplateModel class];
}
return nil;
}
Then you can use BWJSONMatcher to get your data model within one line:
PTModel *model = [PTModel fromJSONString:jsonString];
Detailed examples of how to use BWJSONMatcher can be found here.
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. ;-)
I have an object named friend (I know, first letter should be uppercase). I use this class in many other classes and view controller.
All of a sudden, without changing ANY CODE, every custom object I have, stopped recognize each other. I always get the error Unknown type name "friend".
I already tried to clear the project and restart the mac.
WTF is wrong with this Xcode? All of a sudden my whole project stops working.
Here is my class currentUser.h
#import <Foundation/Foundation.h>
#import "friend.h"
#interface currentUser : NSObject<NSCoding>
{
BOOL fromfacebook;
#private
NSMutableArray *upLoadStack;
}
#property (nonatomic,strong)NSString *token;
#property (nonatomic,strong)NSString *email;
#property (nonatomic,strong)NSString *name;
#property (nonatomic,strong)UIImage *userImg;
#property (nonatomic,strong)NSString *username;
#property (nonatomic,strong)NSString *facebookID;
#property (nonatomic,strong)NSString *userPSW;
#property (nonatomic,strong)NSString *userID;
#property (nonatomic,strong)NSMutableArray *friendsList;
#property (nonatomic,strong)NSMutableArray *groups;
#property (nonatomic,strong)NSData *audioMessage;
#property (nonatomic,strong)NSMutableArray *mimosToDownload;
#property (nonatomic,strong)NSMutableArray *mimosDownloaded;
#property (nonatomic,strong)friend *friendToSend; //Here is where a I get the error of unknown type name
#end
this is the friend.h
#import <Foundation/Foundation.h>
#import "currentUser.h"
#import "Imager.h"
#interface friend : NSObject<NSCoding>
#property (nonatomic,strong)NSString *username;
#property (nonatomic,strong)NSString *userID;
#property (nonatomic,strong)UIImage *userImg;
#property(nonatomic,strong)NSMutableData *buffer;
#property(nonatomic)BOOL flagDownloaded;
-(UIImage*)downloadImageBlocked;
-(id)init;
-(UIImage*)getFriendImg;
-(UIImage*)userImg;
-(NSString*)getUserID;
-(NSString*)getUserName;
- (void)encodeWithCoder:(NSCoder *)encoder;
-(bool)isNil;
#end
fix the stuff that you know is wrong first...
change
#interface currentUser : NSObject<NSCoding>
{
BOOL fromfacebook;
#private
NSMutableArray *upLoadStack;
}
to
#interface User : NSObject<NSCoding>
{
BOOL fromfacebook;
#private
NSMutableArray *upLoadStack;
}
you don't need to know if the user is current or not for the user to have properties of a user, think of them as actually representing objects.
change:
#interface friend : NSObject<NSCoding>
to
#interface Friend : NSObject<NSCoding>
you know it is wrong, fix it now before you have to fix it in 1000 places instead of 100 places.
Convention is very important in Objective-C.
also don't import everything into your header if you don't need to...
#class Friend; //forward class declaration
#interface currentUser : NSObject<NSCoding>
...
#property (nonatomic,strong)Friend *friendToSend; //Here is where a I get the error of unknown type name
and as for your ivar's in currentUser you don't need those in the interface (they can go in a block after the #implimentation line), unless you need them for backwards compatibility...