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

Add support for Swift Package Manager (SPM) as the dependency manager for iOS #587

Open
sja26 opened this issue Jan 24, 2023 · 20 comments
Open

Comments

@sja26
Copy link

sja26 commented Jan 24, 2023

Introduction

Deprecate Cocoapods as the dependency manager for iOS, and add support for Swift Package Manager (SPM) as its successor.

Details

There is growing evidence that SPM is on a trajectory to replace Cocoapods as the most popular dependency manager for iOS apps.

I surmise that Cocoapods, being the only option to integrate React Native into an iOS app, is preventing a significant and growing number of iOS developers from trying out React Native.

Some background:

  • Cocoapods was released in 2011 written in Ruby, it became the de facto dependency manager for iOS apps.
  • SPM was released as part of Swift 3.0 in 2016.

References:

Discussion points

Focusing on the data gathered by JetBrains' most recent annual survey for Swift and Objective-C developers in 2022. The results of 3 questions stand out as the main motivators to address the aforementioned trajectory of SPM.

Do you use Swift and Objective-C together in the same project?

  • 62% Yes, most of my codebase is in Swift
  • 23% Yes, most of my codebase is in Objective-C
  • 16% No

Which dependency manager do you use?

  • 61% CocoaPods
  • 47% Swift Package Manager
  • 18% None
  • 11% Carthage
  • 1% Other

Do you plan to replace CocoaPods dependencies with SPM packages?

  • 48% No, and I don’t plan to in the next 12 months
  • 28% Yes
  • 24% No, but I plan to in the next 12 months
@nplasterer
Copy link

Just wanted to add some support for this discussion. We've been running into a lot of issues with trying to create a react native bridge to iOS for a react native SDK. The main issue is that our iOS SDK cannot be made into a cocoapod as one of our dependencies has dropped cocoapod support entirely https://forums.swift.org/t/swiftnio-is-dropping-support-for-cocoapods/56840. This has made it impossible for us to offer our integrators a RN SDK.

@cipolleschi
Copy link

Hi @sja26 and @nplasterer. Thank you for starting this proposal, it is very valuable indeed.

Swift Package Manager is something we are looking into as well, but currently it is not a feasible path for us.

The major blocker for this effort is that SPM, right now, does not support packages with mixed languages. A package in SPM must be only in Swift, Objective-C or C++. Whenever you try to mix them, SPM raises an error.

Other blockers:

  • we are using Cocoapods also to run some scripts that prepares the project properly (setting the right flags) and that run Codegen.
    • Mitigation: both problems could be mitigated with the new Swift Plugins, but they are still in an unstable state (e.g.: it is not easy to integrate them with an App, see here, for example).
  • the current structure of React Native does not work well with the structure suggested by SPM.
    • Mitigation: refactoring React Native and using the various SPM property in more advanced ways.

I would love to support you if you want to make some experiment with SPM to see how far you can go. I tried a couple of times in the past with not a lot of success, but I'm periodically try this path again because, as you, I also think that we should move to SPM at some point in the future.

@sja26
Copy link
Author

sja26 commented Apr 12, 2023

Hi @cipolleschi, thank you for the summary.

Regarding the major blocker:

A package in SPM must be only in Swift, Objective-C or C++. Whenever you try to mix them, SPM raises an error.

Yes, but only if you want the package to include the source code. If not including the source code is acceptable then a binary framework can be distributed as a Swift package, which can be accomplished in two phases:

  1. Creating an XCFramework
    Creating a multiplatform binary framework bundle (Apple Developer Article)
    Binary Frameworks in Swift (WWDC19)
  2. Distributing as a Swift Package
    Distribute binary frameworks as Swift packages (Apple Developer Article)
    Distribute binary frameworks as Swift packages (WWDC20)

There are trade-offs when using binary frameworks as explained in the above resources, therefore this could be seen as an interim solution until SPM evolves to meet your original desire to include the source code in the Swift package.

we are using Cocoapods also to run some scripts that prepares the project properly (setting the right flags) and that run Codegen.

I wonder if using Gradle could be a suitable replacement see Building Swift projects?

the current structure of React Native does not work well with the structure suggested by SPM.

Could you elaborate further with an example please?

Fingers crossed WWDC23 will deliver more SPM improvements.

@cipolleschi
Copy link

If not including the source code is acceptable then a binary framework can be distributed as a Swift package.

@sja26 currently, React Native can't be really built as an isolated framework. 😅 The technical detail here is that the App is responsible to generate the code of Codegen, so that code won't be available in advance so that we can prepare a framework with it.
So the idea of shipping it as an XCFramework is not feasible as well. 😞

