Skip to content

Instantly share code, notes, and snippets.

@mihaelamj
Last active June 7, 2020 02:52
Show Gist options
  • Save mihaelamj/61b197a905ee1008bc18e9de485a4f06 to your computer and use it in GitHub Desktop.
Save mihaelamj/61b197a905ee1008bc18e9de485a4f06 to your computer and use it in GitHub Desktop.
Utility to make a CSV report on a storyboard, uses `IBDecodable` swift package
//
// StoryboardParser.swift
// StoryboardReader
//
// Created by Mihaela Mihaljevic Jakic on 07/06/2020.
// Copyright © 2020 Mihaela Mihaljevic Jakic. All rights reserved.
//
import Foundation
import IBDecodable
class StoryboardParser {
// usage:
// rename ext for your storyboard to `.xml` if inspecting an external storyboard
// StoryboardParser().inspectSBToCSVFile(name: "EarnGuide")
struct TextLogFile: TextOutputStream {
var fileName = "Endpoints.txt"
var useDateFileNames = false
func write(_ string: String) {
let fm = FileManager.default
var fullFileName = fileName
if useDateFileNames {
let dFormatter = DateFormatter()
dFormatter.dateFormat = "yyyy-MM-"
let date = Date()
let currentMonth = dFormatter.string(from: date)
fullFileName = String(currentMonth) + fileName
}
let log = fm.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(fullFileName)
if let handle = try? FileHandle(forWritingTo: log) {
handle.seekToEndOfFile()
// swiftlint:disable:next force_unwrapping
handle.write(string.data(using: .utf8)!)
handle.closeFile()
} else {
try? string.data(using: .utf8)?.write(to: log)
}
}
static func fileUrl(fileName: String) -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(fileName)
}
static func deleteFile(fileName: String) -> Bool {
let aFileUrl = fileUrl(fileName: fileName)
do {
try FileManager.default.removeItem(at: aFileUrl)
return true
} catch {
return false
}
}
}
func getUrl(forResource resource: String, withExtension ext: String) -> URL? {
let bundle = Bundle(for: type(of: self))
if let url = bundle.url(forResource: resource, withExtension: ext) {
return url
}
return nil
}
func inspectSBToCSVFile(name: String, ext: String = "xml") {
let sep = ";"
let header = "Scene\(sep)ViewController\(sep)StoryBoardId\(sep)VC Id\(sep)Segue\(sep)Kind\(sep)Identifier\(sep)relationship\(sep)destination\(sep)destCreateSelector\(sep)modalTransStyle"
guard let fileURL = getUrl(forResource:name, withExtension: ext) else { return }
guard let file = try? StoryboardFile(url: fileURL) else { return }
guard let scenes = file.document.scenes else { return }
let fileName = "Storyboard-\(name).csv"
_ = TextLogFile.deleteFile(fileName: fileName)
var textLog = TextLogFile(fileName: fileName, useDateFileNames: false)
print(header, to: &textLog)
let viewControllersAndScenes = scenes.compactMap { return (vc: $0.viewController?.viewController, sc: $0.id) }
for touple in viewControllersAndScenes {
guard let viewController = touple.vc else { continue }
let sceneName = touple.sc
let vcName = viewController.customClass ?? viewController.elementClass
let vcId = viewController.id
let vcSBId = viewController.storyboardIdentifier ?? "N/A"
var segId = ""
var segRel = ""
var segDest = ""
var segCreateSel = ""
var segMTS = ""
var segKind = Segue.Kind.custom("none")
var segIdentifier = ""
guard let allConns = viewController.connections else { continue }
let segues = allConns.compactMap { $0.connection as? Segue }
if segues.isEmpty {
let line = "\(sceneName)\(sep)\(vcName)\(sep)\(vcSBId)\(sep)\(vcId)\(sep)\(segId)\(sep)\(segKind)\(sep)\(segId)\(sep)\(segRel)\(sep)\(segDest)\(sep)\(segCreateSel)\(sep)\(segMTS)"
print(line, to: &textLog)
}
// Uncomment if needed
// let actions = allConns.compactMap { $0.connection as? Action }
// let outlets = allConns.compactMap { $0.connection as? Outlet }
// let outColls = allConns.compactMap { $0.connection as? OutletCollection }
for segue in segues {
segId = segue.id
segRel = segue.relationship ?? "N/A"
segDest = segue.destination
let destVc = findVCWithId(segDest, scenes: scenes)
segDest = destVc?.customClass ?? destVc?.elementClass ?? "N/A"
segCreateSel = segue.destinationCreationSelector?.description ?? "N/A"
segMTS = segue.modalTransitionStyle.debugDescription
segIdentifier = segue.identifier ?? "N/A"
segKind = segue.kind
let line = "\(sceneName)\(sep)\(vcName)\(sep)\(vcSBId)\(sep)\(vcId)\(sep)\(segId)\(sep)\(segKind)\(sep)\(segIdentifier)\(sep)\(segRel)\(sep)\(segDest)\(sep)\(segCreateSel)\(sep)\(segMTS)"
print(line, to: &textLog)
}
}
}
func findVCWithId(_ id: String, scenes: [Scene]) -> ViewControllerProtocol? {
let res = scenes.first(where: { $0.viewController?.viewController.id == id })
return res?.viewController?.viewController
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment