openid / AppAuth-iOS

iOS and macOS SDK for communicating with OAuth 2.0 and OpenID Connect providers.
https://openid.github.io/AppAuth-iOS
Apache License 2.0
1.74k stars 762 forks source link

XCFramework archive - No such module on import AppAuth / AppAuthCore #867

Open meavydev opened 1 month ago

meavydev commented 1 month ago

We are building our SDK via xcodebuild archive and we have a dependency on AppAuthCore (but have the same problem with AppAuth if we change the code a bit). When adding our SPM, and doing a build with the import we get an error: Failed to build module 'navenioSDK' for importation due to the errors above; the textual interface may be broken by project issues or a compiler bug ... Library/Developer/Xcode/DerivedData/Sample-ebyeebvxavhdtqcthbulfduyrttt/Build/Products/Debug-iphonesimulator/navenioSDK.framework/Modules/navenioSDK.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface:5:8 No such module 'AppAuthCore' We are adding AppAuth and AppAuthCore to the app as a package dependency using AppAuth-iOS, but it appears not to be found. Doing the same for AWS (the first on the list in arm64-apple-ios-simulator.private.swiftinterface) resolved fine, so it appears to be an AppAuth specific issue.

Screenshot 2024-07-27 at 09 26 26 Screenshot 2024-07-27 at 09 25 41 Screenshot 2024-07-27 at 09 25 20
meavydev commented 1 month ago

If it helps, our SDK is exposing OIDExternalUserAgentSession that it is using for an Auth service, so we can't mark the AppAuthCore imports as @_implementationOnly We also have Swift extensions: extension OIDAuthState

extension OIDAuthorizationService

meavydev commented 1 month ago

It appears to be mainly down to the fact that we need to set the AppAuth modules to dynamic in the SPM Package.Swift (which AppAuth could do, like Realm), so then Xcode will give the option to Embed & Sign / Do Not Embed. For the 'xcodebuild archive' we need to set AppAuth to 'Embed & Sign' and then in the App using our XCFramework set them to 'Do Not Embed'. So far the only way we have found of doing this is via a local build of AppAuth and then wrapping them in XCFrameworks, which is not exactly ideal...

#!/bin/sh
FRAMEWORK_NAME="AppAuth_iOS"
STATIC_FRAMEWORK_NAME="AppAuth-iOS"

BUILD_PATH="/Users/shawn/Downloads/AppAuthBuild"

rm -rf "${BUILD_PATH}"

SIMULATOR_ARCHIVE_PATH="${BUILD_PATH}/${FRAMEWORK_NAME}/simulator.xcarchive"
IOS_DEVICE_ARCHIVE_PATH="${BUILD_PATH}/${FRAMEWORK_NAME}/iOS.xcarchive"
SIMULATOR_STATIC_ARCHIVE_PATH="${BUILD_PATH}/${STATIC_FRAMEWORK_NAME}/simulator.xcarchive"
IOS_DEVICE_STATIC_ARCHIVE_PATH="${BUILD_PATH}/${STATIC_FRAMEWORK_NAME}/iOS.xcarchive"

xcodebuild archive \
-scheme ${FRAMEWORK_NAME} \
-configuration Release \
-destination "generic/platform=iOS Simulator" \
-archivePath "${SIMULATOR_ARCHIVE_PATH}" \
OTHER_SWIFT_FLAGS=-no-verify-emitted-module-interface \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
|| exit 1
echo "Finished build for simulator"

# build for device
echo "Starting build for device"
xcodebuild archive \
-scheme ${FRAMEWORK_NAME} \
-configuration Release \
-destination "generic/platform=iOS" \
-archivePath "${IOS_DEVICE_ARCHIVE_PATH}" \
OTHER_SWIFT_FLAGS=-no-verify-emitted-module-interface \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
|| exit 1
echo "Finished build for device"

xcodebuild archive \
-scheme ${STATIC_FRAMEWORK_NAME} \
-configuration Release \
-destination "generic/platform=iOS Simulator" \
-archivePath "${SIMULATOR_STATIC_ARCHIVE_PATH}" \
OTHER_SWIFT_FLAGS=-no-verify-emitted-module-interface \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
|| exit 1
echo "Finished static build for simulator"

# build for device
echo "Starting build for device"
xcodebuild archive \
-scheme ${STATIC_FRAMEWORK_NAME} \
-configuration Release \
-destination "generic/platform=iOS" \
-archivePath "${IOS_DEVICE_STATIC_ARCHIVE_PATH}" \
OTHER_SWIFT_FLAGS=-no-verify-emitted-module-interface \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES \
|| exit 1
echo "Finished static build for device"

# creating Dynamic XCFramework
echo "Creating Dynamic ${FRAMEWORK_NAME}.xcframework..."
xcodebuild \
-create-xcframework \
-framework ${IOS_DEVICE_ARCHIVE_PATH}/Products/Library/Frameworks/AppAuth.framework \
-framework ${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/AppAuth.framework \
-output "${BUILD_PATH}/${FRAMEWORK_NAME}.xcframework" \
|| exit 1

# creating Static XCFramework
echo "Creating Static ${STATIC_FRAMEWORK_NAME}.xcframework..."
xcodebuild \
-create-xcframework \
-library ${IOS_DEVICE_STATIC_ARCHIVE_PATH}/Products/usr/local/lib/libAppAuth-iOS.a \
-library ${SIMULATOR_STATIC_ARCHIVE_PATH}/Products/usr/local/lib/libAppAuth-iOS.a \
-output "${BUILD_PATH}/${STATIC}/${STATIC_FRAMEWORK_NAME}.xcframework" \
|| exit 1
Screenshot 2024-08-07 at 09 32 42
meavydev commented 1 month ago

This appears to be the root cause https://forums.swift.org/t/issue-with-third-party-dependencies-inside-a-xcframework-through-swiftpm/41977/13 Unfortunately for our use case, we can't hide AppAuth with @_implementationOnly, so including the AppAuth XCFrameworks appears to be currently the only solution. Hopefully you will look at adding the dynamic target to your future AppAuth SPM builds.