Skip to content

Commit

Permalink
Merge pull request #14 from iWECon/dev
Browse files Browse the repository at this point in the history
Improved performance and reduced memory footprint
  • Loading branch information
iWECon authored Aug 26, 2023
2 parents f2780d8 + a6168b3 commit 5103714
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 52 deletions.
113 changes: 61 additions & 52 deletions Sources/Lookup/Lookup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fileprivate extension String {
fileprivate func unwrap(_ object: Any?) -> Any {
switch object {
case let lookup as Lookup:
return unwrap(lookup.object)
return unwrap(lookup.rawValue)
case let number as NSNumber:
return number
case let str as String:
Expand Down Expand Up @@ -57,8 +57,24 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
case string
}

fileprivate private(set) var object: Any
public var rawValue: Any { object }
public var rawValue: Any {
get {
switch rawType {
case .none:
return NSNull()
case .dict:
return rawDict
case .array:
return rawArray
case .object:
return rawDict
case .number:
return rawNumber
case .string:
return rawString
}
}
}

fileprivate let rawType: RawType
fileprivate private(set) var rawDict: [String: Any] = [:]
Expand All @@ -67,16 +83,14 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
fileprivate private(set) var rawNumber: NSNumber = 0

private init(jsonObject: Any) {
self.object = jsonObject

switch jsonObject {
case Optional<Any>.none:
self.rawType = .none
case _ as NSNull:
self.rawType = .none

default:
switch unwrap(object) {
switch unwrap(jsonObject) {
case let number as NSNumber:
self.rawNumber = number
self.rawType = .number
Expand All @@ -96,7 +110,6 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
case _ as AnyObject:
self.rawDict = mirrors(reflecting: jsonObject)
self.rawType = .object

default:
self.rawType = .none
}
Expand Down Expand Up @@ -137,6 +150,12 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
}
}

// Resolve build warning:
// heterogeneous collection literal could only be inferred to '[String : Any]'; add explicit type annotation if this is intentional
public init(_ anyDictionary: [String: Any]) {
self.init(anyDictionary as Any)
}

private func makeLookup(from dynamicMember: String) -> Lookup {
if dynamicMember.contains(".") {
var keys = dynamicMember.components(separatedBy: ".")
Expand All @@ -146,17 +165,17 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
case .none:
return .null
case .dict, .object:
let value = rawDict[key, default: NSNull()]
let value: Any = rawDict[key, default: NSNull()]
let innerLookup = Lookup(value)
keys.removeFirst()

let newKey = keys.joined(separator: ".")
let newKey: String = keys.joined(separator: ".")
return innerLookup[dynamicMember: newKey]
case .array, .string:
if key.isPurnInt, let index = Int(key) {
keys.removeFirst()

let newKey = keys.joined(separator: ".")
let newKey: String = keys.joined(separator: ".")
return self[index][dynamicMember: newKey]
}
return .null
Expand Down Expand Up @@ -187,21 +206,14 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
switch rawType {
case .none:
break
case .dict:
case .dict, .object:
rawDict[dynamicMember] = value.rawValue
case .array:
switch value.rawType {
case .array:
rawArray = value.rawArray
default:
break
}
case .object:
break
rawArray = value.rawArray
case .number:
break
rawNumber = value.rawNumber
case .string:
break
rawString = value.rawString
}
}

Expand Down Expand Up @@ -240,34 +252,13 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
}
}

private func jsonToPrettyString(value: Any) -> String? {
if JSONSerialization.isValidJSONObject(value),
let data = try? JSONSerialization.data(withJSONObject: value, options: .prettyPrinted),
private func castValueToString(value: Any) -> String {
if let data: Data = try? JSONSerialization.data(withJSONObject: value, options: .prettyPrinted),
let str = String(data: data, encoding: .utf8)
{
return str
}
return nil
}

private func castValueToString(value: Any) -> String {
if let str = jsonToPrettyString(value: value) {
return str
}
// map to string
switch value {
case let dict as Dictionary<String, Any>:
let strDict = Dictionary(uniqueKeysWithValues: dict.map { (key: String, value: Any) in
(key, "\(value)")
})
return jsonToPrettyString(value: strDict) ?? "\(strDict)"

case let arr as [Any]:
return jsonToPrettyString(value: arr) ?? "\(arr)"

default:
return "\(value)"
}
return "Can not cast value to string"
}

public var description: String {
Expand All @@ -280,7 +271,7 @@ public struct Lookup: Swift.CustomStringConvertible, Swift.CustomDebugStringConv
case .number:
desc = "\(rawNumber)"
case .string:
desc = "\(object)"
desc = "\(rawValue)"
case .none:
desc = "nil"
}
Expand Down Expand Up @@ -325,6 +316,24 @@ extension Lookup: ExpressibleByStringLiteral {
}
}

extension Lookup: ExpressibleByIntegerLiteral {
public init(integerLiteral value: IntegerLiteralType) {
self.init(jsonObject: value)
}
}

extension Lookup: ExpressibleByFloatLiteral {
public init(floatLiteral value: FloatLiteralType) {
self.init(jsonObject: value)
}
}

extension Lookup: ExpressibleByNilLiteral {
public init(nilLiteral: ()) {
self.init(jsonObject: NSNull())
}
}

extension Lookup: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: BooleanLiteralType) {
self.init(jsonObject: value)
Expand Down Expand Up @@ -483,7 +492,7 @@ public extension Lookup {
case .dict:
return rawDict
case .string:
if let originString = object as? String,
if let originString = rawValue as? String,
let stringData = originString.data(using: .utf8)
{
return try? JSONSerialization.jsonObject(with: stringData, options: []) as? [String: Any]
Expand All @@ -502,7 +511,7 @@ public extension Lookup {
case .dict:
return Lookup(rawDict)
case .string:
if let originString = object as? String,
if let originString = rawValue as? String,
let stringData = originString.data(using: .utf8),
let _dict = try? JSONSerialization.jsonObject(with: stringData, options: []) as? [String: Any]
{
Expand All @@ -520,7 +529,7 @@ public extension Lookup {
case .array:
return rawArray
case .string:
if let originString = object as? String,
if let originString = rawValue as? String,
let stringData = originString.data(using: .utf8)
{
return try? JSONSerialization.jsonObject(with: stringData, options: []) as? [Any]
Expand All @@ -539,7 +548,7 @@ public extension Lookup {
case .array:
return rawArray.map { Lookup($0) }
case .string:
if let originString = object as? String,
if let originString = rawValue as? String,
let stringData = originString.data(using: .utf8),
let _array = try? JSONSerialization.jsonObject(with: stringData, options: []) as? [Any]
{
Expand Down Expand Up @@ -652,11 +661,11 @@ extension Lookup: Codable {
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
if object is NSNull {
if rawValue is NSNull {
try container.encodeNil()
return
}
switch object {
switch rawValue {
case let intValue as Int:
try container.encode(intValue)
case let int8Value as Int8:
Expand Down
23 changes: 23 additions & 0 deletions Tests/LookupTests/LookupTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ final class LookupTests: XCTestCase {
let lookup6 = Lookup([1, 2, 3, 4, 5])
let lookup7 = lookup6 + [4, 5, 6, 7, 8]
XCTAssertEqual(lookup7.arrayValue.count, 10)

let lookup8 = Lookup([
"userID": 00001,
"nickname": "Lookup"
])
XCTAssertEqual(lookup8.userID.string, "1")
}
mergeDictionaryLookups()

Expand Down Expand Up @@ -249,6 +255,23 @@ final class LookupTests: XCTestCase {
}
try codable()

func setValue() throws {
let jsonString = "{\"name\": \"lookup\"}"
let data = jsonString.data(using: .utf8)
XCTAssertFalse(data == nil)

var lookup = Lookup(data!)
lookup.name = "Lookup YYDS"
XCTAssertEqual(lookup.name.string, "Lookup YYDS")

lookup.name = 1.0
XCTAssertEqual(lookup.name.double, 1.0)

lookup.name = nil
XCTAssertEqual(lookup.name.isNone, true)
}
try setValue()

#if os(iOS)
func uiView() throws {
let view = UIView()
Expand Down

0 comments on commit 5103714

Please sign in to comment.