I am not sure if this is necessarily the correct approach, because I'm not sure if we can still use a single app target for "ios" and "iosmac".
We need to ship 3 .a files now instead of 2 because of the conflict for the x86_64 slice being used by both the iOS simulator and "UIKit for macOS".
# built for macOS (x86_64, i386)
s.macos.vendored_libraries = 'libs/mac/*.a'
# built for iOS and iOS simulator (armv7, arm64, x86_64, i386)
s.ios.vendored_libraries = 'libs/ios/*.a'
# built for "UIKit for macOS" (x86_64)
s.iosmac.vendored_libraries = 'libs/iosmac/*.a'
_Originally posted by @chrisballinger in https://github.com/CocoaPods/CocoaPods/issues/8877#issuecomment-499206609_
This will probably need a CocoaPods/Core DSL improvement to add the new platform. Could transfer issue there.
I'm wondering if it would be better served as an override for ios specs, because "source only" specs should be mostly okay as-is, and it would require less updates from podspec authors.
s.ios.source_files = 'Source/*.swift'
s.ios.vendored_libraries = 'libs/ios/*.a'
s.ios.vendored_libraries.iosmac = 'libs/iosmac/*.a'
vs
s.ios.source_files = 'Source/*.swift'
s.ios.vendored_libraries = 'libs/ios/*.a'
s.iosmac.source_files = 'Source/*.swift'
s.iosmac.vendored_libraries= 'libs/iosmac/*.a'
I'm not familiar with the CocoaPods internals enough to guess which would be easier to implement in practice.
As far as I'm seeing from my initial work, iosmac isn't a separate platform. As far as Xcode is concerned, it's just a deployment target for iOS apps. Imagine it to be kinda like a new 'architecture' only it can't be, since i386 is already an existing arch for the simulator. 🤦🏻♂️
For vendored frameworks, Apple invented this new 'extra fat' XCFramework format that can contain frameworks for simulator along with frameworks for Catalyst.
Perhaps the correct way forward is generating XCFrameworks. Do you know if it's possible to vendor a static library inside an xcframework bundle?
Haven't had the chance to figure out what an XCFramework actually is inside yet.
You can always vend a static framework instead of a library.
BTW, this is the relevant talk, which I haven't watched yet: https://developer.apple.com/videos/play/wwdc2019/416/
So I watched the video and according to it, it's possible to bundle an .a library into a XCFramework. They didn't specify how, however in the xcodebuild man page there is a clue:
xcodebuild -create-xcframework [-help] [-framework <path>] [-library <path> [-headers <path>]] -output <path>
In any case, adding a XCFramework seemingly creates the same project nodes as a .framework. I couldn't get it to work with vendored_frameworks because CP is very particular about what goes where.
Could this be relevant? An answer from Apple but also a comment about getting one framework working with UIKit-for-mac:
Very interesting, key point from the linked post:
Libraries coming into the UIKit for Mac environment need to be compiled for the iOS platform, not the macOS platform.
Seems like we might be able to get away with using the iOS platform - CocoaPods would need to add support for including iOS pods into macOS targets which might get tricky
These aren't macOS targets, these are iOS targets. I've managed to compile out app with lots of pods for UIKitForMac and the only things I had to rip out were vendored frameworks, because they weren't compatible.
CocoaPods needs to be modified to recognize the new .xcframework extension but that's it I think.
Right but couldn't we support integrating iOS pods into macOS targets when the deployment target of the user target is Catalina or higher? Even if a Podspec doesn't claim to support macOS, if it supports iOS 13 then it (theoretically) implicitly supports Catalyst apps on macOS. I'm not saying we _should_ do it that way but it sounds possible
I haven't figured out yet how this is supported by Xcode. Ultimately if it builds, that's good.
But the biggest use case is porting iOS targets.
I think I should close this issue and open a new one. UIKitForMac uses the iOS platform, and you can't mix a UIKitForMac target with a native AppKit target.
The issue comes from vendored libraries and frameworks needing two x86_64 slices compiled for different targets (iOS simulator and UIKitForMac). The status quo is to include a fat binary with all your slices for example libFoo.a with armv7, arm64 and x86_64 (simulator) slices. Since we can't add an additional x86_64 UIKitForMac slice to the fat static lib, we'll probably need to create an xcframework.
UPDATED
Few findings when distributing xcframework through pods.
First I've tried using podpec like this:
Pod::Spec.new do |s|
s.name = "FRAMEWORK_NAME"
s.version = "x.y.z"
s.platform = :ios
s.source = { :http => 'http://distribution_server.com:8081/repository/xi-pods/FRAMEWORK_NAME-x.y.z.zip'}
s.ios.deployment_target = '11.0'
s.ios.vendored_frameworks = ['FRAMEWORK_NAME.xcframework']
end
but I've run into multiple issues:
In generated Pods.xcconfig file, the framework was referenced in _OTHER_LDFLAGS_ in format _-framework "FRAMEWORK_NAME.xcframework"_ the extension .xcframeworks is unnecessary at this place and it needs to be referenced in format _-framework "FRAMEWORK_NAME"'_
In generated Pods.xcconfig file, the _FRAMEWORK_SEARCH_PATHS_ was set same way as for classic framework in format: _"${PODS_ROOT}/FRAMEWORK_NAME"_. But as now xcframework contains new folder structure for multiple architectures it will not be able to find the right framework for concrete architecture.
To workaround those issues I've temporarily updated Podfile to correct xcframework reference in _OTHER_LDFLAGS_
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name.match(/^Pods_*/)
puts "Updating #{target.name} OTHER_LDFLAGS"
target.build_configurations.each do |config|
unless config.base_configuration_reference.nil?
xcconfig_path = config.base_configuration_reference.real_path
# read from xcconfig to build_settings dictionary
build_settings = Hash[*File.read(xcconfig_path).lines.map{|x| x.split(/\s*=\s*/, 2)}.flatten]
# modify OTHER_LDFLAGS
other_ldFlags = build_settings['OTHER_LDFLAGS']
if other_ldFlags
build_settings['OTHER_LDFLAGS'] = other_ldFlags.gsub('.xcframework', '')
# write build_settings dictionary to xcconfig
File.open(xcconfig_path, "w") do |file|
build_settings.each do |key,value|
file.puts "#{key} = #{value}"
end
end
end
end
end
end
end
end
The second issue with _FRAMEWORK_SEARCH_PATHS_ is a bit more complicated. If you have xcframework directly added in your project in Target->General->Frameworks, Libraries & Embeded content Xcode is executing Copy framework script:
ProcessXCFrameworkLibrary /Users/USER_NAME/Developer/APP_NAME/Pods/FRAMEWORK_NAME/FRAMEWORK_NAME.xcframework/ios-arm64/FRAMEWORK_NAME.framework /Users/peternusios/Library/Developer/Xcode/DerivedData/APP_NAME-ccbqkzqgxqzxxhcohuttroxaoeis/Build/Products/Debug-iphoneos/FRAMEWORK_NAME.framework (in target: APP_NAME)
cd /Users/USER_NAME/Developer/APP_NAME/ builtin-copy /Users/USER_NAME/Developer/APP_NAME/Pods/FRAMEWORK_NAME/FRAMEWORK_NAME.xcframework/ios-arm64/FRAMEWORK_NAME.framework /Users/USER_NAME/Library/Developer/Xcode/DerivedData/APP_NAME-ccbqkzqgxqzxxhcohuttroxaoeis/Build/Products/Debug-iphoneos
which is taking correct architecture from xcframework and putting it to DerivedData for later usage.
I didn't find any quick solution to workaround this issue, so I'm manually referencing xcframework from pods directory in Target->General->Frameworks, Libraries & Embeded content, so xcode take cares about picking correct architecture.
Looks like there is quite some work to support xcframework in CocoaPods where also scripts like install_framework and strip_invalid_archs would need to be updated.
Is there any plan on supporting the xcframeworks ? Should there be another issue opened in these regards?
My understanding is that xcframework support is the main work required for this issue so that binary pods can be supported. Source pods already are enabled if the app selects the Mac box on the iOS target.
I can confirm what @paulb777 is saying. I've been successful in building and running a large application (Moovit) for Mac Catalyst. The only things I had to comment out were the binary frameworks.
Close in favor of #9148
Most helpful comment
UPDATED
Few findings when distributing xcframework through pods.
First I've tried using podpec like this:
but I've run into multiple issues:
In generated Pods.xcconfig file, the framework was referenced in _OTHER_LDFLAGS_ in format _-framework "FRAMEWORK_NAME.xcframework"_ the extension .xcframeworks is unnecessary at this place and it needs to be referenced in format _-framework "FRAMEWORK_NAME"'_
In generated Pods.xcconfig file, the _FRAMEWORK_SEARCH_PATHS_ was set same way as for classic framework in format: _"${PODS_ROOT}/FRAMEWORK_NAME"_. But as now xcframework contains new folder structure for multiple architectures it will not be able to find the right framework for concrete architecture.
To workaround those issues I've temporarily updated Podfile to correct xcframework reference in _OTHER_LDFLAGS_
The second issue with _FRAMEWORK_SEARCH_PATHS_ is a bit more complicated. If you have xcframework directly added in your project in Target->General->Frameworks, Libraries & Embeded content Xcode is executing Copy framework script:
which is taking correct architecture from xcframework and putting it to DerivedData for later usage.
I didn't find any quick solution to workaround this issue, so I'm manually referencing xcframework from pods directory in Target->General->Frameworks, Libraries & Embeded content, so xcode take cares about picking correct architecture.
Looks like there is quite some work to support xcframework in CocoaPods where also scripts like install_framework and strip_invalid_archs would need to be updated.