Last active
June 7, 2020 02:52
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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