Allow change in property only from one specific class - ios

Suppose I have a class Participant which looks like this
Participant.h
#interface Participant : NSObject
#property(nonatomic, readonly) NSString *name;
#property(nonatomic, readonly) NSString *id;
#end
Here properties are readonly because I don't want anyone using this interface to change it
Besides ParticipantManager.h
What changes should I do in Participant class and how would I create ParticipantManager such that only ParticipantManager can change properties of Participant
More context
I get an event from react-native when value changes. to keep things in sync, I want my interface ParticipantManager to only change the values.
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
#interface ParticipantManager : RCTEventEmitter <RCTBridgeModule>
#end
^^ Above class should only be able to change properties of Participant class
For a user to change a value, he would call changeName method, which will send an event back to react-native where react-native would change value and send back to native code
what I have tried.
So, I thought about using class extensions concept but I am getting an error.
Here is what I did
I create Participant+Private.h which implements setName method
#import " Participant.h"
interface Participant()
- (void)setName:(NSString)name
- (void)setId:(NSString)name
#end
PS: I implemented setName and setId method in Participant.h
- (void)setName:(NSString)name {
_name = name;
}
but then when I am using it in my ParticipantManager.h, it is throwing error
No visible #interface for Participantdeclares the selector
setName
I am using it like this
#import "Participant+Private.h"
NSString* value = #"varun";
[[Participant sharedInstance] setName:value];
Can someone help me in fixing error?
Slightly detailed question here:
No visible #interface for Participant declares the selector setName

Objective-C doesn't provide that sort of reasoned privacy, e.g. "I am private to everyone except one other specific class which I hereby name". (Actually, I don't know any language that behaves that way, but that's not to say that there are no such languages of course.)
If this is a framework, you can use #package privacy to confine the privacy of something to other classes in the same framework.
Otherwise, Objective-C generally solves the visibility problem by importing headers, so if you put public accessors for Participant into a header and the only class that imports that header is ParticipantManager, then only ParticipantManager sees them.

Related

How to create a new object and assign properties in Objective-C?

I'm trying to bridge an Objective C SDK with React Native and I'm having some trouble. I have a Subclass of NSObject and I'm trying to set some property values but I can't get it to work.
I have tried to change the property in the header, and in the imp file with out any difference.
PrinterSDK.h (which has libPrinterSDK.a)
#interface Printer : NSObject
#property (nonatomic, readonly) NSString* name;
#property (nonatomic, readonly) NSString* UUIDString;
#end
RNPosPrint.m
#interface Printer ()
#property (readwrite) NSString* name;
#property (readwrite) NSString* UUIDString;
#end
RCT_EXPORT_METHOD(printTestPaper:(NSString*)name:(NSString*)uuid)
{
Printer* printer = [[Printer alloc] init];
printer.name = name;
}
But I keep facing issue with the setter for some reason I can't figure out.
ExceptionsManager.js:94 Exception '-[Printer setPrinterName:]: unrecognized selector sent to instance 0x13fd25b90' was thrown while invoking printTestPaper on target RNPosPrint with params (
"Test Printer",
"XXX-XXX-XXX"
)
You do not report the names of your .h and .m files or what else is in the .m – e.g. #implementation of Printer? The class printTestPaper belongs to? Without details like this it is difficult for anyone to help you, you need to help people help you.
That said some points that may help you:
The #interface Printer () where you open up the properties to be writeable should be in the your Printer.m file – in general do not try to open up access to a type's properties from outside the type's implementation, it is both bad design and may not work as you hope (as you just found out).
The code to support a #property is generated by the compiler when it compiles the #implementation, #interface's themselves produce no executable code – they describe the accessible parts of the #implementation.
setter=<name> provides a different name for the auto-created property setter function. While a method <name> will be created to set the property using dot syntax the properties name is still used, e.g. in your case printer.name = ... is still used even with the setter=setPrinterName:. You can call the auto-created method using standard method syntax, that failed in your case for the reasons above.
Using setter=<name> or getter=<name> are really advanced features and you probably will never need to use them – when you do need to use them you will know! Just avoid them till then.
If you wish to provide a method which creates the object and sets properties then do this in the type's implementation. The usual way of doing this is to provide an init method that does this, e.g. in this case it might be - initWithName:(NSString *)printerName { ... }, or an equivalent class method which does the allocation and sets the parameters, e.g. in this case it might be + newWithName:(NSString *)printerName { ... }.
HTH
Since it's an interface from statically linked library it is simply not possible to extend or manipulate. Not without tempering with the compiler.

In Swift&Objc mixed app Xcode cannot find protocol for Realm to-many relationship

For some reason I need to have some objc code in my project but all the new code is being written in Swift. And I am starting to add Realm into the project. Since RealmSwift cannot be used in a mixed application so I need to go with Realm Objective-C.
So I defined my two models: item and appraisal like this:
#interface Item : RLMObject
#property NSString *name;
#property NSString *upc;
#property Appraisal *appraisal;
#end
RLM_ARRAY_TYPE(Item)
#interface Appraisal : NSObject
#property NSString *name;
#property RLMArray<Item> *items;
#end
I need to use these models in my new Swift code so i put them in the bridging-header file. like this:
#import "Item.h"
#import "Appraisal.h"
As long as I have them in the bridging-heder I cannot compile the code because of the error "Cannot find protocol declaration for "item"".
Anybody seen this before and had a solution?
Thanks in advance!
Since your error message is Cannot find protocol declaration for "item", it is possible you wrote RLMArray<item> instead of RLMArray<Item> ?

Obj-c exporting .h files for a Static Library - don't want to expose class definition

I am struggling to create an iOS static library correctly/cleanly.
So far I have used Extension a to create a separation between my internal (private) .h declarations and the public .h declaration (that gets exported).
I have "successfully" created the library and seen it working. However I am still exposing the class declaration in the public WTDevice.h
#interface WTDevice : NSObject <WTMinorStateDelegate,CBPeripheralDelegate>
As WTDevice inherits from WTMinorStateDelegate I have to export protocol WTMinorStateDelegate too, which I don't wish todo as this is only used within the library.
BTW the WTDevice extension is in WTDevice_internal.h which doesn't get exported.
I am sure there is a way of putting the line:
#interface WTDevice : NSObject <WTMinorStateDelegate,CBPeripheralDelegate>
into WTDevice.m (making it private), however I have failed so far. The question is what replaces it when I remove it from the WTDevice.h file?
Thanks
In WTDevice.m add this before the #implementation block to create a private category.
#interface WTDevice () <WTMinorStateDelegate>
#end
In WTDevice.h remove WTMinorStateDelegate from the #interface line.

Im working on "Matchismo" the app for the stanford itunes university courses, and on the first assignment I get an issue on my PlayingCrad.m file.

#import "PlayingCard.h"
#implementation PlayingCard
-(NSString *)contents
{
NSArray *rankStrings = [PlayingCard rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
/Users/Pichard93/Desktop/Matchismo2/Matchismo2/PlayingCard.m:18:33: Property 'rank' not found on object of type 'PlayingCard'
//This is the issue i get ^^
It looks like rank should be declared as a property and is not. Declare it the interface file or as a class extension in the implementation file.
Basically the error messages do mostly make sense, try to figure out what them mean pertaining to your code. In this case the error message rather explicitly stated that rank is not found for the class PlayingCard which means not declared.
Add this line to your PlayingCard.h
#property (nonatomic)NSUInteger rank;

Purpose of Synthesize

I am using an iOS5 book to learn iOS programming.
#synthesize coolWord;
^synthesize is used for all properties in .m files
I heard that in iOS6 there is no need for synthesize, since it is automatically done for you. Is this true?
Does synthesize play any role for iOS6?
Thanks for the clarification. :)
#synthesize in objective-c just implements property setters and getters:
- (void)setCoolWord:(NSString *)coolWord {
_coolWord = coolWord;
}
- (NSString *)coolWord {
return _coolWord;
}
It is true with Xcode 4 that this is implemented for you (iOS6 requires Xcode 4). Technically it implements #synthesize coolWord = _coolWord (_coolWord is the instance variable and coolWord is the property).
To access these properties use self.coolWord both for setting self.coolWord = #"YEAH!"; and getting NSLog(#"%#", self.coolWord);
Also note, both the setter and getter can still be manually implemented. If you implement BOTH the setter and getter though you NEED to also manually include #synthesize coolWord = _coolWord; (no idea why this is).
Autosynthesis in iOS6 still requires #synthesize
to generate accessor methods for properties defined in a #protocol.
to generate a backing variable when you included your own accessors.
The second case can be verified like this:
#import <Foundation/Foundation.h>
#interface User : NSObject
#property (nonatomic, assign) NSInteger edad;
#end
#implementation User
#end
Type: clang -rewrite-objc main.m and check that the variable is generated. Now add accessors:
#implementation User
-(void)setEdad:(NSInteger)nuevaEdad {}
-(NSInteger)edad { return 0;}
#end
Type: clang -rewrite-objc main.m and check that the variable is NOT generated. So in order to use the backing variable from the accessors, you need to include the #synthesize.
It may be related to this:
Clang provides support for autosynthesis of declared properties. Using
this feature, clang provides default synthesis of those properties not
declared #dynamic and not having user provided backing getter and
setter methods.
I'm not sure how #synthesize relates to iOS6 but since Xcode 4.0, it's essentially been deprecated. Basically, you don't need it! Just use the #property declaration and behind the scenes, the compiler generates it for you.
Here's an example:
#property (strong, nonatomic) NSString *name;
/*Code generated in background, doesn't actually appear in your application*/
#synthesize name = _name;
- (NSString*)name
{
return _name;
}
- (void) setName:(NSString*)name
{
_name = name;
}
All that code is taken care of the complier for you. So if you have an applications that have #synthesize, it's time to do some cleanup.
You can view my similar question here which might help to clarify.
I believe that #synthesize directives are automatically inserted in the latest Obj-C compiler (the one that comes with iOS 6).
The point of #synthesize pre-iOS 6 is to automatically create getters & setters for instance variables so that [classInstance getCoolWord] and [classInstance setCoolWord:(NSString *)aCoolWord] are generated. Because they are declared with #property, you also get the convenience of dot syntax for the getter and setter.
hope this will help little more
yes previously we have to synthesis the property by using #synthesis now it done by IDE itself.
but we can use it like
// what IDE does internally
#synthesis name=_name;
we use _name to access particular property but now you want synthesis by some other way like
firstname you can do it like
#synthesis name= firstname
or just by name
#synthesis name=name
With automatic synthesis in iOS6, it is no longer necessary to specifically declare backing ivars or write the #synthesize statement. When the compiler finds a #property statement, it will do both on our behalf using the guidelines we’ve just reviewed. So all we need to do is declare a property like this:
#property (nonatomic, strong) NSString *abc;
and in iOS 6, #synthesize abc = _abc, will be added automatically at compile time.

Resources