Cocoapods: Allow podfile to specify if dependency should be statically linked

Created on 12 Feb 2018  ·  16Comments  ·  Source: CocoaPods/CocoaPods

Thanks for adding support for static frameworks in v1.5.0! 🌈

I'd like to switch several of my dependencies to be statically linked, but I'm not sure if it safe for the library authors to make this change for everyone.

Here is a discussion where Keychain-Swift is weighing the pros/cons: https://github.com/evgenyneu/keychain-swift/issues/74

Rather than having the library writer force static or dynamic, can we have the user of the pod choose?

hard enhancement discussion

Most helpful comment

An easy but non-standard solution to choosing between static or dynamic linking is adding the following to the Podfile (I've tried and used it with CP 1.4):

use_frameworks!
...
dynamic_frameworks = ['PromiseKit']

# make all the other frameworks into static frameworks by overriding the static_framework? function to return true
pre_install do |installer|
  installer.pod_targets.each do |pod|
    if !dynamic_frameworks.include?(pod.name)
      puts "Overriding the static_framework? method for #{pod.name}"
      def pod.static_framework?;
        true
      end
    end
  end
end

We enable the use of frameworks and then make us of the static_framework? ruby function to force all the modules (except a list we define) to be compiled as static frameworks.

Curious to see what the CocoaPods team think about this. It's probably not their first recommendation, but I'm putting it out here just for the discussion.

All 16 comments

This issue is a precursor to a larger refactor we would like to do in potentially a CocoaPods 2.0 version in which we allow app developers to choose how to pack and link a given pod (of course with some limitations and input from the pod author).

I can keep this issue open since we do not have any other issue that closely asks for this, however, I do not anticipate any immediate work on this.

An ugly solution is to publish a separate podspec that uses the static framework flag. It's the best I can give you for now.

Just for reference, this library can be used in the meantime: https://github.com/keith/swift-staticlibs

An easy but non-standard solution to choosing between static or dynamic linking is adding the following to the Podfile (I've tried and used it with CP 1.4):

use_frameworks!
...
dynamic_frameworks = ['PromiseKit']

# make all the other frameworks into static frameworks by overriding the static_framework? function to return true
pre_install do |installer|
  installer.pod_targets.each do |pod|
    if !dynamic_frameworks.include?(pod.name)
      puts "Overriding the static_framework? method for #{pod.name}"
      def pod.static_framework?;
        true
      end
    end
  end
end

We enable the use of frameworks and then make us of the static_framework? ruby function to force all the modules (except a list we define) to be compiled as static frameworks.

Curious to see what the CocoaPods team think about this. It's probably not their first recommendation, but I'm putting it out here just for the discussion.

It's a nice hack IMO ;)

There hasn't been any activity on this issue recently. Due to the high number of incoming GitHub notifications, we have to clean some of the old issues, as many of them have already been resolved with the latest updates.

Thanks for posting @bpoplauschi! I've spent hours trying to figure this out and your solution worked for me.

This is what worked best for my project for others still looking at this-

use_frameworks!
$static_framework = ['Flipper']

pre_install do |installer|
    installer.pod_targets.each do |pod|
        if $static_framework.include?(pod.name)
            pod.host_requires_frameworks = false
        end
    end
  end

Update for CP 1.7+ - I tried @dmac81's solution, but it didn't work (all frameworks were still built as dynamic). What worked for me was

pre_install do |installer|
  installer.pod_targets.each do |pod|
    if $static_framework.include?(pod.name)
      def pod.build_as_static_framework?;
        true
      end
    end
  end
end

This is the same principle as above, just taking into account the deprecation of static_framework in favour of build_as_static_framework from CP 1.7 - https://www.rubydoc.info/gems/cocoapods/Pod/Target:static_framework%3F

@bpoplauschi's updated solution still didn't work for me (Xcode 11 beta 4 + CP 1.7.5) :(

We have just upgraded from 1.6.x to 1.7.3 and using the new build_as_static_framework seems to work partially where the static_framework was working fine. With 1.7.3, the framework's code does seem to be getting linked statically into the main binary, but the framework's resources are not being copied over to the main directory and are therefore not found at runtime in the main bundle.

We are actually seeing the framework getting embedded in the app's Frameworks directory (which is odd, since it's not a dynamic framework - running file on the binary reveals that it is an ar archive) while the Copy Pods Resources phase is missing all of the framework's resources.

This is all using Xcode 10.2.1.

Is this expected behavior? It feels like if the framework's code is getting linked into the main binary, the resources should follow suit? As it is right now, it feels like the framework is part dynamically linked, part statically.

@FarAndHeight @hujunfeng you are probably right. After all, we are hacking around the built in CP features, so it's expected that as some point, things will go south.
Maybe someone from the @orta or someone else from the CP team can comment on why this behaviour has changed and how / if we can update the hack.
(in my case, I am using Rome which means CP is used to build the binaries and we link them manually, that is why this issue hasn't been a problem to me).

I noticed that there are some duplicated file linker errors that show up when overriding build_as_static_framework?. This is due to some configuration that occurs based on the actual underlying build_type.

Here is what fixed this for me. Riffing off of @bpoplauschi

pre_install do |installer|
  installer.pod_targets.each do |pod|
    if $static_framework.include?(pod.name)
      def pod.build_type;
        Pod::Target::BuildType.static_framework
      end
    end
  end
end

@bpoplauschi @dmac81 : I am using Cocoapods 1.8.4 and neither of these solutions work. Do you have any pointers?

@dnkoutso

An ugly solution is to publish a separate podspec that uses the static framework flag. It's the best I can give you for now.

What do you mean by "publish a separate podspec". Can you please elaborate?

Part of this will be available in 1.9 but not on a per-pod basis and rather on on a global basis through use_frameworks!(:linkage => :static)

An easy but non-standard solution to choosing between static or dynamic linking is adding the following to the Podfile (I've tried and used it with CP 1.4):

use_frameworks!
...
dynamic_frameworks = ['PromiseKit']

# make all the other frameworks into static frameworks by overriding the static_framework? function to return true
pre_install do |installer|
  installer.pod_targets.each do |pod|
    if !dynamic_frameworks.include?(pod.name)
      puts "Overriding the static_framework? method for #{pod.name}"
      def pod.static_framework?;
        true
      end
    end
  end
end

We enable the use of frameworks and then make us of the static_framework? ruby function to force all the modules (except a list we define) to be compiled as static frameworks.

Curious to see what the CocoaPods team think about this. It's probably not their first recommendation, but I'm putting it out here just for the discussion.

I managed to change a good percentage of my frameworks from dynamic to static. However, when trying to send the binary do AppStore the following error is displayed:
"”Invalid Bundle Structure — The binary file ‘[NAME_OF_STATIC_FRAMEWORK]’ is not permitted. Your app can’t contain standalone executables or libraries, other than a valid CFBundleExecutable of supported bundles. Refer to the Bundle Programming Guide at https://developer.apple.com/go/?id=bundle-structure for information on the iOS app bundle structure.”"
Did this happen to anybody?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

soleares picture soleares  ·  3Comments

Mingmingmew picture Mingmingmew  ·  3Comments

steffendsommer picture steffendsommer  ·  3Comments

iosdev-republicofapps picture iosdev-republicofapps  ·  3Comments

dawnnnnn picture dawnnnnn  ·  3Comments