Optional Framework Not Working (CoreAudioKit not on Simulator)
Asked Answered
N

2

8

To get MIDI over Bluetooth working, I need to use the CoreAudioKit framework. This works perfectly, but I am not able to compile on the simulator.

  1. Making the framework "optional" doesn't help, error is ld: framework not found CoreAudioKit

enter image description here

I think it should work according to the docs

  1. Deleting the framework allows my code to compile

I've got this in code, which is why I can delete the framework without issues.

#if !TARGET_IPHONE_SIMULATOR
#import <CoreAudioKit/CoreAudioKit.h>
#endif


How can I get this optional compilation to work?
Newport answered 22/7, 2015 at 21:36 Comment(0)
P
7

I actually would have thought that would work, but I think you can solve it another way. This worked for me:

  1. remove all references to CoreAudioKit in your target settings Build Phases (Link Binary With Libraries)

  2. make sure there's no similar settings entered in manually. for example, don't add this setting: -weak_framework CoreAudioKit in the Other Linker Flags

  3. use preprocessor flags to conditionally compile your code for the simulator:

#import "ViewController.h"

#if !TARGET_IPHONE_SIMULATOR
@import CoreAudioKit;
#endif

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];
   // Do any additional setup after loading the view, typically from a nib.

#if !TARGET_IPHONE_SIMULATOR
   if ([CABTMIDICentralViewController class]) {   // maybe not needed?
      CABTMIDICentralViewController *vc = [[CABTMIDICentralViewController alloc] init];
   }
#endif
}

Note: in my example above, you might not need to test for the existence of the CABTMIDICentralViewController class. It depends on whether your app is targeting only iOS 8+, or also iOS 7.

Update

Per comments below by @Yar and @JeremyHuddlestonSequoia, note that this solution requires you to Enable Modules and Link Frameworks Automatically in project build settings. These Xcode settings now default to the proper values for this technique, but in case you're managing an older project, make sure they're enabled.

Other References

https://mcmap.net/q/493790/-ios-simulator-7-1-crash-running-on-yosemite-with-weak-linked-new-frameworks-symbol-not-found-_objc_isauto

https://mcmap.net/q/378096/-can-39-t-import-embedded-framework-with-xcode-6-gm

Publias answered 23/7, 2015 at 1:48 Comment(8)
Amazing! This works perfectly, and I don't think you need the safety with the CABTMIDICentralViewController... The thing that surprised me about this was the use of @import, and the need to turn it on per this answer: https://mcmap.net/q/378096/-can-39-t-import-embedded-framework-with-xcode-6-gm BIG THANKS for helping resolve this issue. Great!Newport
@Yar, yeah that if ([CABTMIDICentralViewController class]) test is more for testing iOS8 vs iOS7, not testing device vs simulator.Publias
At some point somebody has to check if they're working with a UIViewController or nil, but as long as my code is compiling, I'm happy for the current question. Thx againNewport
Edited to explain why weak linking doesn't work and also recommend using -framework instead of -weak_framework when targeting iOS8+.Vaginismus
@JeremyHuddlestonSequoia, you have some useful information, but please don't edit an accepted answer to fundamentally change its meaning. If you like, simply add a new answer of your own. The phrase "The way to address this is by using link arguments that are conditional on the SDK" you inserted does not match with the solution (#1-3) I described, so it makes the answer confusing and contradictory.Publias
@JeremyHuddlestonSequoia, I didn't forget it at all. I specifically suggested not adding the link argument. The code I show above includes @import CoreAudioKit;. Can you explain what you mean by "breaks the user's ability to build targeting the device"? I've built a hello world program with this exact code, and it definitely builds for simulator, or device. Runs, too.Publias
Ah, in that case, you're using autolinking. You should probably add a note that your answer depends on Xcode's "Link Frameworks Automatically" build setting turned on.Vaginismus
@JeremyHuddlestonSequoia, ok, that's a valid critique, although those settings are enabled by default now. Answer updated. I had already upvoted Yar's comment, which linked to an answer concerning these settings.Publias
V
4

In order to link something (even weakly), it needs to be present in the SDK. It doesn't matter if you don't actually use the framework; the linker will error out if instructed to include a link to a file it cannot find.

You will need to conditionally compile and link your project based on the SDK being used. Specifically, when targeting the iOS SDK, you will want to include support for and link against CoreAudioKit.framework. When targeting iOS Simulator SDK, you will want to not include this support nor linkage.

To conditionalize your code, you will want to include the header and use the TARGET_OS_SIMULATOR macro (or the deprecated TARGET_IPHONE_SIMULATOR macro for SDKs older than iOS 9.0). This header is often pulled in via other includes, but it is best to do so yourself.

Eg:

#import "MyController.h"
#import <TargetConditionals.h>

#if !TARGET_IPHONE_SIMULATOR
#import <CoreAudioKit/CoreAudioKit.h>
#endif

@implementation ViewController

- (void)viewDidLoad {
   [super viewDidLoad];

#if !TARGET_IPHONE_SIMULATOR
   // Stuff dependent on CoreAudioKit
#endif
}
@end

Xcode does not support SDK-conditional linking in the build phases for targets, so make sure you do not include CoreAudioKit.framework in Link Binary With Libraries build phase for your target. To handle linking, you basically have two options:

  1. Use automatic linking support from clang modules
  2. Use SDK-conditional linker flags

To use automatic linking, you must set Xcode's Enable Modules (C and Objective C) and Link Frameworks Automatically build settings turned on.

If you are trying to accomplish something like this with older toolchains or just like having tighter control over linking, you can still accomplish this with SDK-conditional Other Linker Flags build settings. Create SDK-conditional entries for this build setting such that you use "-framework CoreAudioKit" (or "-weak_framework CoreAudioKit") by default and nothing when targeting the simulator SDK. This screenshot should make it more clear.

build settings screenshot

If your iOS Deployment Target is older than iOS 8, you should be sure to weak link the framework because it was added in iOS 8. If targeting iOS 8 or newer, you can safely use -framework CoreAudioKit.

Vaginismus answered 12/8, 2015 at 6:38 Comment(4)
Lots to read here, so: what's different here compared to Nate's answer?Newport
I added this answer to provide more explanation as to why you are seeing the issue and explain your various options. Nate's answer assumes "Link Frameworks Automatically" is enabled, and I provide an additional option for when "Link Frameworks Automatically" is not (or cannot be) enabled.Vaginismus
Thanks. This is great. I'd love it if you could make it much more clear. It might be helpful to break this answer into sections (and divide those sections). Or not, I'm sure it'll be useful to somebody as is. Thanks for the help.Newport
It is divided into separate paragraphs already. If you want to make it even clearer, then by all means please edit it :)Vaginismus

© 2022 - 2024 — McMap. All rights reserved.