Also, I feel that being able to step into the React Native code is extremely useful for contributors that have to debug their libraries. We would like to keep this option open in the foreseeable future.

I wonder if using Gradle could be a suitable replacement see Building Swift projects?

Second problem, React Native internals can not be implemented in Swift. The core engine of React Native is in C++ and there is not full interoperability between Swift and C++. We should pass through Objective-C++, but the extra layer could have some performance implication (that we still have to test, thought) but that, so far, we haven't had time to explore. So, currently, we can't use Gradle to build Swift projects. However, it looks interesting and it could be interesting to use the same tooling for both Android and iOS.

Could you elaborate further with an example please?

React Native is structured as it follows (simplifying it):

  • React (with iOS code)
  • ReactCommon (with C++ code)

Within those folders, there are subfolders which are separated modules.
In some of those subfolders, there are folders called platform that contains ios, android and cxx folders for the specific platforms.

A simple difference between the structure suggested by SPM and our current structure is the absence of the Sources and Tests folders, for example. Also, we have nested folders that should be extracted in their own packages.

I'm not saying that it is not doable, but currently, this work will require a lot of effort and it will break several things also internally at Meta.

@chuckluck44
Copy link

chuckluck44 commented May 9, 2023

@cipolleschi For clarification Swift Packages do support mixed languages without use of frameworks. The only requirement is that Swift and any C family languages be separated into their own folders/targets, but they ship as one product and the code can be stepped through normally.

Our own SDK is shipped as a swift package that uses a mix of swift, C, and C++. Swift is able to call C++ by wrapping calls in a C header in the c++ target, but Swift/C++ interoperability is also in the works https://github.com/apple/swift/tree/main/docs/CppInteroperability . You also have the option of creating a package completely in obj-c, c, and/or c++ (c++ requires a c or objc wrapper at the moment). In that case everything could be contained in a single target.

Regarding the folder structure issues, symlinks may work as a temporary fix. This is the approach we took when initially moving from an xcframework to swift package distribution. But that hasn't been tested in a while.

I understand this is a pretty large undertaking. There just isn't much of reason for newer iOS sdks to support CocoaPods st this point since SPM ships with xcode, can work in conjunction with both Carthage and CocoaPods (assuming there aren't any duplicated dependencies requirements across the graphs), and is much more tightly integrated with the ecosystem. Our SDK is only going to support cocoa pods because we have many integrators in our space that use react native and we need to distribute a node module, but other teams may just forgo supporting at all.

@cipolleschi
Copy link

@chuckluck44 I understand what you are saying. I completely agree that it is something important, but currently, we don't have much capacity to work on this, unfortunately.

The only requirement is that Swift and any C family languages be separated into their own folders/targets,

That's not the case, currently. For example, see the ImageManager. As you can see, in this folder we have a mixture of cpp and mm files. Restructuring the structure of the framework is a delicate task as React Native is also used internally, where we have yet another build system and dependency manager.
We need to make sure that all the changes we make for the OSS do not break the internal system and viceversa.

There just isn't much of reason for newer iOS sdks to support CocoaPods st this point since SPM ships with xcode, can work in conjunction with both Carthage and CocoaPods (assuming there aren't any duplicated dependencies requirements across the graphs), and is much more tightly integrated with the ecosystem. Our SDK is only going to support cocoa pods because we have many integrators in our space that use react native and we need to distribute a node module, but other teams may just forgo supporting at all.

I completely agree with this point, and we received the feedback also from other partners. The problem is slightly wider than this though.

We don't only use Cocoapods to install dependencies, but is also running some other tasks: it configures build flags to make sure that the project is properly configured and it runs some code generation tasks.
Of course, we can move both of these tasks to some script that SPM can invoke, especially now with the Xcode Plugins, but this is extra work that we need to plan for.

@chuckluck44
Copy link

chuckluck44 commented May 11, 2023

@cipolleschi Thanks for the quick response. So the ImageManager example would actually work with its current folder structure. As the SPM Docs mention, its only swift and C-family languages that need to be separated:

Targets can contain Swift, Objective-C/C++, or C/C++ code, but an individual target can’t mix Swift with C-family languages. For example, a Swift package can have two targets, one that contains Objective-C, Objective-C++, and C code, and a second one that contains Swift code.

That being said, I have total respect for your teams current situation. Folder structure is obviously just a single aspect of migrating a project of this size/complexity. I only wanted to surface the fact that a majority of your mixed language issues may not be a problem anymore.

@cipolleschi
Copy link

