👊 commit
This commit is contained in:
parent
5284b6ac55
commit
4f9ba07a1c
|
@ -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
|
||||
}
|
|
@ -5,6 +5,12 @@ import PackageDescription
|
|||
|
||||
let package = Package(
|
||||
name: "ObjectUI",
|
||||
platforms: [
|
||||
.iOS(.v13),
|
||||
.macOS(.v10_15),
|
||||
.watchOS(.v6),
|
||||
.tvOS(.v13)
|
||||
],
|
||||
products: [
|
||||
// Products define the executables and libraries a package produces, and make them visible to other packages.
|
||||
.library(
|
||||
|
@ -14,13 +20,26 @@ let package = Package(
|
|||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
// .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 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: "ObjectUI",
|
||||
dependencies: []),
|
||||
dependencies: [
|
||||
"WTV",
|
||||
"SURL",
|
||||
"SwiftFu",
|
||||
"Chronicle",
|
||||
"Task",
|
||||
"Yarn"
|
||||
]),
|
||||
.testTarget(
|
||||
name: "ObjectUITests",
|
||||
dependencies: ["ObjectUI"]),
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# ObjectUI
|
||||
|
||||
A description of this package.
|
||||
*Create SwiftUI Views with any data*
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
struct ObjectUI {
|
||||
var text = "Hello, World!"
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -3,9 +3,11 @@
|
|||
|
||||
final class ObjectUITests: 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(ObjectUI().text, "Hello, World!")
|
||||
enum Value: String {
|
||||
case text
|
||||
}
|
||||
let object = Object()
|
||||
object.set(variable: Value.text, value: "Hello, World!")
|
||||
XCTAssertEqual(object.text.value(), "Hello, World!")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue