diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..0165212
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,32 @@
+// swift-tools-version:5.3
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "MetalUI",
+ platforms: [
+ .iOS(.v13),
+ .macOS(.v10_15)
+ ],
+ products: [
+ // Products define the executables and libraries a package produces, and make them visible to other packages.
+ .library(
+ name: "MetalUI",
+ targets: ["MetalUI"]),
+ ],
+ dependencies: [
+ // Dependencies declare other packages that this package depends on.
+ // .package(url: /* package url */, from: "1.0.0"),
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+ // Targets can depend on other targets in this package, and on products in packages this package depends on.
+ .target(
+ name: "MetalUI",
+ dependencies: []),
+ .testTarget(
+ name: "MetalUITests",
+ dependencies: ["MetalUI"]),
+ ]
+)
diff --git a/Sources/MetalUI/Data/MetalRenderingVertex.swift b/Sources/MetalUI/Data/MetalRenderingVertex.swift
new file mode 100644
index 0000000..a3888e2
--- /dev/null
+++ b/Sources/MetalUI/Data/MetalRenderingVertex.swift
@@ -0,0 +1,14 @@
+import MetalKit
+
+public struct MetalRenderingVertex {
+ public var position: SIMD3
+ public var color: SIMD4
+
+ public init(
+ position: SIMD3,
+ color: SIMD4
+ ) {
+ self.position = position
+ self.color = color
+ }
+}
diff --git a/Sources/MetalUI/MTKView/MetalPresenting.swift b/Sources/MetalUI/MTKView/MetalPresenting.swift
new file mode 100644
index 0000000..b322ddd
--- /dev/null
+++ b/Sources/MetalUI/MTKView/MetalPresenting.swift
@@ -0,0 +1,26 @@
+import MetalKit
+
+public protocol MetalPresenting: MTKView {
+ var renderer: MetalRendering! { get set }
+
+ init()
+
+ func configure(device: MTLDevice?)
+
+ func configureMTKView()
+ func renderer(forDevice device: MTLDevice) -> MetalRendering
+}
+
+public extension MetalPresenting {
+
+ func configure(device: MTLDevice? = MTLCreateSystemDefaultDevice()) {
+ // Make sure we are on a device that can run metal!
+ guard let defaultDevice = device else {
+ fatalError("Device loading error")
+ }
+
+ self.renderer = renderer(forDevice: defaultDevice)
+ self.delegate = renderer
+ self.configureMTKView()
+ }
+}
diff --git a/Sources/MetalUI/MTKViewDelegate/MetalRendering.swift b/Sources/MetalUI/MTKViewDelegate/MetalRendering.swift
new file mode 100644
index 0000000..9721318
--- /dev/null
+++ b/Sources/MetalUI/MTKViewDelegate/MetalRendering.swift
@@ -0,0 +1,38 @@
+import MetalKit
+
+public protocol MetalRendering: NSObject, MTKViewDelegate {
+ var commandQueue: MTLCommandQueue? { get set }
+ var renderPipelineState: MTLRenderPipelineState? { get set }
+ var vertexBuffer: MTLBuffer? { get set }
+
+ var vertices: [MetalRenderingVertex] { get set }
+
+ init()
+ init(
+ vertices: [MetalRenderingVertex],
+ device: MTLDevice
+ )
+
+ func createCommandQueue(device: MTLDevice)
+ func createPipelineState(
+ withLibrary library: MTLLibrary?,
+ forDevice device: MTLDevice
+ )
+ func createBuffers(device: MTLDevice)
+
+ // MARK: MTKViewDelegate
+ func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize)
+ func draw(in view: MTKView)
+}
+
+public extension MetalRendering {
+ init(vertices: [MetalRenderingVertex], device: MTLDevice) {
+ self.init()
+
+ self.vertices = vertices
+
+ createCommandQueue(device: device)
+ createPipelineState(withLibrary: device.makeDefaultLibrary(), forDevice: device)
+ createBuffers(device: device)
+ }
+}
diff --git a/Sources/MetalUI/SwiftUI/MetalView.swift b/Sources/MetalUI/SwiftUI/MetalView.swift
new file mode 100644
index 0000000..64c20cd
--- /dev/null
+++ b/Sources/MetalUI/SwiftUI/MetalView.swift
@@ -0,0 +1,83 @@
+import SwiftUI
+import MetalKit
+
+#if os(iOS)
+public struct MetalView: UIViewRepresentable where Content: MetalPresenting {
+ public var wrappedView: Content
+
+ private var handleUpdateUIView: ((Content, Context) -> Void)?
+ private var handleMakeUIView: ((Context) -> Content)?
+
+ public init(closure: () -> Content) {
+ wrappedView = closure()
+ }
+
+ public func makeUIView(context: Context) -> Content {
+ guard let handler = handleMakeUIView else {
+ return wrappedView
+ }
+
+ return handler(context)
+ }
+
+ public func updateUIView(_ uiView: Content, context: Context) {
+ handleUpdateUIView?(uiView, context)
+ }
+}
+
+public extension MetalView {
+ mutating func setMakeUIView(handler: @escaping (Context) -> Content) -> Self {
+ handleMakeUIView = handler
+
+ return self
+ }
+
+ mutating func setUpdateUIView(handler: @escaping (Content, Context) -> Void) -> Self {
+ handleUpdateUIView = handler
+
+ return self
+ }
+}
+#elseif os(macOS)
+public struct MetalView: NSViewRepresentable where Content: MetalPresenting {
+ public typealias NSViewType = Content
+
+ public var wrappedView: Content
+
+ private var handleUpdateNSView: ((Content, Context) -> Void)?
+ private var handleMakeNSView: ((Context) -> Content)?
+
+ public init(closure: () -> Content) {
+ wrappedView = closure()
+ }
+
+ public func makeNSView(context: Context) -> Content {
+ guard let handler = handleMakeNSView else {
+ return wrappedView
+ }
+
+ return handler(context)
+ }
+
+ public func updateNSView(_ nsView: Content, context: Context) {
+ handleUpdateNSView?(nsView, context)
+ }
+}
+
+public extension MetalView {
+ mutating func setMakeNSView(handler: @escaping (Context) -> Content) -> Self {
+ handleMakeNSView = handler
+
+ return self
+ }
+
+ mutating func setUpdateNSView(handler: @escaping (Content, Context) -> Void) -> Self {
+ handleUpdateNSView = handler
+
+ return self
+ }
+}
+
+#endif
+
+
diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift
new file mode 100644
index 0000000..384af03
--- /dev/null
+++ b/Tests/LinuxMain.swift
@@ -0,0 +1,7 @@
+import XCTest
+
+import MetalUITests
+
+var tests = [XCTestCaseEntry]()
+tests += MetalUITests.allTests()
+XCTMain(tests)
diff --git a/Tests/MetalUITests/MetalUITests.swift b/Tests/MetalUITests/MetalUITests.swift
new file mode 100644
index 0000000..8d8a50a
--- /dev/null
+++ b/Tests/MetalUITests/MetalUITests.swift
@@ -0,0 +1,15 @@
+import XCTest
+@testable import MetalUI
+
+final class MetalUITests: XCTestCase {
+ func testExample() {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct
+ // results.
+ XCTAssertEqual("MetalUI().text", "Hello, World!")
+ }
+
+ static var allTests = [
+ ("testExample", testExample),
+ ]
+}
diff --git a/Tests/MetalUITests/XCTestManifests.swift b/Tests/MetalUITests/XCTestManifests.swift
new file mode 100644
index 0000000..1889a48
--- /dev/null
+++ b/Tests/MetalUITests/XCTestManifests.swift
@@ -0,0 +1,9 @@
+import XCTest
+
+#if !canImport(ObjectiveC)
+public func allTests() -> [XCTestCaseEntry] {
+ return [
+ testCase(MetalUITests.allTests),
+ ]
+}
+#endif