Simple Swift report application source code

This program is provided for use as input to the procedure Quick Start: Write a Swift report application.

It is a simple example; in a real world situation, the data would be read from another source, an iterator would read the data into memory and stream that data one record at a time. For information about the requirements of a Swift report application, see Provide the data source (GRW for Swift).
/*
 * FOURJS_START_COPYRIGHT(U,2016)
 * Property of Four Js*
 * (c) Copyright Four Js 2016, 2022. All Rights Reserved.
 * * Trademark of Four Js Development Tools Europe Ltd
 *   in the United States and elsewhere
 * 
 * Four Js and its suppliers do not warrant or guarantee that these samples are
 * accurate and suitable for your purposes.
 * Their inclusion is purely for information purposes only.
 * FOURJS_END_COPYRIGHT
 */

import Foundation
import libgre
import AppKit

func addElement(_ content_handler: UnsafeMutablePointer<ContentHandler>, _ name: String, _ content: String)
{
    startElement(content_handler, name)
    characters(content_handler, content)
    endElement (content_handler, name)
}

func convertDateComponentsToString(_ dateComponents: DateComponents?, _ format: String? = "yyyy-MM-dd HH:mm:ss.SSSSS") -> String?
{
    if dateComponents == nil { return nil }

    if let calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.ISO8601),
        let date = calendar.date(from: dateComponents!) {
        let dateFormatter = DateFormatter()
        dateFormatter.calendar = Calendar(identifier: .iso8601)
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        dateFormatter.dateFormat = format

        return dateFormatter.string(from: date)
    }

    return ""
}

public class Sales
{
    public var content_handler: UnsafeMutablePointer<ContentHandler>

    var shopName: String
    var zipCode: Int
    var day: DateComponents
    var items = [SalesItem]()

    public init?(_ content_handler: UnsafeMutablePointer<ContentHandler>,
                    _ shopName: String,
                    _ zipCode: Int,
                    _ day: DateComponents)
    {
        self.content_handler = content_handler

        self.shopName = shopName
        self.zipCode = zipCode
        self.day = day

        self.items.append(SalesItem("Tablelamp", SalesItem.Category.Furniture, 23.00, nil));
        self.items.append(SalesItem("Tablelamp", SalesItem.Category.Furniture, 267.00, self.items.last));
        self.items.append(SalesItem("Officechair", SalesItem.Category.Furniture, 155.00, self.items.last));
        self.items.append(SalesItem("Grandfather clock", SalesItem.Category.Furniture, 329.00, self.items.last));
        self.items.append(SalesItem("Scissors", SalesItem.Category.Supplies, 19.00, self.items.last));
        self.items.append(SalesItem("Measuring tape", SalesItem.Category.Supplies, 23.00, self.items.last));
        self.items.append(SalesItem("Sunglasses", SalesItem.Category.Travelling, 15.95, self.items.last));
        self.items.append(SalesItem("Penknife", SalesItem.Category.Travelling, 6.25, self.items.last));
        self.items.append(SalesItem("Ornateangel", SalesItem.Category.Art, 1.95, self.items.last));

        self.fetch()
    }

    public func fetch()
    {
        startElement(content_handler, "sales")
        attribute(content_handler, "xmlns", "http://www.4js.com/2004/REPORT")

        addElement(content_handler, "shopName", shopName)
        addElement(content_handler, "zipCode", "\(zipCode)")
        addElement(content_handler, "day", convertDateComponentsToString(day)!)

        for item in items
        {
            item.serializeToXML(content_handler)
        }

        endElement(content_handler, "sales")
    }

    class SalesItem
    {
        enum Category: String {
            case Furniture, Art, Supplies, Travelling
        }

        var articleName: String
        var category: Category
        var price: Double
        var runningTotal: Double

        // The previous item is passed to allow computing the running total.
        init(_ articleName: String, _ category: Category, _ price: Double, _ previousItem: SalesItem?)
        {
            self.articleName = articleName
            self.category = category
            self.price = price

            self.runningTotal = (previousItem == nil ? price : previousItem!.runningTotal + price)
        }

        func serializeToXML(_ content_handler: UnsafeMutablePointer<ContentHandler>)
        {
            startElement(content_handler, "items")

            addElement(content_handler, "articleName", articleName)
            addElement(content_handler, "category", category.rawValue)
            addElement(content_handler, "price", "\(price)")
            addElement(content_handler, "runningTotal", "\(runningTotal)")

            endElement(content_handler, "items")
        }
    }
}

/**
 * MAIN
 *
 *  Runs the report using the design file specified in args[0] or "SalesList.4rp" otherwise.
 *  The program creates the file "SalesList.pdf" and opens it using
 *  NSWorkspace.shared().open() which will typically
 *  invoke the Preview application.
 */

var designFile: String
var outputFilename: String = "SalesList.pdf"

if CommandLine.argc == 1 {
    designFile = "SalesList.4rp"
} else {
    designFile = CommandLine.arguments[1]
}

let rcPtr = createRuntimeConfiguration(designFile)
setOutputFileName(rcPtr, outputFilename)
selectDevice (rcPtr, PDF)
configureDistributedProcessing(rcPtr, "127.0.0.1", 7000)
guard let content_handler = createContentHandler(rcPtr) else {
    print("Info: Report Writer server may not be started. Launch it with the following command: greportwriter -l 7000")
    exit(EXIT_FAILURE)
}

let currentDate = Date()
let userCurrentCalendar = Calendar.current
let currentDateComponents = userCurrentCalendar.dateComponents([.year, .month, .day], from: currentDate)

let reportLauncher = Sales(content_handler, "Columbus Arts", 75038, currentDateComponents)

// Open the file

let reportFileURL = URL(fileURLWithPath: "../" + outputFilename).absoluteURL
print(reportFileURL)
guard NSWorkspace.shared().open(reportFileURL) else {
    print("Error opening \(outputFilename).")
    exit(EXIT_FAILURE)
}

exit(EXIT_SUCCESS)