Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to add framework with inner framework in an app #688

Closed
jsslai opened this issue Aug 17, 2015 · 10 comments
Closed

How to add framework with inner framework in an app #688

jsslai opened this issue Aug 17, 2015 · 10 comments
Labels

Comments

@jsslai
Copy link

jsslai commented Aug 17, 2015

I have a following project setup:

  • TestApp
    • TestAppKit.framework
      • ReactiveCocoa.framework
      • Result.framework

Is it necessary to add ReactiveCocoa and Result frameworks in TestApp's "Embedded Binaries"? Is it possible for TestAppKit.framework to include both ReactiveCocoa and Result frameworks in TestAppKit.framework bundle? Then I don't have to include all the dependencies in the application target. I found something related in this stackoverflow answer:
http://stackoverflow.com/questions/31250342/how-to-use-realm-installed-with-carthage-with-a-framework-in-a-swift-app

So if I add carthage copy-frameworks in TestAppKit.framework build phases I don't have to include ReactiveCocoa and Result frameworks in TestApp target or test targets and it seems to work in simulator. Only problem is when I try to run the app on the device I get:

dyld: Library not loaded: @rpath/ReactiveCocoa.framework/ReactiveCocoa
  Referenced from: /private/var/mobile/Containers/Bundle/Application/18D48163-7D97-43C1-A8D2-031B0EBF47D3/TestApp.app/Frameworks/TestAppKit.framework/TestAppKit
  Reason: no suitable image found.  Did find:
    /private/var/mobile/Containers/Bundle/Application/18D48163-7D97-43C1-A8D2-031B0EBF47D3/TestApp.app/Frameworks/TestAppKit.framework/Frameworks/ReactiveCocoa.framework/ReactiveCocoa: mmap() error 1 at address=0x10126C000, size=0x00120000 segment=__TEXT in Segment::map() mapping /private/var/mobile/Containers/Bundle/Application/18D48163-7D97-43C1-A8D2-031B0EBF47D3/TestApp.app/Frameworks/TestAppKit.framework/Frameworks/ReactiveCocoa.framework/ReactiveCocoa

So there is something wrong with the setup.

To summarise:

  • Is it possible to embed dependencies inside a framework?
  • If not, what is the correct setup?

If I add frameworks in app's "Embedded Binaries" and also have “Copy Frameworks" step in my framework's build step like instructed in Carthage's readme, does the application bundle contain the frameworks twice?

@mdiep
Copy link
Member

mdiep commented Aug 17, 2015

Carthage doesn't support this setup because it doesn't allow TestAppKit.frameworks dependencies to be managed independently. Much of the benefit of Carthage derives from the ability to specify version requirements for a dependency in multiple places and resolve them into a single compatible version.

