👊 commit

This commit is contained in:
Zach Eriksen 2021-05-24 21:48:14 -05:00
parent 5284b6ac55
commit 4f9ba07a1c
7 changed files with 353 additions and 9 deletions

61
Package.resolved Normal file
View File

@ -0,0 +1,61 @@
{
"object": {
"pins": [
{
"package": "Chronicle",
"repositoryURL": "https://github.com/0xLeif/Chronicle",
"state": {
"branch": null,
"revision": "5d5e80f63943969eb2c33d686958b3938536a455",
"version": "0.2.3"
}
},
{
"package": "SURL",
"repositoryURL": "https://github.com/0xLet/SURL",
"state": {
"branch": null,
"revision": "c04f941eea250f78f5c7151c03cd2489ca9ffc2b",
"version": "0.1.1"
}
},
{
"package": "SwiftFu",
"repositoryURL": "https://github.com/0xLet/SwiftFu",
"state": {
"branch": null,
"revision": "818f889edc5a0c1657b50f3a1e841aa508177323",
"version": "1.0.1"
}
},
{
"package": "Task",
"repositoryURL": "https://github.com/0xLeif/Task",
"state": {
"branch": null,
"revision": "cfe31fd675b88c36194a34e8ccb8e8f8068d41c8",
"version": "1.0.0"
}
},
{
"package": "WTV",
"repositoryURL": "https://github.com/0xLet/WTV",
"state": {
"branch": null,
"revision": "8eefc65bf823eb7612047dbd2df4c6c73864c500",
"version": "0.1.1"
}
},
{
"package": "Yarn",
"repositoryURL": "https://github.com/0xLeif/Yarn",
"state": {
"branch": null,
"revision": "3c50cc133bc71879aaf3bbb15fa271e9506489f8",
"version": "1.0.0"
}
}
]
},
"version": 1
}

View File

@ -5,6 +5,12 @@ import PackageDescription
let package = Package( let package = Package(
name: "ObjectUI", name: "ObjectUI",
platforms: [
.iOS(.v13),
.macOS(.v10_15),
.watchOS(.v6),
.tvOS(.v13)
],
products: [ products: [
// Products define the executables and libraries a package produces, and make them visible to other packages. // Products define the executables and libraries a package produces, and make them visible to other packages.
.library( .library(
@ -14,13 +20,26 @@ let package = Package(
dependencies: [ dependencies: [
// Dependencies declare other packages that this package depends on. // Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"), // .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/0xLet/WTV", from: "0.1.1"),
.package(url: "https://github.com/0xLet/SURL", from: "0.1.0"),
.package(url: "https://github.com/0xLet/SwiftFu", from: "1.0.1"),
.package(url: "https://github.com/0xLeif/Chronicle", from: "0.2.3"),
.package(url: "https://github.com/0xLeif/Task", from: "1.0.0"),
.package(url: "https://github.com/0xLeif/Yarn", from: "1.0.0")
], ],
targets: [ targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite. // 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. // Targets can depend on other targets in this package, and on products in packages this package depends on.
.target( .target(
name: "ObjectUI", name: "ObjectUI",
dependencies: []), dependencies: [
"WTV",
"SURL",
"SwiftFu",
"Chronicle",
"Task",
"Yarn"
]),
.testTarget( .testTarget(
name: "ObjectUITests", name: "ObjectUITests",
dependencies: ["ObjectUI"]), dependencies: ["ObjectUI"]),

View File

@ -1,3 +1,3 @@
# ObjectUI # ObjectUI
A description of this package. *Create SwiftUI Views with any data*

View File

@ -0,0 +1,239 @@
//
// Object.swift
// ObjectUI
//
// Created by Leif on 5/24/21.
//
import Foundation
import SwiftFu
import SwiftUI
import Combine
@dynamicMemberLookup
public class Object: FuableClass, ObservableObject {
public enum ObjectVariable: String, Hashable {
case value
case child
case array
case json
}
/// Variables of the object
@Published public var variables: [AnyHashable: Any] = [:]
/// @dynamicMemberLookup
public subscript(dynamicMember member: String) -> Object {
variable(named: member)
}
// MARK: public init
public init() { }
public convenience init(_ closure: (Object) -> Void) {
self.init()
closure(self)
}
public init(_ value: Any? = nil, _ closure: ((Object) -> Void)? = nil) {
defer {
if let closure = closure {
configure(closure)
}
}
guard let value = value else {
return
}
let unwrappedValue = unwrap(value)
if let _ = unwrappedValue as? NSNull {
return
}
if let object = unwrappedValue as? Object {
consume(object)
} else if let array = unwrappedValue as? [Any] {
consume(Object(array: array))
} else if let dictionary = unwrappedValue as? [AnyHashable: Any] {
consume(Object(dictionary: dictionary))
} else if let data = unwrappedValue as? Data {
consume(Object(data: data))
} else {
consume(Object().set(value: unwrappedValue))
}
}
// MARK: private init
private init(array: [Any]) {
set(variable: ObjectVariable.array, value: array.map {
Object($0)
})
}
private init(dictionary: [AnyHashable: Any]) {
variables = dictionary
}
private init(data: Data) {
defer {
set(variable: ObjectVariable.json, value: String(data: data, encoding: .utf8))
set(value: data)
}
if let json = try? JSONSerialization.jsonObject(with: data,
options: .allowFragments) as? [Any] {
set(variable: ObjectVariable.array, value: json)
return
}
guard let json = try? JSONSerialization.jsonObject(with: data,
options: .allowFragments) as? [AnyHashable: Any] else {
return
}
consume(Object(json))
}
}
// MARK: public variables
public extension Object {
var array: [Object] {
if let array = variables[ObjectVariable.array] as? [Data] {
return array.map { Object(data: $0) }
} else if let array = variables[ObjectVariable.array] as? [Any] {
return array.map { value in
guard let json = value as? [AnyHashable: Any] else {
return Object(value)
}
return Object(dictionary: json)
}
}
return []
}
var child: Object {
(variables[ObjectVariable.child] as? Object) ?? Object()
}
var value: Any {
variables[ObjectVariable.value] ?? Object()
}
}
// MARK: public functions
public extension Object {
/// Retrieve a Value from the current object
@discardableResult
func variable(named: AnyHashable) -> Object {
guard let value = variables[named] else {
return Object()
}
if let array = value as? [Any] {
return Object(array: array)
}
guard let object = value as? Object else {
return Object(unwrap(value))
}
return object
}
/// Set a named Value to the current object
@discardableResult
func set(variable named: AnyHashable = ObjectVariable.value, value: Any?) -> Self {
guard let value = value,
(unwrap(value) as? NSNull) == nil else {
return self
}
variables[named] = value
return self
}
/// Set a named Value to the current object
@discardableResult
func set<T>(variable named: T, value: Any?) -> Self where T: RawRepresentable, T.RawValue == String {
guard let value = value,
(unwrap(value) as? NSNull) == nil else {
return self
}
variables[named.rawValue] = value
return self
}
/// Modify a Value with a name to the current object
@discardableResult
func modify<T>(variable named: AnyHashable = ObjectVariable.value, modifier: (T?) -> T?) -> Self {
guard let variable = variables[named],
let value = variable as? T else {
variables[named] = modifier(nil)
return self
}
variables[named] = modifier(value)
return self
}
/// Update a Value with a name to the current object
@discardableResult
func update<T>(variable named: AnyHashable = ObjectVariable.value, modifier: (T) -> T) -> Self {
guard let variable = variables[named],
let value = variable as? T else {
return self
}
variables[named] = modifier(value)
return self
}
/// Set the ChildObject with a name of `_object` to the current object
@discardableResult
func set(childObject object: Object) -> Self {
variables[ObjectVariable.child] = object
return self
}
/// Set the Array with a name of `_array` to the current object
@discardableResult
func set(array: [Any]) -> Self {
variables[ObjectVariable.array] = array
return self
}
@discardableResult
func configure(_ closure: (Object) -> Void) -> Object {
closure(self)
return self
}
@discardableResult
func consume(_ object: Object) -> Object {
object.variables.forEach { (key, value) in
self.set(variable: key, value: value)
}
return self
}
func value<T>(as type: T.Type? = nil) -> T? {
value as? T
}
func value<T>(decodedAs type: T.Type) -> T? where T: Decodable {
guard let data = value(as: Data.self) else {
return nil
}
return try? JSONDecoder().decode(T.self, from: data)
}
}
private extension Object {
/// Unwraps the <Optional> Any type
func unwrap(_ value: Any) -> Any {
let mValue = Mirror(reflecting: value)
let isValueOptional = mValue.displayStyle != .optional
let isValueEmpty = mValue.children.isEmpty
if isValueOptional { return value }
if isValueEmpty { return NSNull() }
guard let (_, unwrappedValue) = mValue.children.first else { return NSNull() }
return unwrappedValue
}
}

View File

@ -1,3 +0,0 @@
struct ObjectUI {
var text = "Hello, World!"
}

View File

@ -0,0 +1,26 @@
//
// ObjectView.swift
// ObjectUI
//
// Created by Leif on 5/24/21.
//
import SwiftUI
public struct ObjectView<Content>: View where Content: View {
@ObservedObject private var object: Object
private var content: (Object) -> Content
public init(
data: Any? = nil,
content: @escaping (Object) -> Content
) {
self.object = Object(data)
self.content = content
}
public var body: some View {
content(object)
}
}

View File

@ -3,9 +3,11 @@
final class ObjectUITests: XCTestCase { final class ObjectUITests: XCTestCase {
func testExample() { func testExample() {
// This is an example of a functional test case. enum Value: String {
// Use XCTAssert and related functions to verify your tests produce the correct case text
// results. }
XCTAssertEqual(ObjectUI().text, "Hello, World!") let object = Object()
object.set(variable: Value.text, value: "Hello, World!")
XCTAssertEqual(object.text.value(), "Hello, World!")
} }
} }