Customize static libraries - objective-c

I've created a XCode projet that contains 2 targets and static library that I implement in both targets. Some of the classes of my static library needs to be slightly different depending on the target I compile.
I do not have any idea how to do it properly.
Any idea?
Thanks

In what way to they need to be different?
(I am basing my answer on a guess that it can be handled by setting some sort of state-variable in the classes or by a custom init method)
I would suggest you create some form of custom init methods or instance variables that you set for the classes that need to have different behaviors. You can then use User defined setting in your build settings for each target.
Check this question & answer for more information: iphone: get User Defined variable in Target's setting by code?
Basicly you could have a setting that would be a string like so: "Standard", you fetch it from the
FooBarClass.h
typedef enum { FooBarSettingNormal, FooBarSettingFast } FooBarSetting;
-(id)initWithSetting:(FooBarSetting)setting;
And then fetch the variable set in buildsetting from the code and init the FooBar object like so:
SomeViewController.m
NSNumber* fooBarSetting = [[[NSBundle mainBundle] infoDictionary] valueForKey:#"FooBarSetting"];
FooBar * baz = [[FooBar alloc] initWithSetting:[fooBarSetting intValue]];
This enables you to have different behavior in your classes and keeping the static library separate and stand alone from the project you are using.
I hope you find this somewhat helpful :)

Related

Changing the implementation of Objective C method in Swift subclass

I am using an external library in my project. It is integrated via CocoaPods. I have access to the source code. The library is working well, but I need some modifications at some places in order for it to serve my purposes. It is written in Objective C. My project is in Swift.
I need to change the implementation of one method in the library. The problem is it is in the .m file and uses a lot of stuff declared only in the .m file. For example:
-(NSString*)methodToChange
{
NSArray<NSNumber*>* data = [self someInternalMethod:1];
uint value = data[0].unsignedIntValue;
return [self anotherInternalMethod:value];
}
I tried subclassing it like this:
class MySubclass : MySuperclassWithMethodToChange {
override var methodToChange: String {
//trying to use someInternalMethod and anotherInternalMethod
//unsuccessfully because they are not visible
}
}
The internal methods are using and modifying properties from the .m file that are also not accessible. Is there any way to solve this?
I would suggest forking the original library repository and making the necessary changes in your fork. You can then have your Podfile point to that. If your changes could be useful to others, make them in a way that doesn't break existing functionality and contribute them back to the library by opening a pull request.
If the original library gets updated later, you will have to merge those changes from the so-called "upstream" repository into yours. This does not happen automatically, so you'll have full control (and responsibility) over that process. See https://help.github.com/en/articles/syncing-a-fork for how this would look like.

Objective-C: Do non-imported classes get compiled and linked into final binary?

In Objective-C, do all source files in the project get compiled and linked into the final binary even if they are not imported by any classes?
That depends on your target settings. If you go to the target's Build Phases, anything listed under Compile Sources will be compiled.
If the source file is part of the build then its classes end up in the binary and available at runtime. That's because the runtime is reflective and can be queried later to find them. The compiler cannot be certain that won't happen.
EDIT: a classic use case is an informal protocol. You may write code like:
- (void)initWithObject:(id)object
{
self = [super init];
if(self)
{
_title = [object title];
_value = [object value];
}
return self;
}
Then you might decide you want to add an implementation of that informal protocol to e.g. NSString:
#interface NSString (MYPropertyProtocol)
#end
#implementation NSString (MYPropertyProtocol)
- (NSString *)title
{
return #"Content";
}
- (NSString *)value
{
return self;
}
#end
Just because nobody imports the NSString category doesn't mean the methods aren't used.
Going beyond that, in my current app we have a protocol like (this is a heavily cut-down version):
#protocol MYTableViewCell
+ (NSArray *)acceptedObjectClasses;
- (void)setObject:(id)object;
#end
Subsequently UITableView subclasses can opt to implement MYTableViewCell. If so they are found automatically by traversing objc_getClassList (and a lot of caching) and used automatically by my table view data source if it wants to display any object type declared as supported by acceptedObjectClasses. So that's taken most of the hassle out of writing the table view data source — I've got just one, which can accept any combination of objects used anywhere in the app, yet it's very short and easy mentally to check for errors, and need never grow even as the app learns about more things that need to be displayed in tables.
Nobody ever explicitly imports those table view cells, because it'd just mean having to do the same work twice — declare the class in Objective-C, which the compiler will check for me, then go and redeclare it to the custom cut-down lookup system that I've implemented instead of just using Objective-C's, which I probably need to do manual verification on.
Click on a .m file in the Project Navigator on the left side of Xcode, then open the right-hand tile (with the right-hand "View" button in the toolbar) and look at the "Target Membership" section. There is a checkbox with your project name beside it. If the box is checked, that .m file is included in the build. If the checkbox is not checked then the .m is essentially ignored.
You can also check this by clicking on your project name at the top of the ProjectNavigator column, selecting the project under "TARGETS", selecting "Build Phases", and selecting "Compile Sources". All of the included .m files will be shown and you can add or delete files from the list using the + - buttons at the bottom of the list.

Adding a class in objective-c

