Module not found for framework dependency in Xcode - objective-c

I have a project with multiple framework targets, that also have pods dependencies.
I have:
No circular dependencies between targets
Everything, including pods, is in Objective-C, no Swift at all.
use_frameworks! in Podfile, so all pods are frameworks, not libraries.
Here is my structure:
TUSystemKit depends on TUModels (which is a framework).
TUModels depends on Pods_TUModels (generated by pods).
Pods_TUModels depends on JSONModel
TUModels is automatically linked with its own pod framework (which contains JSONModel).
TUSystemKit has TUModels as target dependency.
TUSystemKit is linked with TUModels.
Visually, the dependencies are like this:
TUSystemKit ➔ TUModels ➔ Pods_TUModels ➔ JSONModel
When I select MyModels as the build target in Xcode, build succeeds. However, when I select TUSystemKit, the build fails, saying that module JSONModel is not found while building module TUSystemKit (TUUser in screenshot belongs to TUModels):
What am I doing wrong? Obviously I shouldn't be explicitly linking against all the frameworks in the dependency tree. Why does TUModels build perfectly but TUSystemKit errs on a module import inside a linked framework's code? Do I need to change something with pods?

After hours of refactoring, I've managed to build my project. I can't tell what exactly was wrong as it took me literally a day to organize all the dependencies and frameworks and it kept failing at a different point, more than a 100 times, but here are some observations to lead to a successful build:
All the public-facing classes are added as public headers to the target, and not to any other target.
All the code (.m files) are in Compile Sources section of the target, and not in any other target.
All the public facing classes' headers are included at umbrella header (a header with the exact same name with the framework)
The application embeds all the custom frameworks (not the pods).
All the files inside a framework target only #import required files within the same target or a file listed on any targets umbrella header that the framework has a dependency on.
Obvious, redundant, but worth noting again: no classes between frameworks should have circular dependencies (e.g. ClassA in FrameworkA depends on ClassB in FrameworkB, while some class in FrameworkB depends on some class on FrameworkA). I had some, and created delegates. Do whatever pattern fits your design: IoT/dependency injection, notifications/publisher-subscriber etc. But do it: separate the concerns clearly.
Try to avoid using same classes in multiple targets. Instead, have it in one target, and make the other target depend on the containing target, creating a dependency chain.
After refactoring many files and playing with project settings, I managed to build and run everything again. My previous setup had various number of combinations of the issues that I mentioned above, messing everything up. After cleaning all the bits and grouping code into functional, modular frameworks, I could build it.

If you came here due to the parse error -> module not found,
in certain occasions you may be forced to add the path manually.
This is...
go to your project at the top
select your project target
select build settings
search the parameter Framework Search Paths under the title Search Paths
add the one where yours is located. Example: (using cocoa pods) $(SRCROOT)/Pods
indicate/set it to be recursive (access to the option by double-clicking your previously added path)
The problem should have been resolved by the 3erd party lib with commands like install / update / build or similar but if it fails and you are stuck, this is an option in order to continue.
In the same tone, if you get an error from pods indicating that
The sandbox is not in sync with the Podfile because the builder is unable to find files like Podfile.lock, then you may consider to go in the same direction adding some user-defined settings:
select build settings
press the '+' symbol, "Add User-Defined Setting".
add this pair:
param= PODS_PODFILE_DIR_PATH value = ${SRCROOT}/.
param = PODS_ROOT value = ${SRCROOT}/Pods
Cheers

Related

How to resolve Swift Package Manager target overlapping sources error

