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

Build fails on Mac Catalyst #311

Closed
cuttlas opened this issue Feb 7, 2021 · 18 comments
Closed

Build fails on Mac Catalyst #311

cuttlas opened this issue Feb 7, 2021 · 18 comments
Labels

Comments

@cuttlas
Copy link

cuttlas commented Feb 7, 2021

Your Environment

  • Plugin version: 3.1.0
  • Platform: macOS
  • OS version: Catalina
  • React Native version (react-native -v): 0.63.0

Expected Behavior

Be able to successfully build and use the library when building an iOS app for Mac Catalyst. The BGTaskScheduler which this library uses is available for Mac Catalyst so I'm guessing it should be possible: https://developer.apple.com/documentation/backgroundtasks/bgtaskscheduler?language=objc
If it's not possible to make it work, at least the build should not fail.

Actual Behavior

Build fails in the linking step with the following error:
In ../node_modules/react-native-background-fetch/ios/RNBackgroundFetch/TSBackgroundFetch.framework/TSBackgroundFetch(TSBGTask.o), building for Mac Catalyst, but linking in object file built for iOS Simulator, file '../node_modules/react-native-background-fetch/ios/RNBackgroundFetch/TSBackgroundFetch.framework/TSBackgroundFetch' for architecture x86_64

Steps to Reproduce

  1. Turn on Mac Catalyst for your iOS app: https://developer.apple.com/tutorials/mac-catalyst/turning-on-mac-catalyst
  2. Change the XCode target to "My Mac" and build the app.

Context

The library works perfectly when building for iOS.

Thanks!

@mikehardy
Copy link
Contributor

mikehardy commented Feb 7, 2021

@christocracy this isn't really possible to do well while still using a .framework, I think you have to switch to an .xcframework (which implies Xcode 12+ and cocoapods 1.10+ as well I think?) then do something sort of like this as build_xcframework.sh and call it to build the thing:

#!/bin/bash
set -e

WORKING_DIR=$(pwd)
FRAMEWORK_FOLDER_NAME="TSBackgroundFetch_XCFramework"
FRAMEWORK_NAME="TSBackgroundFetch"
FRAMEWORK_PATH="${WORKING_DIR}/${FRAMEWORK_FOLDER_NAME}/${FRAMEWORK_NAME}.xcframework"
BUILD_SCHEME="TSBackgroundFetch" # or whatever the scheme you build is named...
SIMULATOR_ARCHIVE_PATH="${WORKING_DIR}/${FRAMEWORK_FOLDER_NAME}/simulator.xcarchive"
IOS_DEVICE_ARCHIVE_PATH="${WORKING_DIR}/${FRAMEWORK_FOLDER_NAME}/iOS.xcarchive"
CATALYST_ARCHIVE_PATH="${WORKING_DIR}/${FRAMEWORK_FOLDER_NAME}/catalyst.xcarchive"
OUTPUT_FOLDER=${WORKING_DIR}/../packages/react-native/ios/ # 'packages/react-native' is actually a git sub-module, where the WORKING_DIR is closed source, and the sub-module is the open source API wrapper.

rm -rf "${WORKING_DIR:?}/${FRAMEWORK_FOLDER_NAME}"
echo "Deleted ${FRAMEWORK_FOLDER_NAME}"
mkdir "${FRAMEWORK_FOLDER_NAME}"
echo "Created ${FRAMEWORK_FOLDER_NAME}"

echo "Archiving for iOS Simulator"
xcodebuild archive -workspace "./${FRAMEWORK_NAME}.xcworkspace" -scheme ${BUILD_SCHEME} -configuration Release \
  -destination="iOS Simulator" -archivePath "${SIMULATOR_ARCHIVE_PATH}" \
  -sdk iphonesimulator SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES | xcpretty -k

echo "Archiving for iOS"
xcodebuild archive -workspace "./${FRAMEWORK_NAME}.xcworkspace" -scheme ${BUILD_SCHEME} -configuration Release \
  -destination="iOS" -archivePath "${IOS_DEVICE_ARCHIVE_PATH}" \
  -sdk iphoneos SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES | xcpretty -k

# Do not be tempted to use "-sdk macosx" instead of the "destination" argument. It switches you to AppKit from UIKit
# https://developer.apple.com/forums/thread/120542?answerId=374124022#374124022
echo "Archiving for Mac Catalyst"
xcodebuild archive -workspace "./${FRAMEWORK_NAME}.xcworkspace" -scheme ${BUILD_SCHEME} -configuration Release \
  -destination='platform=macOS,arch=x86_64,variant=Mac Catalyst' -archivePath "${CATALYST_ARCHIVE_PATH}" \
  SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES | xcpretty -k

echo "Packaging archives into ${FRAMEWORK_NAME}.xcframework bundle"
xcodebuild -create-xcframework \
  -framework "${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework" \
  -framework "${IOS_DEVICE_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework" \
  -framework "${CATALYST_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework" \
  -output "${FRAMEWORK_PATH}" | xcpretty

rm -rf "${SIMULATOR_ARCHIVE_PATH}"
rm -rf "${IOS_DEVICE_ARCHIVE_PATH}"
rm -rf "${CATALYST_ARCHIVE_PATH}"

# Catalyst framework directory structure flattening step 1:
# - Copy the framework in a specific way: de-referencing symbolic links on purpose
echo "Installing framework into packages/react-native submodule"
rm -rf "${OUTPUT_FOLDER}/${FRAMEWORK_NAME}.xcframework" && \
  cp -rp "${FRAMEWORK_PATH}" "${OUTPUT_FOLDER}"

# Catalyst framework directory structure flattening step 2:
# - Remove the catalyst framework "Versions" directory structure after symbolic link de-reference
rm -rf "${OUTPUT_FOLDER}/${FRAMEWORK_NAME}.xcframework/ios-arm64_x86_64-maccatalyst/${FRAMEWORK_NAME}.framework/Versions"

echo "Cleaning up intermediate files"
rm -rf "${WORKING_DIR:?}/${FRAMEWORK_FOLDER_NAME}"

...as a bonus, you also need to do this on Apple Silicon and it handles both at once. react-native-background-geolocation probably needs similar treatment, I just went through this on https://notifee.app in order to support Catalyst and Apple Silicon invertase/react-native-notifee#162

The script was just hand-hacked right here to excise Notifee and smash TSBackgroundFetch in there, you may need to adjust. Also, while it is working in production on Notifee it may not be perfect yet, next step for me is to finalize a tvOS/macOS/watchOS port (already building but needs testing) then do a blog post of some sort of "The ultimate Apple xcframework build script", so I'm keen to hear any feedback.

Hope this helps

@christocracy
Copy link
Member

Thanks @mikehardy. I got an M1 Macbook Air a couple weeks ago for testing.

@mikehardy
Copy link
Contributor

mikehardy commented Feb 8, 2021

Without switching to xcframework I believe the symptom is that you can't build an app with the lib for release for simulator as lipo will refuse to put 2 arm64 slices in the library blob and the real device needs arm64. There workarounds about arch exclusion etc but they are all dead-end, xcframework and different lipo'd blobs per target is the clean path forward, with xcode 12 auto lipoing for you in the above script. In the end easy to reason about but so much trial and error in there, especially the catalyst directory structure. Stripe and onesignal are good repos to cross check, as well as notifee of course

@christocracy
Copy link
Member

@mikehardy Ok, I've got TSBackgroundFetch.xcframework building and working in the example app. Thanks for the recipe.

@christocracy
Copy link
Member

This is going to be a few more days yet. I'm going to package this update with another feature that's been waiting to get out the door for months but was delayed due to Android 11 / iOS 14BackgroundFetch will now support a new "timeout callback", called when the OS reports your task is about to expire.

But everything is under control wrt the TSBackgroundFetch.xcframework and Catalyst support.

@christocracy
Copy link
Member

