Open wwdrew opened 2 years ago
FWIW I've managed to semi-solve the problem based on this comment (https://github.com/cjam/react-native-spotify-remote/issues/198#issuecomment-1162941598). It is, indeed, down to not using the .xcframework - by manually adding it into the project under Build Phases / Link Binary With Libraries
, I can get my test app to build on my M1 machine.
However, I don't think is a great solution as CocoaPods should be able to handle this. I've tried various changes to get it to work, but I can't figure out why it's not integrating the correct framework properly.
If I can sort this out I'm more than happy to submit a PR for it. @cjam do you have any suggestions?
I came across this issue a couple months ago. Nice to see its getting some coverage - also using M1
Hey @wwdrew,
Thanks for your work on this. Man, i remember it took me quite some time to get the cocoapod working correctly to pull in the framework from the package etc. I think the best way I found to debug the podspecs was to install the package and then go into the node_modules and update the podspec and then try to run pod install
.
I clearly should've documented something, because I'm trying to remember how I found to work on it the first time.
Hi @cjam, no worries, happy to help out! You're not wrong about getting the framework working correctly being difficult, I've been playing with it off-and-on over the past few days and I still don't have it right yet :)
One of the changes I've made was to remove the framework from the .xcproject and add the .xcframework instead, which updates the references inside the project, but the final step of actually getting the project to see it has still eluded me. I thought I had it working last night, but I was building on my Intel machine which worked already.
I'll still keep tinkering and let you know how I'm getting on, but yeah if you have any suggestions I'm happy to hear them! :)
I think I just got this working, but I'm not entirely sure which change I made caused it! Fingers crossed I'll crack it today.
I've made a config plugin for Expo managed apps. Maybe someone will find it helpful as it took me some time to crack it, and couldn't find a solution anywhere else. Might need some tweaking to specific project, but basically it worked for me.
index.js
module.exports = require('./withSpotifyRemote')
withSpotifyRemote.js
const withAddedSpotifyRemotePod = require('./ios/withAddedSpotifyRemotePod')
const withAppDelegateMod = require('./ios/withAppDelegateMod')
const withLinkedBinaryToProject = require('./ios/withLinkedBinaryToProject')
exports.default = (config) => {
// iOS
config = withAddedSpotifyRemotePod(config)
config = withAppDelegateMod(config)
config = withLinkedBinaryToProject(config)
return config
}
ios/withAddedSpotifyRemotePod.js
const fs = require('fs');
const path = require('path');
const generateCode = require('@expo/config-plugins/build/utils/generateCode');
const configPlugins = require('@expo/config-plugins');
const code = ` pod 'RNSpotifyRemote', :path => '../node_modules/react-native-spotify-remote'`;
/**
* @typedef {import('@expo/config').ExpoConfig} ExpoConfig
* @typedef {import('@expo/config').ConfigPlugin} ConfigPlugin
*/
/**
* Function adds RNSpotifyRemote pod to the project.
*
* @param config { ExpoConfig }
* @returns { ExpoConfig }
*
* @type {ConfigPlugin}
*/
const withAddedSpotifyRemotePod = (config) => {
return configPlugins.withDangerousMod(config, [
'ios',
async (config) => {
const filePath = path.join(
config.modRequest.platformProjectRoot,
'Podfile'
);
const contents = fs.readFileSync(filePath, 'utf-8');
const addCode = generateCode.mergeContents({
tag: 'withAddedSpotifyRemotePod',
src: contents,
newSrc: code,
anchor: /\s*get_default_flags\(\)/i,
offset: 2,
comment: '#',
});
if (!addCode.didMerge) {
console.error(
"ERROR: Cannot add withReactNativeFirebase to the project's ios/Podfile because it's malformed."
);
return config;
}
fs.writeFileSync(filePath, addCode.contents);
return config;
},
]);
};
module.exports = withAddedSpotifyRemotePod;
ios/withAppDelegateMod.js
'use strict';
const configPlugins = require('@expo/config-plugins');
/**
* @typedef {import('@expo/config').ExpoConfig} ExpoConfig
* @typedef {import('@expo/config').ConfigPlugin} ConfigPlugin
*/
const importString = `#import <RNSpotifyRemote.h>`
const addLinkingString = ` || [RCTLinkingManager application:application openURL:url options:options] || [[RNSpotifyRemoteAuth sharedInstance] application:application openURL:url options:options]`
const addImport = (stringContents) => {
const importRegex = /^(#import .*)\n/m;
const match = stringContents.match(importRegex);
let endOfMatchIndex;
if (!match || match.index === undefined) {
// No imports found, just add to start of file:
endOfMatchIndex = 0;
} else {
// Add after first import:
endOfMatchIndex = match.index + match[0].length;
}
stringContents = [
stringContents.slice(0, endOfMatchIndex),
importString,
stringContents.slice(endOfMatchIndex),
].join('\n');
return stringContents;
};
/**
* Function adds an url handler, in order to support the callback from the Spotify App.
*
* @param config { ExpoConfig }
* @returns { ExpoConfig }
*
* @type { ConfigPlugin }
*/
const withAppDelegateMod = (config) => {
const linkingApiRegex =
/ \|\| \[RCTLinkingManager application:application openURL:url options:options]/;
return configPlugins.withAppDelegate(config, (config) => {
let stringContents = config.modResults.contents;
stringContents = addImport(stringContents);
stringContents = stringContents.replace(linkingApiRegex, addLinkingString)
config.modResults.contents = stringContents;
return config
})
}
module.exports = withAppDelegateMod
ios/withLinkedBinaryToProject.js
'use strict';
const configPlugins = require('@expo/config-plugins');
/**
* @typedef {import('@expo/config').ExpoConfig} ExpoConfig
* @typedef {import('@expo/config').ConfigPlugin} ConfigPlugin
*/
/**
* Function links SpotifyiOS.xcframework binary with libraries in the project.pbxproj file.
*
* @param config { ExpoConfig }
* @returns { ExpoConfig }
*
* @type {ConfigPlugin}
*/
const withLinkedBinaryToProject = (config) => {
return configPlugins.withXcodeProject((config), async (config) => {
const basename = 'SpotifyiOS.xcframework'
const fileRef = '378A9F1929953C0600C87106'
const frameworksBuildFileId = config.modResults.generateUuid()
const embedFrameworksBuildFileId = config.modResults.generateUuid()
const target = config.modResults.getTarget('com.apple.product-type.application')
/** 1. Link Binary With Libraries **/
config.modResults.addToPbxBuildFileSection({
group: 'Frameworks',
basename,
uuid: frameworksBuildFileId,
fileRef,
})
config.modResults.addToPbxFileReferenceSection({
fileRef,
basename,
lastKnownFileType: 'wrapper.xcframework',
name: 'SpotifyiOS.xcframework',
path: '../node_modules/react-native-spotify-remote/ios/external/SpotifySDK/SpotifyiOS.xcframework',
sourceTree: '"<group>"',
})
config.modResults.addToPbxFrameworksBuildPhase({
group: 'Frameworks',
basename,
uuid: frameworksBuildFileId,
})
config.modResults.addToPbxGroup({ basename, fileRef },
/**
* PBXGroup 'Frameworks' object key.
* @type {string}
*/
Object.keys(config.modResults.hash.project.objects.PBXGroup)[2]
)
/** 2. Embed and Sign **/
config.modResults.addToPbxBuildFileSection({
group: 'Embed Frameworks',
basename,
uuid: embedFrameworksBuildFileId,
fileRef,
settings: {
ATTRIBUTES: ['CodeSignOnCopy', 'RemoveHeadersOnCopy']
}
})
const pbxCopyFilesBuildPhase = config.modResults.addBuildPhase(
['../node_modules/react-native-spotify-remote/ios/external/SpotifySDK/SpotifyiOS.xcframework'],
'PBXCopyFilesBuildPhase',
'Embed Frameworks',
target.uuid,
{},
''
)
pbxCopyFilesBuildPhase.buildPhase.dstSubfolderSpec = 10
return config;
});
}
module.exports = withLinkedBinaryToProject
How can I use your plugin with my Expo app?
@kvbalib you are a legend man, thank you so much for sharing! We should open a PR and submit the config plugin to the repo itself, so expo users will have an easier time! You probably just saved me hours of hacking it out myself, much appreciated <3
@griffinbaker12
First get familiar with the development builds, then the config plugins.
And then just add the path to the plugin's index.js
in your app.config.js/json
file's "plugins".
I’ve not had a chance to try this out yet but I’m heartened to hear it works. It’s funny, going back to this issue suddenly became really important this week, amazing timing!
Completely agree this needs a PR.
I’d still like to figure out how to get this original issue working though. I think the project is a couple of SDK versions behind too which would be nice to update.
Hi, I've been testing this library out for a project and I'm having various difficulties with it. I've tried building the example application that comes with the package but neither one builds properly for me, so I've created an entirely new app which all it does is include the spotify-remote library, just to test whether it will build. The repo is here:
https://github.com/wwdrew/spotify-remote-testing
I've tried building this on both an Intel and M1 Mac.
At first I couldn't get Android to build, but patching the package using the latest commit in the repo has fixed it for both Intel and M1.
I can get my iOS test app to build in Intel, but when I try building it on M1 I end up with this error log.
I suspect this can be fixed by importing the
SpotifyiOS.xcframework
instead ofSpotifyiOS.framework
in the podspec. I've tried getting this to work on my local machine but couldn't get it working though, otherwise I'd have submitted a PR instead of an issue :)