I have a GitHub repo that is an Objective-C project that defines multiple targets (one per each targeted OS: macOS, iOS, tvOS, etc.). These targets share most of the source code files of the project.
I am trying to add SPM support to the project and in the Package.swift file I define matching targets with source path referring to the same folder containing those source code files shared across the targets.
When I add this SPM-"enabled" project/package to a test project that is trying to use it as a dependency via SPM, I get an error saying that the second target in the dependency package having overlapping sources with another target (the first one that uses the same shared source code files).
Any ideas about how to fix the problem? I would assume my multi-target configuration over the shared source code files is not that uniqueue...
I finally figured out what was the problem and what should be a solution to it.
My mistake was to think of SPM targets the same way Xcode thinks of them. In reality, SPM targets are essentially named folder containing source code files that need to be built to produce a product or products. They do not assume a definition of a recipe regarding how to build (the way Xcode targets do). However, a codebase that depends on a given SPM package does apply its Xcode targets to build itself and the dependency (prior to building its own codebase). The SPM targets are completely agnostic to the targeted device.
Hence, when I tried to map all my Xcode targets as similar SPM targets (at the same time referring to the same source code files from multiple SPM targets) it was wrong from the SPM point of view.
The correct solution anded up to be a definition of a single SPM target over a single codebase location (path) and everything went well from there.
Here is a good video that explains the SPM concepts well and that subsequently led me towards the working solution:
Creating Swift Packages

iOS test target build breaks, library not getting built for right architecture

