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

Allow target-type specific podspec definitions #5373

Closed
mrackwitz opened this issue May 19, 2016 · 21 comments
Closed

Allow target-type specific podspec definitions #5373

mrackwitz opened this issue May 19, 2016 · 21 comments
Labels
s1:awaiting input Waiting for input from the original author t3:discussion These are issues that can be non-issues, and encompass best practices, or plans for the future.

Comments

@mrackwitz
Copy link
Member

CocoaPods target deduplication can be undesirable in some cases, when pods are used, which offer an extended API for targets, which are not limited to the extension API. In these cases, it would be desirable to use the full podspec in the app and only the limited features in the extension. This can be achieved in podspec with preprocessor conditionals, which require the presence of macros. A post_install hook would be required to define these macros for the pod targets.

The following could be a better solution to that: We could allow to specify attributes specific to iOS extensions in podspecs. This could look like that:

s.ios.extension.pod_target_xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SV_APP_EXTENSIONS' }

The analyzer could use that relatively easy as indication, that a pod can't be deduplicated across iOS apps and extensions. This could be used as well on tvOS.

This was created based on what I proposed in #5343 (comment).

/c @segiddins @neonichu @MuscleRumble

@mrackwitz mrackwitz added the t3:discussion These are issues that can be non-issues, and encompass best practices, or plans for the future. label May 19, 2016
@segiddins
Copy link
Member

I think we need a proposal on how this would work in practice to be able to evaluate it (but I'm leaning towards this is a massive undertaking for practically no benefit)

@neonichu
Copy link
Member

IMO, we already have a solution to this problem with subspecs. A pod which has a subset of functionality that works within an app extension should separate that out into one.

@tobihagemann
Copy link

I disagree, @neonichu. I don't think subspecs are suitable for this particular issue. I can already give you two real world examples:

https://github.com/SVProgressHUD/SVProgressHUD
https://github.com/google/gtm-session-fetcher

Two things have to happen in order to use these pods in a project with app extensions:

  1. Target deduplication has to be deactivated on these pods, so that APPLICATION_EXTENSION_API_ONLY is set to NO on the main target and APPLICATION_EXTENSION_API_ONLY to YES on the app extension targets.
  2. In order to avoid compile-time errors on the app extension targets, I have to set preprocessor macros. In this example, I have to set SV_APP_EXTENSIONS and GTM_BACKGROUND_TASK_FETCHING=0.

You can't just "outsource" functionality to subspecs, because they're part of the main functionality. I don't think that this is a "design flaw" by the creators of these pods. It's just the way to develop more complex applications that are using app extensions.

I think @mrackwitz's solution is clever and it would help tremendously for these kinds of projects.

@neonichu
Copy link
Member

@MuscleRumble This can also be solved by providing a subspec, though.

If we take the GTMSessionFetcher example, they could add a subspec:

s.subspec 'AppExtension' do |ap|
  ap.source_files = 
  ap.pod_target_xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) GTM_BACKGROUND_TASK_FETCHING=0' }
end

and in your Podfile you'd do:

target 'App' do
  pod 'GTMSessionFetcher'
end

target 'Extension' do
  pod 'GTMSessionFetcher/AppExtension'
end

Two distinct Pod targets would be generated and the files which are part of the AppExtension subspec would be compiled with the preprocessor definition set.

@tobihagemann
Copy link

Then you would have to duplicate code for ap.source_files = …. In this case, you wouldn't even need to set preprocessors. This is bad for code maintenance.

In this example, you would have to copy GTMSessionFetcher.m explicitly for app extensions, because the preprocessor macro GTM_BACKGROUND_TASK_FETCHING is used inline. You can't just separate the relevant code into another file, without breaking either the main functionality or making the maintenance unnecessarily complicated.

If you wouldn't do that, you'll get compile-time errors. The easiest way is to deactive target deduplication for this pod.

@neonichu
Copy link
Member

