Moya: Combine support mega-thread.

Created on 1 Oct 2019  路  21Comments  路  Source: Moya/Moya

We've introduced Combine support in 14.0.0-beta.1 and unfortunately we introduced some linking issues with that. Unfortunately, it doesn't seem to be an easy fix as we have to do a lot of workarounds to not crash your app:

Carthage

When adding -weak_framework Combine linker flags it's actually working great on both debug and release builds! Oh, unfortunately only on Xcode 11. On Xcode 10 we created a compatibility script that you would need to use instead of normal carthage update/bootstrap:

carthage update Moya --platform iOS --no-build
./Carthage/Checkouts/Moya/scripts/carthage-xcode10-compat.sh
carthage build Moya --platform iOS

CocoaPods

It works good, but only in debug builds. It's correctly weak-linking the Combine library but when you archive your project, it suddenly links strongly. I'm no expert to know why would that happen, but I guess it might be something with automatic linking? Not sure.

We also have a workaround for that: adding s.pod_target_xcconfig = { 'OTHER_LDFLAGS' => '-weak_framework Combine' } to the .podspec works (probably s.weak_framework would work as well) but it breaks Xcode 10 compatibility as well. I haven't found a way of adding a workaround for Xcode 10 yet.

Swift Package Manager

It also works good in debug builds, but doesn't on release builds. Unfortunately the only workaround for release builds I found is using linker flags in swift build phase:

swift build -c release -Xlinker "-weak_framework" -Xlinker "Combine"

It's really bad in terms of user experience and also not sure how to pass that to the Xcode-SPM integration. SPM has ability to pass flags but weak linking isn't something we can pass right now so it will take some time to introduce it (FWIW there is a way to strongly link a framework so there is a hope).

Next steps

I'm still investigating possible options but we might just remove Combine support from version 14 and introduce it in later on.

Note: If anyone has more experience in this area and could help I would really appreciate that. I'll keep you all posted on my findings as well.

discussion

Most helpful comment

I'm here with an update for our Combine extensions! So it seems like the issue with weak-linking is fixed in Xcode 11.5, and so we just merged the PR with bringing back the Combine extensions in #2024 (kudos to @MaxDesiatov). Now, given it's in development branch already, I'm gonna play with it and look for any documentation updates that need to be done, but I will want to release it as soon as possible with Moya 15 early access. Will keep y'all posted 馃殌

All 21 comments

An update: I was talking with Ankit from SwiftPM and basically it seems like a bug in Combine framework (see linked FB issue) and there is nothing we can do more for SwiftPM integration in terms of workarounds, unfortunately.

I will be most likely removing Combine extensions from Moya for now and maybe introduce it later on, but not sure yet.

Just release 14.0.0-beta.4 which removes Combine support & thus removed runtime crashes on iOS lower than 13.

In case I want to use iOS 13 as target (without support for lower versions), is it 'combine' compatibility possible to be used in any commit?

@Alexdelgadodiaz We removed it in beta.4, so if you want _any commit_ then previous beta versions would work, but I suggest you just go to this PR and copy these 3 files to your project:

  • AnyPublisher+Response.swift
  • MoyaProvider+Combine.swift
  • MoyaPublisher.swift

We will probably introduce the support in next major version of Moya, which will require Xcode 11+, and the code should be the same as these 3 files in there so when you upgrade you would just replace your classes with the ones bundled in Moya.

@sunshinejr any updates on this?

@vg-identance ah good bump, we should be good to go with introducing Combine now that we released version 14. Though I didn't have time to take care of that yet, but if anyone is up for opening a PR, I'd be happy to help.

I'm here with an update for our Combine extensions! So it seems like the issue with weak-linking is fixed in Xcode 11.5, and so we just merged the PR with bringing back the Combine extensions in #2024 (kudos to @MaxDesiatov). Now, given it's in development branch already, I'm gonna play with it and look for any documentation updates that need to be done, but I will want to release it as soon as possible with Moya 15 early access. Will keep y'all posted 馃殌

@sunshinejr
There is some problem when working in the development branch with SwiftUI.
The live preview failed because it's not finding the Foundation.Process.
I think it is because maybe PackageConfig uses another version of swift.

`class 'Process' does not exist in module 'Foundation'


SchemeBuildError: Failed to build the scheme "SampleApp"

class 'Process' does not exist in module 'Foundation'

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:2:14: error: class 'Process' does not exist in module 'Foundation'
import class Foundation.Process
^ ~~~
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:8:17: error: use of unresolved identifier 'Process'
let process = Process()
^~~
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:46:37: warning: 'encodedOffset' is deprecated: encodedOffset has been deprecated as most common usage is incorrect. Use utf16Offset(in:) to achieve the same behavior.
return String($0.prefix(comment.encodedOffset))
^

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/Error.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:2:14: error: class 'Process' does not exist in module 'Foundation'
import class Foundation.Process
^ ~~~

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/Loader.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:2:14: error: class 'Process' does not exist in module 'Foundation'
import class Foundation.Process
^ ~~~

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/Package.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:2:14: error: class 'Process' does not exist in module 'Foundation'
import class Foundation.Process
^ ~~~
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/Package.swift:10:23: error: use of unresolved identifier 'Process'
let process = Process()
^~~
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/Package.swift:36:23: error: use of unresolved identifier 'Process'
let process = Process()
^~~
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/Package.swift:52:23: error: use of unresolved identifier 'Process'
let process = Process()
^~~

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/PackageConfig.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:2:14: error: class 'Process' does not exist in module 'Foundation'
import class Foundation.Process
^ ~~~

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/PackageConfiguration.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:2:14: error: class 'Process' does not exist in module 'Foundation'
import class Foundation.Process
^ ~~~

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/Writer.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/PackageConfig/DynamicLibraries.swift:2:14: error: class 'Process' does not exist in module 'Foundation'
import class Foundation.Process
^ ~~~

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/ExampleConfig/ExampleConfig.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/PackageConfig/Sources/ExampleConfig/ExampleConfig.swift:2:17: error: no such module 'PackageConfig'
import protocol PackageConfig.PackageConfig
^

Compile Swift source files:
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')
warning: Could not read serialized diagnostics file: Invalid File: Invalid diagnostics signature (in target 'RocketLib' from project 'Rocket')

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/Rocket/Sources/RocketLib/Executors/ScriptLauncher.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/Rocket/Sources/RocketLib/Executors/ScriptLauncher.swift:31:22: error: use of unresolved identifier 'run'
let output = run(bash: runnableContent)
^~~

Compile /Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/Rocket/Sources/RocketLib/Executors/SwiftScriptExecutor.swift:
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/Rocket/Sources/RocketLib/Executors/SwiftScriptExecutor.swift:39:33: error: use of undeclared type 'Process'
func launchProcess(process: Process)
^~~
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/Rocket/Sources/RocketLib/Executors/SwiftScriptExecutor.swift:43:33: error: use of undeclared type 'Process'
func launchProcess(process: Process) {
^~~
/Users/roiperetz/Library/Developer/Xcode/DerivedData/SampleApp-gxjhynhkilkypvbnpclbigrpeqtl/SourcePackages/checkouts/Rocket/Sources/RocketLib/Executors/SwiftScriptExecutor.swift:26:23: error: use of unresolved identifier 'Process'
let process = Process()
^~~
`

hey @roip890, thanks for reporting this one! cc @f-meloni

This is a really interesting one, will look into it

@roip890 can you please give me some steps to reproduce it?
I've tried to reproduce the problem on development and it looks working

@f-meloni

Steps:
1) Open SwiftUI project.
2) Add Moya (on development branch) with SwiftPackageManager.
3) Try to look at the ContentView preview, and it is not working.

The app is running well, but the live preview is failing through all the app views.
When I tried to add Moya on master branch, everything working correctly.

Here, take a look at the error, I think that PackcageConfig cause the problem.

It says the error it is on the line import class Foundation.Process from file PackageConfig/Sources/PackageConfig/DynamicLibraries.swift.

I think it is because the class Foundation.Process is only for MacOS and not for iOS.
Screen Shot 2020-06-16 at 23 58 41

I聽tried it now again to make sure it happen.
(It happened on MacOS Catalina 10.15.5, with XCode 11.5)

@f-meloni

BTW I just tried to add Moya with development branch using CocoaPod and it is working fine.
It happened only when using SwiftPackageManager.

I've seen issues like this with SwiftPM-integrated projects with live previews before, not just with Moya. Seems like live preview builds somehow get a different set of build flags than the proper SwiftPM builds. I'd recommend reporting this as an Xcode bug on FB with a small project to reproduce, preferrably publicly hosted on GitHub so that other people outside Apple could refer to it.

Moya 15.0.0-alpha.1 is released! Just tested the Xcode preview issue with SwiftPM and it's not there on the release tag (though on develop it's still the case). Let me know how it works 馃殌

@sunshinejr did the change to Package.swift that I did to make it work with SPM on iOS didn't work then?

@f-meloni so I pulled develop after your changes and I still got that Process error on iOS previews, but then figured that the Rocket won鈥檛 be there on release tags and so I just released it anyways. You can try yourself by using the develop branch and try to use Xcode Previews.

I'm probably missing something:
Here what I did:

  • Open Xcode Preview
  • Create a new iOS App
  • Add Moya (branch development) to the project as Package dependency
    Schermata 2020-07-19 alle 16 13 16

  • Import Moya and use it

Schermata 2020-07-19 alle 16 21 12

  • Build the project

While it still downloaded Rocket as dependency (the I can probably revert my changes because looks like it didn't do anything), it also builded, run and tested without any problem.
Schermata 2020-07-19 alle 16 13 42
Simulator Screen Shot - iPhone SE (2nd generation) - 2020-07-19 at 16 23 45

@f-meloni did you try to enable the Previews? On the screenshot with Preview on the right hand, you have it disabled - tap on Resume should result in a build error. Though it might just be the SwiftPM issue.

@sunshinejr sorry for the delay.
On that project I can also enable the preview and looks working anyway.

Schermata 2020-07-28 alle 00 34 53

Did I miss anything else? (it could just be that works on xcode 12 but not on 11)

Any updates to release in master?

Was this page helpful?
0 / 5 - 0 ratings