ScreenSaverKit is a lightweight helper layer for building macOS ScreenSaver modules without having to re-implement the plumbing that every saver needs. Treat it as a starting point that you can copy into any new screensaver project. It gives you access to both CPU and Metal-accelerated rendering paths, built-in preference management, configuration sheet scaffolding, and a particle system.
There's plenty of demo savers included to illustrate how to use the various features, plus a complete tutorial in tutorial.md that walks you through building your first saver from scratch.
Currently this is in alpha development, so expect possible breaking changes in future releases. Feedback and contributions are welcome!
- ✅ Automatic default registration and preference persistence
- ✅ Cross-process preference change monitoring (System Settings ↔ saver engine)
- ✅ Convenience accessors for reading/writing
ScreenSaverDefaults - ✅ Proper animation start/stop handling across preview, WallpaperAgent and ScreenSaverEngine hosts
- ✅ Asset loading helpers, animation timing utilities, entity pooling, and diagnostics hooks
- ✅ Pre-built configuration sheet scaffolding with preference binding helpers
- ✅ Hardware-accelerated particle system with Metal rendering support
- ✅ Color palette management and interpolation utilities
- ✅ Vector math helpers for smooth animations
Keeping these concerns in one place lets each screensaver focus on drawing and behavior instead of boilerplate.
New to ScreenSaverKit? Check out the complete tutorial for a step-by-step walkthrough that covers building your first screen saver, understanding the code, debugging, and creating your own custom savers.
-
Copy the kit
Grab theScreenSaverKit/directory and drop it into your saver project. -
Subclass
SSKScreenSaverView#import "ScreenSaverKit/SSKScreenSaverView.h" @interface SimpleLinesView : SSKScreenSaverView @end
-
Provide defaults
- (NSDictionary<NSString *, id> *)defaultPreferences { return @{ @"lineCount": @200, @"colorRate": @0.2 }; }
-
React to preference changes
Whenever the user changes a setting (even from the System Settings pane) the kit calls back into your saver:
- (void)preferencesDidChange:(NSDictionary<NSString *, id> *)prefs changedKeys:(NSSet<NSString *> *)changed { self.lineCount = [prefs[@"lineCount"] integerValue]; self.colorRate = [prefs[@"colorRate"] doubleValue]; if ([changed containsObject:@"lineCount"]) { [self rebuildLines]; } }
-
Draw as normal
Implement
drawRect:,animateOneFrame, etc. just as you would in a plainScreenSaverViewsubclass. For smooth timing callNSTimeInterval dt = [self advanceAnimationClock];in-animateOneFrameand use the returned delta.
SSKAssetManager– cached bundle resource lookup with extension fallbacks for images/data. Available viaself.assetManageron the saver view.SSKAnimationClock– smooth delta-time tracking and FPS reporting. CallNSTimeInterval dt = [self advanceAnimationClock];inside-animateOneFrameand inspectself.animationClock.framesPerSecond.SSKEntityPool– simple object pooling for sprites/particles. Create pools withmakeEntityPoolWithCapacity:factory:.SSKScreenUtilities– helpers for scaling information, wallpaper-host detection, and screen dimensions.SSKDiagnostics– opt-in logging and overlay drawing. Toggle with[SSKDiagnostics setEnabled:YES]and draw overlays inside-drawRect:.SSKPreferenceBinder+SSKConfigurationWindowController– drop-in UI scaffold for settings windows with automatic binding between controls andScreenSaverDefaults.SSKColorPalette+SSKPaletteManager– shared palette definitions with interpolation helpers and registration per saver module.SSKColorUtilities– convenience serializers/deserializers for storingNSColorinstances insideScreenSaverDefaults.SSKVectorMath– small collection of inline NSPoint helpers (add, scale, reflect, clamp) for animation math.SSKParticleSystem– lightweight particle engine with CPU and Metal-accelerated rendering modes. Supports additive/alpha blending, automatic fade behaviors, and custom per-particle rendering callbacks. Ideal for sparks, trails, explosions, and flowing ribbon effects. SeeScreenSaverKit/SSKParticleSystem.mdfor detailed documentation.SSKMetalParticleRenderer– hardware-accelerated particle renderer using Metal. Automatically handles GPU pipeline setup, drawable management, and instanced rendering for high-performance particle effects.SSKMetalRenderer+SSKMetalEffectStage– extensible Metal post-processing effect system. Register custom effect passes (blur, bloom, color grading, etc.) without modifying framework code. Supports dynamic effect chains with configurable parameters. Built-in blur and bloom effects included. Seearchitecture-docs/EFFECT_IMPLEMENTATION_GUIDE.mdfor detailed documentation on creating custom Metal shader effects.SSKMetalSpritePass+SSKSprite– 2D sprite rendering system for classic sprite-based screensavers (flying toasters, DVD logo, etc.). Supports position, rotation, non-uniform scaling, horizontal/vertical flipping, color tinting, opacity, z-ordering, and multiple blend modes. Features include sprite animation sequences with loop/ping-pong modes, viewport culling for performance, and pixel-based coordinate system with Retina support. Uses instanced rendering for efficient batch drawing of multiple sprites. SeeDemos/DVDLogoMetal/README.mdfor a tutorial.SSKSpriteAnimationSequence– Immutable animation sequence data for sprite sheet animations. Supports grid-based and custom rect-based sequences, loop modes (Once, Loop, PingPong), per-frame durations, and time-based frame lookup. Integrates seamlessly withSSKSpritefor automatic UV coordinate updates.SSKMetalRenderDiagnostics– real-time Metal rendering diagnostics overlay. Tracks rendering success/failure rates, displays device/layer/renderer status, and shows FPS. Automatically renders a semi-transparent overlay on your CAMetalLayer for debugging Metal pipeline issues. Perfect for development and troubleshooting GPU initialization problems. SeeDemos/MetalParticleTest/for usage example.
For detailed information about ScreenSaverKit's internal architecture, see the Architecture Documentation Index which provides:
- Architecture Analysis – In-depth technical analysis of the effect chaining system, Metal rendering pipeline, particle system integration, and design patterns
- Architecture Diagrams – Visual component relationships, rendering pipelines, and dependency graphs
- Effect Implementation Guide – Step-by-step guide for creating custom Metal effects, understanding the rendering flow, and debugging tips
The architecture docs cover:
- FX Pass-based architecture (Particle, Blur, Bloom passes)
- Metal shader organization and compute kernels
- Texture cache strategy and memory management
- GPU-accelerated particle spawning with z-depth support
- Testing architecture and test coverage
- Performance optimizations and recent improvements
The particle system supports both CPU (Core Graphics) and GPU (Metal) rendering modes:
Quick Start:
// Create particle system
self.particleSystem = [[SSKParticleSystem alloc] initWithCapacity:1024];
self.particleSystem.blendMode = SSKParticleBlendModeAdditive; // or SSKParticleBlendModeAlpha
// For Metal rendering, set up a CAMetalLayer
self.wantsLayer = YES;
CAMetalLayer *metalLayer = [CAMetalLayer layer];
metalLayer.device = MTLCreateSystemDefaultDevice();
self.layer = metalLayer;
self.metalRenderer = [[SSKMetalParticleRenderer alloc] initWithLayer:metalLayer];
// Spawn particles
[self.particleSystem spawnParticles:100 initializer:^(SSKParticle *particle) {
particle.position = center;
particle.velocity = NSMakePoint(cos(angle) * speed, sin(angle) * speed);
particle.color = [NSColor colorWithHue:hue saturation:0.8 brightness:1.0 alpha:1.0];
particle.maxLife = 2.0;
particle.size = 10.0;
particle.behaviorOptions = SSKParticleBehaviorOptionFadeAlpha | SSKParticleBehaviorOptionFadeSize;
}];
// In animateOneFrame, update and render
[self.particleSystem advanceBy:deltaTime];
[self.particleSystem renderWithMetalRenderer:self.metalRenderer
blendMode:self.particleSystem.blendMode
viewportSize:self.bounds.size];Automatic CPU Fallback: If Metal initialization fails or the renderer returns NO, the particle system automatically falls back to CPU rendering via drawInContext: in your drawRect: method.
See Demos/RibbonFlow/ for a complete working example, and ScreenSaverKit/SSKParticleSystem.md for detailed API documentation.
The sprite system provides hardware-accelerated 2D rendering for classic sprite-based effects with support for scaling, flipping, animation sequences, and viewport culling.
Quick Start:
// Subclass SSKMetalScreenSaverView for automatic Metal setup
@interface MySpriteSaver : SSKMetalScreenSaverView
@property (nonatomic, strong) SSKSprite *logoSprite;
@property (nonatomic, strong) id<MTLTexture> logoTexture;
@end
// Create sprite in setupMetalRenderer:
- (void)setupMetalRenderer:(SSKMetalRenderer *)renderer {
[super setupMetalRenderer:renderer];
self.logoSprite = [[SSKSprite alloc] init];
// Use pixel coordinates (convert from points for Retina)
CGFloat scale = self.window?.backingScaleFactor ?: 1.0;
[self.logoSprite setPositionInPoints:NSMakePoint(100, 100) scale:scale];
self.logoSprite.size = NSMakeSize(180, 100); // Size in pixels
self.logoSprite.scale = CGSizeMake(1.0, 1.0); // Scale multiplier
self.logoSprite.colorTint = [NSColor redColor];
self.logoSprite.opacity = 1.0;
// Load texture from image
self.logoSprite.image = [NSImage imageNamed:@"logo"];
self.logoTexture = [self.logoSprite textureForDevice:renderer.device];
}
// Render in renderMetalFrame:deltaTime:
- (void)renderMetalFrame:(SSKMetalRenderer *)renderer deltaTime:(NSTimeInterval)dt {
[renderer clearWithColor:renderer.clearColor];
// Update sprite animation
NSPoint pos = self.logoSprite.position;
pos.x += self.velocity.x * dt;
CGFloat scale = (self.window != nil) ? self.window.backingScaleFactor : 1.0;
[self.logoSprite setPositionInPoints:pos scale:scale];
// Draw sprite - renderer handles points-to-pixels conversion internally
[renderer drawSprites:@[self.logoSprite]
texture:self.logoTexture
blendMode:SSKParticleBlendModeAlpha
viewportSize:self.bounds.size];
}Sprite Properties:
position– Anchor point position in pixels (not necessarily center)size– Width and height in pixelsscale– Scale multiplier (CGSize). Negative values flip the sprite (canonical flip mechanism)flipX/flipY– Convenience properties that modify scale signrotation– Rotation angle in radians (direction depends on coordinate transform)anchor– Anchor point for rotation/positioning (0,0 = bottom-left, 0.5,0.5 = center, 1,1 = top-right)colorTint– Color multiplied with texture (white = no tint)opacity– Alpha multiplier (0.0-1.0)z– Z-order for depth sorting (higher values render in front)image– Source NSImage for texture creationtextureOffset/textureSize– UV coordinates for sprite sheets (normalized 0-1)
Animation System:
// Create animation sequence from grid-based sprite sheet
SSKSpriteAnimationSequence *anim = [SSKSpriteAnimationSequence
sequenceWithGridColumns:4 rows:2 frameCount:8
duration:0.1 loopMode:SSKAnimationLoopModeLoop];
// Assign to sprite
self.logoSprite.animation = anim;
self.logoSprite.animationRate = 1.0; // Playback speed
self.logoSprite.animationPlaying = YES;
// Advance animation each frame
- (void)renderMetalFrame:(SSKMetalRenderer *)renderer deltaTime:(NSTimeInterval)dt {
[self.logoSprite advanceAnimationByTime:dt];
// ... render ...
}Coordinate System:
- All sprite coordinates and sizes are in pixels (not points)
- On Retina displays, multiply points by
window.backingScaleFactororlayer.contentsScale - Use
setPositionInPoints:scale:helper to convert from points to pixels - When using
SSKMetalRenderer.drawSprites:, passviewportSizein points—the renderer automatically uses the render target's pixel dimensions internally
Advanced Features:
- Viewport Culling – Enable
spritePass.cullingEnabled = YESto skip off-screen sprites - Z-Sorting – Pass
sortByZ:YEStodrawSprites:for automatic back-to-front sorting - Sprite Sheets – Use
setTextureRectInPixels:textureSize:for atlas-based sprites - Flip/Mirror – Set
scale.width < 0for horizontal flip,scale.height < 0for vertical flip, or useflipX/flipYconvenience properties
Blend Modes:
SSKParticleBlendModeAlpha– Standard alpha blending for opaque/transparent sprites (uses premultiplied alpha; color tinting is correctly applied with proper alpha scaling)SSKParticleBlendModeAdditive– Additive blending for glow effects
API Changes:
SSKMetalSpritePassmethods now useviewportPixels:parameter to clarify pixel-based coordinates- Old
viewportSize:methods onSSKMetalSpritePassare deprecated but still work (forwarders provided) SSKMetalRenderer.drawSprites:acceptsviewportSize:in points for backward compatibility—it automatically converts to pixels using the render target dimensions
See Demos/DVDLogoMetal/ for a complete working example with bounce physics, color cycling, and flip-on-bounce effects.
For troubleshooting Metal rendering issues, use SSKMetalRenderDiagnostics:
// Create diagnostics helper
self.renderDiagnostics = [[SSKMetalRenderDiagnostics alloc] init];
// Attach to your Metal layer
[self.renderDiagnostics attachToMetalLayer:self.metalLayer];
// Update status as you initialize components
self.renderDiagnostics.deviceStatus = [NSString stringWithFormat:@"Device: %@", device.name];
self.renderDiagnostics.layerStatus = @"Layer: configured";
self.renderDiagnostics.rendererStatus = @"Renderer: ready";
// In animateOneFrame, record rendering attempts
BOOL renderSuccess = [self.particleSystem renderWithMetalRenderer:self.metalRenderer
blendMode:self.particleSystem.blendMode
viewportSize:self.bounds.size];
[self.renderDiagnostics recordMetalAttemptWithSuccess:renderSuccess];
// Update overlay with custom info
NSArray *extraInfo = @[
[NSString stringWithFormat:@"Particles: %lu", self.particleSystem.aliveParticleCount]
];
[self.renderDiagnostics updateOverlayWithTitle:@"My Saver"
extraLines:extraInfo
framesPerSecond:self.animationClock.framesPerSecond];The diagnostics overlay displays:
- Device: Metal device name and capabilities (e.g., "Apple M1", low power status)
- Layer: CAMetalLayer configuration status
- Renderer: SSKMetalRenderer initialization state
- Drawable: Drawable availability and acquisition success
- Metal successes / fallbacks: Running counter of rendering attempts
- FPS: Current frame rate
Toggle the overlay on/off with:
self.renderDiagnostics.overlayEnabled = NO; // Hide overlay
self.renderDiagnostics.overlayEnabled = YES; // Show overlay (default)See Demos/MetalParticleTest/ for a complete diagnostic implementation example, or Demos/MetalDiagnostic/ for a low-level Metal sanity checker that tests device, layer, and drawable initialization.
ScreenSaverKit includes comprehensive testing and performance evaluation tools, plus major performance optimizations for the particle system.
ScreenSaverKit now includes three major performance optimizations:
- Alive Particle Tracking - Automatic ~100x speedup for sparse particle systems
- Async Rendering Mode - Optional previous-frame rendering to eliminate GPU waits
- Indirect Rendering - Optional GPU-side instance buffer building
See PERFORMANCE_OPTIMIZATIONS.md for complete documentation on how to use these features and maximize performance.
Quick Start:
SSKParticleSystem *system = [[SSKParticleSystem alloc] initWithCapacity:10000];
system.metalSimulationRenderMode = SSKMetalSimulationRenderModePreviousFrame;
SSKMetalParticleRenderer *renderer = [[SSKMetalParticleRenderer alloc] initWithLayer:layer];
renderer.useIndirectRendering = YES;See PERFORMANCE_TESTING.md for detailed documentation on:
- Unit Tests (
Tests/) - Functional correctness tests for core components - Performance Benchmark Screensaver (
Demos/PerformanceBenchmark/) - Real-time metrics visualization with configurable test scenarios - Standalone Benchmark Tool (
Tools/Benchmark/) - Automated performance regression testing with JSON/CSV output
The performance testing suite helps verify functionality, measure frame rates, identify bottlenecks, and track performance regressions across different hardware configurations.
-
TemplateSaverView.h/.m– a minimal saver that animates a few shapes and responds to preference changes. Copy and rename these files to kick off a new project. -
TemplateInfo.plist– barebones bundle metadata. Update the identifiers and version fields to match your saver. -
Makefile.demo– shows how to compile a.saverbundle using the template view plusSSKScreenSaverView. Runmake -f ScreenSaverKit/Makefile.demofrom your project root (or copy it beside your sources) and tweak the variables at the top for your module name and bundle ID. -
The template demonstrates the configuration sheet helpers (sliders + checkbox), diagnostics overlay toggling, and the animation clock workflow.
-
Demos/HelloWorld/– a ready-to-build "Hello, World" saver that bounces text around the screen with optional colour cycling. Build it viamake -f Demos/HelloWorld/Makefile. See tutorial.md for a complete walkthrough using this demo. -
Demos/Starfield/– a classic faux-3D starfield with optional motion blur and drifting trajectory changes. Build it viamake -f Demos/Starfield/Makefile. -
Demos/SimpleLines/– layered drifting lines with palette selection and adjustable colour cycling speed. Build it viamake -f Demos/SimpleLines/Makefile. -
Demos/DVDlogo/– retro floating DVD logo with solid or rotating palette colour modes, adjustable size, speed, colour cycling, and optional random start behaviour. It also uses a multi-file project structure to demo a more advanced project structure. Build it viamake -f Demos/DVDlogo/Makefile. -
Demos/DVDLogoMetal/– Metal-accelerated version of the DVD logo screensaver demonstrating the 2D sprite rendering system. UsesSSKMetalSpritePassfor GPU-accelerated sprite rendering with color cycling on bounce. Includes tutorial documentation explaining sprite rendering concepts. Build it viamake -f Demos/DVDLogoMetal/Makefile. SeeDemos/DVDLogoMetal/README.mdfor detailed documentation. -
Demos/RibbonFlow/– flowing additive ribbons inspired by the classic Apple Flurry screensaver. Demonstrates Metal-accelerated particle rendering with theSSKParticleSystemandSSKMetalParticleRendererworking together for smooth, GPU-powered effects. Build it viamake -f Demos/RibbonFlow/Makefile. -
Demos/Rain/– classic retro rain animation screensaver with GPU-accelerated particle spawning and optional z-depth perspective effects. Demonstrates simple particle-based effects, adjustable rain angle/speed/density, and hardware-accelerated z-depth calculations for realistic 3D depth illusion. Features include brightness control, width/length customization, and optional FPS counter. Perfect example of usingspawnParticlesGPU:parameters:with z-depth support. Build it viamake -f Demos/Rain/Makefile. SeeDemos/Rain/README.mdfor detailed documentation. -
Demos/MetalParticleTest/– diagnostic particle fountain with automatic Metal/CPU fallback. Shows real-time rendering statistics, particle counts, and detailed Metal pipeline status. Perfect for testing GPU availability and debugging Metal particle renderer issues. Build it viamake -f Demos/MetalParticleTest/Makefile. -
Demos/MetalDiagnostic/– low-level Metal sanity checker that displays device capabilities, layer configuration, drawable status, and command buffer lifecycle on-screen. Useful for diagnosing Metal initialization issues or verifying hardware support. Build it viamake -f Demos/MetalDiagnostic/Makefile. -
scripts/install-and-refresh.sh– convenience script that builds, installs, and restarts the relevant macOS services (legacyScreenSaver,WallpaperAgent,ScreenSaverEngine) so macOS immediately sees your latest bundle. Usage:./scripts/install-and-refresh.sh Demos/Starfield ./scripts/install-and-refresh.sh ScreenSaverKit -f Makefile.demo
The first argument is the directory containing the saver Makefile; any additional arguments are passed straight through to each
makeinvocation. -
scripts/refresh-screensaver-services.sh– lightweight helper that clears all macOS screen saver caches and optionally relaunchesScreenSaverEngine. Clears: System Settings,legacyScreenSaver,WallpaperAgent,ScreenSaverEngine,cfprefsd(preferences daemon),iconservicesd(icon cache), andlsd(Launch Services). Use when you've already installed a bundle:./scripts/refresh-screensaver-services.sh # clear caches ./scripts/refresh-screensaver-services.sh --launch # clear caches + relaunch preview
If you tweak code inside ScreenSaverKit/, rebuild any demos you want to test so they pick up the new implementation:
cd Demos/Starfield && make clean all
cd Demos/SimpleLines && make clean allAfter installing the refreshed bundle, restart the caching daemons to force macOS to load the new bits:
./scripts/refresh-screensaver-services.sh
./scripts/refresh-screensaver-services.sh --launch # optionally relaunches the previewThis mirrors the workflow shown earlier (make …, then refresh) and avoids the “preview updated, full screen is stale” confusion that can happen otherwise.
# From the project root (or wherever you copied the kit)
make -f ScreenSaverKit/Makefile.demo clean all-
Outputs a bundle at
ScreenSaverKit/DemoBuild/TemplateSaver.saver. -
Compiles a universal binary that supports both
arm64(Apple Silicon) andx86_64(Intel) via the-archflags already inMakefile.demo. -
Verify the architectures with:
file ScreenSaverKit/DemoBuild/TemplateSaver.saver/Contents/MacOS/TemplateSaver
-
Install locally for testing:
make -f ScreenSaverKit/Makefile.demo run
Update SCREENSAVER_NAME, BUNDLE_ID, and PRINCIPAL_CLASS at the top of the Makefile when you adapt the template for your own saver.
macOS Ventura and newer require downloaded screen savers to be signed with a Developer ID certificate (and ideally notarized) before they will load without warnings. Replace the placeholder values below with your own Team ID and bundle details.
# Sign the bundle
codesign --force --timestamp --options runtime \
--identifier com.example.templatesaver \
--sign "Developer ID Application: Your Name (TEAMID)" \
ScreenSaverKit/DemoBuild/TemplateSaver.saver
# Optional: verify signature
codesign --verify --strict --verbose=2 ScreenSaverKit/DemoBuild/TemplateSaver.saver
# Optional: zip bundle then submit for notarization (requires a notarytool profile)
ditto -c -k --keepParent ScreenSaverKit/DemoBuild/TemplateSaver.saver \
ScreenSaverKit/DemoBuild/TemplateSaver.saver.zip
xcrun notarytool submit ScreenSaverKit/DemoBuild/TemplateSaver.saver.zip \
--keychain-profile your-notary-profile --waitIf you follow the same structure as the demo Makefile, you can reuse the root-level sign, zip, and notarize targets (update the variables to match your saver).
Use the provided convenience methods when you want to manipulate preferences manually:
- (ScreenSaverDefaults *)preferences;- (NSDictionary<NSString *, id> *)currentPreferences;- (void)setPreferenceValue:(id)value forKey:(NSString *)key;- (void)removePreferenceForKey:(NSString *)key;- (void)resetPreferencesToDefaults;
The root Makefile already includes ScreenSaverKit/SSKScreenSaverView.m in the build. When starting a new saver:
- Update
SCREENSAVER_NAME,BUNDLE_ID, andInfo.plistto match your saver. - Add your own
.mfiles to theSOURCESlist. - Run
makeormake testto produce a.saverbundle.
To migrate an older saver code base:
- Replace
ScreenSaverViewsuperclass usages withSSKScreenSaverView. - Remove any custom preference polling timers – the kit handles it now.
- Move default registration into
-defaultPreferences. - Migrate preference reload code into
-preferencesDidChange:changedKeys:.
Use these steps to retrofit the kit into existing code and keep the rendering logic focused on your unique saver behavior.
Symptoms: Metal particle system renders black screen, or always falls back to CPU mode.
Common causes:
-
Fragment shader not receiving instance data - Ensure the instance buffer is bound to both vertex AND fragment shaders:
[encoder setVertexBuffer:instanceBuffer offset:0 atIndex:1]; [encoder setFragmentBuffer:instanceBuffer offset:0 atIndex:1]; // Don't forget this!
-
Particles spawning "dead" - Particles must start with
life = 0.0, notlife = maxLife:particle.life = 0.0; // ✅ Correct - particle starts alive particle.maxLife = 2.0; // NOT: particle.life = particle.maxLife; // ❌ Particle spawns already dead
-
Layer not attached before renderer initialization - Wait for view to be in window:
- (void)viewDidMoveToWindow { [super viewDidMoveToWindow]; if (self.window) { [self setupMetalRenderer]; // Only after window attachment } }
-
Check Console.app for Metal shader compilation errors - Filter for "SSKMetalParticleRenderer" to see detailed error messages.
Symptoms: Rebuilt screen saver but System Settings shows old version.
Solution: Run the cache refresh script:
./scripts/refresh-screensaver-services.shOr use the full install-and-refresh workflow:
./scripts/install-and-refresh.sh Demos/YourSaverSymptoms: Bundle installed to ~/Library/Screen Savers/ but doesn't show up in list.
Common causes:
- Bundle not properly formed - Verify with:
ls -la ~/Library/Screen\ Savers/YourSaver.saver/Contents/MacOS/ - Info.plist issues - Ensure
NSPrincipalClassmatches your view class name exactly - Launch Services database stale - The refresh script now clears this automatically
Symptoms: Changes in System Settings don't appear until restarting preview.
Solution: The kit polls preferences every 2 seconds. Changes should appear automatically. If not:
- Verify you implemented
preferencesDidChange:changedKeys: - Check that
defaultPreferencesreturns the correct keys - Ensure you're not caching values that should update