@chuckluck44 Thank you for your suggestion and understanding. Improving the setup is on our radar. We really hope we would be able to prioritize it soon!

@ScottRobbins
Copy link

Fwiw, there is an open proposal about mixed language support here.

I suspect not much will gain traction for SPM until the fall, but it may be worth the RN team to review if this would achieve what they need when time allows.

@cipolleschi
Copy link

Thanks for the link! Will take a look as soon as I can!

@mfazekas
Copy link

mfazekas commented Oct 26, 2023

FWIW there is an RFC to Cocapods repo to support SPM via cocoa pods.

See CocoaPods/CocoaPods#11942 and related PR: CocoaPods/Core#743

@cipolleschi
Copy link

FWIW there is an RFC to Cocapods repo to support SPM via cocoa pods.

See CocoaPods/CocoaPods#11942 and related PR: CocoaPods/Core#743

Interesting, in this way we can start migrate our packages toward SPM incrementally.
One of the major limitation currently is that SPM does not allow to run preprocess commands. We need to run some preprocessing on pure C++ packages like Glog to make sure they are compatible with iOS, and so far that's not possible. But with this PR we can start migrating the React packages and live in a mixed world.

Unfortunately, we currently don't have bandwidth to work on this, but we know we will have to work on Swift support and SPM sometime soon.

Thanks for signalling this.

@chrisaljoudi
Copy link

What's the latest on this? Would be a pretty big win, Cocoapods is sadly one of the least ergonomic aspects of React Native development.

@cipolleschi
Copy link

No news so far. I do understand that it's a bit of a pain. It is a pain for us internally as well. Unfortunately, we don't have the resources to prioritize this work at the moment: our current priority is to ship the New Architecture to the OSS and we are hyperfocused on that.

Rolling out the New Architecture and deprecating the old one could also help us reducing the surface that cocoapods/SPM needs to cover, ultimately simplifying the work to convert Cocoapods to SPM.

We are also wait for some official support from Appe to run some custom code after a package is downloaded: we are currently adjusting some packaged (Folly and boost, for example) right after they are downloaded.
Cocoapods makes it very easy with prepare_commands.
SPM does not have anything similar. Yes, there are Build Plugins and I have experimented a bit with them. They might partially solve the problem, but the approach needs deeper testing and experimentation.

@wuguishifu
Copy link

Is there any temporary workaround for TPLs that only support SPM and don't have Pod support for latest versions of Swift? I'm trying to use AudioKit, but its Pod version is way out of date. Is there a way to manually compile and inject them?

@tsapeta
Copy link

tsapeta commented May 14, 2024

@wuguishifu In theory you should be able to fork it and publish to CocoaPods on your own or pull in this code into your library codebase (in accordance with the license of course).
The third option that comes to my mind is to make a local podspec and use GitHub as the source, i.e. combination of :podspec and :git params of the pod function (see the last section in the Podfile Syntax Reference). It's probably the best way, if only it works, I've never tried that 😅

@wuguishifu
Copy link

Thanks for the info! That sounds mostly doable. I'm pretty new to native iOS stuff (I've done mostly expo go and native android stuff) so I definitely appreciate the suggestions.

@mfazekas
Copy link

mfazekas commented May 17, 2024

I've added a POC of a library, that patches react-native so libraries can declare swift package manager dependencies.

See https://github.com/mfazekas/rn-spm-rfc-poc/

In this POC the library can define the swift package manager dependencies like this:

ReactNativePodsUtils.spm_dependency(s,  
     url: 'https://github.com/apple/swift-atomics.git', 
     requirement: {kind: 'upToNextMajorVersion', minimumVersion: '1.1.0'}, 
     products: ['Atomics'] 
   ) 

The changes to react-native are in this patch file:
https://github.com/mfazekas/rn-spm-rfc-poc/blob/main/example/patches/react-native%2B0.74.1.patch

This means that we'd still be using Cocoapods, but we'd allow rn-libraries to declare their swift package manager dependencies.

Added a separate RFC: Declare swift package manager dependencies in react-native library podspec

@cipolleschi
Copy link

cipolleschi commented May 20, 2024

@mfazekas, the React Native patch here seems pretty harmless to me, if you can open a PR against core, I'd be happy to land it.
IIUC, the goal is to let a React Native library to depends on some packages that are exposed only through SPM, right?

For example:

  • Imagine that I have a library RNAlgorithms.
  • I want for this library to depend on swift-algorithms

Right now, there is no way to achieve this.

After the patch, there is.

Is my understanding correct?

@mfazekas
Copy link

@cipolleschi yes exactly that is the goal of the patch, I'll open a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants