master b4fe117e11bc cached
33 files
168.1 KB
40.0k tokens
1 requests
Download .txt
Repository: RobertGummesson/BuildTimeAnalyzer-for-Xcode
Branch: master
Commit: b4fe117e11bc
Files: 33
Total size: 168.1 KB

Directory structure:
gitextract_fhrgbqty/

├── .gitignore
├── .travis.yml
├── BuildTimeAnalyzer/
│   ├── AppDelegate.swift
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   └── ScreenShot.imageset/
│   │       └── Contents.json
│   ├── BuildManager.swift
│   ├── BuildTimeAnalyzer-Bridging-Header.h
│   ├── CSVExporter.swift
│   ├── CompileMeasure.swift
│   ├── DerivedDataManager.swift
│   ├── DirectoryMonitor.swift
│   ├── File.swift
│   ├── Info.plist
│   ├── LogProcessor.swift
│   ├── Main.storyboard
│   ├── NSAlert+Extensions.swift
│   ├── ProcessingState.swift
│   ├── ProjectSelection.swift
│   ├── RawMeasure.swift
│   ├── UserSettings.swift
│   ├── ViewController.swift
│   ├── ViewControllerDataSource.swift
│   └── XcodeDatabase.swift
├── BuildTimeAnalyzer.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── xcshareddata/
│       └── xcschemes/
│           └── BuildTimeAnalyzer.xcscheme
├── BuildTimeAnalyzerTests/
│   ├── CompileMeasureTests.swift
│   ├── Info.plist
│   └── ViewControllerDataSourceTest.swift
├── LICENSE
└── README.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## Build generated
build/
DerivedData

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata

## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md

fastlane/report.xml
fastlane/screenshots
.DS_Store


================================================
FILE: .travis.yml
================================================
language: swift
osx_image: xcode9.3
script: xcodebuild -project BuildTimeAnalyzer.xcodeproj -scheme BuildTimeAnalyzer build test


================================================
FILE: BuildTimeAnalyzer/AppDelegate.swift
================================================
//
//  AppDelegate.swift
//  BuildTimeAnalyzer
//

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    
    @IBOutlet weak var projectSelectionMenuItem: NSMenuItem!
    @IBOutlet weak var buildTimesMenuItem: NSMenuItem!
    @IBOutlet weak var alwaysInFrontMenuItem: NSMenuItem!

    @objc var canExport: Bool = false
    
    var viewController: ViewController? {
        return NSApplication.shared.mainWindow?.contentViewController as? ViewController
    }
    
    func applicationDidFinishLaunching(_ notification: Notification) {
        alwaysInFrontMenuItem.state = UserSettings.windowShouldBeTopMost ? .on : .off
    }
    
    func configureMenuItems(showBuildTimesMenuItem: Bool) {
        projectSelectionMenuItem.isEnabled = !showBuildTimesMenuItem
        buildTimesMenuItem.isEnabled = showBuildTimesMenuItem
    }
    
    // MARK: Actions
    
    @IBAction func navigateToProjectSelection(_ sender: NSMenuItem) {
        configureMenuItems(showBuildTimesMenuItem: true)
        
        viewController?.cancelProcessing()
        viewController?.showInstructions(true)
    }
    
    @IBAction func navigateToBuildTimes(_ sender: NSMenuItem) {
        configureMenuItems(showBuildTimesMenuItem: false)
        viewController?.showInstructions(false)
    }
    
    @IBAction func visitGitHubPage(_ sender: AnyObject) {
        let path = "https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode"
        if let url = URL(string: path) {
            NSWorkspace.shared.open(url)
        }
    }
    
    @IBAction func toggleAlwaysInFront(_ sender: NSMenuItem) {
        let alwaysInFront = sender.state == .off
        
        sender.state = alwaysInFront ? .on : .off
        UserSettings.windowShouldBeTopMost = alwaysInFront
        
        viewController?.makeWindowTopMost(topMost: alwaysInFront)
    }
}



================================================
FILE: BuildTimeAnalyzer/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "size" : "16x16",
      "idiom" : "mac",
      "filename" : "logo16.png",
      "scale" : "1x"
    },
    {
      "size" : "16x16",
      "idiom" : "mac",
      "filename" : "logo32-1.png",
      "scale" : "2x"
    },
    {
      "size" : "32x32",
      "idiom" : "mac",
      "filename" : "logo32.png",
      "scale" : "1x"
    },
    {
      "size" : "32x32",
      "idiom" : "mac",
      "filename" : "logo64.png",
      "scale" : "2x"
    },
    {
      "size" : "128x128",
      "idiom" : "mac",
      "filename" : "logo128.png",
      "scale" : "1x"
    },
    {
      "size" : "128x128",
      "idiom" : "mac",
      "filename" : "logo256-1.png",
      "scale" : "2x"
    },
    {
      "size" : "256x256",
      "idiom" : "mac",
      "filename" : "logo256.png",
      "scale" : "1x"
    },
    {
      "size" : "256x256",
      "idiom" : "mac",
      "filename" : "logo512-1.png",
      "scale" : "2x"
    },
    {
      "size" : "512x512",
      "idiom" : "mac",
      "filename" : "logo512.png",
      "scale" : "1x"
    },
    {
      "size" : "512x512",
      "idiom" : "mac",
      "filename" : "logo512@2x.png",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: BuildTimeAnalyzer/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: BuildTimeAnalyzer/Assets.xcassets/ScreenShot.imageset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "universal",
      "filename" : "Screen Shot.png",
      "scale" : "1x"
    },
    {
      "idiom" : "universal",
      "scale" : "2x"
    },
    {
      "idiom" : "universal",
      "scale" : "3x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: BuildTimeAnalyzer/BuildManager.swift
================================================
//
//  BuildManager.swift
//  BuildTimeAnalyzer
//

import Cocoa

protocol BuildManagerDelegate: AnyObject {
    func derivedDataDidChange()
    func buildManager(_ buildManager: BuildManager, shouldParseLogWithDatabase database: XcodeDatabase)
}

class BuildManager: NSObject {
    
    weak var delegate: BuildManagerDelegate?
    
    private let derivedDataDirectoryMonitor = DirectoryMonitor(isDerivedData: true)
    private let logFolderDirectoryMonitor = DirectoryMonitor(isDerivedData: false)
    
    private var currentDataBase: XcodeDatabase?
    
    override func awakeFromNib() {
        super.awakeFromNib()
        
        derivedDataDirectoryMonitor.delegate = self
        logFolderDirectoryMonitor.delegate = self
        
        startMonitoring()
    }
    
    func startMonitoring() {
        stopMonitoring()
        derivedDataDirectoryMonitor.startMonitoring(path: UserSettings.derivedDataLocation)
    }
    
    func stopMonitoring() {
        derivedDataDirectoryMonitor.stopMonitoring()
    }
    
    func database(forFolder URL: URL) -> XcodeDatabase? {
        let databaseURL = URL.appendingPathComponent("Cache.db")
        return XcodeDatabase(fromPath: databaseURL.path)
    }
    
    func processDerivedData() {
        guard let mostRecent = DerivedDataManager.derivedData().first else { return }
        
        let logFolder = mostRecent.url.appendingPathComponent("Logs/Build").path
        guard logFolderDirectoryMonitor.path != logFolder else { return }
        
        logFolderDirectoryMonitor.stopMonitoring()
        logFolderDirectoryMonitor.startMonitoring(path: logFolder)
    }
    
    func processLogFolder(with url: URL) {
        guard let activeDatabase = database(forFolder: url),
            activeDatabase.isBuildType,
            activeDatabase != currentDataBase else { return }
        
        currentDataBase = activeDatabase
        delegate?.buildManager(self, shouldParseLogWithDatabase: activeDatabase)
    }
}

extension BuildManager: DirectoryMonitorDelegate {
    func directoryMonitorDidObserveChange(_ directoryMonitor: DirectoryMonitor, isDerivedData: Bool) {
        if isDerivedData {
            delegate?.derivedDataDidChange()
            processDerivedData()
        } else if let path = directoryMonitor.path {
            // TODO: If we don't dispatch, it seems it fires off too soon
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                self.processLogFolder(with: URL(fileURLWithPath: path))
            }
        }
    }
}


================================================
FILE: BuildTimeAnalyzer/BuildTimeAnalyzer-Bridging-Header.h
================================================
//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "NSData+GZIP.h"

================================================
FILE: BuildTimeAnalyzer/CSVExporter.swift
================================================
//
//  CSVExporter.swift
//  BuildTimeAnalyzer
//
//  Created by Bruno Resende on 16.01.19.
//  Copyright © 2019 Cane Media Ltd. All rights reserved.
//

import Foundation

struct CSVExporter {

    static var filenameDateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyyMMdd-HHmmss"
        return formatter
    }()

    func filename(with prefix: String) -> String {
        return "\(prefix)_\(CSVExporter.filenameDateFormatter.string(from: Date())).csv"
    }

    func export<T>(elements: [T], to url: URL) throws where T: CSVExportable {

        guard let data = elements.joinedAsCSVString(delimiter: .doubleQuote).data(using: .utf8) else {
            throw ExportErrors.couldNotParseStringAsUTF8
        }

        do {
            try data.write(to: url, options: .atomic)
        } catch {
            throw ExportErrors.fileIO(error)
        }
    }

    enum ExportErrors: Error {
        case couldNotParseStringAsUTF8
        case fileIO(Error)
    }
}

enum CSVDelimiter: String {
    case singleQuote = "'"
    case doubleQuote = "\""
    case none = ""
}

protocol CSVExportable {

    static var csvHeaderLine: String { get }

    var csvLine: String { get }
}

extension Array where Element: CSVExportable {

    func joinedAsCSVString(delimiter: CSVDelimiter) -> String {

        return ([Element.csvHeaderLine] + self.map({ $0.csvLine })).joined(separator: "\n")
    }
}

extension Array where Element == String {

    func joinedAsCSVLine(delimiter: CSVDelimiter) -> String {

        let formatter: (String) -> String

        switch delimiter {
        case .singleQuote:  formatter = { $0.replacingOccurrences(of: "'", with: "\\'") }
        case .doubleQuote:  formatter = { $0.replacingOccurrences(of: "\"", with: "\\\"") }
        case .none:         formatter = { $0 }
        }

        return self.map({ "\(delimiter.rawValue)\(formatter($0))\(delimiter.rawValue)" }).joined(separator: ",")
    }
}


================================================
FILE: BuildTimeAnalyzer/CompileMeasure.swift
================================================
//
//  CompileMeasure.swift
//  BuildTimeAnalyzer
//

import Foundation

@objcMembers class CompileMeasure: NSObject {
    
    dynamic var time: Double
    var path: String
    var code: String
    dynamic var filename: String
    var references: Int

    private var locationArray: [Int]

    public enum Order: String {
        case filename
        case time
    }

    var fileAndLine: String {
        return "\(filename):\(locationArray[0])"
    }

    var fileInfo: String {
        return "\(fileAndLine):\(locationArray[1])"
    }

	var fileRow: String {
		"\(locationArray[0])"
	}

	var fileColumn: String {
		"\(locationArray[1])"
	}

    var location: Int {
        return locationArray[0]
    }
    
    var timeString: String {
        return String(format: "%.f", time)
    }
    
    init?(time: Double, rawPath: String, code: String, references: Int) {
        let untrimmedFilename = rawPath.split(separator: "/").map(String.init).last
        
        guard let filepath = rawPath.split(separator: ":").map(String.init).first,
            let filename = untrimmedFilename?.split(separator: ":").map(String.init).first else { return nil }
        
        let locationString = String(rawPath[filepath.endIndex...].dropFirst())
        let locations = locationString.split(separator: ":").compactMap{ Int(String.init($0)) }
        guard locations.count == 2 else { return nil }
        
        self.time = time
        self.code = code
        self.path = filepath
        self.filename = filename
        self.locationArray = locations
        self.references = references
    }

    init?(rawPath: String, time: Double) {
        let untrimmedFilename = rawPath.split(separator: "/").map(String.init).last

        guard let filepath = rawPath.split(separator: ":").map(String.init).first,
            let filename = untrimmedFilename?.split(separator: ":").map(String.init).first else { return nil }

        self.time = time
        self.code = ""
        self.path = filepath
        self.filename = filename
        self.locationArray = [1,1]
        self.references = 1
    }

    subscript(column: Int) -> String {
        switch column {
        case 0:
            return timeString
        case 1:
            return fileInfo
        case 2:
            return "\(references)"
        default:
            return code
        }
    }
}

extension CompileMeasure: CSVExportable {

    static var csvHeaderLine: String = ["time", "file", "row", "column", "references", "code"].joinedAsCSVLine(delimiter: .doubleQuote)

    var csvLine: String
    {
        return [timeString, filename, fileRow, fileColumn, "\(references)", code].joinedAsCSVLine(delimiter: .doubleQuote)
    }
}


================================================
FILE: BuildTimeAnalyzer/DerivedDataManager.swift
================================================
//
//  DerivedDataManager.swift
//  BuildTimeAnalyzer
//

import Foundation

class DerivedDataManager {
    
    static func derivedData() -> [File] {
        let url = URL(fileURLWithPath: UserSettings.derivedDataLocation)
        
        let folders = DerivedDataManager.listFolders(at: url)
        let fileManager = FileManager.default
        
        return folders.compactMap{ (url) -> File? in
            if url.lastPathComponent != "ModuleCache",
                let properties = try? fileManager.attributesOfItem(atPath: url.path),
                let modificationDate = properties[FileAttributeKey.modificationDate] as? Date {
                return File(date: modificationDate, url: url)
            }
            return nil
        }.sorted{ $0.date > $1.date }
    }
    
    static func listFolders(at url: URL) -> [URL] {
        let fileManager = FileManager.default
        let keys = [URLResourceKey.nameKey, URLResourceKey.isDirectoryKey]
        let options: FileManager.DirectoryEnumerationOptions = [.skipsHiddenFiles, .skipsPackageDescendants, .skipsSubdirectoryDescendants]
        
        guard let enumerator = fileManager.enumerator(at: url, includingPropertiesForKeys: keys, options: options, errorHandler: nil) else { return [] }
        
        return enumerator.map{ $0 as! URL }
    }
}


================================================
FILE: BuildTimeAnalyzer/DirectoryMonitor.swift
================================================
//
//  DirectoryMonitor.swift
//  BuildTimeAnalyzer
//

import Foundation

protocol DirectoryMonitorDelegate: AnyObject {
    func directoryMonitorDidObserveChange(_ directoryMonitor: DirectoryMonitor, isDerivedData: Bool)
}

class DirectoryMonitor {
    var dispatchQueue: DispatchQueue
    
    weak var delegate: DirectoryMonitorDelegate?
    
    var fileDescriptor: Int32 = -1
    var dispatchSource: DispatchSourceFileSystemObject?
    var isDerivedData: Bool
    var path: String?
    var timer: Timer?
    var lastDerivedDataDate = Date()
    var isMonitoringDates = false
    
    init(isDerivedData: Bool) {
        self.isDerivedData = isDerivedData
        
        let suffix = isDerivedData ? "deriveddata" : "logfolder"
        dispatchQueue = DispatchQueue(label: "uk.co.canemedia.directorymonitor.\(suffix)", attributes: .concurrent)
    }
    
    func startMonitoring(path: String) {
        self.path = path
        
        guard dispatchSource == nil && fileDescriptor == -1 else { return }
        
        fileDescriptor = open(path, O_EVTONLY)
        guard fileDescriptor != -1 else { return }
        
        dispatchSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: fileDescriptor, eventMask: .all, queue: dispatchQueue)
        dispatchSource?.setEventHandler {
            DispatchQueue.main.async {
                self.delegate?.directoryMonitorDidObserveChange(self, isDerivedData: self.isDerivedData)
            }
        }
        dispatchSource?.setCancelHandler {
            close(self.fileDescriptor)
            
            self.fileDescriptor = -1
            self.dispatchSource = nil
            self.path = nil
        }
        dispatchSource?.resume()
        
