Menu Home

Cocoapods: how to deal with device-only dependencies

This week I had to face a problem that I had been postponing for a long time.

Since Apple released its first MacBook with M1 processors, we developers had to start thinking about creating simulator frameworks with arm64 architecture.

That was the death sentence for fat binaries since a binary cannot contain two identical architectures: an arm64 arch for devices and an arm64 arch for simulators.

From this point, we started working with Xcframeworks: a package containing two or more platforms inside itself.

So a new problem arose: how to link devices-only libraries with simulators using cocoapods?

Before arm64 architecture for simulators, when we tried to link a device-only library in a build to simulator, the linker  simply ignored the library, because it didn’t contain any valid architecture, generating a warning and everything was just fine. But now that the arm64 architecture became a valid architecture for simulator, the linker, when trying to link the build with the device-only library, started to display the following error:

#blindPeople: the image shows a linker error, with the message: ” ld: building for iOS Simulator, but linking In dlyb built for iOS for architecture arm64″.

So we came to the problem: how to tell cocoapods that the library I’m trying to import should be used only for Devices and not for simulators? We can specify configs for a Pod, but not a platform for a configuration.

The solution: a custom podspec

Creating a custom podspec is not a difficult task, but finding the right parameters for the solution you want can be a challenge.

In this case, we need to remove the vendored_frameworks library:

#blindPeople: The image shows a podspec file, with the vendored_frameworks property selected, with the value MyDeviceOnlyFramework.framework.

When specified as a vendored framework, Cocoapods already includes the library wherever it is needed, from search path frameworks to Linker itself.

In this case, we will do the process manually, as we want a very specific configuration.

In order for Cocoapods to download and keep our library as a dependency, we need to specify a path that will be preserved during the Pod installation (in this case, our framework):

 s.preserve_paths = "MyDeviceOnlyFramework.framework/*"

Next, we need to tell Cocoapods all the additional configuration that the library needs to be used by our main project. For this, we include the xcconfig property, passing the necessary information to the configuration file that will be generated.

 s.xcconfig = {
        'FRAMEWORK_SEARCH_PATH[sdk=iphoneos*]' => '$(inherited) "$(PODS_ROOT)/MyDeviceOnlyFramework"',
        'OTHERCFLAGS[sdk=iphoneos*]' => '$(inherited) -iframework "$(PODS_ROOT)/MyDeviceOnlyFramework"',
        'OTHER_LDFLAGS[sdk=iphoneos*]' => '$(inherited) -framework MyDeviceOnlyFramework'
    }

Note that we use the  iphoneos* variant for our settings. This variant allows us to set values ​​only for devices, and no longer for simulators.

When it’s done, just upload your custom podspec to your spec repository or store it in a location that cocoapods has access, and run a pod install in your project (don’t forget the —repo-update to update yours dependencies, if necessary).

The final solution

After pod install, your dependency will display its settings as follows in Xcode:

#blindPeople: the image shows the Other Linker Flags configuration block, with the config debug and release, with the MyDeviceOnlyFramework dependency set only for the platform Any iOS SDK.

In the end, your podspec will look like this:

Pod::Spec.new do |s|

    s.name          = "MyDeviceOnlyFramework"
    s.version       = "1.0.0"
    s.summary       = "This is a Device Only Framework"

    s.description   = <<-DESC
                        Device Only Framework Podspec Example.
                      DESC
    
    s.homepage      = "http://mywebpage.xpto/MyDeviceOnlyFramework"
    s.license       = { :type => "Copyright", :file => "LICENSE" }

    s.author        = { "Andre Salla" => "contato@andresalla.com" }
    s.platform      = :ios, "10.0"

    s.source        = { :git => "http://mygitrepo.xpto/MyDeviceOnlyFramework.git", :tag => "1.0.0"}

    s.preserve_paths = "MyDeviceOnlyFramework.framework/*"

    s.xcconfig = {
        'FRAMEWORK_SEARCH_PATH[sdk=iphoneos*]' => '$(inherited) "$(PODS_ROOT)/MyDeviceOnlyFramework"',
        'OTHERCFLAGS[sdk=iphoneos*]' => '$(inherited) -iframework "$(PODS_ROOT)/MyDeviceOnlyFramework"',
        'OTHER_LDFLAGS[sdk=iphoneos*]' => '$(inherited) -framework MyDeviceOnlyFramework'
    }

end

Special thanks

I want to thank August Jaenicke, as his post helped me to get out of this complicated situation and showed me the light at the end of the tunnel.

Link: https://blog.carbonfive.com/cocoapods-for-device-only-ios-libraries/

Categories: Quick Tips

Tagged as:

André Salla