Ok, [email protected] is merged to master. Would love some feedback if anyone can try this out before releasing to npm.

NOTE: That version cannot yet be used with [email protected] -- 3.10.0 will be released soon for that.

@mikehardy
Copy link
Contributor

Just curious, the changelog has all these breaking changes but it's 3.2.0 instead of 4.0.0 🤔 - I'm not sure I can try it without support in background-geolocation as I'll likely get into a dependency tangle there as I use both

@christocracy
Copy link
Member

Fair enough. I'll call it 4.0.0.

@mikehardy
Copy link
Contributor

mikehardy commented Feb 12, 2021

great! related but sorta separate, if you want to .xcframework react-native-background-geolocation-android and make it react-native-background-fetch@^4 compatible - even if just on master via git reference, then I can test them, and I'd be happy to. I've accumulated a pile of little deprecation items / put-off-upgrades that I'm working through at the moment so it would be right on theme for me

@christocracy
Copy link
Member

if you want to .xcframework react-native-background-geolocation-android

Next week I'll be working on react-background-geolocation

  • Migrate flutter_background_fetch

  • Migrate react-native-background-fetch

  • Migrate cordova-background-fetch

  • Migrate flutter_background_geolocation

  • Migrate react-native-background-geolocation

  • Migrate cordova-background-geolocation

@cuttlas
Copy link
Author

cuttlas commented Feb 14, 2021

Thanks for the quick response!

I've tried the 4.0.0 version and now I'm getting the following error in the AppDelegate.m:

TSBackgroundFetch/TSBackgroundFetch.h' file not found

Hope it helps.

@christocracy
Copy link
Member

Hope it helps.

That's not much help at all.

@mikehardy
Copy link
Contributor

@christocracy that's this part of the script:


# Catalyst framework directory structure flattening step 1:
# - Copy the framework in a specific way: de-referencing symbolic links on purpose
echo "Installing framework into packages/react-native submodule"
rm -rf "${OUTPUT_FOLDER}/${FRAMEWORK_NAME}.xcframework" && \
  cp -rp "${FRAMEWORK_PATH}" "${OUTPUT_FOLDER}"

# Catalyst framework directory structure flattening step 2:
# - Remove the catalyst framework "Versions" directory structure after symbolic link de-reference
rm -rf "${OUTPUT_FOLDER}/${FRAMEWORK_NAME}.xcframework/ios-arm64_x86_64-maccatalyst/${FRAMEWORK_NAME}.framework/Versions"

All the Versions/A - Versions/Current stuff has to be flattened out after compilation / before publishing or you get this. I just checked it and it appears you still have the "bushy" file structure in the .xcframework for catalyst

Don't ignore trail that has been macheted out of the jungle and then paved smoothly for you already :-) invertase/react-native-notifee#162 (comment)

@christocracy
Copy link
Member

I just checked it and it appears you still have the "bushy" file structure

Done.

@cuttlas
Copy link
Author

cuttlas commented Feb 14, 2021

Thanks! I tested it again and now it successfully builds both on iOS and Mac Catalyst.

Now the issue I'm facing is that the authorization status call returns "'BackgroundFetch denied'" on Catalyst when is returning "BackgroundFetch is enabled" on iOS with the exact same configuration.
This is probably not an issue with this library, I'll try to find out if further configuration is needed on Catalyst to run background tasks.

@mikehardy
Copy link
Contributor

Excellent! That sounds like progress - to say, the xcframework is working now. I have no idea why Xcode would happily build a framework structure for the xcframework that doesn't work for the catalyst target, so I'm thinking I'm probably missing some important wisdom, but those two steps of the script do reproducibly make it work, so ...onwards

@stale
Copy link

stale bot commented Jun 2, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You may also mark this issue as a "discussion" and I will leave this open.

@stale stale bot added the stale label Jun 2, 2021
@stale
Copy link

stale bot commented Jun 16, 2021

Closing this issue after a prolonged period of inactivity. Fell free to reopen this issue, if this still affecting you.

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

No branches or pull requests

3 participants