Objective C - Dynamically compile or do not compile class and codes - ios

First of all; I don't know if this question is duplicate or not. Because I don't know how to search for this question.
Let me explain my question with scenario.
I've iOS 11 Project (Deployment target 9.2). Let's call it Master.
I've coded 2 POD Projects and Master project has reference for them.
POD1 has classes A.class B.class and C.class
POD2 has classes D.class E.class and F.class
Master project is using both POD1 and POD2
Here is the question; What if I want to remove POD1 reference and want to distribute project with just POD2?
I have to remove all codes inside Master which using POD1? I don't think so... It is very amateur way to do it. And there should be professional way to do it.
Maybe Run Script?
or
Maybe putting some flags inside code where using POD1 classes to exclude from build? So I don't get error as File Not Found..
I know the way using
#ifndef HIDE_<insert name here>
CODE
#endif
But don't think it's a correct way to do it..
Any ideas & suggestions are welcome.
Thank you.

Using Objective-C categories, you can conditionally compile groups of methods for different targets. Here's an example.
Let's say you have two targets that both use a class A. In one target (a background daemon) you need just the core functionality of class A. In the GUI version of your app you need the same functionality plus additional methods to support the user interface. The problem is that you can't simply compile all of class A in the daemon target because it will reference Cocoa classes that the daemon target doesn't get linked to. You need to isolate the user interface code and compile/link it only in the target that uses it. Here's how:
Base classes
A.h
#interface A : NSObject
#property NSUInteger someProperty;
- (void)doSomething;
#end
A.m
#implementation A
- (void)doSomething
{
// Do something useful
}
#end
Now define the GUI specific methods in a category:
A+ViewAdditions.h
#interface A (ViewAdditions)
#property (readonly,nonatomic) NSView* view;
#end
A+ViewAdditions.m
#implementation A (ViewAdditions)
- (NSView*)view
{
// Create a view that will display this object
NSView* view = [[NSView alloc] initWithFrame:NSZeroRect];
return view;
}
#end
In both targets, you include/compile the A.m module, so both targets compile the core class A which includes its someProperty and doSomething method. But in your GUI target, you also compile the A+ViewAdditions.m module. In your GUI app, the A class has a view property, but in your daemon it will not. You can test for this at runtime:
A* a = [A new];
if ([a respondsToSelector:#selector(view)])
NSLog(#"a.view is a %#",a.view.className); // prints "is a NSView"
else
NSLog(#"a has no view property");
This can be extended to subclasses:
B.h
#interface B : A
#end
B.m
#implementation B
#end
B+ViewAdditions.h
#interface B (ViewAdditions)
#end
B+ViewAdditions.m
#implementation B (ViewAdditions)
- (NSView*)view
{
NSTextField* fieldView = [[NSTextField alloc] initWithFrame:NSZeroRect];
return fieldView;
}
#end
And...
A* a = [A new];
B* b = [B new];
if ([a respondsToSelector:#selector(view)])
NSLog(#"a.view is a %#",a.view.className); // prints "is a NSView"
if ([b respondsToSelector:#selector(view)])
NSLog(#"b.view is a %#",b.view.className); // prints "is a NSTextField"
There are limitations to categories, which you should be aware of. The most problematic is that you can't add instance variables or stored properties to a class via a category. But category methods can access private variables in the class, and I'll sometimes define private ivars in the base class that only get used by the category. Your style may vary.

Related

My custom native module is not present inside NativeModules object

So, i wanted to create a native module which will detect, if the app is running on emulator/simulator or an actual device.
Everything works fine on android, but i'm facing issue on iOS.
I have create a AbcModule.h and a AbcModule.m file
#import <React/RCTBridgeModule.h>
#interface AbcModule : NSObject <RCTBridgeModule>
#end
This is AbcModule.h
#import "AbcModule.h"
#implementation AbcModule
RCT_EXPORT_MODULE(GetDetails);
- (BOOL) xyzFunctn {
#if TARGET_IPHONE_SIMULATOR
return YES;
#else
return NO;
#endif
}
RCT_EXPORT_METHOD(xyzFunctn: (RCTPromiseResolveBlock)resolve rejecter: (RCTPromiseRejectBlock)reject) {
resolve self.xyzFunctn;
}
#end
This is AbcModule.m
Here i have followed the react native documentation for implementing the Native Modules.
But i'm consistently facing this error which says
"TypeError null is not an object, evaluating GetDetails.xyzFunctn"
I have went through several solutions and articles but nothing seems to be working here.
Need help guys!
from the docs
If you do not specify a name, the JavaScript module name will match the Objective-C class name, with any "RCT" or "RK" prefixes removed.
so just do not specify any name,
#implementation AbcModule
// To export a module named AbcModule
RCT_EXPORT_MODULE();
#end
in your case it should then be accessible from within JS with
AbcModule
But the documentation is not clear if the Objective-C Class declaration needs to be written with prefixed "RCT" or "RK".. but because both prefixes seem to be valid, you should be able to just use AbcModule without prefix.
In other words, if you want to use GetDetails from within JS you need to name your interface and implementation accordingly
#implementation RCTGetDetails
RCT_EXPORT_MODULE(GetDetails);
// or
// RCT_EXPORT_MODULE();
#end
Okay, if there is someone who is facing this issue and feels like their code should work but it isn't and any solution online not working for you as well.
Try this:
When you create your .h and .m file for header and objective-c or swift file, make sure you do it in Xcode and not from VSCode.
VSCode eventually doesn't adds you .h file in the required resources folder, i have wasted my 2 weeks trying to find out solution for it, but lastly, that was it, yes this is it.
in your .m file, let's say GetDetails is a class of NSObject .swift
you need:
#interface RCT_EXTERN_MODULE(WidgetManager, NSObject)
// method to export
RCT_EXTERN_METHOD(isAuthenticated: (BOOL *)isAuthenticated)
#end
in your GetDetails.swift:
#objc(GetDetails)
class GetDetails: NSObject {
#objc(isAuthenticated:)
func isAuthenticated(_ isAuthenticated: Bool) {
}
}

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.

How to check if class is available or not in Static lib?

I will try to explain my problem.
I am creating two libs home.a & room.a independently. From home lib I have calls to the functions which I implemented in room.a
I am want two use this two libs in one project, the case is I want to keep room.a as optional. If I don't add room.a in project, I am not able to build project.
Error is:
Undefined symbols for architecture
"_RoomViewController", referenced from:
-[ParentViewController openView:] in home.a
Here RoomViewController is class from room.a & ParentViewController is class from home.a
I want to add condition in code home.a to check RoomViewController is present then create a object of RoomViewController.
Please suggest me a way for to do this.
Thanks in advance.
If you want the project to compile without errors, you need to add a header file that declares the RoomViewController class. For instance, write a RoomViewController+Private.h file.
#interface RoomViewController: UIViewController
#end
#interface RoomViewController()
//List of methods you want to use
- (void)methodA;
- (void)methodB;
#end
To check whether you linked the library room.a at runtime, you need to do the following:
if ([RoomViewController class]) {
// class exists
RoomViewController *instance = [[RoomViewController alloc] init];
} else {
// class doesn't exist
}

When to declare Class Extension Methods

I've made use of Class Extensions in the .m as a way to have "private" methods and variables. I've read that since Xcode 4.4, the compiler no longer needed the private methods declared.
For example this would compile even though helperMethodC is not declared:
in .h
#interface MyClass : NSObject
-(void)publicMethodA;
#end
in .m
#interface MyClass ()
- (void) pseudoPrivateMethodB;
#end
#implementation MyClass
- (void)publicMethodA
{
//Do Something
}
- (void)pseudoPrivateMethodB
{
[self helperMethodC];
}
- (void) helperMethodC
{
// Do something
}
While private methods no longer have to be declared to compile (helperMethodC), is there a style guide, historical reason, or rule that all private methods (i.e. helperMethodC) should still be declared? Or a "rule" for when to declare and not declare private methods?
Declare them if they help you. From a documentation point of view they are very useful. The compiler will also tell you if you have specified that a method will exist and then not implemented it. There is no rule, but its a good idea to add them. Consider how you'll feel if you have to come back in 6 months and edit the class - will having the methods listed there help you?
While private methods no longer have to be declared to compile (helperMethodC), is there a style guide, historical reason, or rule that all private methods (i.e. helperMethodC) should still be declared? Or a "rule" for when to declare and not declare private methods?
There are multiple conventions, but no standard.
You really should have them when/if you need to support older toolchains -- GCC or older versions of Clang.
Once that restriction is removed, I think it's best if you just phase the (redundant) declarations out where they are not needed. High warning levels and ARC semantics can guide you here.
If you introduce types:
Something * s = [array objectAtIndex:i];
s.string = #"string";
// rather than: [array objectAtIndex:i].string = #"string";
And name your selectors uniquely for the parameter/return types:
// BAD: May be ambiguous.
// Solution: Choose a more descriptive selector name.
// class A
- (void)setX:(int)x;
// class B
- (void)setX:(double)x;
then the compiler can inform you of ambiguities.

EXC_BAD_ACCESS using ARC only during testing

I have an issue where I'm getting bad access exceptions but only when running a testing build (calling the same methods in a debug build doesn't cause the problem to come up). The project has ARC enabled and I'm running this on the iPad 5.1 simulator using Xcode 4.3:
Here's where the problem crops up:
- (void)testChangeFoodNotification {
Player* p = [[Player alloc] init];
[p addObserver:self forKeyPath:#"food" options:0 context:0]; // <-EXC_BAD_ACCESS (code=2)
p.food += 1;
STAssertTrue(_wasNotifiedOfFoodChange, nil);
}
At the point when the addObserver: method is called it doesn't seem like any of the objects involved should have been released so what could be causing the exception?
EDIT:
Apologies if it wasn't clear but the code above is being executed as part of a test case (using the standard Xcode OCUnit). Also in case it clarifies anything here's the relevant code from the player class (there's other ivars and methods but they don't have any connection to the property or methods being tested):
// Public interface
#interface Player : NSObject
#property (nonatomic, assign) NSInteger food;
#end
// Private interface
#interface Player() {
NSInteger _food;
}
#end
#implementation Player
#synthesize food = _food;
#pragma mark - Getters/Setters
- (void)setFood:(NSInteger)food {
[self willChangeValueForKey:#"food"];
_food = food;
[self didChangeValueForKey:#"food"];
}
If your class is indeed key-value compliant, ensure that the implementation for the class exhibiting the issue is not included in your test product. This means that the Target Membership panel of the Identity inspector for your .m file should only have your app checked (not YourAppTests).
I experienced the same issue in Xcode 4.3.1 when an implementation was included in both products and I registered observers in both production and test code. The following logs tipped me off:
Class YourClass is implemented in both /Users/yourUser/Library/Application Support/iPhone Simulator/5.1/Applications//YourApp.app/YourApp and /Users/yourUser/Library/Developer/Xcode/DerivedData/YourApp-/Build/Products/Debug-iphonesimulator/YourAppTests.octest/YourAppTests. One of the two will be used. Which one is undefined.
As per the Key-Value Observing Programming Guide, is your Player key-value-compliant? You want to make sure you are Ensuring KVC Compliance. I also assume that you have also implemented your observeValueForKeyPath:ofObject:change:context:? If you think you've done all of this and it's still not working, then perhaps you can share your code.
Also, minor thing, but I assume this is a code snippet to highlight the issue. I only mention it because ARC is going to be releasing your p object at the end of your testChangeFoodNotification and I would have thought that you'd want to remove your observer first.

Resources