I'm new to Xcode and I'm trying to understand how things work, studying from books, reading tutorials etc.. and I got a minor problem
when I create a new project using " Command Line Tool" if I manually create the class interface, implementation and then write the codes to the main it works perfectly
however, as you know when a project is created through CommandLineTool it only provides main.m
I click on the file -> add new file and select objective-c class, name the class and then it provides me classname.m and classname.h and when I write the codes they don't work, the main somehow doesn't see the other files I mean main and class.h and class.m are not connected I think because when I start writing
classname *newclassname= [[classname alloc] init];
the classname doesn't show up so any suggestions what am I doing wrong?
I am sorry for my bad english and thanks in advance
Objective-C is case sensitive so take care when writing classname or Classname, check that for all the code. Also the convention is to use Uppercase for class names and lowercase for variables and instances. Variables are usually written with camelCase so newclassname will be newClassname:
Classname *newClassname= [[Classname alloc] init];
Seems like you need this one:
#import "classname.h"

Where would I place a global utility function in Objective C?

When writing an iOS app, where would I place a function that I intend to use from any other file?
For example, a function to convert a NSDate to a relative time string ("5 secs ago").
Would I make a class and make these functions all static?
Functions can be placed wherever convenient. If a function or group of functions is likely to be imported in many places, you can declare and implement them in their own .h/.m pair. So for example you might implement your date conversion function in a file named XYZDateUtilities.m, and declare it in XYZDateUtilities.h.
Declaring functions with the static qualifier would limit their scope to the file in which they were declared, so you wouldn't want to do that; in fact you'd want to do the opposite -- declare them as extern in the .h file so that they'll be visible in other files.
You have a couple options:
1) If you're extending the behavior of a class (such as the NSDate string conversion method you described), it may work best to simply create a category on said class.
Here's a tutorial on iOS categories:
http://mobile.tutsplus.com/tutorials/iphone/objective-c-categories/
Important Note:
Categories change a class's behavior (if you override a method) everywhere within the project whether or not you include the header (.h) file in another specific class's imports
For this reason, it's generally best to not override methods via a category, but instead, to create a subclass if you want to change certain methods.
For adding new methods, however, categories can be very convenient and useful.
2) If you want to create a new class that's imported everywhere, you can create said class and put its header import, i.e. #import "MyClass.h", into your project's prefix.pch file (found under the "supporting files" group within the project by default).
Anything that you put into the prefix.pch file will be available anywhere within your app. This is also a useful place to put constants (such as strings) or define enums that are used across many classes within the app.
I hope this helps. Let me know if further clarification is needed, and I'll do my best to help.
Cheers!
Another option would be to create a class for your helper methods and implement all the helpers as class methods.
e.g. HelperClass.h
+ (NSString *)getFrenchCapital
e.g. HelperClass.m
+ (NSString *)getFrenchCapital
{
return #"Paris";
}
Then import your helper class wherever you need it, and simply call the class methods:
e.g. Foo.m
#import "HelperClass.h"
...
- (void)logFrenchCapital
{
NSLog(#"Capital of France: %#", [HelperClass getFrenchCapital]);
}
If you make all functions static in a class, then alternative is to just define functions in .m file, and extern functions in .h file, just like what you do in C.

NSClassFromString returns nil

Why does NSClassFromString return nil ? As per the definition it has to return class name.
How should I take care to rectify this problem? I need to instantiate a class from string and call the method, which is in the class, using the instance created.
This is how my code looks like:
id myclass = [[NSClassFromString(#"Class_from_String") alloc] init];
[myclass method_from_class];
But the method_from_class function is not being called, control is not going into it. And my code is error free. Any idea how to solve this in Objective-C?
If you are trying to instantiate a class from a static library, you must add the "-ObjC" flag to the "Other Linker Flags" build setting.
The Documentation for the function says:
Return Value
The class object named by
aClassName, or nil if no class by that
name is currently loaded. If
aClassName is nil, returns nil.
An example of how this should be properly used is as follows:
Class dictionaryClass = NSClassFromString(#"NSMutableDictionary");
id object = [[dictionaryClass alloc] init];
[object setObject:#"Foo" forKey:#"Bar"];
It is possible that your class is not getting linked if this is the only reference to it.
I had a factory method to instantiate various types of subclass. The factory had a switch statement that went to the appropriate subclass and alloc'd and init'ed it. I noticed that all of the alloc/init statements were exactly the same, except for the name of the class. So I was able to eliminate the entire switch block using the NSClassFromString() function.
I ran into the same problem - the return was nil. This was because the class was not used elsewhere in the program, so it wasn't getting linked, so it could not be found at runtime.
You can solve this by including the following statement:
[MyClass class];
That defeats the whole purpose of what I was trying to accomplish, but it might be all you need.
This happened to me when I add an external file to the Xcode project. Adding the .m file to Build Phases > Compile Sources solve the problem.
You also need to make sure the class you are trying to instantiate is included in the project. If you added it later, you made need to click the checkbox next to the Target you are building.
Why not decomposing all these calls ? This way, you can check the values between the calls:
Class myclass = NSClassFromString(#"Class_from_String");
id obj = [[myclass alloc] init];
[obj method_from_class];
By the way, is method_from_class an instance method or a class method ? If it is the later, then you can directly call method_from_class on the myclass value:
[myclass method_from_class];
I also saw an oddity where adding the standard singleton code espoused by apple prevented the class from being loaded. The code was working as expected, then I added the singleton, and suddenly the NSClassFromString started returning nil. Commenting out the singleton code resulted in the NSClassFromString resolving the class correctly. I don't understand the interaction, but I think the singleton static var was somehow getting mangled to hide the class name...?