Skip to content

Commit

Permalink
Add Initial Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Rehan Ali committed Sep 16, 2022
1 parent 7c00e56 commit 9476d3f
Show file tree
Hide file tree
Showing 11 changed files with 330 additions and 11 deletions.
91 changes: 91 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/SGSerializable.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1400"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SGSerializable"
BuildableName = "SGSerializable"
BlueprintName = "SGSerializable"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SGSerializableTests"
BuildableName = "SGSerializableTests"
BlueprintName = "SGSerializableTests"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SGSerializableTests"
BuildableName = "SGSerializableTests"
BlueprintName = "SGSerializableTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "SGSerializable"
BuildableName = "SGSerializable"
BlueprintName = "SGSerializable"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
11 changes: 4 additions & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@ import PackageDescription

let package = Package(
name: "SGSerializable",
platforms: [
.iOS(.v11), .macOS(.v10_15)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "SGSerializable",
targets: ["SGSerializable"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
dependencies: [],
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: "SGSerializable",
dependencies: []),
Expand Down
10 changes: 10 additions & 0 deletions Sources/SGSerializable/SGCodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//
// SGCodable.swift
//
//
// Created by Rehan Ali on 16/09/2022.
//

import Foundation

public typealias SGCodable = SGEncodable & SGDecodable
26 changes: 26 additions & 0 deletions Sources/SGSerializable/SGCodingKey.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// SGCodingKey.swift
//
//
// Created by Rehan Ali on 16/09/2022.
//

import Foundation

struct SGCodingKey: CodingKey {
var stringValue: String
var intValue: Int?

init(name: String) {
self.stringValue = name
}

init?(stringValue: String) {
self.stringValue = stringValue
}

init?(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
39 changes: 39 additions & 0 deletions Sources/SGSerializable/SGDecodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// SGDecodable.swift
//
//
// Created by Rehan Ali on 16/09/2022.
//

import Foundation

public protocol SGDecodable: Decodable {
init()
}

extension SGDecodable {
public init(from decoder: Decoder) throws {
self.init()
let container = try decoder.container(keyedBy: SGCodingKey.self)

var mirror: Mirror? = Mirror(reflecting: self)

repeat {

guard let children = mirror?.children else { break }

for child in children {
guard let decodableValue = child.value as? SGDecoder,
var key = child.label else { continue }

if key.hasPrefix("_") {
key.remove(at: key.startIndex)
}

try decodableValue.decodeValue(from: container, with: key)
}
mirror = mirror?.superclassMirror
}while mirror != nil
}
}

33 changes: 33 additions & 0 deletions Sources/SGSerializable/SGDecoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// SGDecoder.swift
//
//
// Created by Rehan Ali on 16/09/2022.
//

import Foundation

protocol SGDecoder {
typealias DecodeContainer = KeyedDecodingContainer<SGCodingKey>
func decodeValue(from container: DecodeContainer, with key: String) throws
}

extension SGSerializable: SGDecoder where Value: Decodable {
internal func decodeValue(from container: DecodeContainer, with key: String) throws {

if Value.self is Int.Type {
if let int = try? container.decodeIfPresent(Int.self, forKey: getKey(with: key)) {
wrappedValue = int as! Value
}else if let string = try? container.decodeIfPresent(String.self, forKey: getKey(with: key)), let int = Int(string) {
wrappedValue = int as! Value
}else {
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: "Invalid \(Value.self) Value"))
}
}else if let value = try container.decodeIfPresent(Value.self, forKey: getKey(with: key)) {
wrappedValue = value
}else {
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: "Invalid \(Value.self) Value"))
}
}
}

37 changes: 37 additions & 0 deletions Sources/SGSerializable/SGEncodable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// SGEncodable.swift
//
//
// Created by Rehan Ali on 16/09/2022.
//

import Foundation

public protocol SGEncodable: Encodable {}

extension SGEncodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: SGCodingKey.self)

var mirror: Mirror? = Mirror(reflecting: self)

repeat {

guard let children = mirror?.children else { break }

for child in children {
guard let encodableValue = child.value as? SGEncoder,
var key = child.label else { continue }

if key.hasPrefix("_") {
key.remove(at: key.startIndex)
}

try encodableValue.encodeValue(from: &container, with: key)
}

mirror = mirror?.superclassMirror
}while mirror != nil
}
}

20 changes: 20 additions & 0 deletions Sources/SGSerializable/SGEncoder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// SGEncoder.swift
//
//
// Created by Rehan Ali on 16/09/2022.
//

import Foundation

protocol SGEncoder {
typealias EncodeContainer = KeyedEncodingContainer<SGCodingKey>
func encodeValue(from container: inout EncodeContainer, with key: String) throws
}

extension SGSerializable: SGEncoder where Value: Encodable {
func encodeValue(from container: inout EncodeContainer, with key: String) throws {

try container.encodeIfPresent(wrappedValue, forKey: getKey(with: key))
}
}
43 changes: 43 additions & 0 deletions Sources/SGSerializable/SGSerializable+ProtocolConformance.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// SGSerializable+ProtocolConformance.swift
//
//
// Created by Rehan Ali on 16/09/2022.
//

import Foundation

extension SGSerializable: Equatable where Value: Equatable {
public static func == (lhs: SGSerializable<Value>, rhs: SGSerializable<Value>) -> Bool {
lhs.wrappedValue == rhs.wrappedValue
}

public static func == (lhs: Value, rhs: SGSerializable<Value>) -> Bool {
lhs == rhs.wrappedValue
}

public static func == (lhs: SGSerializable<Value>, rhs: Value) -> Bool {
lhs.wrappedValue == rhs
}
}

extension SGSerializable: Comparable where Value: Comparable {
public static func < (lhs: SGSerializable<Value>, rhs: SGSerializable<Value>) -> Bool {
lhs.wrappedValue < rhs.wrappedValue
}

public static func < (lhs: Value, rhs: SGSerializable<Value>) -> Bool {
lhs < rhs.wrappedValue
}

public static func < (lhs: SGSerializable<Value>, rhs: Value) -> Bool {
lhs.wrappedValue < rhs
}
}

extension SGSerializable: Hashable where Value: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(wrappedValue)
}
}

30 changes: 27 additions & 3 deletions Sources/SGSerializable/SGSerializable.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
public struct SGSerializable {
public private(set) var text = "Hello, World!"
import Foundation

public init() {
@propertyWrapper
public final class SGSerializable<Value> {
public var wrappedValue: Value
public var name: String?
internal var type: CodableType

public enum CodableType {
case int, string, float, double, bool, none
}

public init(wrappedValue: Value, name: String? = nil, type: CodableType = .none) {
self.wrappedValue = wrappedValue
self.name = name
self.type = type
}
}

extension SGSerializable {
internal func getKey(with key: String) -> SGCodingKey {
let cKey: SGCodingKey!
if let name = name, !name.isEmpty {
cKey = SGCodingKey(name: name)
}else {
cKey = SGCodingKey(name: key)
}
return cKey
}
}
1 change: 0 additions & 1 deletion Tests/SGSerializableTests/SGSerializableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ final class SGSerializableTests: XCTestCase {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct
// results.
XCTAssertEqual(SGSerializable().text, "Hello, World!")
}
}

0 comments on commit 9476d3f

Please sign in to comment.