17

I am having problems with my universal app. I have set the Base SDK to iPhone 3.2 and set the target to 3.1.

I have moved the files that are using the UIPopoverController to their own files and they shouldn't be loaded when loading the app on the iPhone. Still, when I build my app I get the following error when I build and debug to my device:

dyld: Symbol not found: _OBJC_CLASS_$_UIPopoverController Referenced from: /var/mobile/Applications/B3B90643-92DC-4E5C-8B2F-83A42D6D57E0/citybikes.app/citybikes Expected in: /System/Library/Frameworks/UIKit.framework/UIKit in /var/mobile/Applications/B3B90643-92DC-4E5C-8B2F-83A42D6D57E0/citybikes.app/citybikes

I really hope someone can help me.

Best regards, Paul Peelen

Paul Peelen
  • 10,073
  • 15
  • 85
  • 168

2 Answers2

27

Even if they're not loaded in runtime, those files are still processed in linking. The symbol _OBJC_CLASS_$_UIPopoverController is therefore added into your app as one of those requiring dynamic linking.

There are 2 workarounds,

  1. Replace all appearance of UIPopoverController in a message (e.g. [UIPopoverController alloc]) with NSClassFromString(@"UIPopoverController"), or
  2. Make UIKit weakly linked.
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Thank you for your answer. What do you mean with "Make UIKit weakly linked"? The first one seems logic otherwise but best would be not to change my code at all, but I don't see that happening :( . /Paul – Paul Peelen Jun 09 '10 at 11:50
  • Also, how whould you first option work in the @interface section. I need to add the and initiate a "UIPopoverController *popoverController;" – Paul Peelen Jun 09 '10 at 12:04
  • @Paul: That doesn't need to be changed. Only those inside `[...]` have to be changed. – kennytm Jun 09 '10 at 12:15
  • 7
    @Paul: To make a library weakly linked, select your target, open "Info", then change the type from Required to Weak. – kennytm Jun 09 '10 at 12:16
  • Great! I got it working now. Thank you for your help, your my hero! :) – Paul Peelen Jun 09 '10 at 14:17
  • 1
    are there any consequences for making uikit weakly linked? – Andy Jacobs Jun 18 '10 at 13:30
  • 3
    @Andy: Some link-time error may be hidden, and cause the app to crash at run-time. – kennytm Jun 18 '10 at 15:13
  • 1
    great question and answer . .i am also facing the same issue. Which is the safest option of the two ? 1st or 2nd one ? – thndrkiss Feb 17 '11 at 14:35
  • will the 2nd option makes the app to get rejected in the appstore ? – thndrkiss Feb 17 '11 at 14:41
  • 1
    @thndrkiss No, it will still be approved. Thats the option I went with. – Paul Peelen May 23 '11 at 12:37
  • The only place I see it is: `UIKit.framework/Headers/UIPopoverController.h`in the nib files. I do not use nib files but someone else added them... Any way to use `NSClassFromString()` in a nib file? I just went with weak linking. That works but seems like strong linking would be better. – ma11hew28 Jul 08 '11 at 18:04
  • 1
    Think about it: it's UIKit. It's not really an optional library. Choosing to weak link UIKit will bite you later. – AWrightIV Sep 17 '11 at 00:37
  • @AWrightIV Why? Since, as you say, its not really optional it will always exists. – Paul Peelen Oct 19 '11 at 14:32
  • @PaulPeelen Why are you comfortable with it? Moreover, this is likely to hide linker errors. Unless you're okay with fragile code, I'd recommend against this approach. I'll lend more advice in a formal answer to the problem. – AWrightIV Oct 20 '11 at 05:58
5

I strongly recommend you not weakly link UIKit. As KennyTM and I mentioned in our comments, this may hide future linker issues and result in crashes. I'm also simply not comfortable with telling the linker that UIKit is optional when it plainly isn't. Big hack.

Instead, initialize and call UIPopoverController indirectly using NSClassFromString:

Class popover = NSClassFromString(@"UIPopoverController");
if (nil != popover)
{
    self.myPopover = [[popover alloc] initWithContentViewController:myContent];
}

If you still have linker errors, you may need to call UIPopoverController's messages using NSSelectorFromString:

Class popover = NSClassFromString(@"UIPopoverController");
if (nil != popover)
{
    SEL myInit = NSSelectorFromString(@"initWithContentViewController:");
    self.myPopover = [[popover alloc] performSelector:myInit withObject:myContent];
}

For portability, I recommend writing a proxy object to handle these implementation details.

AWrightIV
  • 553
  • 7
  • 15
  • 1
    This is the best approach. However, personally I like using `if ([UIPopoverControll class] != nil) { ... }` instead. – simonbs Dec 07 '12 at 13:51