29

I'm writing a command line tool with Swift and I'm having trouble displaying colors in my shell. I'm using the following code:

println("\033[31;32mhey\033[39;39m")

or even

NSFileHandle.fileHandleWithStandardOutput().writeData("\033[31;32mhey\033[39;39m".dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: true)!)

It works when I use a simple echo in php (the text is displayed in green) but is there a reason it doesn't work in a Swift command line tool?

Thanks!

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Romain Pouclet
  • 864
  • 2
  • 8
  • 17

7 Answers7

42

Swift has built in unicode support. This invalidates using of back slash. So that I use color codes with "\u{}" syntax. Here is a println code which works perfectly on terminal.

// \u{001B}[\(attribute code like bold, dim, normal);\(color code)m

// Color codes
// black   30
// red     31
// green   32
// yellow  33
// blue    34
// magenta 35
// cyan    36
// white   37

println("\u{001B}[0;33myellow")

Hope it helps.

Diego Freniche
  • 5,225
  • 3
  • 32
  • 45
cyt
  • 1,164
  • 14
  • 15
  • 1
    I doesn't work for me with swift 1.2 programming for iOS, I don't see the colors in the output – eliocs Jun 29 '15 at 14:03
  • 2
    The Xcode console doesn't print colors in you don't install the XcodeColors plugin -> https://github.com/robbiehanson/XcodeColors – eliocs Jun 29 '15 at 14:26
  • 1
    Is there a way to have partial colored string and the rest of the string in the default console color? – Giuseppe Lanza Sep 13 '17 at 13:49
34

Based on @cyt answer, I've written a simple enum with these colors and also overloaded + operator so you can print using that enum.

It's all up on Github, but it's really that simple:

enum ANSIColors: String {
    case black = "\u{001B}[0;30m"
    case red = "\u{001B}[0;31m"
    case green = "\u{001B}[0;32m"
    case yellow = "\u{001B}[0;33m"
    case blue = "\u{001B}[0;34m"
    case magenta = "\u{001B}[0;35m"
    case cyan = "\u{001B}[0;36m"
    case white = "\u{001B}[0;37m"
    case `default` = "\u{001B}[0;0m"
    
    func name() -> String {
        switch self {
        case .black: return "Black"
        case .red: return "Red"
        case .green: return "Green"
        case .yellow: return "Yellow"
        case .blue: return "Blue"
        case .magenta: return "Magenta"
        case .cyan: return "Cyan"
        case .white: return "White"
        case .default: return "Default"
        }
    }
    
    static func all() -> [ANSIColors] {
        return [.black, .red, .green, .yellow, .blue, .magenta, .cyan, .white]
    }
}

func + (left: ANSIColors, right: String) -> String {
    return left.rawValue + right
}

// END


// Demo:

for c in ANSIColors.all() {
    print(c + "This is printed in " + c.name())
}
Justin Vallely
  • 5,932
  • 3
  • 30
  • 44
Diego Freniche
  • 5,225
  • 3
  • 32
  • 45
  • 13
    `"\u{001B}[0;0m"` to reset to default color. – devios1 Dec 10 '17 at 19:06
  • 2
    By the way, Swift now provides a `CaseIterable` protocol, which synthesizes an `allCases` method for your enum that returns an array of all the cases. It doesn't work for cases with associated values, although it is compatible with cases with raw values. See https://developer.apple.com/documentation/swift/caseiterable – Peter Schorn Jun 04 '20 at 16:12
12

You can use Rainbow if you don't mind using it as a framework.

import Rainbow
print("Red text".red)
print("Yellow background".onYellow)
print("Light green text on white background".lightGreen.onWhite)

https://github.com/onevcat/Rainbow

webcpu
  • 3,174
  • 2
  • 24
  • 17
8

Combining some of @Diego's answer, you can use Swift's new DefaultStringInterpolation structure to extend this decoration into your string literals–

enum ASCIIColor: String {
    case black = "\u{001B}[0;30m"
    case red = "\u{001B}[0;31m"
    case green = "\u{001B}[0;32m"
    case yellow = "\u{001B}[0;33m"
    case blue = "\u{001B}[0;34m"
    case magenta = "\u{001B}[0;35m"
    case cyan = "\u{001B}[0;36m"
    case white = "\u{001B}[0;37m"
    case `default` = "\u{001B}[0;0m"
}

extension DefaultStringInterpolation {
    mutating func appendInterpolation<T: CustomStringConvertible>(_ value: T, color: ASCIIColor) {
        appendInterpolation("\(color.rawValue)\(value)\(ASCIIColor.default.rawValue)")
    }
}
// USAGE:
// "\("only this string will be green!", color: .green)"
Charlton Provatas
  • 2,184
  • 25
  • 18
5

Elegant Solution:

struct Colors {
    static let reset = "\u{001B}[0;0m"
    static let black = "\u{001B}[0;30m"
    static let red = "\u{001B}[0;31m"
    static let green = "\u{001B}[0;32m"
    static let yellow = "\u{001B}[0;33m"
    static let blue = "\u{001B}[0;34m"
    static let magenta = "\u{001B}[0;35m"
    static let cyan = "\u{001B}[0;36m"
    static let white = "\u{001B}[0;37m"
}

Demo

print(Colors.yellow + "Please Enter the Output Directory Name:" + Colors.reset)

or

print(Colors.yellow + "Please " + Colors.blue + "Enter " + Colors.magenta + "the Output Directory Name:" + Colors.reset)
MGY
  • 7,245
  • 5
  • 41
  • 74
4

Expanding upon Diego Freniche's answer we can incorporate the functionality of Rainbow, as referenced in Uncharted Works's Answer, without needing to import the framework itself using a simple String extension:

enum ANSIColor: String {

    typealias This = ANSIColor

    case black = "\u{001B}[0;30m"
    case red = "\u{001B}[0;31m"
    case green = "\u{001B}[0;32m"
    case yellow = "\u{001B}[0;33m"
    case blue = "\u{001B}[0;34m"
    case magenta = "\u{001B}[0;35m"
    case cyan = "\u{001B}[0;36m"
    case white = "\u{001B}[0;37m"
    case `default` = "\u{001B}[0;0m"

    static var values: [This] {
        return [.black, .red, .green, .yellow, .blue, .magenta, .cyan, .white, .default]
    }

    static var names: [This: String] = {
        return [
            .black: "black",
            .red: "red",
            .green: "green",
            .yellow: "yellow",
            .blue: "blue",
            .magenta: "magenta",
            .cyan: "cyan",
            .white: "white",
            .default: "default",
        ]
    }

    var name: String {
        return This.names[self] ?? "unknown"
    }

    static func + (lhs: This, rhs: String) -> String {
        return lhs.rawValue + rhs
    }

    static func + (lhs: String, rhs: This) -> String {
        return lhs + rhs.rawValue
    }

}
extension String {

    func colored(_ color: ANSIColor) -> String {
        return color + self + ANSIColor.default
    }

    var black: String {
        return colored(.black)
    }

    var red: String {
        return colored(.red)
    }

    var green: String {
        return colored(.green)
    }

    var yellow: String {
        return colored(.yellow)
    }

    var blue: String {
        return colored(.blue)
    }

    var magenta: String {
        return colored(.magenta)
    }

    var cyan: String {
        return colored(.cyan)
    }

    var white: String {
        return colored(.white)
    }

}
rolling_codes
  • 15,174
  • 22
  • 76
  • 112
1

Use the font from ColoredConsole plus an extension to String.

  1. Install ColoredConsole-Bold.ttf
  2. In Xcode, set the font for console output to "Colored Console Bold"
  3. Add this extension to String in your project (loosely based on jjrscott)

import Foundation

public extension String {
    var consoleColor: ColoredConsoleString {
        ColoredConsoleString(self)
    }
}

public class ColoredConsoleString {
    var string: String
    
    public enum ColorSelectors: String {
        case red = "\u{fe06}"
        case green = "\u{fe07}"
        case brown = "\u{fe08}"
        case teal = "\u{fe09}"
        case purple = "\u{fe0a}"
    }
    
    init(_ str: String) {
        string = str
    }
    
    public var teal: String {
        wrappedString(color: .teal, string)
    }
    
    public var purple: String {
        wrappedString(color: .purple, string)
    }
    
    public var red: String {
        wrappedString(color: .red, string)
    }
    
    public var green: String {
        wrappedString(color: .green, string)
    }
    
    public var brown: String {
        wrappedString(color: .brown, string)
    }
    
    private func wrappedString(color: ColorSelectors, _ string: String) -> String {
        Array(string).map({ "\($0)\(color.rawValue)"}).joined()
    }
}

  1. You can now print strings of 3 different colors to the console:
    print("red".consoleColor.red)
    print("green!".consoleColor.green)
    print("purple".consoleColor.purple)
    print("brown".consoleColor.brown)
    print("teal".consoleColor.teal)

enter image description here

Works with Xcode 14.3, and probably older versions.

  • Note: The po in po print("red".consoleColor.red) in the screenshot tells Xcode's debugger (lldb) to evaluate and print the expression. More about po here
spnkr
  • 952
  • 9
  • 18