        if isDerivedData && !isMonitoringDates {
            isMonitoringDates = true
            monitorModificationDates()
        }
    }
    
    func stopMonitoring() {
        dispatchSource?.cancel()
        path = nil
    }
    
    func monitorModificationDates() {
        if let date = DerivedDataManager.derivedData().first?.date, date > lastDerivedDataDate {
            lastDerivedDataDate = date
            self.delegate?.directoryMonitorDidObserveChange(self, isDerivedData: self.isDerivedData)
        }
        
        if path != nil {
            DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                self.monitorModificationDates()
            }
        } else {
            isMonitoringDates = false
        }
    }
}


================================================
FILE: BuildTimeAnalyzer/File.swift
================================================
//
//  File.swift
//  BuildTimeAnalyzer
//

import Foundation

struct File {
    let date: Date
    let url: URL
}


================================================
FILE: BuildTimeAnalyzer/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIconFile</key>
	<string></string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0.12</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>LSMinimumSystemVersion</key>
	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
	<key>NSHumanReadableCopyright</key>
	<string>Copyright © 2016 Cane Media Ltd. All rights reserved.</string>
	<key>NSMainStoryboardFile</key>
	<string>Main</string>
	<key>NSPrincipalClass</key>
	<string>NSApplication</string>
</dict>
</plist>


================================================
FILE: BuildTimeAnalyzer/LogProcessor.swift
================================================
//
//  LogProcessor.swift
//  BuildTimeAnalyzer
//

import Foundation

typealias CMUpdateClosure = (_ result: [CompileMeasure], _ didComplete: Bool, _ didCancel: Bool) -> ()

protocol LogProcessorProtocol: AnyObject {
    var rawMeasures: [String: RawMeasure] { get set }
    var updateHandler: CMUpdateClosure? { get set }
    var shouldCancel: Bool { get set }
    
    func processingDidStart()
    func processingDidFinish()
}

extension LogProcessorProtocol {
    func processDatabase(database: XcodeDatabase, updateHandler: CMUpdateClosure?) {
        guard let text = database.processLog() else {
            updateHandler?([], true, false)
            return
        }
        
        self.updateHandler = updateHandler
        DispatchQueue.global().async {
            self.process(text: text)
        }
    }
    
    // MARK: Private methods
    
    private func process(text: String) {
        let characterSet = CharacterSet(charactersIn:"\r\"")
        var remainingRange = text.startIndex..<text.endIndex
        let regex = try! NSRegularExpression(pattern:  "^\\d*\\.?\\d*ms\\t/", options: [])
        
        rawMeasures.removeAll()
        
        processingDidStart()
        
        while let nextRange = text.rangeOfCharacter(from: characterSet, options: [], range: remainingRange) {
            let text = String(text[remainingRange.lowerBound..<nextRange.upperBound])
            
            defer {
                remainingRange = nextRange.upperBound..<remainingRange.upperBound
            }
            
            // From LuizZak: (text as NSString).length improves the performance by about 2x compared to text.characters.count
            let range = NSMakeRange(0, (text as NSString).length)
            guard let match = regex.firstMatch(in: text, options: [], range: range) else { continue }
            
            let timeString = text[..<text.index(text.startIndex, offsetBy: match.range.length - 4)]
            if let time = Double(timeString) {
                let value = String(text[text.index(text.startIndex, offsetBy: match.range.length - 1)...])
                if var rawMeasure = rawMeasures[value] {
                    rawMeasure.time += time
                    rawMeasure.references += 1
                    rawMeasures[value] = rawMeasure
                } else {
                    rawMeasures[value] = RawMeasure(time: time, text: value)
                }
            }
            guard !shouldCancel else { break }
        }
        processingDidFinish()
    }
    
    fileprivate func updateResults(didComplete completed: Bool, didCancel: Bool) {
        var filteredResults = rawMeasures.values.filter{ $0.time > 10 }
        if filteredResults.count < 20 {
            filteredResults = rawMeasures.values.filter{ $0.time > 0.1 }
        }
        
        let sortedResults = filteredResults.sorted(by: { $0.time > $1.time })
        updateHandler?(processResult(sortedResults), completed, didCancel)
        
        if completed {
            rawMeasures.removeAll()
        }
    }
    
    private func processResult(_ unprocessedResult: [RawMeasure]) -> [CompileMeasure] {
        let characterSet = CharacterSet(charactersIn:"\r\"")
        
        var result: [CompileMeasure] = []
        for entry in unprocessedResult {
            let code = entry.text.split(separator: "\t").map(String.init)
            let method = code.count >= 2 ? trimPrefixes(code[1]) : "-"
            
            if let path = code.first?.trimmingCharacters(in: characterSet), let measure = CompileMeasure(time: entry.time, rawPath: path, code: method, references: entry.references) {
                result.append(measure)
            }
        }
        return result
    }
    
    private func trimPrefixes(_ code: String) -> String {
        var code = code
        ["@objc ", "final ", "@IBAction "].forEach { (prefix) in
            if code.hasPrefix(prefix) {
                code = String(code[code.index(code.startIndex, offsetBy: prefix.count)...])
            }
        }
        return code
    }
}

class LogProcessor: NSObject, LogProcessorProtocol {
    
    var rawMeasures: [String: RawMeasure] = [:]
    var updateHandler: CMUpdateClosure?
    var shouldCancel = false
    var timer: Timer?
    
    func processingDidStart() {
        DispatchQueue.main.async {
            self.timer = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(self.timerCallback(_:)), userInfo: nil, repeats: true)
        }
    }
    
    func processingDidFinish() {
        DispatchQueue.main.async {
            self.timer?.invalidate()
            self.timer = nil
            let didCancel = self.shouldCancel
            self.shouldCancel = false
            self.updateResults(didComplete: true, didCancel: didCancel)
        }
    }
    
    @objc func timerCallback(_ timer: Timer) {
        updateResults(didComplete: false, didCancel: false)
    }
}


================================================
FILE: BuildTimeAnalyzer/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="24506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24506"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Application-->
        <scene sceneID="JPo-4y-FX3">
            <objects>
                <application id="hnw-xV-0zn" sceneMemberID="viewController">
                    <menu key="mainMenu" title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
                        <items>
                            <menuItem title="Build Time Analyzer" id="1Xt-HY-uBw">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Build Time Analyzer" systemMenu="apple" id="uQy-DD-JDr">
                                    <items>
                                        <menuItem title="About Build Time Analyzer" id="5kV-Vb-QxS">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="orderFrontStandardAboutPanel:" target="Ady-hI-5gd" id="Exp-CZ-Vem"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="GitHub Page" id="ZvE-gI-Jj2">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="visitGitHubPage:" target="Voe-Tx-rLC" id="OKx-FW-Ire"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
                                        <menuItem title="Hide TestMenu" keyEquivalent="h" id="Olw-nP-bQN">
                                            <connections>
                                                <action selector="hide:" target="Ady-hI-5gd" id="PnN-Uc-m68"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="hideOtherApplications:" target="Ady-hI-5gd" id="VT4-aY-XCT"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Show All" id="Kd2-mp-pUS">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="unhideAllApplications:" target="Ady-hI-5gd" id="Dhg-Le-xox"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
                                        <menuItem title="Quit TestMenu" keyEquivalent="q" id="4sb-4s-VLi">
                                            <connections>
                                                <action selector="terminate:" target="Ady-hI-5gd" id="Te7-pn-YzF"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="File" id="dMs-cI-mzQ">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="File" id="bib-Uj-vzu">
                                    <items>
                                        <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
                                            <connections>
                                                <action selector="performClose:" target="Ady-hI-5gd" id="HmO-Ls-i7Q"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="Kk2-Jz-LKd"/>
                                        <menuItem title="Export data as CSV…" keyEquivalent="e" id="3xt-bK-nQa">
                                            <connections>
                                                <action selector="exportAsCSVClicked:" target="Ady-hI-5gd" id="kbQ-xo-OtY"/>
                                                <binding destination="Voe-Tx-rLC" name="enabled" keyPath="self.canExport" id="nAf-82-Fb2"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Edit" id="5QF-Oa-p0T">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Edit" id="W48-6f-4Dl">
                                    <items>
                                        <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
                                            <connections>
                                                <action selector="undo:" target="Ady-hI-5gd" id="M6e-cu-g7V"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
                                            <connections>
                                                <action selector="redo:" target="Ady-hI-5gd" id="oIA-Rs-6OD"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
                                        <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
                                            <connections>
                                                <action selector="cut:" target="Ady-hI-5gd" id="YJe-68-I9s"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
                                            <connections>
                                                <action selector="copy:" target="Ady-hI-5gd" id="G1f-GL-Joy"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
                                            <connections>
                                                <action selector="paste:" target="Ady-hI-5gd" id="UvS-8e-Qdg"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="pasteAsPlainText:" target="Ady-hI-5gd" id="cEh-KX-wJQ"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Delete" id="pa3-QI-u2k">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="delete:" target="Ady-hI-5gd" id="0Mk-Ml-PaM"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
                                            <connections>
                                                <action selector="selectAll:" target="Ady-hI-5gd" id="VNm-Mi-diN"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
                                        <menuItem title="Speech" id="xrE-MZ-jX0">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
                                                <items>
                                                    <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="startSpeaking:" target="Ady-hI-5gd" id="654-Ng-kyl"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="stopSpeaking:" target="Ady-hI-5gd" id="dX8-6p-jy9"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Navigate" id="dzK-lU-FKH" userLabel="Navigate">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Navigate" autoenablesItems="NO" id="4cu-Mh-AKu">
                                    <items>
                                        <menuItem title="Project Selection" enabled="NO" keyEquivalent="0" id="JMA-rD-5Ql">
                                            <connections>
                                                <action selector="navigateToProjectSelection:" target="Voe-Tx-rLC" id="DMt-ga-nyF"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Build Times" enabled="NO" keyEquivalent="1" id="SfY-cQ-Dau">
                                            <connections>
                                                <action selector="navigateToBuildTimes:" target="Voe-Tx-rLC" id="O18-0k-1fo"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Window" id="aUF-d1-5bR">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
                                    <items>
                                        <menuItem title="Minimize" keyEquivalent="m" id="OY7-WF-poV">
                                            <connections>
                                                <action selector="performMiniaturize:" target="Ady-hI-5gd" id="VwT-WD-YPe"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Zoom" id="R4o-n2-Eq4">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
                                        <menuItem title="Bring All to Front" id="LE2-aR-0XJ">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="arrangeInFront:" target="Ady-hI-5gd" id="DRN-fu-gQh"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Always in Front" state="on" id="vT5-eJ-cMt">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAlwaysInFront:" target="Voe-Tx-rLC" id="a4N-d5-ic4"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                        </items>
                    </menu>
                    <connections>
                        <outlet property="delegate" destination="Voe-Tx-rLC" id="Xt6-Qh-l1j"/>
                    </connections>
                </application>
                <customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="BuildTimeAnalyzer" customModuleProvider="target">
                    <connections>
                        <outlet property="alwaysInFrontMenuItem" destination="vT5-eJ-cMt" id="hyc-ts-nzJ"/>
                        <outlet property="buildTimesMenuItem" destination="SfY-cQ-Dau" id="aXl-6m-zoY"/>
                        <outlet property="projectSelectionMenuItem" destination="JMA-rD-5Ql" id="ErS-4H-fO2"/>
                    </connections>
                </customObject>
                <customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-33" y="-217"/>
        </scene>
        <!--Window Controller-->
        <scene sceneID="R2V-B0-nI4">
            <objects>
                <windowController id="B8D-0N-5wS" sceneMemberID="viewController">
                    <window key="window" title="Build Time Analyzer" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA">
                        <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
                        <rect key="contentRect" x="196" y="240" width="999" height="473"/>
                        <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
                        <value key="minSize" type="size" width="900" height="323"/>
                        <connections>
                            <outlet property="delegate" destination="B8D-0N-5wS" id="Rb5-sO-360"/>
                        </connections>
                    </window>
                    <connections>
                        <segue destination="XfG-lQ-9wD" kind="relationship" relationship="window.shadowedContentViewController" id="cq2-FE-JQM"/>
                    </connections>
                </windowController>
                <customObject id="Oky-zY-oP4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="258" y="196"/>
        </scene>
        <!--View Controller-->
        <scene sceneID="hIz-AP-VOD">
            <objects>
                <customObject id="rPt-NT-nkU" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
                <viewController id="XfG-lQ-9wD" customClass="ViewController" customModule="BuildTimeAnalyzer" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" misplaced="YES" id="TUB-te-74g">
                        <rect key="frame" x="0.0" y="0.0" width="994" height="451"/>
                        <autoresizingMask key="autoresizingMask"/>
                        <subviews>
                            <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cll-Z4-M4w">
                                <rect key="frame" x="20" y="389" width="34" height="24"/>
                                <buttonCell key="cell" type="push" bezelStyle="rounded" image="NSGoLeftTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="YqD-Wn-Ga3">
                                    <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
                                    <font key="font" metaFont="system"/>
                                </buttonCell>
                                <connections>
                                    <action selector="leftButtonClicked:" target="XfG-lQ-9wD" id="A8o-kz-ykA"/>
                                </connections>
                            </button>
                            <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wGP-fY-d6m">
                                <rect key="frame" x="62" y="393" width="51" height="16"/>
                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Status: " id="Mm0-J1-a5S">
                                    <font key="font" metaFont="system"/>
                                    <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                </textFieldCell>
                            </textField>
                            <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="RvS-xK-GoL">
                                <rect key="frame" x="109" y="393" width="83" height="16"/>
                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Processing..." id="9P7-Sg-abF">
                                    <font key="font" metaFont="system"/>
                                    <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                </textFieldCell>
                            </textField>
                            <progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="xEn-wZ-b65">
                                <rect key="frame" x="195" y="393" width="16" height="16"/>
                            </progressIndicator>
                            <button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Phm-qp-QGE">
                                <rect key="frame" x="221" y="389" width="66" height="24"/>
                                <buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="eJH-bZ-4cr">
                                    <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
                                    <font key="font" metaFont="system"/>
                                    <string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
                                </buttonCell>
                                <connections>
                                    <action selector="cancelButtonClicked:" target="XfG-lQ-9wD" id="pcf-gG-1AX"/>
                                </connections>
                            </button>
                            <searchField wantsLayer="YES" focusRingType="none" horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bBY-pQ-ugQ">
                                <rect key="frame" x="231" y="389" width="567" height="24"/>
                                <searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="tSv-aC-SRU">
                                    <font key="font" metaFont="system"/>
                                    <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                    <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                                </searchFieldCell>
                                <connections>
                                    <outlet property="delegate" destination="XfG-lQ-9wD" id="b1G-WP-MAV"/>
                                </connections>
                            </searchField>
                            <button translatesAutoresizingMaskIntoConstraints="NO" id="N1E-R8-mNr">
                                <rect key="frame" x="808" y="394" width="60" height="14"/>
                                <buttonCell key="cell" type="check" title="per file" bezelStyle="regularSquare" imagePosition="left" inset="2" id="zIu-rY-2Qd">
                                    <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
                                    <font key="font" metaFont="system"/>
                                </buttonCell>
                                <constraints>
                                    <constraint firstAttribute="height" constant="14" id="Ev3-lW-a4O"/>
                                    <constraint firstAttribute="width" constant="60" id="rhf-80-RU9"/>
                                </constraints>
                                <connections>
                                    <action selector="perFileCheckboxClicked:" target="XfG-lQ-9wD" id="Pxx-d4-CZO"/>
                                </connections>
                            </button>
                            <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Px1-f3-h0i">
                                <rect key="frame" x="886" y="394" width="90" height="16"/>
                                <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Build Time: 0s" id="YXf-T1-4h3">
                                    <font key="font" metaFont="system"/>
                                    <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                    <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                </textFieldCell>
                            </textField>
                            <scrollView hidden="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="K1Q-qf-FZ3">
                                <rect key="frame" x="20" y="20" width="954" height="354"/>
                                <clipView key="contentView" id="UfE-vR-QZu">
                                    <rect key="frame" x="1" y="1" width="952" height="352"/>
                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                    <subviews>
                                        <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" headerView="zp0-vs-Vct" viewBased="YES" id="Wag-HG-nHe">
                                            <rect key="frame" x="0.0" y="0.0" width="952" height="329"/>
                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                            <size key="intercellSpacing" width="3" height="2"/>
                                            <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                            <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
                                            <tableColumns>
                                                <tableColumn width="90" minWidth="40" maxWidth="1000" id="g67-YV-my9">
                                                    <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Cumulative time">
                                                        <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
                                                    </tableHeaderCell>
                                                    <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="FF6-Rn-rW6">
                                                        <font key="font" metaFont="system"/>
                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                                    </textFieldCell>
                                                    <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
                                                    <prototypeCellViews>
                                                        <tableCellView identifier="Cell0" misplaced="YES" id="8cX-uP-CmA">
                                                            <rect key="frame" x="1" y="1" width="95" height="17"/>
                                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                                            <subviews>
                                                                <textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="JjP-9o-frO">
                                                                    <rect key="frame" x="-2" y="0.0" width="99" height="16"/>
                                                                    <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table " id="suh-0M-gYO">
                                                                        <font key="font" metaFont="system"/>
                                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                                    </textFieldCell>
                                                                </textField>
                                                            </subviews>
                                                            <constraints>
                                                                <constraint firstAttribute="trailing" secondItem="JjP-9o-frO" secondAttribute="trailing" id="6Eh-ns-FoN"/>
                                                                <constraint firstItem="JjP-9o-frO" firstAttribute="top" secondItem="8cX-uP-CmA" secondAttribute="top" id="N9I-Ay-0V3"/>
                                                                <constraint firstItem="JjP-9o-frO" firstAttribute="leading" secondItem="8cX-uP-CmA" secondAttribute="leading" id="nap-PT-9Xu"/>
                                                                <constraint firstAttribute="bottom" secondItem="JjP-9o-frO" secondAttribute="bottom" id="tXV-IA-hXr"/>
                                                            </constraints>
                                                            <connections>
                                                                <outlet property="textField" destination="JjP-9o-frO" id="1b8-wn-HmW"/>
                                                            </connections>
                                                        </tableCellView>
                                                    </prototypeCellViews>
                                                </tableColumn>
                                                <tableColumn width="250" minWidth="40" maxWidth="1000" id="I8k-1E-YFi">
                                                    <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Location">
                                                        <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
                                                    </tableHeaderCell>
                                                    <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="v7P-IT-kt6">
                                                        <font key="font" metaFont="system"/>
                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                                    </textFieldCell>
                                                    <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
                                                    <prototypeCellViews>
                                                        <tableCellView identifier="Cell1" misplaced="YES" id="rtd-z4-z3H">
                                                            <rect key="frame" x="99" y="1" width="250" height="17"/>
                                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                                            <subviews>
                                                                <textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="gaM-Z3-6r5">
                                                                    <rect key="frame" x="-2" y="0.0" width="254" height="16"/>
                                                                    <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="ShZ-hG-2RX">
                                                                        <font key="font" metaFont="system"/>
                                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                                    </textFieldCell>
                                                                </textField>
                                                            </subviews>
                                                            <constraints>
                                                                <constraint firstAttribute="trailing" secondItem="gaM-Z3-6r5" secondAttribute="trailing" id="CST-mz-Cev"/>
                                                                <constraint firstItem="gaM-Z3-6r5" firstAttribute="top" secondItem="rtd-z4-z3H" secondAttribute="top" id="Mhl-GM-DcR"/>
                                                                <constraint firstAttribute="bottom" secondItem="gaM-Z3-6r5" secondAttribute="bottom" id="OfP-Vv-Oww"/>
                                                                <constraint firstItem="gaM-Z3-6r5" firstAttribute="leading" secondItem="rtd-z4-z3H" secondAttribute="leading" id="cWF-CT-nmB"/>
                                                            </constraints>
                                                            <connections>
                                                                <outlet property="textField" destination="gaM-Z3-6r5" id="VAL-7L-QYZ"/>
                                                            </connections>
                                                        </tableCellView>
                                                    </prototypeCellViews>
                                                </tableColumn>
                                                <tableColumn width="73" minWidth="10" maxWidth="3.4028234663852886e+38" id="pNQ-e7-chu">
                                                    <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Occurrences">
                                                        <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
                                                    </tableHeaderCell>
                                                    <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="2FU-BF-t5B">
                                                        <font key="font" metaFont="system"/>
                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                                    </textFieldCell>
                                                    <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
                                                    <prototypeCellViews>
                                                        <tableCellView identifier="Cell2" id="Nfh-T6-pKh">
                                                            <rect key="frame" x="352" y="1" width="73" height="40"/>
                                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                                            <subviews>
                                                                <textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="0iN-e0-gn8">
                                                                    <rect key="frame" x="0.0" y="12" width="109" height="16"/>
                                                                    <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="NrA-Gj-lWm">
                                                                        <font key="font" metaFont="system"/>
                                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                                    </textFieldCell>
                                                                </textField>
                                                            </subviews>
                                                            <constraints>
                                                                <constraint firstItem="0iN-e0-gn8" firstAttribute="centerY" secondItem="Nfh-T6-pKh" secondAttribute="centerY" id="IdL-Mf-JRi"/>
                                                                <constraint firstAttribute="trailing" secondItem="0iN-e0-gn8" secondAttribute="trailing" constant="-34" id="LwB-EE-cDe"/>
                                                                <constraint firstItem="0iN-e0-gn8" firstAttribute="leading" secondItem="Nfh-T6-pKh" secondAttribute="leading" constant="2" id="qbx-Wq-paT"/>
                                                            </constraints>
                                                            <connections>
                                                                <outlet property="textField" destination="0iN-e0-gn8" id="hYB-5j-Xqa"/>
                                                            </connections>
                                                        </tableCellView>
                                                    </prototypeCellViews>
                                                </tableColumn>
                                                <tableColumn width="518" minWidth="40" maxWidth="1000" id="Rka-lG-OGR">
                                                    <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Function">
                                                        <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
                                                    </tableHeaderCell>
                                                    <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="HHv-yh-Ikp">
                                                        <font key="font" metaFont="system"/>
                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                        <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                                    </textFieldCell>
                                                    <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
                                                    <prototypeCellViews>
                                                        <tableCellView identifier="Cell3" misplaced="YES" id="t5F-Tt-KtO">
                                                            <rect key="frame" x="428" y="1" width="522" height="17"/>
                                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                                            <subviews>
                                                                <textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="MfB-eS-1vB">
                                                                    <rect key="frame" x="-2" y="0.0" width="526" height="16"/>
                                                                    <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="lez-yt-53a">
                                                                        <font key="font" metaFont="system"/>
                                                                        <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                        <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                                    </textFieldCell>
                                                                </textField>
                                                            </subviews>
                                                            <constraints>
                                                                <constraint firstItem="MfB-eS-1vB" firstAttribute="top" secondItem="t5F-Tt-KtO" secondAttribute="top" id="4aR-hk-T5b"/>
                                                                <constraint firstAttribute="trailing" secondItem="MfB-eS-1vB" secondAttribute="trailing" id="5Kg-44-yUS"/>
                                                                <constraint firstAttribute="bottom" secondItem="MfB-eS-1vB" secondAttribute="bottom" id="WWU-T4-LN2"/>
                                                                <constraint firstItem="MfB-eS-1vB" firstAttribute="leading" secondItem="t5F-Tt-KtO" secondAttribute="leading" id="hzi-Kk-Wyb"/>
                                                            </constraints>
                                                            <connections>
                                                                <outlet property="textField" destination="MfB-eS-1vB" id="ZWE-af-66X"/>
                                                            </connections>
                                                        </tableCellView>
                                                    </prototypeCellViews>
                                                </tableColumn>
                                            </tableColumns>
                                            <connections>
                                                <outlet property="dataSource" destination="XfG-lQ-9wD" id="gzU-SG-v2N"/>
                                                <outlet property="delegate" destination="XfG-lQ-9wD" id="kPR-hV-RmL"/>
                                            </connections>
                                        </tableView>
                                    </subviews>
                                </clipView>
                                <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="QJr-l7-VY7">
                                    <rect key="frame" x="1" y="363" width="952" height="17"/>
                                    <autoresizingMask key="autoresizingMask"/>
                                </scroller>
                                <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="7gW-pK-P4I">
                                    <rect key="frame" x="224" y="17" width="15" height="102"/>
                                    <autoresizingMask key="autoresizingMask"/>
                                </scroller>
                                <tableHeaderView key="headerView" wantsLayer="YES" id="zp0-vs-Vct">
                                    <rect key="frame" x="0.0" y="0.0" width="952" height="23"/>
                                    <autoresizingMask key="autoresizingMask"/>
                                </tableHeaderView>
                            </scrollView>
                            <customView translatesAutoresizingMaskIntoConstraints="NO" id="wyZ-eU-XEt">
                                <rect key="frame" x="0.0" y="0.0" width="994" height="424"/>
                                <subviews>
                                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="x1K-8w-91Y">
                                        <rect key="frame" x="18" y="382" width="97" height="20"/>
                                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Instructions" id="9Ga-i4-eZ4">
                                            <font key="font" metaFont="systemMedium" size="17"/>
                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                        </textFieldCell>
                                    </textField>
                                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="c6J-cx-fi8">
                                        <rect key="frame" x="189" y="384" width="404" height="16"/>
                                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="1) Ensure the below flags are added to your target's Build Settings" id="R8V-zK-2ya">
                                            <font key="font" metaFont="system"/>
                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                        </textFieldCell>
                                    </textField>
                                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="NbS-C3-WfY">
                                        <rect key="frame" x="606" y="380" width="92" height="24"/>
                                        <buttonCell key="cell" type="push" title="Copy flag 1" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="s9H-YB-qWq">
                                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
                                            <font key="font" metaFont="system"/>
                                        </buttonCell>
                                        <connections>
                                            <action selector="clipboardButtonClicked:" target="XfG-lQ-9wD" id="xYh-dG-c8k"/>
                                        </connections>
                                    </button>
                                    <button verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="032-pL-jcU">
                                        <rect key="frame" x="1200" y="211" width="132" height="24"/>
                                        <buttonCell key="cell" type="push" title="Copy flag 2" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ZfB-8n-cDW">
                                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
                                            <font key="font" metaFont="system"/>
                                        </buttonCell>
                                        <connections>
                                            <action selector="clipboardButton2Clicked:" target="XfG-lQ-9wD" id="Fw6-Hi-c3r"/>
                                        </connections>
                                    </button>
                                    <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Um0-zQ-09O">
                                        <rect key="frame" x="191" y="307" width="612" height="69"/>
                                        <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="ScreenShot" id="vJs-DY-m0y"/>
                                    </imageView>
                                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Zkv-Tf-sdq">
                                        <rect key="frame" x="189" y="256" width="224" height="16"/>
                                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="2) Clean your project (⌘ + Shift + K)" id="eMR-lR-OuM">
                                            <font key="font" metaFont="system"/>
                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                        </textFieldCell>
                                    </textField>
                                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PV7-TD-diE">
                                        <rect key="frame" x="189" y="253" width="339" height="16"/>
                                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="3) Build your project (⌘ + B) and wait for it to complete" id="WcG-TO-qa4">
                                            <font key="font" metaFont="system"/>
                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                        </textFieldCell>
                                    </textField>
                                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="O5K-4i-z6g">
                                        <rect key="frame" x="189" y="154" width="245" height="16"/>
                                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Alternatively, choose an existing project" id="j64-c6-4L7">
                                            <font key="font" metaFont="system"/>
                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                        </textFieldCell>
                                    </textField>
                                    <scrollView wantsLayer="YES" autohidesScrollers="YES" horizontalLineScroll="27" horizontalPageScroll="10" verticalLineScroll="27" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ddp-S7-yG2">
                                        <rect key="frame" x="191" y="77" width="612" height="120"/>
                                        <clipView key="contentView" id="hf9-7R-VGA">
                                            <rect key="frame" x="1" y="1" width="610" height="118"/>
                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                            <subviews>
                                                <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="25" headerView="aAe-a7-ga6" viewBased="YES" id="vE3-G0-gZw">
                                                    <rect key="frame" x="0.0" y="0.0" width="610" height="95"/>
                                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                                    <size key="intercellSpacing" width="3" height="2"/>
                                                    <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                                    <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
                                                    <tableColumns>
                                                        <tableColumn width="380" minWidth="40" maxWidth="1000" id="EHR-zb-SmS">
                                                            <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Name">
                                                                <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
                                                                <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
                                                            </tableHeaderCell>
                                                            <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="5Pm-ZP-JFn">
                                                                <font key="font" metaFont="system"/>
                                                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                                            </textFieldCell>
                                                            <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
                                                            <prototypeCellViews>
                                                                <tableCellView identifier="Cell0" id="Od6-nA-b4s">
                                                                    <rect key="frame" x="1" y="1" width="385" height="40"/>
                                                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                                                    <subviews>
                                                                        <textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eRA-xw-ZhX">
                                                                            <rect key="frame" x="0.0" y="23" width="385" height="17"/>
                                                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
                                                                            <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="vAr-Ew-aeF">
                                                                                <font key="font" metaFont="system"/>
                                                                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                                <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                                            </textFieldCell>
                                                                        </textField>
                                                                    </subviews>
                                                                    <connections>
                                                                        <outlet property="textField" destination="eRA-xw-ZhX" id="5lq-L9-C1L"/>
                                                                    </connections>
                                                                </tableCellView>
                                                            </prototypeCellViews>
                                                        </tableColumn>
                                                        <tableColumn width="215" minWidth="40" maxWidth="1000" id="mpO-wX-dkq">
                                                            <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Date">
                                                                <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
                                                                <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
                                                            </tableHeaderCell>
                                                            <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="suz-qG-dIq">
                                                                <font key="font" metaFont="system"/>
                                                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
                                                            </textFieldCell>
                                                            <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
                                                            <prototypeCellViews>
                                                                <tableCellView identifier="Cell1" id="rFj-Lv-vAO">
                                                                    <rect key="frame" x="389" y="1" width="219" height="40"/>
                                                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                                                    <subviews>
                                                                        <textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ukd-Oi-mjC">
                                                                            <rect key="frame" x="0.0" y="23" width="219" height="17"/>
                                                                            <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
                                                                            <textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="7jY-Q2-1y9">
                                                                                <font key="font" metaFont="system"/>
                                                                                <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                                                                <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                                                            </textFieldCell>
                                                                        </textField>
                                                                    </subviews>
                                                                    <connections>
                                                                        <outlet property="textField" destination="Ukd-Oi-mjC" id="adD-ul-2zd"/>
                                                                    </connections>
                                                                </tableCellView>
                                                            </prototypeCellViews>
                                                        </tableColumn>
                                                    </tableColumns>
                                                    <connections>
                                                        <action selector="didSelectCell:" target="Ffg-BK-bSD" id="l29-b7-dcK"/>
                                                        <outlet property="dataSource" destination="Ffg-BK-bSD" id="SjW-4d-Dgm"/>
                                                        <outlet property="delegate" destination="Ffg-BK-bSD" id="L1S-A9-dAn"/>
                                                    </connections>
                                                </tableView>
                                            </subviews>
                                        </clipView>
                                        <constraints>
                                            <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="120" id="V1C-tO-aNA"/>
                                        </constraints>
                                        <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="YXc-0k-3G9">
                                            <rect key="frame" x="1" y="102" width="610" height="17"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                        </scroller>
                                        <scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="OkT-YA-gco">
                                            <rect key="frame" x="224" y="17" width="15" height="102"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                        </scroller>
                                        <tableHeaderView key="headerView" wantsLayer="YES" id="aAe-a7-ga6">
                                            <rect key="frame" x="0.0" y="0.0" width="610" height="23"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                        </tableHeaderView>
                                    </scrollView>
                                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MtA-kf-7kN">
                                        <rect key="frame" x="18" y="31" width="142" height="16"/>
                                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Derived Data Location:" id="TXm-dt-Qp1">
                                            <font key="font" metaFont="system"/>
                                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                                            <color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
                                        </textFieldCell>
                                    </textField>
                                    <textField focusRingType="none" verticalHuggingPriority="750" verticalCompressionResistancePriority="751" translatesAutoresizingMaskIntoConstraints="NO" id="ptj-Cb-LDq">
                                        <rect key="frame" x="191" y="27" width="612" height="24"/>
                                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="SDJ-mZ-FgV">
                                            <font key="font" metaFont="system"/>
                                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                                        </textFieldCell>
                                        <connections>
                                            <outlet property="delegate" destination="XfG-lQ-9wD" id="kcF-Uo-l1I"/>
                                        </connections>
                                    </textField>
                                    <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="coU-tR-Iyy">
                                        <rect key="frame" x="811" y="33" width="22" height="12"/>
                                        <buttonCell key="cell" type="inline" bezelStyle="inline" image="NSFollowLinkFreestandingTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="zPS-Vu-Noe">
                                            <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
                                            <font key="font" metaFont="smallSystemBold"/>
                                        </buttonCell>
                                        <connections>
                                            <action selector="visitDerivedData:" target="XfG-lQ-9wD" id="HW7-Il-IM3"/>
                                        </connections>
                                    </button>
                                </subviews>
                                <constraints>
                                    <constraint firstItem="ddp-S7-yG2" firstAttribute="trailing" secondItem="Um0-zQ-09O" secondAttribute="trailing" id="7s6-Yg-1kY"/>
                                    <constraint firstItem="NbS-C3-WfY" firstAttribute="leading" secondItem="c6J-cx-fi8" secondAttribute="trailing" constant="15" id="8SE-Qr-vIk"/>
                                    <constraint firstItem="x1K-8w-91Y" firstAttribute="centerY" secondItem="c6J-cx-fi8" secondAttribute="centerY" id="93U-LK-sEV"/>
                                    <constraint firstItem="ptj-Cb-LDq" firstAttribute="leading" secondItem="ddp-S7-yG2" secondAttribute="leading" id="9ZQ-n0-bEL"/>
                                    <constraint firstItem="PV7-TD-diE" firstAttribute="top" secondItem="Zkv-Tf-sdq" secondAttribute="bottom" constant="35" id="BO5-sq-vNo"/>
                                    <constraint firstItem="Um0-zQ-09O" firstAttribute="top" secondItem="c6J-cx-fi8" secondAttribute="bottom" constant="8" id="CCC-q5-86U"/>
                                    <constraint firstItem="O5K-4i-z6g" firstAttribute="leading" secondItem="PV7-TD-diE" secondAttribute="leading" id="Dpn-sp-Fni"/>
                                    <constraint firstItem="032-pL-jcU" firstAttribute="leading" secondItem="NbS-C3-WfY" secondAttribute="trailing" constant="6" id="Ic5-61-Mq9"/>
                                    <constraint firstItem="c6J-cx-fi8" firstAttribute="leading" secondItem="Um0-zQ-09O" secondAttribute="leading" id="NIy-Q9-e38"/>
                                    <constraint firstItem="Um0-zQ-09O" firstAttribute="centerX" secondItem="wyZ-eU-XEt" secondAttribute="centerX" id="O4l-6w-kjo"/>
                                    <constraint firstItem="coU-tR-Iyy" firstAttribute="leading" secondItem="ptj-Cb-LDq" secondAttribute="trailing" constant="8" id="Ou4-bQ-rT0"/>
                                    <constraint firstItem="ddp-S7-yG2" firstAttribute="leading" secondItem="Um0-zQ-09O" secondAttribute="leading" id="QZf-de-QEb"/>
                                    <constraint firstItem="ptj-Cb-LDq" firstAttribute="trailing" secondItem="ddp-S7-yG2" secondAttribute="trailing" id="QsI-Oh-lIn"/>
                                    <constraint firstItem="PV7-TD-diE" firstAttribute="leading" secondItem="Zkv-Tf-sdq" secondAttribute="leading" id="U3M-6g-IlA"/>
                                    <constraint firstItem="MtA-kf-7kN" firstAttribute="top" secondItem="ddp-S7-yG2" secondAttribute="bottom" constant="30" id="Wsv-7h-dXk"/>
                                    <constraint firstItem="Zkv-Tf-sdq" firstAttribute="leading" secondItem="Um0-zQ-09O" secondAttribute="leading" id="dMc-8a-uJH"/>
                                    <constraint firstItem="032-pL-jcU" firstAttribute="centerY" secondItem="NbS-C3-WfY" secondAttribute="centerY" id="dgp-d4-cU5"/>
                                    <constraint firstItem="MtA-kf-7kN" firstAttribute="leading" secondItem="x1K-8w-91Y" secondAttribute="leading" id="doX-4F-aoa"/>
                                    <constraint firstItem="x1K-8w-91Y" firstAttribute="leading" secondItem="wyZ-eU-XEt" secondAttribute="leading" constant="20" id="fYD-Gu-iO9"/>
                                    <constraint firstItem="ptj-Cb-LDq" firstAttribute="centerY" secondItem="MtA-kf-7kN" secondAttribute="centerY" id="hWs-e1-6dI"/>
                                    <constraint firstAttribute="trailing" secondItem="ddp-S7-yG2" secondAttribute="trailing" constant="191" id="jvl-63-wCZ"/>
                                    <constraint firstItem="NbS-C3-WfY" firstAttribute="centerY" secondItem="c6J-cx-fi8" secondAttribute="centerY" id="lGh-3Z-UEi"/>
                                    <constraint firstAttribute="bottom" secondItem="ptj-Cb-LDq" secondAttribute="bottom" constant="27" id="nQn-b1-UO6"/>
                                    <constraint firstItem="ddp-S7-yG2" firstAttribute="top" secondItem="O5K-4i-z6g" secondAttribute="bottom" constant="5" id="oCY-Ao-8bF"/>
                                    <constraint firstItem="Zkv-Tf-sdq" firstAttribute="top" secondItem="Um0-zQ-09O" secondAttribute="bottom" constant="35" id="oan-Z5-mK3"/>
                                    <constraint firstItem="coU-tR-Iyy" firstAttribute="centerY" secondItem="ptj-Cb-LDq" secondAttribute="centerY" id="rN7-rQ-Q8B"/>
                                    <constraint firstItem="NbS-C3-WfY" firstAttribute="top" secondItem="wyZ-eU-XEt" secondAttribute="top" constant="20" id="rsF-no-xAd"/>
                                    <constraint firstItem="O5K-4i-z6g" firstAttribute="top" secondItem="PV7-TD-diE" secondAttribute="bottom" constant="35" id="wss-bT-SAy"/>
                                </constraints>
                            </customView>
                        </subviews>
                        <constraints>
                            <constraint firstItem="cll-Z4-M4w" firstAttribute="leading" secondItem="TUB-te-74g" secondAttribute="leading" constant="20" id="6I5-O1-qtA"/>
                            <constraint firstItem="Px1-f3-h0i" firstAttribute="centerY" secondItem="N1E-R8-mNr" secondAttribute="centerY" constant="-1" id="6wl-rn-DSR"/>
                            <constraint firstItem="RvS-xK-GoL" firstAttribute="leading" secondItem="wGP-fY-d6m" secondAttribute="trailing" id="97q-Nd-g4K"/>
                            <constraint firstAttribute="bottom" secondItem="wyZ-eU-XEt" secondAttribute="bottom" id="9Tx-hc-Cr8"/>
                            <constraint firstItem="wyZ-eU-XEt" firstAttribute="leading" secondItem="TUB-te-74g" secondAttribute="leading" id="9VV-Zf-ztM"/>
                            <constraint firstItem="N1E-R8-mNr" firstAttribute="leading" secondItem="bBY-pQ-ugQ" secondAttribute="trailing" constant="10" id="9xK-9A-L8b"/>
                            <constraint firstItem="xEn-wZ-b65" firstAttribute="leading" secondItem="RvS-xK-GoL" secondAttribute="trailing" constant="5" id="CWs-Zw-U9N"/>
                            <constraint firstItem="Phm-qp-QGE" firstAttribute="leading" secondItem="xEn-wZ-b65" secondAttribute="trailing" constant="10" id="HCc-AM-YeT"/>
                            <constraint firstItem="Px1-f3-h0i" firstAttribute="leading" secondItem="N1E-R8-mNr" secondAttribute="trailing" constant="20" id="IWD-nK-o2R"/>
                            <constraint firstAttribute="trailing" secondItem="K1Q-qf-FZ3" secondAttribute="trailing" constant="20" id="MHW-8Q-xwg"/>
                            <constraint firstItem="RvS-xK-GoL" firstAttribute="centerY" secondItem="wGP-fY-d6m" secondAttribute="centerY" id="QHM-Pg-2ag"/>
                            <constraint firstItem="K1Q-qf-FZ3" firstAttribute="top" secondItem="TUB-te-74g" secondAttribute="top" constant="50" id="Qav-aG-fZq"/>
                            <constraint firstItem="bBY-pQ-ugQ" firstAttribute="centerY" secondItem="cll-Z4-M4w" secondAttribute="centerY" id="Roh-wf-zeZ"/>
                            <constraint firstItem="xEn-wZ-b65" firstAttribute="centerY" secondItem="RvS-xK-GoL" secondAttribute="centerY" id="UO8-dG-b6I"/>
                            <constraint firstItem="K1Q-qf-FZ3" firstAttribute="leading" secondItem="TUB-te-74g" secondAttribute="leading" constant="20" id="W02-7b-1rF"/>
                            <constraint firstItem="bBY-pQ-ugQ" firstAttribute="leading" secondItem="Um0-zQ-09O" secondAttribute="leading" constant="40" id="XGm-QC-YLA"/>
                            <constraint firstItem="N1E-R8-mNr" firstAttribute="centerY" secondItem="bBY-pQ-ugQ" secondAttribute="centerY" id="ec0-jZ-yN9"/>
                            <constraint firstAttribute="trailing" secondItem="Px1-f3-h0i" secondAttribute="trailing" constant="20" id="ftF-AU-XEl"/>
                            <constraint firstAttribute="trailing" secondItem="wyZ-eU-XEt" secondAttribute="trailing" id="fvn-gV-Dbr"/>
                            <constraint firstItem="wyZ-eU-XEt" firstAttribute="top" secondItem="TUB-te-74g" secondAttribute="top" id="gBL-BQ-VHN"/>
                            <constraint firstItem="wGP-fY-d6m" firstAttribute="leading" secondItem="cll-Z4-M4w" secondAttribute="trailing" constant="10" id="slo-AQ-lkr"/>
                            <constraint firstItem="cll-Z4-M4w" firstAttribute="centerY" secondItem="wGP-fY-d6m" secondAttribute="centerY" id="tTb-24-hhh"/>
                            <constraint firstAttribute="bottom" secondItem="K1Q-qf-FZ3" secondAttribute="bottom" constant="20" id="tYg-T3-Agj"/>
                            <constraint firstItem="wGP-fY-d6m" firstAttribute="top" secondItem="TUB-te-74g" secondAttribute="top" constant="15" id="xMr-bP-2jr"/>
                            <constraint firstItem="Phm-qp-QGE" firstAttribute="centerY" secondItem="xEn-wZ-b65" secondAttribute="centerY" id="zhM-OK-89S"/>
                        </constraints>
                    </view>
                    <connections>
                        <outlet property="buildManager" destination="f4z-Qu-43g" id="aek-kF-HmQ"/>
                        <outlet property="cancelButton" destination="Phm-qp-QGE" id="q44-19-Lli"/>
                        <outlet property="compileTimeTextField" destination="Px1-f3-h0i" id="XbZ-D3-wwe"/>
                        <outlet property="derivedDataTextField" destination="ptj-Cb-LDq" id="Hqz-Sh-zea"/>
                        <outlet property="instructionsView" destination="wyZ-eU-XEt" id="CSN-0u-vnQ"/>
                        <outlet property="leftButton" destination="cll-Z4-M4w" id="dZK-eZ-LHa"/>
                        <outlet property="perFileButton" destination="N1E-R8-mNr" id="CAb-hw-afy"/>
                        <outlet property="progressIndicator" destination="xEn-wZ-b65" id="ISf-c4-IX4"/>
                        <outlet property="projectSelection" destination="Ffg-BK-bSD" id="0N9-RU-Xyw"/>
                        <outlet property="searchField" destination="bBY-pQ-ugQ" id="Eqb-dJ-Eov"/>
                        <outlet property="statusLabel" destination="wGP-fY-d6m" id="ZJT-MH-vIf"/>
                        <outlet property="statusTextField" destination="RvS-xK-GoL" id="Vew-jo-Hex"/>
                        <outlet property="tableView" destination="Wag-HG-nHe" id="TKE-UK-M6w"/>
                        <outlet property="tableViewContainerView" destination="K1Q-qf-FZ3" id="ufv-Ah-QDA"/>
                    </connections>
                </viewController>
                <customObject id="Ffg-BK-bSD" customClass="ProjectSelection" customModule="BuildTimeAnalyzer" customModuleProvider="target">
                    <connections>
                        <outlet property="tableView" destination="vE3-G0-gZw" id="Mkh-Ri-cQN"/>
                    </connections>
                </customObject>
                <customObject id="f4z-Qu-43g" customClass="BuildManager" customModule="BuildTimeAnalyzer" customModuleProvider="target"/>
            </objects>
            <point key="canvasLocation" x="258" y="933"/>
        </scene>
    </scenes>
    <resources>
        <image name="NSFollowLinkFreestandingTemplate" width="20" height="20"/>
        <image name="NSGoLeftTemplate" width="12" height="17"/>
        <image name="ScreenShot" width="616" height="69"/>
    </resources>
</document>


================================================
FILE: BuildTimeAnalyzer/NSAlert+Extensions.swift
================================================
//
//  NSAlert+Extensions.swift
//  BuildTimeAnalyzer
//

import Cocoa

extension NSAlert {
    static func show(withMessage message: String, andInformativeText informativeText: String = "") {
        let alert = NSAlert()
        alert.messageText = message
        alert.informativeText = informativeText
        alert.alertStyle = .warning
        alert.addButton(withTitle: "OK")
        alert.runModal()
    }
}


================================================
FILE: BuildTimeAnalyzer/ProcessingState.swift
================================================
//
//  ProcessingState.swift
//  BuildTimeAnalyzer
//

enum ProcessingState {
    case processing
    case waiting
    case completed(didSucceed: Bool, stateName: String)

    static let cancelledString       = "Cancelled"
    static let completedString       = "Completed"
    static let failedString          = "No valid logs found"
    static let processingString      = "Processing log..."
    static let waitingForBuildString = "Waiting..."
}

extension ProcessingState : Equatable {}

func ==(lhs: ProcessingState, rhs: ProcessingState) -> Bool {
    switch (lhs, rhs) {
    case (let .completed(didSucceed1, _), let .completed(didSucceed2, _)):
        return didSucceed1 == didSucceed2
        
    case (.processing, .processing), (.waiting, .waiting):
        return true
        
    default:
        return false
    }
}


================================================
FILE: BuildTimeAnalyzer/ProjectSelection.swift
================================================
//
//  ProjectSelection.swift
//  BuildTimeAnalyzer
//

import Cocoa

protocol ProjectSelectionDelegate: AnyObject {
    func didSelectProject(with database: XcodeDatabase)
}

class ProjectSelection: NSObject {
    
    @IBOutlet weak var tableView: NSTableView!
    weak var delegate: ProjectSelectionDelegate?
    
    private var dataSource: [XcodeDatabase] = []
    
    static private let dateFormatter: DateFormatter = {
        let dateFormatter = DateFormatter()
        dateFormatter.timeStyle = .short
        dateFormatter.dateStyle = .medium
        return dateFormatter
    }()
    
    func listFolders() {
        dataSource = DerivedDataManager.derivedData().compactMap{
            XcodeDatabase(fromPath: $0.url.appendingPathComponent("Logs/Build/LogStoreManifest.plist").path)
        }.sorted(by: { $0.modificationDate > $1.modificationDate })
        
        tableView.reloadData()
    }
    
    // MARK: Actions
    
    @IBAction func didSelectCell(_ sender: NSTableView) {
        guard sender.selectedRow != -1 else { return }
        delegate?.didSelectProject(with: dataSource[sender.selectedRow])
    }
}

// MARK: NSTableViewDataSource

extension ProjectSelection: NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        return dataSource.count
    }
}

// MARK: NSTableViewDelegate

extension ProjectSelection: NSTableViewDelegate {
    
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        guard let tableColumn = tableColumn, let columnIndex = tableView.tableColumns.firstIndex(of: tableColumn) else { return nil }
        
        let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell\(columnIndex)"), owner: self) as? NSTableCellView
        
        let source = dataSource[row]
        var value = ""
        
        switch columnIndex {
        case 0:
            value = source.schemeName
        default:
            value = ProjectSelection.dateFormatter.string(from: source.modificationDate)
        }
        cellView?.textField?.stringValue = value
        
        return cellView
    }
}


================================================
FILE: BuildTimeAnalyzer/RawMeasure.swift
================================================
//
//  RawMeasure.swift
//  BuildTimeAnalyzer
//

import Foundation

struct RawMeasure {
    var time: Double
    var text: String
    var references: Int
    
    init(time: Double, text: String) {
        self.time = time
        self.text = text
        self.references = 1
    }
}

// MARK: Equatable

extension RawMeasure: Equatable {}

func ==(lhs: RawMeasure, rhs: RawMeasure) -> Bool {
    return lhs.time == rhs.time && lhs.text == rhs.text
}

// MARK: Hashable

extension RawMeasure: Hashable {
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(time)
        hasher.combine(text)
        hasher.combine(references)
    }
}


================================================
FILE: BuildTimeAnalyzer/UserSettings.swift
================================================
//
//  UserCache.swift
//  BuildTimeAnalyzer
//

import Foundation

class UserSettings {
    
    static private let derivedDataLocationKey = "derivedDataLocationKey"
    static private let windowLevelIsNormalKey = "windowLevelIsNormalKey"
    
    static private var _derivedDataLocation: String?
    static private var _windowLevelIsNormal: Bool?
    
    static var derivedDataLocation: String {
        get {
            if _derivedDataLocation == nil {
                _derivedDataLocation = UserDefaults.standard.string(forKey: derivedDataLocationKey)
            }
            if _derivedDataLocation == nil, let libraryFolder = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first {
                _derivedDataLocation = "\(libraryFolder)/Developer/Xcode/DerivedData"
            }
            return _derivedDataLocation ?? ""
        }
        set {
            _derivedDataLocation = newValue
            UserDefaults.standard.set(newValue, forKey: derivedDataLocationKey)
            UserDefaults.standard.synchronize()
        }
    }
    
    static var windowShouldBeTopMost: Bool {
        get {
            if _windowLevelIsNormal == nil {
                _windowLevelIsNormal = UserDefaults.standard.bool(forKey: windowLevelIsNormalKey)
            }
            return !(_windowLevelIsNormal ?? false)
        }
        set {
            _windowLevelIsNormal = !newValue
            UserDefaults.standard.set(_windowLevelIsNormal, forKey: windowLevelIsNormalKey)
            UserDefaults.standard.synchronize()
        }
    }
}


================================================
FILE: BuildTimeAnalyzer/ViewController.swift
================================================
//
//  ViewController.swift
//  BuildTimeAnalyzer
//

import Cocoa

class ViewController: NSViewController {
    
    @IBOutlet var buildManager: BuildManager!
    @IBOutlet weak var cancelButton: NSButton!
    @IBOutlet weak var compileTimeTextField: NSTextField!
    @IBOutlet weak var derivedDataTextField: NSTextField!
    @IBOutlet weak var instructionsView: NSView!
    @IBOutlet weak var leftButton: NSButton!
    @IBOutlet weak var perFileButton: NSButton!
    @IBOutlet weak var progressIndicator: NSProgressIndicator!
    @IBOutlet weak var projectSelection: ProjectSelection!
    @IBOutlet weak var searchField: NSSearchField!
    @IBOutlet weak var statusLabel: NSTextField!
    @IBOutlet weak var statusTextField: NSTextField!
    @IBOutlet weak var tableView: NSTableView!
    @IBOutlet weak var tableViewContainerView: NSScrollView!

    private let dataSource = ViewControllerDataSource()
    
    private var currentKey: String?
    private var nextDatabase: XcodeDatabase?

    private(set) var lastProcessedDatabaseSchemeName: String? = nil
    {
        didSet
        {
            (NSApp.delegate as? AppDelegate)?.canExport = lastProcessedDatabaseSchemeName != nil
        }
    }
    
    private var processor = LogProcessor()
    
    var processingState: ProcessingState = .waiting {
        didSet {
            updateViewForState()
        }
    }
    
    // MARK: Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        
        configureLayout()
        
        buildManager.delegate = self
        projectSelection.delegate = self
        projectSelection.listFolders()

        tableView.tableColumns[0].sortDescriptorPrototype = NSSortDescriptor(key: CompileMeasure.Order.time.rawValue, ascending: true)
        tableView.tableColumns[1].sortDescriptorPrototype = NSSortDescriptor(key: CompileMeasure.Order.filename.rawValue, ascending: true)

        NotificationCenter.default.addObserver(self, selector: #selector(windowWillClose(notification:)), name: NSWindow.willCloseNotification, object: nil)
    }
    
    override func viewWillAppear() {
        super.viewWillAppear()
        
        // Set window level before view is displayed
        makeWindowTopMost(topMost: UserSettings.windowShouldBeTopMost)
    }
    
    override func viewWillDisappear() {
        super.viewWillDisappear()

        // Reset window level before view is hidden
        // Reference: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/WinPanel/Concepts/WindowLevel.html
        makeWindowTopMost(topMost: false)
    }
    
    @objc func windowWillClose(notification: NSNotification) {
        guard let object = notification.object, !(object is NSPanel) else { return }
        NotificationCenter.default.removeObserver(self)
        
        processor.shouldCancel = true
        NSApp.terminate(self)
    }
    
    // MARK: Layout
    
    func configureLayout() {
        updateTotalLabel(with: 0)
        updateViewForState()
        showInstructions(true)
        
        derivedDataTextField.stringValue = UserSettings.derivedDataLocation
        makeWindowTopMost(topMost: UserSettings.windowShouldBeTopMost)
    }
    
    func showInstructions(_ show: Bool) {
        instructionsView.isHidden = !show
        
        let views: [NSView] = [compileTimeTextField, leftButton, perFileButton, searchField, statusLabel, statusTextField, tableViewContainerView]
        views.forEach{ $0.isHidden = show }
        
        if show && processingState == .processing {
            processor.shouldCancel = true
            cancelButton.isHidden = true
            progressIndicator.isHidden = true
        }
    }
    
    func updateViewForState() {
        switch processingState {
        case .processing:
            showInstructions(false)
            progressIndicator.isHidden = false
            progressIndicator.startAnimation(self)
            statusTextField.stringValue = ProcessingState.processingString
            cancelButton.isHidden = false
            
        case .completed(_, let stateName):
            progressIndicator.isHidden = true
            progressIndicator.stopAnimation(self)
            statusTextField.stringValue = stateName
            cancelButton.isHidden = true
            
        case .waiting:
            progressIndicator.isHidden = true
            progressIndicator.stopAnimation(self)
            statusTextField.stringValue = ProcessingState.waitingForBuildString
            cancelButton.isHidden = true
        }
        
        if instructionsView.isHidden {
            searchField.isHidden = !cancelButton.isHidden
        }
    }
    
    func makeWindowTopMost(topMost: Bool) {
        if let window = NSApplication.shared.windows.first {
            let level: CGWindowLevelKey = topMost ? .floatingWindow : .normalWindow
            window.level = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(level)))
        }
    }
    
    // MARK: Actions
    
    @IBAction func perFileCheckboxClicked(_ sender: NSButton) {
        dataSource.aggregateByFile = (sender.state.rawValue == 1)
        tableView.reloadData()
    }
    
    @IBAction func clipboardButtonClicked(_ sender: AnyObject) {
        NSPasteboard.general.clearContents()
        NSPasteboard.general.writeObjects(["-Xfrontend" as NSPasteboardWriting])
    }
    
    @IBAction func clipboardButton2Clicked(_ sender: AnyObject) {
        NSPasteboard.general.clearContents()
        NSPasteboard.general.writeObjects(["-debug-time-function-bodies" as NSPasteboardWriting])
    }
    
    @IBAction func visitDerivedData(_ sender: AnyObject) {
        let url = URL(fileURLWithPath: derivedDataTextField.stringValue, isDirectory: true)
        NSWorkspace.shared.open(url)
    }
    
    
    @IBAction func cancelButtonClicked(_ sender: AnyObject) {
        processor.shouldCancel = true
    }
    
    @IBAction func leftButtonClicked(_ sender: NSButton) {
        configureMenuItems(showBuildTimesMenuItem: true)
        
        cancelProcessing()
        showInstructions(true)
        projectSelection.listFolders()
    }

    @IBAction func exportAsCSVClicked(_ sender: Any?) {
        guard let keyWindow = NSApp.keyWindow, let scheme = lastProcessedDatabaseSchemeName else {
            return
        }

        let exporter = CSVExporter()

        let savePanel = NSSavePanel()
        savePanel.title = "Exporting data as CSV…"
        savePanel.message = "Pick location for CSV file to be exported:"
        savePanel.prompt = "Export"
        savePanel.allowedFileTypes = ["csv"]
        savePanel.nameFieldStringValue = exporter.filename(with: scheme)

        savePanel.beginSheetModal(for: keyWindow) { [dataSource] (response) in
            guard response == NSApplication.ModalResponse.OK, let fileUrl = savePanel.url else {
                return
            }

            do
            {
                try dataSource.exportProcessedData(using: exporter, to: fileUrl)
            }
            catch
            {
                NSAlert(error: error).runModal()
            }
        }
    }
    
    func controlTextDidChange(_ obj: Notification) {
        if let field = obj.object as? NSSearchField, field == searchField {
            dataSource.filter = searchField.stringValue
            tableView.reloadData()
        } else if let field = obj.object as? NSTextField, field == derivedDataTextField {
            buildManager.stopMonitoring()
            UserSettings.derivedDataLocation = field.stringValue

            projectSelection.listFolders()
            buildManager.startMonitoring()
        }
    }
    
    // MARK: Utilities
    
    func cancelProcessing() {
        guard processingState == .processing else { return }
        
        processor.shouldCancel = true
        cancelButton.isHidden = true
    }
    
    func configureMenuItems(showBuildTimesMenuItem: Bool) {
        if let appDelegate = NSApp.delegate as? AppDelegate {
            appDelegate.configureMenuItems(showBuildTimesMenuItem: showBuildTimesMenuItem)
        }
    }
    
    func processLog(with database: XcodeDatabase) {
        guard processingState != .processing else {
            if let currentKey = currentKey, currentKey != database.key {
                nextDatabase = database
                processor.shouldCancel = true
            }
            return
        }
        
        configureMenuItems(showBuildTimesMenuItem: false)
        
        processingState = .processing
        currentKey = database.key
        lastProcessedDatabaseSchemeName = database.schemeName
        
        updateTotalLabel(with: database.buildTime)
        
        processor.processDatabase(database: database) { [weak self] (result, didComplete, didCancel) in
            self?.handleProcessorUpdate(result: result, didComplete: didComplete, didCancel: didCancel)
        }
    }
    
    func handleProcessorUpdate(result: [CompileMeasure], didComplete: Bool, didCancel: Bool) {
        dataSource.resetSourceData(newSourceData: result)
        tableView.reloadData()
        
        if didComplete {
            completeProcessorUpdate(didCancel: didCancel)
        }
    }
    
    func completeProcessorUpdate(didCancel: Bool) {
        let didSucceed = !dataSource.isEmpty()
        
        var stateName = ProcessingState.failedString
        if didCancel {
            stateName = ProcessingState.cancelledString
        } else if didSucceed {
            stateName = ProcessingState.completedString
        }
        
        processingState = .completed(didSucceed: didSucceed, stateName: stateName)
        currentKey = nil
        
        if let nextDatabase = nextDatabase {
            self.nextDatabase = nil
            processLog(with: nextDatabase)
        }
        
        if !didSucceed {
            let text = "Ensure the Swift compiler flags has been added."
            NSAlert.show(withMessage: ProcessingState.failedString, andInformativeText: text)
            
            showInstructions(true)
            configureMenuItems(showBuildTimesMenuItem: true)
        }
    }
    
    func updateTotalLabel(with buildTime: Int) {
        let text = "Build duration: " + (buildTime < 60 ? "\(buildTime)s" : "\(buildTime / 60)m \(buildTime % 60)s")
        compileTimeTextField.stringValue = text
    }
}

// MARK: NSTableViewDataSource

extension ViewController: NSTableViewDataSource {
    func numberOfRows(in tableView: NSTableView) -> Int {
        return dataSource.count()
    }
    
    func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool {
        guard let item = dataSource.measure(index: row) else { return false }
        let url = URL(fileURLWithPath: item.path, isDirectory: true)
        NSWorkspace.shared.open(url)

        let gotoLineScript =
            "tell application \"Xcode\"\n" +
            "  activate\n" +
            "end tell\n" +
            "tell application \"System Events\"\n" +
            "  keystroke \"l\" using command down\n" +
            "  keystroke \"\(item.location)\"\n" +
            "  keystroke return\n" +
            "end tell"

        DispatchQueue.global().async {
            if let script = NSAppleScript(source: gotoLineScript) {
                script.executeAndReturnError(nil)
            }
        }
        
        return true
    }
}

// MARK: NSTableViewDelegate

extension ViewController: NSTableViewDelegate {
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        guard let tableColumn = tableColumn, let columnIndex = tableView.tableColumns.firstIndex(of: tableColumn) else { return nil }
        guard let item = dataSource.measure(index: row) else { return nil }

        let result = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell\(columnIndex)"), owner: self) as? NSTableCellView
        result?.textField?.stringValue = item[columnIndex]
        
        return result
    }

    func tableView(_ tableView: NSTableView, sortDescriptorsDidChange oldDescriptors: [NSSortDescriptor]) {
        dataSource.sortDescriptors = tableView.sortDescriptors
        tableView.reloadData()
    }
}

// MARK: BuildManagerDelegate 

extension ViewController: BuildManagerDelegate {
    func buildManager(_ buildManager: BuildManager, shouldParseLogWithDatabase database: XcodeDatabase) {
        processLog(with: database)
    }
    
    func derivedDataDidChange() {
        projectSelection.listFolders()
    }
}

// MARK: ProjectSelectionDelegate

extension ViewController: ProjectSelectionDelegate {
    func didSelectProject(with database: XcodeDatabase) {
        processLog(with: database)
    }
}


================================================
FILE: BuildTimeAnalyzer/ViewControllerDataSource.swift
================================================
//
//  ViewControllerDataSource.swift
//  BuildTimeAnalyzer
//
//  Created by Dmitrii on 02/12/2017.
//  Copyright © 2017 Cane Media Ltd. All rights reserved.
//

import Foundation

class ViewControllerDataSource {

    var aggregateByFile = false {
        didSet {
            processData()
        }
    }

    var filter = "" {
        didSet {
            processData()
        }
    }

    var sortDescriptors = [NSSortDescriptor]() {
        didSet {
            processData()
        }
    }

    private var originalData = [CompileMeasure]()
    private var processedData = [CompileMeasure]()

    func resetSourceData(newSourceData: [CompileMeasure]) {
        originalData = newSourceData
        processData()
    }

    func isEmpty() -> Bool {
        return processedData.isEmpty
    }

    func count() -> Int {
        return processedData.count
    }

    func measure(index: Int) -> CompileMeasure? {
        guard index < processedData.count && index >= 0 else { return nil }
        return processedData[index]
    }

    func exportProcessedData(using exporter: CSVExporter, to url: URL) throws {
        try exporter.export(elements: processedData, to: url)
    }

    // MARK: - Private methods

    private func processData() {
        var newProcessedData = aggregateIfNeeded(originalData)
        newProcessedData = applySortingIfNeeded(newProcessedData)
        newProcessedData = applyFilteringIfNeeded(newProcessedData)

        processedData = newProcessedData
    }

    private func aggregateIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
        guard aggregateByFile else { return input }
        var fileTimes: [String: CompileMeasure] = [:]
        for measure in input {
            if let fileMeasure = fileTimes[measure.path] {
                fileMeasure.time += measure.time
                fileTimes[measure.path] = fileMeasure
            } else {
                let newFileMeasure = CompileMeasure(rawPath: measure.path, time: measure.time)
                fileTimes[measure.path] = newFileMeasure
            }
        }
        return Array(fileTimes.values)
    }

    private func applySortingIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
        if sortDescriptors.isEmpty { return input }
        return (input as NSArray).sortedArray(using: sortDescriptors) as! Array
    }

    private func applyFilteringIfNeeded(_ input: [CompileMeasure]) -> [CompileMeasure] {
        guard !filter.isEmpty else { return input }
        return input.filter{ textContains($0.code, pattern: filter) || textContains($0.filename, pattern: filter) }
    }

    private func textContains(_ text: String, pattern: String) -> Bool {
        return text.lowercased().contains(pattern.lowercased())
    }
}


================================================
FILE: BuildTimeAnalyzer/XcodeDatabase.swift
================================================
//
//  XcodeDatabase.swift
//  BuildTimeAnalyzer
//

import Foundation
import Compression

struct XcodeDatabase {
    var path: String
    var modificationDate: Date
    
    var key: String
    var schemeName: String
    var title: String
    var timeStartedRecording: Int
    var timeStoppedRecording: Int
    
    var isBuildType: Bool {
        return title.hasPrefix("Build ") ||  title.hasPrefix("Compile ")
    }
    
    var url: URL {
        return URL(fileURLWithPath: path)
    }
    
    var logUrl: URL {
        return folderPath.appendingPathComponent("\(key).xcactivitylog")
    }
    
    var folderPath: URL {
        return url.deletingLastPathComponent()
    }
    
    var buildTime: Int {
        return timeStoppedRecording - timeStartedRecording
    }
    
    init?(fromPath path: String) {
        guard let data = NSDictionary(contentsOfFile: path)?["logs"] as? [String: AnyObject],
            let key = XcodeDatabase.sortKeys(usingData: data).last?.key,
            let value = data[key] as? [String : AnyObject],
            let schemeName = value["schemeIdentifier-schemeName"] as? String,
            let title = value["title"] as? String,
            let timeStartedRecording = value["timeStartedRecording"] as? NSNumber,
            let timeStoppedRecording = value["timeStoppedRecording"] as? NSNumber,
            let fileAttributes = try? FileManager.default.attributesOfItem(atPath: path),
            let modificationDate = fileAttributes[FileAttributeKey.modificationDate] as? Date
            else { return nil }
        
        self.modificationDate = modificationDate
        self.path = path
        self.key = key
        self.schemeName = schemeName
        self.title = title
        self.timeStartedRecording = timeStartedRecording.intValue
        self.timeStoppedRecording = timeStoppedRecording.intValue
    }
    
    func processLog() -> String? {
        guard let rawData = try? Data(contentsOf: URL(fileURLWithPath: logUrl.path)),
              let data = rawData.gunzipped() else { return nil }
        return String(data: data, encoding: .utf8)
    }
    
    private static let gzipHeaderSize = 10
    
    static func gunzip(_ data: Data) -> Data? {
        guard data.count > gzipHeaderSize else { return nil }
        
        // Skip the gzip header (10 bytes) to get raw deflate data
        let deflateData = data.dropFirst(gzipHeaderSize)
        
        let bufferSize = data.count * 4
        let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
        defer { buffer.deallocate() }
        
        var result = Data()
        deflateData.withUnsafeBytes { rawBuffer in
            guard let sourcePointer = rawBuffer.baseAddress?.bindMemory(to: UInt8.self, capacity: deflateData.count) else { return }
            let stream = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
            defer { stream.deallocate() }
            
            var status = compression_stream_init(stream, COMPRESSION_STREAM_DECODE, COMPRESSION_ZLIB)
            guard status == COMPRESSION_STATUS_OK else { return }
            defer { compression_stream_destroy(stream) }
            
            stream.pointee.src_ptr = sourcePointer
            stream.pointee.src_size = deflateData.count
            stream.pointee.dst_ptr = buffer
            stream.pointee.dst_size = bufferSize
            
            repeat {
                status = compression_stream_process(stream, 0)
                if stream.pointee.dst_size == 0 || status == COMPRESSION_STATUS_END {
                    let outputSize = bufferSize - stream.pointee.dst_size
                    result.append(buffer, count: outputSize)
                    stream.pointee.dst_ptr = buffer
                    stream.pointee.dst_size = bufferSize
                }
            } while status == COMPRESSION_STATUS_OK
        }
        return result.isEmpty ? nil : result
    }
    
    static private func sortKeys(usingData data: [String: AnyObject]) -> [(Int, key: String)] {
        var sortedKeys: [(Int, key: String)] = []
        for key in data.keys {
            if let value = data[key] as? [String: AnyObject],
                let timeStoppedRecording = value["timeStoppedRecording"] as? NSNumber {
                sortedKeys.append((timeStoppedRecording.intValue, key))
            }
        }
        return sortedKeys.sorted{ $0.0 < $1.0 }
    }
}

private extension Data {
    func gunzipped() -> Data? {
        return XcodeDatabase.gunzip(self)
    }
}

extension XcodeDatabase : Equatable {}

func ==(lhs: XcodeDatabase, rhs: XcodeDatabase) -> Bool {
    return lhs.path == rhs.path && lhs.modificationDate == rhs.modificationDate
}


================================================
FILE: BuildTimeAnalyzer.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 54;
	objects = {

/* Begin PBXBuildFile section */
		2839B8691FD2896F004C075C /* ViewControllerDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */; };
		2839B86B1FD32766004C075C /* ViewControllerDataSourceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */; };
		2A3164C81D21D73F00064045 /* CompileMeasure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C01D21D73F00064045 /* CompileMeasure.swift */; };
		2A3164C91D21D73F00064045 /* LogProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C11D21D73F00064045 /* LogProcessor.swift */; };
		2A3164CB1D21D73F00064045 /* ProcessingState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C31D21D73F00064045 /* ProcessingState.swift */; };
		2A3164CC1D21D73F00064045 /* RawMeasure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164C41D21D73F00064045 /* RawMeasure.swift */; };
		2A3164D01D21D74A00064045 /* CompileMeasureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3164CF1D21D74A00064045 /* CompileMeasureTests.swift */; };
		2A3698AA1D80A1AC002C5CDA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2A3698A91D80A1AC002C5CDA /* Main.storyboard */; };
		2A3698AC1D80A33B002C5CDA /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A3698AB1D80A33B002C5CDA /* ViewController.swift */; };
		2A5404011D86D01700DBD44C /* BuildManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5404001D86D01700DBD44C /* BuildManager.swift */; };
		2A5404031D86DE0C00DBD44C /* XcodeDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5404021D86DE0C00DBD44C /* XcodeDatabase.swift */; };
		2A5404051D86F3C700DBD44C /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5404041D86F3C700DBD44C /* File.swift */; };
		2A9807DD1D7C71F900B9232C /* DirectoryMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9807DC1D7C71F900B9232C /* DirectoryMonitor.swift */; };
		2A9807DF1D7C76FD00B9232C /* ProjectSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A9807DE1D7C76FD00B9232C /* ProjectSelection.swift */; };
		2ABFB6CE1D81F2DE00D060BF /* NSAlert+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ABFB6CD1D81F2DE00D060BF /* NSAlert+Extensions.swift */; };
		2ACBFD0C1D8835E60009567E /* UserSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2ACBFD0B1D8835E60009567E /* UserSettings.swift */; };
		2AE775121D225D5D00D1A744 /* DerivedDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AE775111D225D5D00D1A744 /* DerivedDataManager.swift */; };
		2AF821441D21D6B900D65186 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF821431D21D6B900D65186 /* AppDelegate.swift */; };
		2AF821461D21D6B900D65186 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2AF821451D21D6B900D65186 /* Assets.xcassets */; };
		5603EB6221EF93E90013D77B /* CSVExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5603EB6121EF93E90013D77B /* CSVExporter.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		2AF821501D21D6B900D65186 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 2AF821381D21D6B900D65186 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 2AF8213F1D21D6B900D65186;
			remoteInfo = BuildTimeAnalyzer;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
		2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerDataSource.swift; sourceTree = "<group>"; };
		2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewControllerDataSourceTest.swift; sourceTree = "<group>"; };
		2A3164C01D21D73F00064045 /* CompileMeasure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompileMeasure.swift; sourceTree = "<group>"; };
		2A3164C11D21D73F00064045 /* LogProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogProcessor.swift; sourceTree = "<group>"; };
		2A3164C31D21D73F00064045 /* ProcessingState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcessingState.swift; sourceTree = "<group>"; };
		2A3164C41D21D73F00064045 /* RawMeasure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawMeasure.swift; sourceTree = "<group>"; };
		2A3164CF1D21D74A00064045 /* CompileMeasureTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompileMeasureTests.swift; sourceTree = "<group>"; };
		2A3698A91D80A1AC002C5CDA /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
		2A3698AB1D80A33B002C5CDA /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
		2A5404001D86D01700DBD44C /* BuildManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildManager.swift; sourceTree = "<group>"; };
		2A5404021D86DE0C00DBD44C /* XcodeDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XcodeDatabase.swift; sourceTree = "<group>"; };
		2A5404041D86F3C700DBD44C /* File.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = "<group>"; };
		2A9807DC1D7C71F900B9232C /* DirectoryMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DirectoryMonitor.swift; sourceTree = "<group>"; };
		2A9807DE1D7C76FD00B9232C /* ProjectSelection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectSelection.swift; sourceTree = "<group>"; };
		2ABFB6CD1D81F2DE00D060BF /* NSAlert+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAlert+Extensions.swift"; sourceTree = "<group>"; };
		2ACBFD0B1D8835E60009567E /* UserSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserSettings.swift; sourceTree = "<group>"; };
		2AE775111D225D5D00D1A744 /* DerivedDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DerivedDataManager.swift; sourceTree = "<group>"; };
		2AF821401D21D6B900D65186 /* BuildTimeAnalyzer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BuildTimeAnalyzer.app; sourceTree = BUILT_PRODUCTS_DIR; };
		2AF821431D21D6B900D65186 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
		2AF821451D21D6B900D65186 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = BuildTimeAnalyzer/Assets.xcassets; sourceTree = "<group>"; };
		2AF8214A1D21D6B900D65186 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		2AF8214F1D21D6B900D65186 /* BuildTimeAnalyzerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BuildTimeAnalyzerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
		2AF821551D21D6B900D65186 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		5603EB6121EF93E90013D77B /* CSVExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CSVExporter.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		2AF8213D1D21D6B900D65186 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		2AF8214C1D21D6B900D65186 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		2A3164D11D21D74F00064045 /* Supporting Files */ = {
			isa = PBXGroup;
			children = (
				2AF8214A1D21D6B900D65186 /* Info.plist */,
			);
			name = "Supporting Files";
			sourceTree = "<group>";
		};
		2A3164D71D21D7A800064045 /* Supporting Files */ = {
			isa = PBXGroup;
			children = (
				2AF821551D21D6B900D65186 /* Info.plist */,
			);
			name = "Supporting Files";
			sourceTree = "<group>";
		};
		2ABFB6CF1D81F34300D060BF /* Extensions */ = {
			isa = PBXGroup;
			children = (
				2ABFB6CD1D81F2DE00D060BF /* NSAlert+Extensions.swift */,
			);
			name = Extensions;
			sourceTree = "<group>";
		};
		2ABFB6D01D81F35400D060BF /* Application */ = {
			isa = PBXGroup;
			children = (
				2AF821431D21D6B900D65186 /* AppDelegate.swift */,
				2A3698A91D80A1AC002C5CDA /* Main.storyboard */,
			);
			name = Application;
			sourceTree = "<group>";
		};
		2ABFB6D11D81F37300D060BF /* Models */ = {
			isa = PBXGroup;
			children = (
				2A3164C01D21D73F00064045 /* CompileMeasure.swift */,
				2A5404041D86F3C700DBD44C /* File.swift */,
				2A3164C31D21D73F00064045 /* ProcessingState.swift */,
				2A3164C41D21D73F00064045 /* RawMeasure.swift */,
				2839B8681FD2896F004C075C /* ViewControllerDataSource.swift */,
				2A5404021D86DE0C00DBD44C /* XcodeDatabase.swift */,
			);
			name = Models;
			sourceTree = "<group>";
		};
		2ABFB6D21D81F81400D060BF /* ViewControllers */ = {
			isa = PBXGroup;
			children = (
				2A3698AB1D80A33B002C5CDA /* ViewController.swift */,
			);
			name = ViewControllers;
			sourceTree = "<group>";
		};
		2ABFB6D31D81F82600D060BF /* Utilities */ = {
			isa = PBXGroup;
			children = (
				2A5404001D86D01700DBD44C /* BuildManager.swift */,
				2AE775111D225D5D00D1A744 /* DerivedDataManager.swift */,
				2A9807DC1D7C71F900B9232C /* DirectoryMonitor.swift */,
				2A3164C11D21D73F00064045 /* LogProcessor.swift */,
				2A9807DE1D7C76FD00B9232C /* ProjectSelection.swift */,
				2ACBFD0B1D8835E60009567E /* UserSettings.swift */,
				5603EB6121EF93E90013D77B /* CSVExporter.swift */,
			);
			name = Utilities;
			sourceTree = "<group>";
		};
		2AF821371D21D6B900D65186 = {
			isa = PBXGroup;
			children = (
				2AF821421D21D6B900D65186 /* BuildTimeAnalyzer */,
				2AF821451D21D6B900D65186 /* Assets.xcassets */,
				2AF821521D21D6B900D65186 /* BuildTimeAnalyzerTests */,
				2AF821411D21D6B900D65186 /* Products */,
			);
			sourceTree = "<group>";
		};
		2AF821411D21D6B900D65186 /* Products */ = {
			isa = PBXGroup;
			children = (
				2AF821401D21D6B900D65186 /* BuildTimeAnalyzer.app */,
				2AF8214F1D21D6B900D65186 /* BuildTimeAnalyzerTests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		2AF821421D21D6B900D65186 /* BuildTimeAnalyzer */ = {
			isa = PBXGroup;
			children = (
				2ABFB6D01D81F35400D060BF /* Application */,
				2ABFB6CF1D81F34300D060BF /* Extensions */,
				2ABFB6D11D81F37300D060BF /* Models */,
				2A3164D11D21D74F00064045 /* Supporting Files */,
				2ABFB6D31D81F82600D060BF /* Utilities */,
				2ABFB6D21D81F81400D060BF /* ViewControllers */,
			);
			path = BuildTimeAnalyzer;
			sourceTree = "<group>";
		};
		2AF821521D21D6B900D65186 /* BuildTimeAnalyzerTests */ = {
			isa = PBXGroup;
			children = (
				2A3164CF1D21D74A00064045 /* CompileMeasureTests.swift */,
				2839B86A1FD32766004C075C /* ViewControllerDataSourceTest.swift */,
				2A3164D71D21D7A800064045 /* Supporting Files */,
			);
			path = BuildTimeAnalyzerTests;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		2AF8213F1D21D6B900D65186 /* BuildTimeAnalyzer */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 2AF821581D21D6B900D65186 /* Build configuration list for PBXNativeTarget "BuildTimeAnalyzer" */;
			buildPhases = (
				2AF8213C1D21D6B900D65186 /* Sources */,
				2AF8213D1D21D6B900D65186 /* Frameworks */,
				2AF8213E1D21D6B900D65186 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
			);
			name = BuildTimeAnalyzer;
			productName = BuildTimeAnalyzer;
			productReference = 2AF821401D21D6B900D65186 /* BuildTimeAnalyzer.app */;
			productType = "com.apple.product-type.application";
		};
		2AF8214E1D21D6B900D65186 /* BuildTimeAnalyzerTests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 2AF8215B1D21D6B900D65186 /* Build configuration list for PBXNativeTarget "BuildTimeAnalyzerTests" */;
			buildPhases = (
				2AF8214B1D21D6B900D65186 /* Sources */,
				2AF8214C1D21D6B900D65186 /* Frameworks */,
				2AF8214D1D21D6B900D65186 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				2AF821511D21D6B900D65186 /* PBXTargetDependency */,
			);
			name = BuildTimeAnalyzerTests;
			productName = BuildTimeAnalyzerTests;
			productReference = 2AF8214F1D21D6B900D65186 /* BuildTimeAnalyzerTests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		2AF821381D21D6B900D65186 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				BuildIndependentTargetsInParallel = YES;
				LastSwiftUpdateCheck = 0730;
				LastUpgradeCheck = 2630;
				ORGANIZATIONNAME = "Cane Media Ltd";
				TargetAttributes = {
					2AF8213F1D21D6B900D65186 = {
						CreatedOnToolsVersion = 7.3.1;
						LastSwiftMigration = 1250;
					};
					2AF8214E1D21D6B900D65186 = {
						CreatedOnToolsVersion = 7.3.1;
						LastSwiftMigration = 1250;
						TestTargetID = 2AF8213F1D21D6B900D65186;
					};
				};
			};
			buildConfigurationList = 2AF8213B1D21D6B900D65186 /* Build configuration list for PBXProject "BuildTimeAnalyzer" */;
			compatibilityVersion = "Xcode 3.2";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 2AF821371D21D6B900D65186;
			productRefGroup = 2AF821411D21D6B900D65186 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				2AF8213F1D21D6B900D65186 /* BuildTimeAnalyzer */,
				2AF8214E1D21D6B900D65186 /* BuildTimeAnalyzerTests */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		2AF8213E1D21D6B900D65186 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2A3698AA1D80A1AC002C5CDA /* Main.storyboard in Resources */,
				2AF821461D21D6B900D65186 /* Assets.xcassets in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		2AF8214D1D21D6B900D65186 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		2AF8213C1D21D6B900D65186 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2A9807DD1D7C71F900B9232C /* DirectoryMonitor.swift in Sources */,
				2A3164C91D21D73F00064045 /* LogProcessor.swift in Sources */,
				2839B8691FD2896F004C075C /* ViewControllerDataSource.swift in Sources */,
				2A5404011D86D01700DBD44C /* BuildManager.swift in Sources */,
				5603EB6221EF93E90013D77B /* CSVExporter.swift in Sources */,
				2A5404051D86F3C700DBD44C /* File.swift in Sources */,
				2ABFB6CE1D81F2DE00D060BF /* NSAlert+Extensions.swift in Sources */,
				2A5404031D86DE0C00DBD44C /* XcodeDatabase.swift in Sources */,
				2A3164CB1D21D73F00064045 /* ProcessingState.swift in Sources */,
				2AF821441D21D6B900D65186 /* AppDelegate.swift in Sources */,
				2AE775121D225D5D00D1A744 /* DerivedDataManager.swift in Sources */,
				2A3698AC1D80A33B002C5CDA /* ViewController.swift in Sources */,
				2A3164CC1D21D73F00064045 /* RawMeasure.swift in Sources */,
				2ACBFD0C1D8835E60009567E /* UserSettings.swift in Sources */,
				2A3164C81D21D73F00064045 /* CompileMeasure.swift in Sources */,
				2A9807DF1D7C76FD00B9232C /* ProjectSelection.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		2AF8214B1D21D6B900D65186 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2A3164D01D21D74A00064045 /* CompileMeasureTests.swift in Sources */,
				2839B86B1FD32766004C075C /* ViewControllerDataSourceTest.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		2AF821511D21D6B900D65186 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 2AF8213F1D21D6B900D65186 /* BuildTimeAnalyzer */;
			targetProxy = 2AF821501D21D6B900D65186 /* PBXContainerItemProxy */;
		};
/* End PBXTargetDependency section */

/* Begin XCBuildConfiguration section */
		2AF821561D21D6B900D65186 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				CODE_SIGN_IDENTITY = "-";
				COPY_PHASE_STRIP = NO;
				DEAD_CODE_STRIPPING = YES;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				ENABLE_USER_SCRIPT_SANDBOXING = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MACOSX_DEPLOYMENT_TARGET = 11.0;
				MTL_ENABLE_DEBUG_INFO = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = macosx;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				SWIFT_VERSION = 5.0;
			};
			name = Debug;
		};
		2AF821571D21D6B900D65186 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
				CLANG_CXX_LIBRARY = "libc++";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				CODE_SIGN_IDENTITY = "-";
				COPY_PHASE_STRIP = NO;
				DEAD_CODE_STRIPPING = YES;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_USER_SCRIPT_SANDBOXING = YES;
				GCC_C_LANGUAGE_STANDARD = gnu99;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				MACOSX_DEPLOYMENT_TARGET = 11.0;
				MTL_ENABLE_DEBUG_INFO = NO;
				SDKROOT = macosx;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
				SWIFT_VERSION = 5.0;
			};
			name = Release;
		};
		2AF821591D21D6B900D65186 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_IDENTITY = "-";
				COMBINE_HIDPI_IMAGES = YES;
				DEAD_CODE_STRIPPING = YES;
				INFOPLIST_FILE = BuildTimeAnalyzer/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 26.0;
				PRODUCT_BUNDLE_IDENTIFIER = uk.co.canemedia.BuildTimeAnalyzer;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "";
			};
			name = Debug;
		};
		2AF8215A1D21D6B900D65186 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				CODE_SIGN_IDENTITY = "-";
				COMBINE_HIDPI_IMAGES = YES;
				DEAD_CODE_STRIPPING = YES;
				INFOPLIST_FILE = BuildTimeAnalyzer/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 26.0;
				PRODUCT_BUNDLE_IDENTIFIER = uk.co.canemedia.BuildTimeAnalyzer;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "";
			};
			name = Release;
		};
		2AF8215C1D21D6B900D65186 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CLANG_ENABLE_MODULES = YES;
				COMBINE_HIDPI_IMAGES = YES;
				DEAD_CODE_STRIPPING = YES;
				INFOPLIST_FILE = BuildTimeAnalyzerTests/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
					"@loader_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 26.0;
				PRODUCT_BUNDLE_IDENTIFIER = uk.co.canemedia.BuildTimeAnalyzerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "";
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BuildTimeAnalyzer.app/Contents/MacOS/BuildTimeAnalyzer";
			};
			name = Debug;
		};
		2AF8215D1D21D6B900D65186 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				BUNDLE_LOADER = "$(TEST_HOST)";
				CLANG_ENABLE_MODULES = YES;
				COMBINE_HIDPI_IMAGES = YES;
				DEAD_CODE_STRIPPING = YES;
				INFOPLIST_FILE = BuildTimeAnalyzerTests/Info.plist;
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/../Frameworks",
					"@loader_path/../Frameworks",
				);
				MACOSX_DEPLOYMENT_TARGET = 26.0;
				PRODUCT_BUNDLE_IDENTIFIER = uk.co.canemedia.BuildTimeAnalyzerTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_OBJC_BRIDGING_HEADER = "";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BuildTimeAnalyzer.app/Contents/MacOS/BuildTimeAnalyzer";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		2AF8213B1D21D6B900D65186 /* Build configuration list for PBXProject "BuildTimeAnalyzer" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				2AF821561D21D6B900D65186 /* Debug */,
				2AF821571D21D6B900D65186 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		2AF821581D21D6B900D65186 /* Build configuration list for PBXNativeTarget "BuildTimeAnalyzer" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				2AF821591D21D6B900D65186 /* Debug */,
				2AF8215A1D21D6B900D65186 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		2AF8215B1D21D6B900D65186 /* Build configuration list for PBXNativeTarget "BuildTimeAnalyzerTests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				2AF8215C1D21D6B900D65186 /* Debug */,
				2AF8215D1D21D6B900D65186 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */
	};
	rootObject = 2AF821381D21D6B900D65186 /* Project object */;
}


================================================
FILE: BuildTimeAnalyzer.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:BuildTimeAnalyzer.xcodeproj">
   </FileRef>
</Workspace>


================================================
FILE: BuildTimeAnalyzer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: BuildTimeAnalyzer.xcodeproj/xcshareddata/xcschemes/BuildTimeAnalyzer.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
   LastUpgradeVersion = "2630"
   version = "1.3">
   <BuildAction
      parallelizeBuildables = "YES"
      buildImplicitDependencies = "YES">
      <BuildActionEntries>
         <BuildActionEntry
            buildForTesting = "YES"
            buildForRunning = "YES"
            buildForProfiling = "YES"
            buildForArchiving = "YES"
            buildForAnalyzing = "YES">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "2AF8213F1D21D6B900D65186"
               BuildableName = "BuildTimeAnalyzer.app"
               BlueprintName = "BuildTimeAnalyzer"
               ReferencedContainer = "container:BuildTimeAnalyzer.xcodeproj">
            </BuildableReference>
         </BuildActionEntry>
      </BuildActionEntries>
   </BuildAction>
   <TestAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      shouldUseLaunchSchemeArgsEnv = "YES">
      <MacroExpansion>
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "2AF8213F1D21D6B900D65186"
            BuildableName = "BuildTimeAnalyzer.app"
            BlueprintName = "BuildTimeAnalyzer"
            ReferencedContainer = "container:BuildTimeAnalyzer.xcodeproj">
         </BuildableReference>
      </MacroExpansion>
      <Testables>
         <TestableReference
            skipped = "NO">
            <BuildableReference
               BuildableIdentifier = "primary"
               BlueprintIdentifier = "2AF8214E1D21D6B900D65186"
               BuildableName = "BuildTimeAnalyzerTests.xctest"
               BlueprintName = "BuildTimeAnalyzerTests"
               ReferencedContainer = "container:BuildTimeAnalyzer.xcodeproj">
            </BuildableReference>
         </TestableReference>
      </Testables>
   </TestAction>
   <LaunchAction
      buildConfiguration = "Debug"
      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
      launchStyle = "0"
      useCustomWorkingDirectory = "NO"
      ignoresPersistentStateOnLaunch = "NO"
      debugDocumentVersioning = "YES"
      debugServiceExtension = "internal"
      allowLocationSimulation = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "2AF8213F1D21D6B900D65186"
            BuildableName = "BuildTimeAnalyzer.app"
            BlueprintName = "BuildTimeAnalyzer"
            ReferencedContainer = "container:BuildTimeAnalyzer.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </LaunchAction>
   <ProfileAction
      buildConfiguration = "Release"
      shouldUseLaunchSchemeArgsEnv = "YES"
      savedToolIdentifier = ""
      useCustomWorkingDirectory = "NO"
      debugDocumentVersioning = "YES">
      <BuildableProductRunnable
         runnableDebuggingMode = "0">
         <BuildableReference
            BuildableIdentifier = "primary"
            BlueprintIdentifier = "2AF8213F1D21D6B900D65186"
            BuildableName = "BuildTimeAnalyzer.app"
            BlueprintName = "BuildTimeAnalyzer"
            ReferencedContainer = "container:BuildTimeAnalyzer.xcodeproj">
         </BuildableReference>
      </BuildableProductRunnable>
   </ProfileAction>
   <AnalyzeAction
      buildConfiguration = "Debug">
   </AnalyzeAction>
   <ArchiveAction
      buildConfiguration = "Release"
      revealArchiveInOrganizer = "YES">
   </ArchiveAction>
</Scheme>


================================================
FILE: BuildTimeAnalyzerTests/CompileMeasureTests.swift
================================================
//
//  CompileMeasureTests.swift
//  CMBuildTimeAnalyzerTests
//

import XCTest

@testable import BuildTimeAnalyzer

class BuildTimeAnalyzerTests: XCTestCase {
    
    func testInit() {
        // Given
        let time = 25.3
        let timeString = "\(time)ms"
        let filename = "Code.Swift"
        let fileInfo = "\(filename):10:23"
        let location = 10
        let folder = "/User/JohnAppleseed/"
        let path = "\(folder)\(filename)"
        let rawPath = "\(folder)\(fileInfo)"
        let code = "some code"
        let references = 2
        
        // When
        let resultOptional = CompileMeasure(time: time, rawPath: rawPath, code: code, references: references)
        
        // Then 
        XCTAssertNotNil(resultOptional)
        guard let result = resultOptional else { return }
        
        XCTAssertEqual(result.time, time)
        XCTAssertEqual(result.code, code)
        XCTAssertEqual(result.path, path)
        XCTAssertEqual(result.fileInfo, fileInfo)
        XCTAssertEqual(result.filename, filename)
        XCTAssertEqual(result.location, location)
        XCTAssertEqual(result.timeString, timeString)
        XCTAssertEqual(result.references, references)
    }
}


================================================
FILE: BuildTimeAnalyzerTests/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>BNDL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleSignature</key>
	<string>????</string>
	<key>CFBundleVersion</key>
	<string>1</string>
</dict>
</plist>


================================================
FILE: BuildTimeAnalyzerTests/ViewControllerDataSourceTest.swift
================================================
//
//  ViewControllerDataSourceTest.swift
//  BuildTimeAnalyzerTests
//
//  Created by Dmitrii on 02/12/2017.
//  Copyright © 2017 Cane Media Ltd. All rights reserved.
//

import XCTest
@testable import BuildTimeAnalyzer

class ViewControllerDataSourceTest: XCTestCase {

    var measArray: [CompileMeasure]!

    override func setUp() {
        super.setUp()
        let meas1 = CompileMeasure(rawPath: "FileName1.swift:1:1", time: 10)
        let meas2 = CompileMeasure(rawPath: "FileName2.swift:2:2", time: 2)
        let meas3 = CompileMeasure(rawPath: "FileName3.swift:3:3", time: 8)
        let meas4 = CompileMeasure(rawPath: "FileName3.swift:4:4", time: 0)
        let meas5 = CompileMeasure(rawPath: "FileName1.swift:5:5", time: 2)
        measArray = [meas4!, meas5!, meas2!, meas3!, meas1!]
    }

    func testInit() {
        let dataSource = ViewControllerDataSource()

        XCTAssertFalse(dataSource.aggregateByFile)
        XCTAssertEqual(dataSource.filter, "")
        XCTAssertNotNil(dataSource.sortDescriptors)
        XCTAssertEqual(dataSource.sortDescriptors.count, 0)
        XCTAssertTrue(dataSource.isEmpty())
    }

    func testAggregate() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        dataSource.aggregateByFile = true

        XCTAssertEqual(dataSource.count(), 3)
        XCTAssertFalse(dataSource.isEmpty())
    }

    func testFilter_1() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        dataSource.filter = "1"

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 2)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName1.swift")
        XCTAssertEqual(dataSource.measure(index: 1)!.filename, "FileName1.swift")
    }

    func testFilter_2() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        dataSource.filter = "2"

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 1)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName2.swift")
    }

    func testFilter_noMatch() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        dataSource.filter = "noMatch"

        XCTAssertTrue(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 0)
    }

    func testSortTimeAscending() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        let desc = NSSortDescriptor(key: "time", ascending: true)
        dataSource.sortDescriptors = [desc]

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 5)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 1)!.filename, "FileName1.swift")
        XCTAssertEqual(dataSource.measure(index: 2)!.filename, "FileName2.swift")
        XCTAssertEqual(dataSource.measure(index: 3)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 4)!.filename, "FileName1.swift")
    }

    func testSortFilenameDescending() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        let desc = NSSortDescriptor(key: "filename", ascending: false)
        dataSource.sortDescriptors = [desc]

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 5)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 1)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 2)!.filename, "FileName2.swift")
        XCTAssertEqual(dataSource.measure(index: 3)!.filename, "FileName1.swift")
        XCTAssertEqual(dataSource.measure(index: 4)!.filename, "FileName1.swift")
    }

    func testSortFilenameAscending_TimeAscending() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        let descFilename = NSSortDescriptor(key: "filename", ascending: true)
        let descTime = NSSortDescriptor(key: "time", ascending: true)
        dataSource.sortDescriptors = [descFilename, descTime]

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 5)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName1.swift")
        XCTAssertEqual(dataSource.measure(index: 0)!.time, 2)
        XCTAssertEqual(dataSource.measure(index: 1)!.filename, "FileName1.swift")
        XCTAssertEqual(dataSource.measure(index: 1)!.time, 10)
        XCTAssertEqual(dataSource.measure(index: 2)!.filename, "FileName2.swift")
        XCTAssertEqual(dataSource.measure(index: 3)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 3)!.time, 0)
        XCTAssertEqual(dataSource.measure(index: 4)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 4)!.time, 8)
    }

    func testSortTimeAscending_FilenameDescending() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        let descTime = NSSortDescriptor(key: "time", ascending: true)
        let descFilename = NSSortDescriptor(key: "filename", ascending: false)
        dataSource.sortDescriptors = [descTime, descFilename]

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 5)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 0)!.time, 0)
        XCTAssertEqual(dataSource.measure(index: 1)!.filename, "FileName2.swift")
        XCTAssertEqual(dataSource.measure(index: 1)!.time, 2)
        XCTAssertEqual(dataSource.measure(index: 2)!.filename, "FileName1.swift")
        XCTAssertEqual(dataSource.measure(index: 2)!.time, 2)
        XCTAssertEqual(dataSource.measure(index: 3)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 3)!.time, 8)
        XCTAssertEqual(dataSource.measure(index: 4)!.filename, "FileName1.swift")
        XCTAssertEqual(dataSource.measure(index: 4)!.time, 10)
    }

    func testSortTimeAscending_Filter3() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        let descTime = NSSortDescriptor(key: "time", ascending: true)
        dataSource.sortDescriptors = [descTime]
        dataSource.filter = "3"

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 2)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 0)!.time, 0)
        XCTAssertEqual(dataSource.measure(index: 1)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 1)!.time, 8)
    }

    func testFilter3_Aggregate() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        dataSource.filter = "3"
        dataSource.aggregateByFile = true

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 1)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName3.swift")
    }

    func testSortFilenameDescending_FilterCanceled_Aggregate() {
        let dataSource = ViewControllerDataSource()
        dataSource.resetSourceData(newSourceData: measArray)
        let descFilename = NSSortDescriptor(key: "filename", ascending: false)
        dataSource.sortDescriptors = [descFilename]
        dataSource.filter = "2"
        dataSource.aggregateByFile = true
        dataSource.filter = ""

        XCTAssertFalse(dataSource.isEmpty())
        XCTAssertEqual(dataSource.count(), 3)
        XCTAssertEqual(dataSource.measure(index: 0)!.filename, "FileName3.swift")
        XCTAssertEqual(dataSource.measure(index: 1)!.filename, "FileName2.swift")
        XCTAssertEqual(dataSource.measure(index: 2)!.filename, "FileName1.swift")

    }
}


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2016 Robert Gummesson

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
Build Time Analyzer for Xcode
======================

[![GitHub release](https://img.shields.io/github/release/RobertGummesson/BuildTimeAnalyzer-for-Xcode.svg)](https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode/releases/latest)
[![Swift](https://img.shields.io/badge/Swift-5-orange.svg)](https://swift.org)
[![Platform](https://img.shields.io/badge/platform-macOS-blue.svg)](https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode)
[![MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

## Overview

Build Time Analyzer is a macOS app that shows you a break down of Swift build times. See [this post]( https://medium.com/p/fc92cdd91e31) and [this post](https://medium.com/p/37b0a7514cbe) on Medium for context. 

## Usage

Open up the app and follow the instructions.

![screenshot.png](https://raw.githubusercontent.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode/master/Screenshots/screenshot.png)

## Installation

Download the code and open it in Xcode, archive the project and export the build. Easy, right?

## Contributions

If you encounter any issues or have ideas for improvement, I am open to code contributions.

## License

    Copyright (c) 2016-2026, Robert Gummesson
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this
      list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Download .txt
gitextract_fhrgbqty/

├── .gitignore
├── .travis.yml
├── BuildTimeAnalyzer/
│   ├── AppDelegate.swift
│   ├── Assets.xcassets/
│   │   ├── AppIcon.appiconset/
│   │   │   └── Contents.json
│   │   ├── Contents.json
│   │   └── ScreenShot.imageset/
│   │       └── Contents.json
│   ├── BuildManager.swift
│   ├── BuildTimeAnalyzer-Bridging-Header.h
│   ├── CSVExporter.swift
│   ├── CompileMeasure.swift
│   ├── DerivedDataManager.swift
│   ├── DirectoryMonitor.swift
│   ├── File.swift
│   ├── Info.plist
│   ├── LogProcessor.swift
│   ├── Main.storyboard
│   ├── NSAlert+Extensions.swift
│   ├── ProcessingState.swift
│   ├── ProjectSelection.swift
│   ├── RawMeasure.swift
│   ├── UserSettings.swift
│   ├── ViewController.swift
│   ├── ViewControllerDataSource.swift
│   └── XcodeDatabase.swift
├── BuildTimeAnalyzer.xcodeproj/
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       └── IDEWorkspaceChecks.plist
│   └── xcshareddata/
│       └── xcschemes/
│           └── BuildTimeAnalyzer.xcscheme
├── BuildTimeAnalyzerTests/
│   ├── CompileMeasureTests.swift
│   ├── Info.plist
│   └── ViewControllerDataSourceTest.swift
├── LICENSE
└── README.md
Condensed preview — 33 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (184K chars).
[
  {
    "path": ".gitignore",
    "chars": 1388,
    "preview": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n"
  },
  {
    "path": ".travis.yml",
    "chars": 129,
    "preview": "language: swift\nosx_image: xcode9.3\nscript: xcodebuild -project BuildTimeAnalyzer.xcodeproj -scheme BuildTimeAnalyzer bu"
  },
  {
    "path": "BuildTimeAnalyzer/AppDelegate.swift",
    "chars": 1875,
    "preview": "//\n//  AppDelegate.swift\n//  BuildTimeAnalyzer\n//\n\nimport Cocoa\n\n@NSApplicationMain\nclass AppDelegate: NSObject, NSAppli"
  },
  {
    "path": "BuildTimeAnalyzer/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1248,
    "preview": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"logo16.png\",\n      \"scale\" : \""
  },
  {
    "path": "BuildTimeAnalyzer/Assets.xcassets/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "BuildTimeAnalyzer/Assets.xcassets/ScreenShot.imageset/Contents.json",
    "chars": 308,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"Screen Shot.png\",\n      \"scale\" : \"1x\"\n    },\n  "
  },
  {
    "path": "BuildTimeAnalyzer/BuildManager.swift",
    "chars": 2544,
    "preview": "//\n//  BuildManager.swift\n//  BuildTimeAnalyzer\n//\n\nimport Cocoa\n\nprotocol BuildManagerDelegate: AnyObject {\n    func de"
  },
  {
    "path": "BuildTimeAnalyzer/BuildTimeAnalyzer-Bridging-Header.h",
    "chars": 127,
    "preview": "//\n//  Use this file to import your target's public headers that you would like to expose to Swift.\n//\n\n#import \"NSData+"
  },
  {
    "path": "BuildTimeAnalyzer/CSVExporter.swift",
    "chars": 1988,
    "preview": "//\n//  CSVExporter.swift\n//  BuildTimeAnalyzer\n//\n//  Created by Bruno Resende on 16.01.19.\n//  Copyright © 2019 Cane Me"
  },
  {
    "path": "BuildTimeAnalyzer/CompileMeasure.swift",
    "chars": 2709,
    "preview": "//\n//  CompileMeasure.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\n\n@objcMembers class CompileMeasure: NSObject {\n "
  },
  {
    "path": "BuildTimeAnalyzer/DerivedDataManager.swift",
    "chars": 1324,
    "preview": "//\n//  DerivedDataManager.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\n\nclass DerivedDataManager {\n    \n    static "
  },
  {
    "path": "BuildTimeAnalyzer/DirectoryMonitor.swift",
    "chars": 2484,
    "preview": "//\n//  DirectoryMonitor.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\n\nprotocol DirectoryMonitorDelegate: AnyObject "
  },
  {
    "path": "BuildTimeAnalyzer/File.swift",
    "chars": 115,
    "preview": "//\n//  File.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\n\nstruct File {\n    let date: Date\n    let url: URL\n}\n"
  },
  {
    "path": "BuildTimeAnalyzer/Info.plist",
    "chars": 1091,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "BuildTimeAnalyzer/LogProcessor.swift",
    "chars": 4943,
    "preview": "//\n//  LogProcessor.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\n\ntypealias CMUpdateClosure = (_ result: [CompileMe"
  },
  {
    "path": "BuildTimeAnalyzer/Main.storyboard",
    "chars": 80507,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB\" version=\"3.0\" t"
  },
  {
    "path": "BuildTimeAnalyzer/NSAlert+Extensions.swift",
    "chars": 417,
    "preview": "//\n//  NSAlert+Extensions.swift\n//  BuildTimeAnalyzer\n//\n\nimport Cocoa\n\nextension NSAlert {\n    static func show(withMes"
  },
  {
    "path": "BuildTimeAnalyzer/ProcessingState.swift",
    "chars": 833,
    "preview": "//\n//  ProcessingState.swift\n//  BuildTimeAnalyzer\n//\n\nenum ProcessingState {\n    case processing\n    case waiting\n    c"
  },
  {
    "path": "BuildTimeAnalyzer/ProjectSelection.swift",
    "chars": 2173,
    "preview": "//\n//  ProjectSelection.swift\n//  BuildTimeAnalyzer\n//\n\nimport Cocoa\n\nprotocol ProjectSelectionDelegate: AnyObject {\n   "
  },
  {
    "path": "BuildTimeAnalyzer/RawMeasure.swift",
    "chars": 654,
    "preview": "//\n//  RawMeasure.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\n\nstruct RawMeasure {\n    var time: Double\n    var te"
  },
  {
    "path": "BuildTimeAnalyzer/UserSettings.swift",
    "chars": 1580,
    "preview": "//\n//  UserCache.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\n\nclass UserSettings {\n    \n    static private let der"
  },
  {
    "path": "BuildTimeAnalyzer/ViewController.swift",
    "chars": 12742,
    "preview": "//\n//  ViewController.swift\n//  BuildTimeAnalyzer\n//\n\nimport Cocoa\n\nclass ViewController: NSViewController {\n    \n    @I"
  },
  {
    "path": "BuildTimeAnalyzer/ViewControllerDataSource.swift",
    "chars": 2760,
    "preview": "//\n//  ViewControllerDataSource.swift\n//  BuildTimeAnalyzer\n//\n//  Created by Dmitrii on 02/12/2017.\n//  Copyright © 201"
  },
  {
    "path": "BuildTimeAnalyzer/XcodeDatabase.swift",
    "chars": 4707,
    "preview": "//\n//  XcodeDatabase.swift\n//  BuildTimeAnalyzer\n//\n\nimport Foundation\nimport Compression\n\nstruct XcodeDatabase {\n    va"
  },
  {
    "path": "BuildTimeAnalyzer.xcodeproj/project.pbxproj",
    "chars": 25369,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "BuildTimeAnalyzer.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 162,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:BuildTimeAnalyz"
  },
  {
    "path": "BuildTimeAnalyzer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "BuildTimeAnalyzer.xcodeproj/xcshareddata/xcschemes/BuildTimeAnalyzer.xcscheme",
    "chars": 3777,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"2630\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "BuildTimeAnalyzerTests/CompileMeasureTests.swift",
    "chars": 1219,
    "preview": "//\n//  CompileMeasureTests.swift\n//  CMBuildTimeAnalyzerTests\n//\n\nimport XCTest\n\n@testable import BuildTimeAnalyzer\n\ncla"
  },
  {
    "path": "BuildTimeAnalyzerTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "BuildTimeAnalyzerTests/ViewControllerDataSourceTest.swift",
    "chars": 8225,
    "preview": "//\n//  ViewControllerDataSourceTest.swift\n//  BuildTimeAnalyzerTests\n//\n//  Created by Dmitrii on 02/12/2017.\n//  Copyri"
  },
  {
    "path": "LICENSE",
    "chars": 1083,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Robert Gummesson\n\nPermission is hereby granted, free of charge, to any person "
  },
  {
    "path": "README.md",
    "chars": 2580,
    "preview": "Build Time Analyzer for Xcode\n======================\n\n[![GitHub release](https://img.shields.io/github/release/RobertGum"
  }
]

About this extraction

This page contains the full source code of the RobertGummesson/BuildTimeAnalyzer-for-Xcode GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 33 files (168.1 KB), approximately 40.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!