Extracting RGBA Components from Color in SwiftUI, UIKit and AppKit


If you’re saving user colours, tweaking animations, or just trying to debug what you see on screen, Color doesn’t give you direct access to its components. In this post, we’ll fix that with simple extensions for UIColor, NSColor, and Color to reliably extract RGBA values across platforms. It’s a lightweight solution that works on all platforms.


UIColor

UIKit gives us built-in support for extracting colour components via the getRed(_:green:blue:alpha:) method, provided the colour can be represented in the RGB colour space. So this extension really is just a more convenient way to get to these values.

Here’s the extension:

extension UIColor {
    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        
        getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        
        return (red, green, blue, alpha)
    }
}

This approach handles nearly all colours you’ll work with in a SwiftUI context. If the colour isn’t compatible with RGB, the method will return zeroes — but that’s a rare edge case unless you’re dealing with system-defined colours or custom colour spaces.


NSColor

On macOS, NSColor supports many colour spaces, so you must explicitly convert it to one that supports RGB components. In this case, we use .deviceRGB:

extension NSColor {
    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        let convertedColor = self.usingColorSpace(.deviceRGB)
        return (
            red: convertedColor?.redComponent ?? 0,
            green: convertedColor?.greenComponent ?? 0,
            blue: convertedColor?.blueComponent ?? 0,
            alpha: convertedColor?.alphaComponent ?? 0
        )
    }
}

By using optional chaining and nil coalescing, we gracefully handle any cases where conversion fails by returning 0 for each component.


SwiftUIColor 

Finally, we bring it all together with an extension on SwiftUI’s Color type. Internally, SwiftUI converts Color to UIColor or NSColor, which we can use to get the RGBA values.

extension Color {
    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        #if os(macOS)
        let convertedColor = NSColor(self)
        #else
        let convertedColor = UIColor(self)
        #endif
        
        return convertedColor.rgba
    }
}

This abstraction allows us to write cross-platform SwiftUI code that inspects colour components in a way that feels native and idiomatic, without worrying about the underlying platform-specific types.


Example: Reading RGBA Components in SwiftUI

Let’s see how this might be used in a SwiftUI view with a complete set of our extensions:

import SwiftUI

struct ColourDetailsView: View {
    let colour: Color
    
    var body: some View {
        let rgba = colour.rgba

        VStack(alignment: .leading, spacing: 8) {
            Rectangle()
                .fill(colour)
                .frame(height: 100)
                .cornerRadius(8)

            Text("Red: \(rgba.red, specifier: "%.2f")")
            Text("Green: \(rgba.green, specifier: "%.2f")")
            Text("Blue: \(rgba.blue, specifier: "%.2f")")
            Text("Alpha: \(rgba.alpha, specifier: "%.2f")")
        }
        .padding()
    }
}

#Preview {
    ColourDetailsView(colour: .red)
}

This simple view gives you both a visual and numerical representation of the colour’s components, useful for debugging or UI prototyping.


Summary

SwiftUI makes it easy to define and use colours, but inspecting them requires some help from the underlying frameworks. By bridging Color to UIColor or NSColor, we can cleanly and safely extract RGBA components for any colour.

This extension gives you a single API to work with across all platforms:

  • Simple and concise: just call .rgba on any Color
  • Cross-platform compatible with minimal code
  • Useful for debugging, persistence, animation, and more

This is the first in a series of practical SwiftUI extensions I rely on across all my projects. These small tools help bridge gaps in the framework and streamline your workflow, making it easier to build robust, flexible, and expressive UIs.


Complete Code

And finally, for ease of use here is the complete code we used for easy copying:

import SwiftUI

struct ColourDetailsView: View {
    let colour: Color
    
    var body: some View {
        let rgba = colour.rgba

        VStack(alignment: .leading, spacing: 8) {
            Rectangle()
                .fill(colour)
                .frame(height: 100)
                .cornerRadius(8)

            Text("Red: \(rgba.red, specifier: "%.2f")")
            Text("Green: \(rgba.green, specifier: "%.2f")")
            Text("Blue: \(rgba.blue, specifier: "%.2f")")
            Text("Alpha: \(rgba.alpha, specifier: "%.2f")")
        }
        .padding()
    }
}

#Preview {
    ColourDetailsView(colour: .red)
}



extension Color {
    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
#if os(macOS)
        let convertedColor = NSColor(self)
#else
        let convertedColor = UIColor(self)
#endif
        
        return convertedColor.rgba
    }
}


#if os(macOS)
extension NSColor {
    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        let convertedColor = self.usingColorSpace(.deviceRGB)
        return (
            red: convertedColor?.redComponent ?? 0,
            green: convertedColor?.greenComponent ?? 0,
            blue: convertedColor?.blueComponent ?? 0,
            alpha: convertedColor?.alphaComponent ?? 0
        )
    }
}
#else

extension UIColor {
    var rgba: (red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) {
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        
        getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        
        return (red, green, blue, alpha)
    }
}
#endif

Leave a comment