Extracting hex value from NSColor
Cocoa Warning: Implicitly declaring C library function ‘objc_getClass’
In Slider 2, I needed to check for the presence for Sparkle, and only load Slider 2′s included version when Sparkle isn’t found.
The code I used to do this is included below, but the point of this article is really to provide a solution to the following warning I encountered:
warning: Semantic Issue: Implicitly declaring C library function 'objc_getClass' with type 'id (const char *)' |
One will get this warning if using code when checking for the existence of a class like:
if (!objc_getClass("SUUpdater")) { ... } |
The solution is to use NSClassFromString instead:
if (!NSClassFromString(@"SUUpdater")) { ... } |
For an in-depth tutorial on how to check for the presence of a framework within a Cocoa bundle, see Detecting Presence of a Framework Within an NSBundle (Plugin).
Sliding Modal Dialogs in Cocoa Applications & Bundles
Example project available on GitHub – CIModalSheetDialog
One thing I wanted to be able to use during the development of Slider 2 was modal dialogs that slide out from the top of Rapid Weaver. This was troublesome for me as Slider 2 is a bundle that runs within Rapid Weaver – not as a standalone application.
Googling and experimentation allowed me to find a solution for problem that I find acceptable. Below is my implementation of a modal sheet dialog, further down one may find some usage examples.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // // SModalSheetDialogs.h // // Created by Michael Robinson on 7/08/11. // Copyright 2011 Code of Interest. All rights reserved. // #import <Foundation/Foundation.h> @interface SModalSheetDialogs : NSObject + (void) showModalMessage:(NSString *)message withInformativeText:(NSString *)informativeText withStyle:(NSAlertStyle)style fromSender:(id)sender; + (void) showModalRequest:(NSString *)message withInformativeText:(NSString *)informativeText withStyle:(NSAlertStyle)style andButtons:(NSArray *)buttons andContextInfo:(void *)contextInfo fromSender:(id)sender; @end |
And the implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | // // SModalSheetDialogs.m //{filelink=1} // Created by Michael Robinson on 7/08/11. // Copyright 2011 Code of Interest. All rights reserved. // #import "SModalSheetDialogs.h" @implementation SModalSheetDialogs + (void) showModalMessage:(NSString *)message withInformativeText:(NSString *)informativeText withStyle:(NSAlertStyle)style fromSender:(id)sender { NSAlert *alert = [[[NSAlert alloc] init] autorelease]; [alert addButtonWithTitle:@"OK"]; [alert setInformativeText:informativeText]; [alert setMessageText:message]; [alert setAlertStyle:style]; [alert beginSheetModalForWindow:[NSApp mainWindow] modalDelegate:sender didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil]; } + (void) showModalRequest:(NSString *)message withInformativeText:(NSString *)informativeText withStyle:(NSAlertStyle)style andButtons:(NSArray *)buttons andContextInfo:(void *)contextInfo fromSender:(id)sender { NSAlert *alert = [[[NSAlert alloc] init] autorelease]; NSEnumerator *e = [buttons objectEnumerator]; id object; while (object = [e nextObject]) { [alert addButtonWithTitle:(NSString*)object]; } [alert setInformativeText:informativeText]; [alert setMessageText:message]; [alert setAlertStyle:style]; [alert beginSheetModalForWindow:[NSApp mainWindow] modalDelegate:sender didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:contextInfo]; } @end |
Showing a dialog is simple, example below.
1 2 3 4 | [CIModalSheetDialog showModalMessage:@"Dialog title" withInformativeText:@"Dialog content" withStyle:NSInformationalAlertStyle fromSender:self]; |
This is fine for cases where we’re just informing the user that something happened – in these cases we don’t care what the user does, as the only option they have is to acknowledge the message. In more complex cases, where the user is presented with a choice, we need to have a way to catch that choice.
As the object handling the user’s choice may differ across use cases, we set the sender (as above). It is expected that this sender implements the following delegate method:
- (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo |
An example, first of showing a more complex dialog, with two buttons:
1 2 3 4 5 6 | [CIModalSheetDialog showModalRequest:@"Dialog title" withInformativeText:@"Dialog content" withStyle:NSInformationalAlertStyle andButtons:[NSArray arrayWithObjects:@"OK", @"Cancel", nil] andContextInfo:@"Some identifying information" fromSender:self]; |
And our implentation of alertDidEnd:
1 2 3 4 5 6 7 8 9 | - (void) alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { if (contextInfo == @"Some identifying information") { if (returnCode == NSAlertSecondButtonReturn) { // OK button was clicked // Handle OK } else { // Handle Cancel } } } |
More Informative NSLog alternative!
After becoming used PHP’s very many debugging and error logging tools, I started to find NSLog somewhat lacking…
When I finally bothered to search for an alternative, and ended up with CCLog, A Variation on NSLog().
CCLog shows much more information, quoting the developer:
My preferred output would be capable of doing this:
- Show on which thread CCLog() was called from.
- Show from which sourcecode file CCLog() was called from.
- Show the linenumber in the sourcecode file CCLog was called from.
I’ve been using it for some time now, and recommend it without reservation!
Detecting Presence of a Framework Within an NSBundle (Plugin)
Introduction
Jump to Tutorial
In light of Cocoa Warning: Implicitly declaring C library function ‘objc_getClass’, this tutorial has been updated.
Recently, it was brought to my attention that my method of including a particular framework (Sparkle) inside a plugin was wrong – the way I was doing it (blindly including the framework as if my plugin was the boss) was causing conflicts within the application in certain situations.
Instead, I should have been ‘sniffing’ for the presence of the framework within my plugin, and only if it wasn’t present, loading the framework dynamically. This is instead of: pretending my plugin was a real application, and including the framework in the traditional manner.
As I’m no Cocoa genius, I didn’t know what set of functions / objects one should use to implement the above process. I Googled, and luckily my searches led me on a path that ended with this: Click to Flash’s GitHub page for a specific commit intended to solve this problem: Plugin implementation.
Tutorial
In an effort to provide aid to anyone who winds up in a similar situation, the following is a short tutorial outlining the exact steps required to integrate this functionality into a plugin.
Required Steps
- [1, 2, 3] Include an internal version of the Sparkle framework in a way that doesn’t cause it to be automatically loaded – to avoid cases where the framework has already been loaded by the application / another plugin
- [4] Check for Sparkle at initialisation, and load an internal version if required
- [4] (Optional) Store the Sparkle updater object internally for use in in the “checkForUpdates” IBAction
1. Download the Sparkle framework
2. Using XCode now, Drop the Sparkle.framework folder into your plugin’s Resources folder 
3. Right-click on your plugin’s target & create a new “Copy Files” build phase, set it’s destination to “Resources”. Drag the Sparkle.framework from your “Resources” folder into this new build phase.
4. In the header for whatever class you want to perform the loading, in this case SomeClass.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 | #import <Sparkle/Sparkle.h> @interface SomeClass { // For convenience, this is set to the SUUpdater object for this plugin (iff Sparkle was loaded) id sparkle; } @property (retain) id sparkle; - (void) loadSparkle; - (IBAction) checkForUpdates:(id)notifier; @end |
And the implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | @implementation SomeClass @synthesize sparkle; - (id) init{ if ((self = [super init]) != nil){ @try { [self loadSparkle]; // Load the framework } @catch (NSException * e) { SLog(@"SomeClass init: %@", e); // http://pagesofinterest.net/blog/2011/09/more-informative-nslog-alternative/ } } return self; } - (void) awakeFromNib{ @try { if (nil != [self sparkle]) { // If sparkle was loaded, check for updates [[self sparkle] setAutomaticallyChecksForUpdates:YES]; [[self sparkle] resetUpdateCycle]; } } @catch (NSException * e) { SLog(@"SomeClass awakeFromNib: %@", e); // http://pagesofinterest.net/blog/2011/09/more-informative-nslog-alternative/ } } - (void) loadSparkle { @try { if (!NSClassFromString(@"SUUpdater")) { // If Sparkle hasn't been loaded, attempt to load it NSString *frameworksPath = [[[NSBundle bundleForClass:[self class]] bundlePath] stringByAppendingPathComponent:@"Contents/Resources/Sparkle.framework"]; NSBundle *sparkleFramework = [NSBundle bundleWithPath:frameworksPath]; NSError *error = nil; BOOL loaded = [sparkleFramework loadAndReturnError:&error]; if (loaded) { NSBundle *pluginBundle = [NSBundle bundleWithIdentifier:@"com.youridentifier.yourplugin"]; NSAssert(pluginBundle, nil); Class updaterClass = NSClassFromString(@"SUUpdater"); NSAssert(updaterClass, nil); [self setSparkle:[updaterClass updaterForBundle:pluginBundle]]; NSAssert([self sparkle], nil); } if (error) SLog(@"error loading Your Plugin's Sparkle: %@", error); // http://pagesofinterest.net/blog/2011/09/more-informative-nslog-alternative/ } else { // Sparkle has been loaded already, use the loaded version [self setSparkle:[SUUpdater updaterForBundle:[NSBundle bundleForClass:[self class]]]]; } } @catch (NSException * e) { SLog(@"SomeClass loadSparkle: %@", e); // http://pagesofinterest.net/blog/2011/09/more-informative-nslog-alternative/ } } - (IBAction) checkForUpdates:(id)notifier{ if (nil != [self sparkle]) { // Check for updates! [[self sparkle] checkForUpdates:nil]; } } @end |
Link your “Check for Updates” button to the “checkForUpdates” IBAction.
Success!
Your plugin should now be able to operate in both environments where Sparkle is missing, and where Sparkle is loaded before your plugin has been initialised.