My project contains two targets, the main application and a separate target for testing. The main target builds no problem but the test target fails with the following linker warning:
"ld: warning: ignoring file .../libj2objc_main.a, file was built for archive which is not the architecture being linked (i386)"
It seems like this is a common problem, and there are several posts that deal with solutions, from setting the "Build Active Architectures Only" flag to No (or is it Yes?) to using shell scripts to force Xcode to build a fat binary. (Using lipo I've confirmed that the compiled .a file only contains the x86_64 binary.) It's really bizarre that when I create a new test target and run the default XCTest suite everything builds fine, but if I attempt to add the Pods config file to the test target in Info -> Configurations and run the tests again everything blows up. This has never been a problem in other projects.
My question is why is this only failing for the test target? I'm using the same architecture in both the regular and test builds, so why is the .a only being a problem in the latter case?
So it turns out that the reason things weren't building was because (unbeknownst to me) my team was manually managing the header search paths. I'd encourage anyone working on a team to check the header search paths inside the primary target and see if they're all being added individually. If they are, you'll similarly need to add any test-specific headers manually in your test target.

Duplicate symbol for class in library and project

I am using SBJson classes in my library and having tried to use the library in my project I am having a duplicate symbol error because I am also using these files in the main project.
I can't add prefixes to these files so how does one usually work around this problem ?
I saw other answers that suggest renaming classes with prefixes, yet this isn't really the solution since it's correct that these files have duplicates - it's an open source code.
You could remove the SBJSON (.m) files from the library project target (using the "Build Phases" screen in Xcode) and then project should be able to compile using the SBJSON files that are part of the main project. There might be issues though, if both projects use different versions of the SBJSON library.
Personally I wish all library projects didn't include other libraries but note (in a "Read Me" file or whatever) that it's a dependency and it's up to the developer to include the dependencies in the main project. I've had a lot of issues with duplicate symbols in the past due to this stuff.
Just Go to Build Setting and
search for No Common Blocks and
set it NO.
And build again you will not get this error again.
CheersKP
You can just include the .h files in your project and remove .m for SBJson. That way you can import the .h in you code and it will compile.

Populating the configuration to the nested library project

I have a main project, which includes a nested library project which produces a libCore.a library.
Both of the projects have the two configurations debug and release. Now if I build the main project with a given configuration, how can I make sure that this is passed down to the library project as well?
Make your project directly dependent on the libCore project. Do achieve this, do the following:
Drag the libCore project file to your main project. It happens sometimes, that only the xcodeproj file is moved. If this happens to you, restart XCode (this happend to me with RestKit and the latest XCode).
After point 1 is done and you can browse the dependency project, go to your target build phases and add the libCore as the target dependencies.
Link against libCore.a by adding in the Link Binary with Libraries phase.
These are 3 basic steps, I don't know what the libCore is, if it needs to be linked with any other libraries then you will also have to link your target against those libraries.

Dependency Management for iOS Library

First off: Why is dependency management for obj-c projects such a pain?!
I am writing a wrapper for my RESTful service in objective-c. The server is a simple sinatra app running locally on 'http://localhost:4567'.
I've included RestKit by following the steps outlined here.
I know RestKit is 'installed' correctly into my project because when I do #import <RestKit/RestKit.h> the project builds just fine.
Now, I'm testing my library using SenTesting.Framework. I have a class in my main library that looks like this:
#import "CITWCore.h"
#import <RestKit/RestKit.h>
#implementation CITWCore
- (id)init
{
self = [super init];
if (self) {
RKObjectManager *manager = [RKObjectManager objectManagerWithBaseURL:#"http://localhost:4567"];
// Initialization code here.
}
return self;
}
#end
And my unit test class:
#import "CITWCoreTests.h"
#implementation CITWCoreTests
- (void)testItCreatesAnInstance
{
CITWCore *newCoreObject = [[CITWCore alloc]init];
STAssertNotNil(newCoreObject, #"new object should not be nil");
}
#end
When I run the tests using ⌘U the test fails with this message:
error: testExample (CITWCoreTests) failed: -[__NSCFString isIPAddress]: unrecognized selector sent to instance 0xa115880
The error is being triggered by line 292 in RKClient.m
if ([newBaseURLString isEqualToString:#"localhost"] || [hostName isIPAddress]) {
There is a header file in the RestKit project called "NSString+RestKit.h" which contains the -isIPAddress method declaration, and as far as I can tell it is getting included, so I have no idea why the compiler/run-time does not know about that particular method. Is there something wrong with the way I've configured my testing target? How can I create an instance of RKObjectManager and get this test to pass?
More abstractly: How are people managing dependencies like this? I'm looking at things like VenderKit, but it seems lacking in documentation and I don't think I have the proper understanding of how compilers and linkers work to go to that big of an abstraction. What are some general guidelines when linking static libraries into my project, which is itself a static library?
Double check that your project build settings for "Other Linker Flags" has "-all_load" and "-ObjC" on your build target. While you are in there, check that you created the "Header Search Paths" entry ("$(SOURCE_ROOT)/RestKit").
The "Installing-RestKit-in-Xcode-4.x" page that you linked to, is slightly out of date with a) Xcode and b) RestKit HEAD (the build process was simplified recently. FMI see the mailing list.
If you want to see a project correctly setup (I just created it recently, with the newest Xcode and Restkit) take a look at https://github.com/lottadot/lottadot-restkit-ios-rails3-1-advanced
My guess is if you clone that project, edit it's configuration and remove "-all_load" you will see the exact same error, when you run it.
In this case, you need to find the file (or image/library) which defines/exports -[NSString isIPAddress]. Then you would need to add that file to your compile phase (if it is a source file), or link the library to your final binary (if it is a library or object file). In addition to linking it to your app, you will also need to compile and or link it into your unit test executable.
I know RestKit is 'installed' correctly into my project because when I do #import the project builds just fine.
#importing will not necessarily link or compile all of the necessary dependencies. You may have to do this manually. Xc4 may detect the dependency automatically, and build and link it for you if the option is enabled -- but it does not always get it right (it's good for basic dependencies).
Why is dependency management for obj-c projects such a pain?!
It's really not, IMO. Specifying files to compile and libraries to link with is something you'll need to get used to when compiling C family languages. Unless you want to be more specific about this criticism…
How are people managing dependencies like this?
Add the dependent projects to your Xcode projects. Configure them as build dependencies -- this will ensure they build before your app is built, and that the builds are up to date. For static libraries (targeting iOS), save the link stage for the final executables. In more complex scenarios, you will want to use xcconfig files in order to easily define build settings for any/all dependencies.
One simple solution is to use cocoapods, which is good dependency management tool similar to maven in Java world.
CocoaPods is quite a powerful and maturing dependency management tool that can manage libraries, the libraries these depend on (transitive dependencies) as well as compiler and header flags.
It works by linking your project to another workspace that includes the libraries in source form, where the main target emits a static lib. This gives a good compromise between speed and being able to see the source-code.