Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The 'Pods-App' target has transitive dependencies that include static binaries when using GTM in a swift framework

I'm trying to rework a part of a swift 3 project to make it a swift framework in order to use it as a pod. The thing is that I need to use GoogleTagManager legacy (v3) as a dependency and I get the following error when doing pod spec lint :

ERROR | [iOS] unknown: Encountered an unknown error (The 'Pods-App' target has transitive dependencies that include static binaries: (/private/var/folders/7t/0cd0n1gn46xd7r1cywrgcy2w0000gn/T/CocoaPods/Lint/Pods/GoogleTagManager/Libraries/libTagManager.a)) during validation.

Or, with GTM v5 :

ERROR | [iOS] unknown: Encountered an unknown error (The 'Pods-App' target has transitive dependencies that include static binaries: (/private/var/folders/7t/0cd0n1gn46xd7r1cywrgcy2w0000gn/T/CocoaPods/Lint/Pods/GoogleTagManager/Frameworks/GoogleTagManager.framework)) during validation.

I'm using the latest cocoapods version (1.1.0.rc.2) and, for what I understood, the problem comes from the dependencies of the GTM pod, which at least one is a static library.

I read some threads where people talk about workarounds to install static libraries within a framework, or to wrap static libraries into a framework when creating a pod. The thing is that the static library is a dependancy of the GTM pod, so I have no control over it. At least that's what I understood in here.

Here is my Podfile :

# Uncomment this line to define a global platform for your project
platform :ios, '8.0'

target 'MyProject' do
  # Comment this line if you're not using Swift and don't want to use dynamic frameworks
  # use_frameworks!

  # Pods for MyProject
    pod 'GoogleTagManager', '~> 3.15.2'
end

and here is my (shortened for StackOverflow) MyProject.podspec :

Pod::Spec.new do |s|

  s.name         = "MyProject"
  s.version      = "0.1.0"

  s.platform = :ios, '8.0'
  s.ios.deployment_target = '8.0'

  s.source_files  = "MyProject", "MyProject/**/*.{h,m}", "MyProject/**/*.{swift}"
  s.dependency 'GoogleTagManager', '~> 5.0.0'

end

I already tried to download the GTM SDK and install it without cocoapods, but I guess I missed a step because I couldn't make it work.

So I'd like some advices on my podspec and podfile files, or an explaination on how to install the SDK without cocoapods. Thanks a lot !

like image 933
n3wbie Avatar asked Oct 05 '16 10:10

n3wbie


People also ask

Which is necessary to import them from Swift when building as static libraries?

To opt into those targets generating module maps (which is necessary to import them from Swift when building as static libraries), you may set use_modular_headers!

What is Use_modular_headers?

The use_modular_headers option generates module maps for Swift and Objective-C pods instead of generating frameworks. See http://blog.cocoapods.org/CocoaPods-1.5.0/ Attached is an example project (AppCode 2018.1, Xcode 9.3, Swift 4.1)

What is Use_frameworks?

use_frameworks! tells CocoaPods that you want to use Frameworks instead of Static Libraries. Since Swift does not support Static Libraries you have to use frameworks.

What are the dependencies of'pods-demomeride'target?

The 'Pods-DemoMeride' target has transitive dependencies that include static binaries: (/Users/romaltandel/Desktop/Demo1/DemoMeride/Pods/google-cast-sdk/GoogleCastSDK-ios-4.3.1_static/GoogleCast.framework) Sorry, something went wrong. @Jignesh1805 did you get any solution for this ?

Is it possible to use the GTM pod with CocoaPods?

Since the GTM pod hasn't change and still has static libraries as dependencies, it is still impossible to create a framework that has the GTM pod as dependency and to distribute it through Cocoapods. I eventually decided to ignore Cocoapods and to manually add the third party frameworks to mine.

How to make a cocoapod that depends on a static library framework?

Currently the only way to make a CocoaPod that depends on a static library framework CocoaPod is to build the CocoaPod outside of a CocoaPods with a script like this and then add it to the CocoaPod as a vendored_framework. The pending pull request #6811 would add support for directly creating a static library framework from source.

What is the biggest obstruction to moving from static libraries to Swift?

Transitive dependencies is definitely the hugest obstruction by now for moving from static libraries to dynamics and from OC to swift. Sorry, something went wrong. There hasn't been any activity on this issue recently.


1 Answers

Well, after months I had to come back to this project. Since the GTM pod hasn't change and still has static libraries as dependencies, it is still impossible to create a framework that has the GTM pod as dependency and to distribute it through Cocoapods.

I eventually decided to ignore Cocoapods and to manually add the third party frameworks to mine. So this answer won't match perfectly my question but it is the only way I found to ditribute my framework.

So, the first step is to create a new project. Select Cocoa Touch Framework :

Select Cocoa Touch Framework

Give it a name, mine will be myFramework. Once your workplace is ready, add a new target to it :

how to add a new target

Click on the cross platform tab and select the Aggregate template. I'll name mine Aggregate (so much originality). But we won't care too much about it for now.

Aggregate target

Once you have done that, your workplace is set for you to create your amazing framework ! Let's dive a bit further !

You're gonna now create or copy your source files. If you copy them, don't forget to check the box Copy items if needed and be sure that they are added to the framework target :

verify the options when copying your files

Until now, no big surprise I guess.

Since my question was about GTM, that's the framework we're gonna add as a dependency for our framework. I couldn't find the source elsewhere that on Cocoapods, so I created a new project (I used the application template and named it PodApp), then I added Cocoapods to it. Open your terminal and navigate into the PodApp project folder :

  1. pod init
  2. vi Podfile
  3. Under #Pods for PodApp, add pod 'GoogleTagManager', '~> 6.0.0'
  4. Save and close the Podfile, and run pod install
  5. Once the dependency is downloaded and installed, close the PodApp project window and run open PodApp.xcworkspace

In your Framework project, add a new new Group you'll name Frameworks (right click in the Xcode Navigator -> new Group). You're gonna copy all the dependencies Cocoapods downloaded in the PodApp project under the Frameworks group of myFramework. Once again, dont forget to add it to myFramework target and to copy items if needed as seen above.

Drag and drop frameworks in your project

On the below image, click on 1, 2, 3, if everything went well, you should see the frameworks/libs you just added on 4.

xcode project

For Firebase to work well, you'll also have to link with AdSupport.framework. Simply click on the + (5), search for it, and add it.

I had some troubles with GoogleToolBoxForMac too, what I had to do was to build the PodApp project, and to copy the GoogleToolBoxForMac.framework under PodApp/Pods/Products/ in the group myFramework/Frameworks/. Remember to add it to the correct target and to copy the item.

Another tricky detail related to GTM : you have to add some resources to your framework. Follow the step on the next image :

enter image description here

On the popup that opens, click on Add Other and find where you saved the GTM.framework, expand it and add the TagManagerResources.bundle (don't forget to copy the item !).

TagManagerResources.bundle

Since the other resources may give you warnings (in this specific example), you can delete them with the -.

If you want the final user of your framework to be able to use the frameworks that yours embeds, right above the "Copy Bundle Resources", open the "Headers" panel, drag and drop all the project headers to public.

All these frameworks can now be accessed by your own framework, but to make them available, you have to import their headers in your myFramework.h :

#import <myFramework/AnObjectiveCClass.h>

#import <GoogleTagManager/GoogleTagManager.h>
#import <GoogleTagManager/TAGCustomFunction.h>

#import <myFramework/GTMDefines.h>
#import <myFramework/GTMNSData+zlib.h>

#import <FirebaseCore/FirebaseCore.h>
#import <FirebaseAnalytics/FirebaseAnalytics.h>
#import <FirebaseInstanceID/FirebaseInstanceID.h>

#import <myFramework/GAI.h>
#import <myFramework/GAIDictionaryBuilder.h>
#import <myFramework/GAIEcommerceFields.h>
#import <myFramework/GAIEcommerceProduct.h>
#import <myFramework/GAIEcommerceProductAction.h>
#import <myFramework/GAIEcommercePromotion.h>
#import <myFramework/GAIFields.h>
#import <myFramework/GAILogger.h>
#import <myFramework/GAITrackedViewController.h>
#import <myFramework/GAITracker.h>

#import <myFramework/pb_encode.h>
#import <myFramework/pb.h>
#import <myFramework/pb_decode.h>
#import <myFramework/pb_common.h>

Alright, we're almost done !

Project Info / Build Settings

Now, select the Info section of your project (1 in the image above), check the version you're building for (mine is 8.0 for backward compatibility with most devices). Under the Configurations panel, remove the Debug by selecting it, and pressing the -. Now select Build Settings (2 on the image), we're gonna make some changes in here.

  • Build Active Architecture Only : No
  • Allow Non-modular Includes In Framework Modules : No
  • Valid Architectures : arm64 armv7 i386 x86_64 (since armv7s doesn't build for some frameworks in this example)
  • Preprocessor Macros : PB_NO_PACKED_STRUCTS=1 PB_FIELD_32BIT=1
  • Enable Modules (C and Objective-C) : Yes

In your two targets, for the mentioned Build Settings, set them to $(inherited), it means that they will inherit from the parameters you just set on the Project Build Settings.

In the framework target, I also set the Other Linker Flags as what was set in the PodApp project, but I'm not 100% sure it is necessary.

(FYI, here they are : $(inherited) -ObjC -l"GoogleAnalytics" -l"c++" -l"sqlite3" -l"z" -framework "AdSupport" -framework "CoreTelephony" -framework "FirebaseAnalytics" -framework "FirebaseCore" -framework "FirebaseInstanceID" -framework "GoogleSymbolUtilities" -framework "GoogleTagManager" -framework "GoogleToolboxForMac" -framework "GoogleUtilities" -framework "JavaScriptCore" -framework "SystemConfiguration" -framework "StoreKit" -framework "AddressBook" -framework "CoreData")

Set Define Modules to yes.

Now select the Aggregate target, which will create a fat framework, a file containing several versions of our framework, one for each architecture we specified a few steps ago (arm64 armv7 i386 x86_64). We will have to tell Xcode what we want it to build. Follow the steps of the below image (1) :

image description

In the Build Phases, you want your Aggregate target to build the myFramework.framework before being built itself.

Once the myFramework.framework is added, press the + annotated as 2 in the picture, and select New Run Script Phase. Paste the following script in it :

# Merge Script

# 1
# Set bash script to exit immediately if any commands fail.
set -e

# 2
# Setup some constants for use later on.
FRAMEWORK_NAME="myFramework"

# 3
# If remnants from a previous build exist, delete them.
if [ -d "${SRCROOT}/build" ]; then
rm -rf "${SRCROOT}/build"
fi

# 4
# Build the framework for device and for simulator (using
# all needed architectures).
xcodebuild -target "${FRAMEWORK_NAME}" -configuration Release -arch arm64 -arch armv7 -arch armv7s only_active_arch=no defines_module=yes -sdk "iphoneos"
xcodebuild -target "${FRAMEWORK_NAME}" -configuration Release -arch x86_64 -arch i386 only_active_arch=no defines_module=yes -sdk "iphonesimulator"

# 5
# Remove .framework file if exists from previous run.
if [ -d "${PROJECT_DIR}/${FRAMEWORK_NAME}.framework" ]; then
rm -rf "${PROJECT_DIR}/${FRAMEWORK_NAME}.framework"
fi

# 6
# Copy the device version of framework to Framework directory.
cp -r "${SRCROOT}/build/Release-iphoneos/${FRAMEWORK_NAME}.framework" "${PROJECT_DIR}/${FRAMEWORK_NAME}.framework"

# 7
# Replace the framework executable within the framework with
# a new version created by merging the device and simulator
# frameworks' executables with lipo.
lipo -create -output "${PROJECT_DIR}/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${SRCROOT}/build/Release-iphoneos/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${SRCROOT}/build/Release-iphonesimulator/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}"

# 8
# Copy the Swift module mappings for the simulator into the
# framework.  The device mappings already exist from step 6.
cp -r "${SRCROOT}/build/Release-iphonesimulator/${FRAMEWORK_NAME}.framework/Modules/${FRAMEWORK_NAME}.swiftmodule/" "${PROJECT_DIR}/${FRAMEWORK_NAME}.framework/Modules/${FRAMEWORK_NAME}.swiftmodule"

# 9
# Delete the most recent build.
if [ -d "${SRCROOT}/build" ]; then
rm -rf "${SRCROOT}/build"
fi

# 10
# Remove .zip file if exists from previous run.
if [ -d "${PROJECT_DIR}/${FRAMEWORK_NAME}.zip" ]; then
rm -rf "${PROJECT_DIR}/${FRAMEWORK_NAME}.zip"
fi

# 11
# Zip the Framework and License all together.
zip -r "${PROJECT_DIR}/${FRAMEWORK_NAME}.zip" "${PROJECT_DIR}/LICENSE" "${PROJECT_DIR}/${FRAMEWORK_NAME}.framework"

On the script's step 2, change the name with your framework's name. Take time to understand what it does, and change its behavior if it doesn't meet your requirements. What it does in the end is that it creates a framework and an zip archive in your project's folder. I did that to link it with each of the Github release.

Everything should work fine now. Select Aggregate as the active Scheme, and run it.

Run Aggregate Scheme

If you want to add your framework in a test application, create a new Application project, simply drag and drop your myFramework.framework (from your myFramework folder for example) to your Application project. Once again, don't forget to add it to the correct target, and to copy the items. In your Application target, under the General tab, add your framework in the Embedded Binaries panel, but delete it once from the Linked Frameworks and Libraries panel since it should be there twice (check it out before doing it). Import your framework

import myFramework

and start using what you built !

NB : if you're trying with GTM, do not forget to add the stuff related to a GTM integration (your container and the GoogleInfo plist):

GTM related stuff

I really hope this will help you ! Do not hesitate to comment if anything isn't clear or if you meet a problem, I'll do my best to help you.

Please find my sources below :

  • Steps to Create Swift Framework + Cocoapods
  • cf answer of mattwritescode on 10/31/2017
  • cf answer of stewartmiles on Aug 17, 2017
  • Deep dive into Swift frameworks
  • iOS universal frameworks
  • Universal Framework in Swift
like image 102
n3wbie Avatar answered Sep 17 '22 12:09

n3wbie