-
Notifications
You must be signed in to change notification settings - Fork 127
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
Comments
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. |
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:
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. |
Hi @cipolleschi, thank you for the summary. Regarding the major blocker:
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:
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.
I wonder if using Gradle could be a suitable replacement see Building Swift projects?
Could you elaborate further with an example please? Fingers crossed WWDC23 will deliver more SPM improvements. |
@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. 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.
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.
React Native is structured as it follows (simplifying it):
Within those folders, there are subfolders which are separated modules. A simple difference between the structure suggested by SPM and our current structure is the absence of the 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. |
@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. |
@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.
That's not the case, currently. For example, see the ImageManager. As you can see, in this folder we have a mixture of
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. |
@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:
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. |
@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! |
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. |
Thanks for the link! Will take a look as soon as I can! |
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. 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. |
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. |
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. |
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? |
@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). |
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. |
I've added a POC of a library, that patches 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: 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 |
@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. For example:
Right now, there is no way to achieve this. After the patch, there is. Is my understanding correct? |
@cipolleschi yes exactly that is the goal of the patch, I'll open a PR. |
…44627) Summary: React-Native uses Cocapods for native dependency management on iOS. While CocoaPods is flexible and popular, Apple's Swift Package Manager is the new standard. Currently consuming packages available only via Swift Package Manager is not possible. This change implements a single extension so .podspec files can declare Swift Package Manager dependencies via ```ruby ReactNativePodsUtils.spm_dependency(s, url: 'https://github.com/apple/swift-atomics.git', requirement: {kind: 'upToNextMajorVersion', minimumVersion: '1.1.0'}, products: ['Atomics'] ) ``` bypass-github-export-checks ## Changelog: [IOS] [ADDED] - libraries can now declare Swift Package Manager dependencies in their .podspec with `ReactNativePodsUtils.spm_dependency` Pull Request resolved: #44627 Test Plan: https://github.com/mfazekas/rn-spm-rfc-poc/ Is a simple demo for the feature: 1. Podspec declare dependency with: ```ruby if const_defined?(:ReactNativePodsUtils) && ReactNativePodsUtils.respond_to?(:spm_dependency) ReactNativePodsUtils.spm_dependency(s, url: 'https://github.com/apple/swift-atomics.git', requirement: {kind: 'upToNextMajorVersion', minimumVersion: '1.1.0'}, products: ['Atomics'] ) else raise "Please upgrade React Native to >=0.75.0 to use SPM dependencies." end ``` 2. [`import Atomics`](https://github.com/mfazekas/rn-spm-rfc-poc/blob/e4eb1034f7498dedee4cb673d327c34a6048bda2/ios/MultiplyInSwift.swift#L1C2-L1C15) and [`ManagedAtomic`](https://github.com/mfazekas/rn-spm-rfc-poc/blob/e4eb1034f7498dedee4cb673d327c34a6048bda2/ios/MultiplyInSwift.swift#L7-L13) is used in the code 3.) `spm_dependency` causes the dependency to be added via `post_install` hook in the workspace <img width="261" alt="image" src="https://github.com/facebook/react-native/assets/52435/ad6aee1c-ac88-4c84-8aa3-50e148c4f5b2"> 4.) `spm_dependecy` causes the library to be linked with `Atomics` library <img width="817" alt="image" src="https://github.com/facebook/react-native/assets/52435/bfc8dfc0-aeb7-4c75-acbd-937eab1cbf80"> Limitations: 1.) only works `USE_FRAMEWORKS=dynamic pod install` otherwise the linker fails [with known Xcode issue - duplicate link issue](https://forums.swift.org/t/objc-flag-causes-duplicate-symbols-with-swift-packages/27926) 2.) .xcworkspace needs to be reopened after `pod install` - this could be worked around by not removing/readding spm dependencies ### See also: react-native-community/discussions-and-proposals#587 (comment) react-native-community/discussions-and-proposals#787 Reviewed By: cortinico Differential Revision: D58947066 Pulled By: cipolleschi fbshipit-source-id: ae3bf955cd36a02cc78472595fa003cc9e843dd5
This is no longer true and hasn't been for a fair amount of time. SPM allows pre-build and build commands. Both are suitable for this sort of pre-build processing. SPM has come a long ways in just a few short years, I'm currently exploring the feasibility of actually vending a fork that would be SPM compatible. |
…44627) Summary: React-Native uses Cocapods for native dependency management on iOS. While CocoaPods is flexible and popular, Apple's Swift Package Manager is the new standard. Currently consuming packages available only via Swift Package Manager is not possible. This change implements a single extension so .podspec files can declare Swift Package Manager dependencies via ```ruby ReactNativePodsUtils.spm_dependency(s, url: 'https://github.com/apple/swift-atomics.git', requirement: {kind: 'upToNextMajorVersion', minimumVersion: '1.1.0'}, products: ['Atomics'] ) ``` bypass-github-export-checks ## Changelog: [IOS] [ADDED] - libraries can now declare Swift Package Manager dependencies in their .podspec with `ReactNativePodsUtils.spm_dependency` Pull Request resolved: #44627 Test Plan: https://github.com/mfazekas/rn-spm-rfc-poc/ Is a simple demo for the feature: 1. Podspec declare dependency with: ```ruby if const_defined?(:ReactNativePodsUtils) && ReactNativePodsUtils.respond_to?(:spm_dependency) ReactNativePodsUtils.spm_dependency(s, url: 'https://github.com/apple/swift-atomics.git', requirement: {kind: 'upToNextMajorVersion', minimumVersion: '1.1.0'}, products: ['Atomics'] ) else raise "Please upgrade React Native to >=0.75.0 to use SPM dependencies." end ``` 2. [`import Atomics`](https://github.com/mfazekas/rn-spm-rfc-poc/blob/e4eb1034f7498dedee4cb673d327c34a6048bda2/ios/MultiplyInSwift.swift#L1C2-L1C15) and [`ManagedAtomic`](https://github.com/mfazekas/rn-spm-rfc-poc/blob/e4eb1034f7498dedee4cb673d327c34a6048bda2/ios/MultiplyInSwift.swift#L7-L13) is used in the code 3.) `spm_dependency` causes the dependency to be added via `post_install` hook in the workspace <img width="261" alt="image" src="https://github.com/facebook/react-native/assets/52435/ad6aee1c-ac88-4c84-8aa3-50e148c4f5b2"> 4.) `spm_dependecy` causes the library to be linked with `Atomics` library <img width="817" alt="image" src="https://github.com/facebook/react-native/assets/52435/bfc8dfc0-aeb7-4c75-acbd-937eab1cbf80"> Limitations: 1.) only works `USE_FRAMEWORKS=dynamic pod install` otherwise the linker fails [with known Xcode issue - duplicate link issue](https://forums.swift.org/t/objc-flag-causes-duplicate-symbols-with-swift-packages/27926) 2.) .xcworkspace needs to be reopened after `pod install` - this could be worked around by not removing/readding spm dependencies ### See also: react-native-community/discussions-and-proposals#587 (comment) react-native-community/discussions-and-proposals#787 Reviewed By: cortinico Differential Revision: D58947066 Pulled By: cipolleschi fbshipit-source-id: ae3bf955cd36a02cc78472595fa003cc9e843dd5
0.75-rc.6 does have facebook/react-native#44627 integrated now |
@mfazekas thank you for this PR, I used it with |
Updates from @orta of the CocoaPods team:
Sources: |
@karlhorky thanks for flagging it. We were aware of that, we had been informed already. |
@aslattum first of all, which expo SDK are you using? We introduced this feature in 0.75, so you need an SDK that supports that. USE_FRAMEWORKS=dynamic bundle exec pod install |
@cipolleschi thanks for the quick reply, so I can actually see and execute the function from looking in node modules; however, I wasn't doing |
one way would be to modify the ENV["USE_FRAMEWORKS"] = "dynamic" at the top of the |
@cipolleschi yeah that makes sense and I could do that; however, I'm working on creating a expo native module and the user of the module should only have to touch the javascript portion of the package. So I definitely can modify the Seems like I should try to find a config plugin to do it on generation then 👍🏻 |
There is already a plugin for that. Check documentation for |
@jpudysz so I was able to use this. It seems like exactly what I want. The issue I'm running into is I'm trying to create a native expo module that will be used within another react native app so I would want run this plugin on install of the package. All of the examples online show adding the config to the example project in the plugin, not at the root of the package. Any suggestions here? |
Because main app decides how to build dependencies. |
@jpudysz yeah that makes sense. Basically what I'm trying is:
The part that is getting wonky is number 2. Any good suggestions for that? |
Do you have control over those two libraries? Because if that's the case, the best solution would be to add support for Cocoapods by writing a podspec. |
@cipolleschi yeah we do have control over those libraries and I've written podspecs for them. The problem is we didn't want to push those podspecs to a public repository, but if we pushed them to the cocoapods trunk spec repo then we could reference them in the react native podspec. Is that the path you'd follow or is there another way to keep them private? Can't really reference git or path dependencies in a podspec. |
@cipolleschi in the example you give, I see that you have RCT-Folly.podspec and you are referencing that in the React-Core.podspec. How is the |
You have two alternatives:
|
@cipolleschi |
This is exciting! We've been following the development of SPM quite closely, and I have mixed feelings with SPM being the most sensible replacement within the context of React Native. Note that I might be biased, but here are some thoughts that I had regarding the idea:
While I understand the concern of depending on something that's not Apple-official, which is something we've been hearing since the day we wrote the first line of code in the generation code, Xcode projects are here to stay, and the generation logic is so battle-tested at this point, that we could extract it (in fact we are doing it), and treat that a community effort shared between Meta, Expo and Tuist. The solution would be something along the lines of:
We don't have a lot of resources to spare to implement something like this, but we'd be happy to share the context and the knowledge and figure out a model where React Native feels comfortable building on this foundation like it did when it made the decision to use CocoaPods. I think eliminating the dependency on Ruby, CocoaPods, and the need to maintain an Xcode project can significantly improve the developer experience when building React Native apps for Apple platforms. |
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:
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 Swift23%
Yes, most of my codebase is in Objective-C16%
NoWhich dependency manager do you use?
61%
CocoaPods47%
Swift Package Manager18%
None11%
Carthage1%
OtherDo you plan to replace CocoaPods dependencies with SPM packages?
48%
No, and I don’t plan to in the next 12 months28%
Yes24%
No, but I plan to in the next 12 monthsThe text was updated successfully, but these errors were encountered: