Color ouput with Swift command line tool
Asked Answered
B

7

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!

Bentinck answered 6/1, 2015 at 21:59 Comment(1)
check out this amazing solution here: #9006269Casta
A
43

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.

Arlon answered 6/1, 2015 at 22:34 Comment(3)
I doesn't work for me with swift 1.2 programming for iOS, I don't see the colors in the outputGrandmotherly
The Xcode console doesn't print colors in you don't install the XcodeColors plugin -> github.com/robbiehanson/XcodeColorsGrandmotherly
Is there a way to have partial colored string and the rest of the string in the default console color?Pled
P
36

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())
}
Paulpaula answered 26/5, 2015 at 9:36 Comment(2)
"\u{001B}[0;0m" to reset to default color.Awe
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 developer.apple.com/documentation/swift/caseiterableWarta
O
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

Outworn answered 4/6, 2016 at 17:28 Comment(0)
R
9

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)"
Robynroc answered 8/6, 2019 at 22:54 Comment(1)
Thanks for also adding defaultContrastive
B
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)
Bilingual answered 13/11, 2020 at 6:5 Comment(0)
C
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)
    }

}
Contrastive answered 11/6, 2019 at 15:53 Comment(0)
E
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
Eveliaevelin answered 15/6, 2023 at 6:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.