So, for instance, if TestAppKit.framework requires ReactiveCocoa.framework >~ 2 and SomeOther.framework requires ReactiveCocoa.framework >~ 2.5, TestApp can use ReactiveCocoa 2.8 to satisfy both requirements. Otherwise you need to update TestAppKit.framework anytime that you want to update `ReactiveCocoa.framework.

@mdiep mdiep added the question label Aug 17, 2015
@jsslai
Copy link
Author

jsslai commented Aug 18, 2015

Thanks, that makes sense.

@jsslai jsslai closed this as completed Aug 18, 2015
@aral
Copy link
Contributor

aral commented Dec 20, 2015

So to try and understand this, let’s say that my app has 10 Carthage frameworks, each of which rely on 10 other frameworks. According to the ‘as-designed’ flow, do I have to go through all 10 of my app’s dependencies, find their 100 dependencies and add those frameworks to the app?

In the scenario the original poster posited (which is also what I just tried as it feels natural from having worked with Node.js, for example), I would expect Carthage to do this grunt work for me.

Will think about this a little to see how this workflow could be supported.

Thoughts?

@mdiep
Copy link
Member

mdiep commented Dec 20, 2015

According to the ‘as-designed’ flow, do I have to go through all 10 of my app’s dependencies, find their 100 dependencies and add those frameworks to the app?

All of these dependencies will end up in the top-level Carthage/Build/$PLATFORM directory. So you just need to drag-and-drop them into Xcode. You don't really need to find them.

@aral
Copy link
Contributor

aral commented Dec 20, 2015

Hey Matt, you’re right, I wasn’t precise in my summary. However, the first time I remove one of the frameworks, I do have to sift through all the frameworks to find which sub-frameworks I must also remove.

I realise we’re not working with a great system to begin with but this really violates how I expect dependency management to work. (e.g., On a platform with functional dependency management like Node, I can’t even imagine manually keeping track of sub-dependency trees like this — it just wouldn’t be practical given how many packages and packages within packages you end up with in npm.) That said, talk is cheap and I’m not sure how we can fix this while staying through to Carthage’s philosophy of not altering the rest of the project. Going to play with it tonight; will let you know if I come up with anything :)


A quick update: (this might become a PR for the readme as it’s not clear from reading it) — for frameworks that have their own dependencies, it seems like the Run Script stage (copy-frameworks) is the only thing necessary to get an app to run.

So far, I haven’t been able to get things to work by update the Runpath Search Paths (which I thought was the issue).


I just tried it on a new project that has the following structure:

Main App: Cartfile
       |_ Framework A
               |_ Framework A: Cartfile
                       |_ Framework B

In Main App, I’ve only added Framework A. In Framework A, I have a Copy Files phase that copies Framework B to Frameworks.

And that works, without adding Framework B to the Main App.

In the scenario that wasn’t working, I had a workspace that had FrameWork A as a project in it and I was adding it to a demo app that was in the same workspace without using Carthage.

I’m going to stop here before I confuse myself and anyone else any further but I wanted to document that, at least when you use Carthage for all dependencies (as I am in my new project), embedded sub-frameworks appears to just work (at least when codesigned by the same entity, I haven’t tested with 3rd-party frameworks). Hope this helps someone else; I’ll try and document this more properly in a post on the Ind.ie forum.

@mdiep
Copy link
Member

mdiep commented Dec 20, 2015

embedded sub-frameworks appears to just work

Using embedded frameworks loses a number of the benefits of dependency management, as noted here, here, and here. (And probably other places as well.)

@aral
Copy link
Contributor

aral commented Dec 21, 2015

Thanks for those links, Matt. Good points (I’d read the one on this thread already and understand the reasoning.)

I wonder if we can modify copy-frameworks so that it recursively looks through all sub-frameworks and “moves” them to the main app bundle folder. (In effect automatically doing what we’re supposed to be doing manually at the moment and thus abstracting this step from the developer.) The usual caveats regarding different framework versions would apply, of course, and Carthage should fail if two incompatile versions of a framework are found (e.g., 1.x & 2.x). I’m not sure if this would lead to any other issues — I might have to brave ReactiveCocoa (shudder) and try it out :)

I wrote a bit more about all this (with screenshots of my project structure) at https://forum.ind.ie/t/carthage-embedded-frameworks-within-frameworks/1040/1

Thanks again for your feedback, appreciate it.

@solidcell
Copy link

solidcell commented May 16, 2016

I'm still not clear on how to build a framework that has dependencies of its own. How can you develop/build it without it being in a main app where you finally carthage bootstrap and link all the frameworks?

Without doing that, how should you be able to open a framework on its own to develop and build it?

In the README, the only relevant parts that I can see are:

In rare cases, you may want to also copy each dependency into the build product (e.g., to embed dependencies within the outer framework, or make sure dependencies are present in a test bundle). To do this, create a new “Copy Files” build phase with the “Frameworks” destination, then add the framework reference there as well.

Which is the only part that seems to be not be testing/private-dependency-specific. However, what would the "framework reference" be, if we aren't supposed to be building and embedding the nested dependency frameworks within the framework?

Also, possibly relevant:

Carthage will only build Xcode schemes that are shared from your .xcodeproj.

However, this seems to be referring to schemas for your own framework, and not dependent framework schemas. Although, if it is also intending to mean dependent .xcodeprojs from nested dependencies, how should you have them in the first place?

I also read over #844 and it didn't help my confusion. I came across this Thoughtbot article and I tried it (omitting the CocoaPods step) and building was unsuccessful. It couldn't find the dependent module at an import XXXX statement.

@mdiep
Copy link
Member

mdiep commented May 16, 2016

Without doing that, how should you be able to open a framework on its own to develop and build it?

Most people have an xcodeproj that builds the framework with a shared scheme and a xcworkspace that includes that project and any dependent projects. You can look at any number of open source projects for an example, like ReactiveCocoa or Tentacle.

This allows building, through the workspace, of a working copy of the framework. But it also allows consumers to include the project in their workspace and use different versions of the dependencies.

Although, if it is also intending to mean dependent .xcodeprojs from nested dependencies, how should you have them in the first place?

You can build the dependencies with carthage build or by using carthage bootstrap --no-build and including the projects in your framework's xcworkspace.

I came across this Thoughtbot article and I tried it (omitting the CocoaPods step) and building was unsuccessful. It couldn't find the dependent module at an import XXXX statement.

That article is a good one.

I think you just need to add your dependencies to your frameworks workspace.

@solidcell
Copy link

Thanks @mdiep. I tried it again and I got that issue resolved. I think the first time I may have added the xcodeprojs nested within the main xcodeproj in the navigator, rather than top-level. Thanks again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants