Created
May 17, 2021 08:33
-
-
Save vibrazy/ced9d3b9959fbfbd7ee55a7fb6c2a42a to your computer and use it in GitHub Desktop.
Generate strongly typed PreviewDevice for SwiftUI
This file contains 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
// | |
// main.swift | |
// PreviewDeviceExporter | |
// | |
// Created by Dan Tavares on 13/05/2021. | |
// https://github.com/JohnSundell/Files | |
// https://github.com/JohnSundell/ShellOut.git | |
// Packages used | |
import Foundation | |
import Files | |
import ShellOut | |
let template = | |
""" | |
// | |
// PreviewDeviceExporter | |
// | |
// Created by Daniel Tavares on 13/05/2021. | |
// http://twitter.com/vibrazy | |
// https://github.com/vibrazy | |
// http://dev.to/vibrazy | |
import SwiftUI | |
extension PreviewDevice { | |
%@ | |
} | |
struct PreviewDeviceModifier: ViewModifier { | |
let previewDevice: PreviewDevice | |
func body(content: Content) -> some View { | |
content | |
.previewDevice(previewDevice) | |
.previewDisplayName(previewDevice.rawValue) | |
} | |
} | |
public extension View { | |
func preview(on previewDevice: PreviewDevice) -> some View { | |
self.modifier( | |
PreviewDeviceModifier( | |
previewDevice: previewDevice | |
) | |
) | |
} | |
} | |
""" | |
enum PreviewDeviceExporterError: Error { | |
case faieldToRetriveDevices | |
} | |
do { | |
let output = try shellOut(to: "xcrun simctl list --json devices available") | |
guard let data = output.data(using: .utf8) else { | |
throw PreviewDeviceExporterError.faieldToRetriveDevices | |
} | |
let deviceTypes = try JSONDecoder().decode(SimCTL.self, from: data) | |
let finalOutput = String(format: template, "\(deviceTypes)") | |
let folder = Folder.current | |
let file = try folder.createFile(named: "PreviewDeviceUtils.swift") | |
try file.write(finalOutput) | |
print("PreviewDeviceUtils.swift generated successfully ✅") | |
} catch let error { | |
print("Error occured: \(error) ❌") | |
} | |
// MARK: - String Interpolation | |
extension String.StringInterpolation { | |
mutating func appendInterpolation(_ value: SimCTL) { | |
let devicesString = value | |
.supportedDevices | |
.map({ "static let \($0.name.camelized) = Self(rawValue: \"\($0.name)\")" }) | |
.joined(separator: "\n\t") | |
appendLiteral(devicesString) | |
} | |
} | |
// MARK: - String Utils | |
// https://gist.github.com/reitzig/67b41e75176ddfd432cb09392a270218 | |
extension String { | |
var uppercasingFirst: String { | |
return prefix(1).uppercased() + dropFirst() | |
} | |
var lowercasingFirst: String { | |
return prefix(1).lowercased() + dropFirst() | |
} | |
var camelized: String { | |
guard !isEmpty else { | |
return "" | |
} | |
let parts = self.components(separatedBy: CharacterSet.alphanumerics.inverted) | |
let first = String(describing: parts.first!).lowercasingFirst | |
let rest = parts.dropFirst().map({String($0).uppercasingFirst}) | |
return ([first] + rest).joined(separator: "") | |
} | |
} | |
// MARK: - Models | |
public struct SimCTL: Codable, Equatable { | |
private var devices: [String: [Device]] | |
public var supportedDevices: [Device] = [] | |
enum CodingKeys: String, CodingKey { | |
case devices = "devices" | |
} | |
public init(devices: [String: [Device]]) { | |
self.devices = devices | |
} | |
public init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
devices = try container.decode([String: [Device]].self, forKey: CodingKeys.devices) | |
supportedDevices = devices | |
.filter({ !$0.value.isEmpty }) | |
.map({ $0.value }) | |
.flatMap({ $0 }) | |
} | |
} | |
// MARK: - Device | |
public struct Device: Codable, Equatable { | |
public let dataPath: String | |
public let logPath: String | |
public let udid: String | |
public let isAvailable: Bool | |
public let deviceTypeIdentifier: String | |
public let state: State | |
public let name: String | |
enum CodingKeys: String, CodingKey { | |
case dataPath = "dataPath" | |
case logPath = "logPath" | |
case udid = "udid" | |
case isAvailable = "isAvailable" | |
case deviceTypeIdentifier = "deviceTypeIdentifier" | |
case state = "state" | |
case name = "name" | |
} | |
public init(dataPath: String, logPath: String, udid: String, isAvailable: Bool, deviceTypeIdentifier: String, state: State, name: String) { | |
self.dataPath = dataPath | |
self.logPath = logPath | |
self.udid = udid | |
self.isAvailable = isAvailable | |
self.deviceTypeIdentifier = deviceTypeIdentifier | |
self.state = state | |
self.name = name | |
} | |
} | |
public enum State: String, Codable, Equatable { | |
case booted = "Booted" | |
case shutdown = "Shutdown" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment