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

Library fails to build on arm64 architecture #208

Open
wwdrew opened this issue Aug 12, 2022 · 10 comments
Open

Library fails to build on arm64 architecture #208

wwdrew opened this issue Aug 12, 2022 · 10 comments

Comments

@wwdrew
Copy link

wwdrew commented Aug 12, 2022

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.

Undefined symbols for architecture arm64:
  "_OBJC_CLASS_$_SPTConfiguration", referenced from:
      objc-class-ref in libRNSpotifyRemote.a(RNSpotifyRemoteAuth.o)
  "_OBJC_CLASS_$_SPTSessionManager", referenced from:
      objc-class-ref in libRNSpotifyRemote.a(RNSpotifyRemoteAuth.o)
  "_SPTAppRemoteErrorDescriptionKey", referenced from:
      -[RNSpotifyRemoteAuth application:openURL:options:] in libRNSpotifyRemote.a(RNSpotifyRemoteAuth.o)
  "_OBJC_CLASS_$_SPTAppRemote", referenced from:
      objc-class-ref in libRNSpotifyRemote.a(RNSpotifyRemoteAppRemote.o)
  "_SPTAppRemoteContentTypeDefault", referenced from:
      -[RNSpotifyRemoteAppRemote constantsToExport] in libRNSpotifyRemote.a(RNSpotifyRemoteAppRemote.o)
      -[RNSpotifyRemoteAppRemote getRootContentItems:resolve:reject:] in libRNSpotifyRemote.a(RNSpotifyRemoteAppRemote.o)
      -[RNSpotifyRemoteAppRemote getRecommendedContentItems:resolve:reject:] in libRNSpotifyRemote.a(RNSpotifyRemoteAppRemote.o)
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)


2022-08-12 11:55:23.795 xcodebuild[61691:293968] Requested but did not find extension point with identifier Xcode.IDEKit.ExtensionSentinelHostApplications for extension Xcode.DebuggerFoundation.AppExtensionHosts.watchOS of plug-in com.apple.dt.IDEWatchSupportCore
2022-08-12 11:55:23.796 xcodebuild[61691:293968] Requested but did not find extension point with identifier Xcode.IDEKit.ExtensionPointIdentifierToBundleIdentifier for extension Xcode.DebuggerFoundation.AppExtensionToBundleIdentifierMap.watchOS of plug-in com.apple.dt.IDEWatchSupportCore
** BUILD FAILED **


The following build commands failed:
        Ld /Users/<home>/Library/Developer/Xcode/DerivedData/SpotifyTesting-gsxrqkotmdstexblpxdqjmlnctlv/Build/Products/Debug-iphonesimulator/SpotifyTesting.app/SpotifyTesting normal (in target 'SpotifyTesting' from project 'SpotifyTesting')
(1 failure)

I suspect this can be fixed by importing the SpotifyiOS.xcframework instead of SpotifyiOS.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 :)

@wwdrew
Copy link
Author

wwdrew commented Aug 18, 2022

FWIW I've managed to semi-solve the problem based on this comment (#198 (comment)). 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?

@TSB1999
Copy link

TSB1999 commented Aug 18, 2022

I came across this issue a couple months ago. Nice to see its getting some coverage - also using M1

@cjam
Copy link
Owner

cjam commented Aug 22, 2022

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.

@wwdrew
Copy link
Author

wwdrew commented Aug 22, 2022

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! :)

@wwdrew
Copy link
Author

wwdrew commented Aug 23, 2022

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.

@kvbalib
Copy link

kvbalib commented Feb 13, 2023

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

@griffinbaker12
Copy link

How can I use your plugin with my Expo app?

@hannojg
Copy link

hannojg commented Nov 26, 2023

@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

@kvbalib
Copy link

kvbalib commented Nov 26, 2023

@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".

@wwdrew
Copy link
Author

wwdrew commented Nov 26, 2023

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.

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

No branches or pull requests

6 participants