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 !
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!
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)
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.
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 ?
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.
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.
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.
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 :
Give it a name, mine will be myFramework. Once your workplace is ready, add a new target to it :
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.
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 :
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 :
pod init
vi Podfile
pod 'GoogleTagManager', '~> 6.0.0'
pod install
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.
On the below image, click on 1, 2, 3, if everything went well, you should see the frameworks/libs you just added on 4.
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 :
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 !).
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 !
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.
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) :
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.
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):
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 :
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With