neonichu commented May 20, 2016

I don't think I understand what you're saying. There's nothing to copy, except for the definition of the list of files in the podspec, if it happens to be exactly the same as for another subspec. If that happens, it can be extracted out, since the podspec is just Ruby code.

No actual source code has to be duplicated and targets will be deduplicated automatically, because they use different sets of subspecs.

@mrackwitz
Copy link
Member Author

What @neonichu proposes here makes sense. It might be not obvious though, but subspecs allow already to apply these sorts of variations to a podspec. They are way more powerful than just adding additional files, which made it already a massive undertaking to support them correctly with target deduplication. Given that we already have a way to express that, I'd be in favor of avoiding further complexity as @segiddins already suggested. Instead a tutorial or guide around that topic would be likely very helpful.

@orta
Copy link
Member

orta commented May 20, 2016

Aye, I'm also on the side of "this is what subspecs are for."

Instead a tutorial or guide around that topic would be likely very helpful.

Would be nice to have a blog post that just explores subspecs, and some interesting uses cases ( e.g. AFNetworking, ARAnalytics, RestKit ) ( we use them for code / dependency organization in Artsy for example )

@tobihagemann
Copy link

tobihagemann commented May 20, 2016

I think I understand now how subspecs can be utilized for my use case. The thing is: There aren't a lot of frameworks/libraries that deal with app extensions properly. That's why I'm so frustrated and I have to fork/patch everything. I'll give subspecs a try, but I have two questions:

  1. How does this even work? Don't I have to "exclude" the AppExtension subspec for the main target? Because I don't want to set the preprocessor macro on the GTMSessionFetcher pod. Aren't all subspecs automatically included, when using pod 'GTMSessionFetcher'?

  2. Let's take OneDriveSDK as an example: https://github.com/OneDrive/onedrive-sdk-ios/blob/master/OneDriveSDK.podspec

    There is a subspec OneDriveSDK/Auth that has the dependency ADALiOS. In my OneDriveSDK fork (in which I'm going to create the OneDriveSDK/AppExtension subspec) I would like to distinguish between ADALiOS for my main target and ADALiOS/AppExtension (that doesn't exist yet, I'll have to fork this as well) for my app extension targets.

    I'm a little overwhelmed by how to structure the podspecs properly. How do I achieve this?

@neonichu
Copy link
Member

That's true, I don't think many podspec authors have realised that they could support application extensions quite nicely using this approach. If we do a blog post / guide and you have implemented the approach in some Pod(s), we could add that as an example and spread that knowledge this way.

To the questions:

  1. There's a default_subspecs attribute: https://guides.cocoapods.org/syntax/podspec.html#default_subspecs — but by default, all subspecs are included. Podspecs using this approach should use the attribute to make sure the app extension related subspecs aren't included by default.
  2. You can do something like:
s.subspec "AppExtension" do |oda|
    oda.dependency 'ADALiOS/AppExtension', '~> 1.2'
end

@segiddins
Copy link
Member

The real answer is to use NS_EXTENSION_UNAVAILABLE_IOS (I think that's the macro) to mark class / method declarations that are incompatible with extensions instead of using a custom macro.

@neonichu
Copy link
Member

@segiddins that only works for marking symbols, though, doesn't it? Many Pods seem to have conditional code depending on their use within an application extension.

@tobihagemann
Copy link

Alright, thanks @neonichu! I'll give it a try in the near future. Subspecs seem to be the way to go! 👍

@ConfusedVorlon
Copy link

Hi Folks - any chance of that blog post explaining how this would work?
I'm working on a pod that needs to separate out some app extension functionality. I'm not an expert on podspecs, so a 'worked example' blogpost would be great.

@DarthMike
Copy link

Hey @ConfusedVorlon, might be a little late, but I recently encountered the issue and had to fix it by using @neonichu 's suggestion. I've written the reasoning and example in my blog (here) if you're interested.

@zierka
Copy link

zierka commented Dec 8, 2016

@neonichu I'm a bit confused about the described solution here, because there's this issue #5643 where it says that it's not possible the use the same pod with different subspecs in app target and it's extension target.

See the linked issue and your comment for more information.

@tobihagemann
Copy link

Just wanted to follow-up that I can confirm it's all working out with subspecs. Yes, I waited over half a year to do the change, because I couldn't use 1.0.0.beta.3 anymore. 😂

It wasn't pretty, I had to fork 7 projects and customize the podspecs, which will probably break my neck in the future, because of poor maintainability. But it wasn't pretty to begin with, so I guess it's just the way it is with App Extensions. ☹️

Nevertheless, I think this issue can be closed, because we found a solution. 😄 Thanks again!

@zierka
Copy link

zierka commented May 9, 2017

I just can't make the proposed subspec approach work (share a subset of functionality through a subspec for an extension). I always get the famous "target has frameworks with conflicting names" error.

I reproduced the proposed approach with a simple demo.

I have a private framework with two subspecs:

  • App: for the main app target
  • Extension: for the extension target, uses some files "from" the App subspec

Here's the podspec:

Pod::Spec.new do |s|
  s.name        = 'MyFramework'
  s.version       = '0.1.0'
  s.summary       = 'Dummy Framework'
  s.homepage      = 'https://github.com/zierka'
  s.license       = 'MIT'
  s.author        = {'Zier Erik' => 'erik.interwebz@gmail.com'}

  s.source        = {:git => 'git@github.com:zierka/subspec-error-example.git', :tag => s.version.to_s}

  s.requires_arc  = true
  s.platform      = :ios
  s.ios.deployment_target = '10.0'

  s.default_subspecs = 'App'

  s.subspec 'App' do |app|
    app.source_files = [
      'App/**/*.{swift}'
    ]
  end

  s.subspec 'Extension' do |ext|
    ext.source_files = [
      'App/CoreClass.swift',
      'Extension/**/*.{swift}'
    ]

    ext.exclude_files = [
      'App/AppClass.swift'
    ]
  end

end

I have a simple xcode project, with 2 targets:

  • the main app target has the MyFramework as dependency in the Podfile
  • the extension target has the MyFramework/Extension as dependency

Here's the Podfile:

platform :ios, '10.0'
use_frameworks!

target 'SubspecProblemExample' do
  pod 'MyFramework', :path => "../MyFramework" . # equal to MyFramework/App as that's declared for default_subspecs
end

target 'todayWidget' do
  pod 'MyFramework/Extension', :path => "../MyFramework"
end

Running pod install (1.2.1) on the project, I get:

Eriks-Machine:SubspecProblemExample erik$ pod install
Analyzing dependencies
Fetching podspec for `MyFramework` from `../MyFramework`
Downloading dependencies
Installing MyFramework (0.1.0)
[!] The 'Pods-SubspecProblemExample' target has frameworks with conflicting names: myframework.

I read through all the issues about this error, and it's still not clear to me what the real problem is, because this is the same setup as previously described, and it's presented as a working solution and further confirmed. Am I missing something here?

I uploaded the full demo project here: https://github.com/zierka/subspec-error-example

Any help is greatly appreciated!

@stale stale bot added the s1:awaiting input Waiting for input from the original author label Aug 12, 2017
@stale
Copy link

stale bot commented Aug 12, 2017

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.

@stale stale bot closed this as completed Aug 19, 2017
@stale
Copy link

stale bot commented Aug 19, 2017

This issue will be auto-closed because there hasn't been any activity for a few months. Feel free to open a new one if you still experience this problem 👍

@wangpeiyan
Copy link

That is what need, thank you @neonichu neonichu

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
s1:awaiting input Waiting for input from the original author t3:discussion These are issues that can be non-issues, and encompass best practices, or plans for the future.
Projects
None yet
Development

No branches or pull requests

9 participants