title: How to manually create dynamic (dark/light) NSImage instances?
body: <p>It seems in macOS 10.14 Mojave, the only way to create <code>NSImage</code> instances that automatically draw a light and dark version is via asset catalogs and <code>+[NSImage imageNamed:]</code>. However, I need to create dynamic images at runtime and there doesn't seem to be a way to do so without using private API.</p>
  
  <p>Under the hood, it seems a private property <code>_appearanceName</code> has been introduced to <code>NSImageRep</code> that is somehow used to select the correct representation. It should be straight forward to create an <code>NSImage</code> with image representations that have the corresponding <code>_appearanceName</code> set but I would like to avoid this.</p>
  
  <p>I found a simple workaround (posted below) but it doesn't seem to work correctly when the system appearance is changing (i.e. user is switching from light to dark or vice versa) or when used in view hierarchies that have the <code>appearance</code> property set to different appearances (e.g. one view hard-coded to dark mode, another view hard-coded to light mode).</p>
  
  <p>So, how can I manually create a dynamic <code>NSImage</code> that is correctly showing a light or dark version, like the asset catalog images do?</p>
  
  <hr>
  
  <pre><code>@implementation NSImage (CustomDynamic)
  
  + (NSImage *)imageWithLight:(NSImage *)light dark:(NSImage *)dark
  {
      if (@available(macOS 10.14, *)) {
          return [NSImage
              imageWithSize:light.size
              flipped:NO
              drawingHandler:^(NSRect dstRect) {
                  if ([NSImage appearanceIsDarkMode:NSAppearance.currentAppearance]) {
                      [dark drawInRect:dstRect];
                  } else {
                      [light drawInRect:dstRect];
                  }
                  return YES;
              }
          ];
      } else {
          return light;
      }
  }
  
  + (BOOL)appearanceIsDarkMode:(NSAppearance *)appearance
  {
      if (@available(macOS 10.14, *)) {
          NSAppearanceName basicAppearance = [appearance bestMatchFromAppearancesWithNames:@[
              NSAppearanceNameAqua,
              NSAppearanceNameDarkAqua
          ]];
          return [basicAppearance isEqualToString:NSAppearanceNameDarkAqua];
  
      } else {
          return NO;
      }
  }
  
  @end
  </code></pre>