diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6cb6e6f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+.DS_Store
+/.build
+/.swiftpm
+/Packages
+/*.xcodeproj
+xcuserdata/
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..9d312f0
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,15 @@
+// swift-tools-version:5.3
+
+import PackageDescription
+
+let package = Package(
+ name: "ChainSwift",
+ products: [
+ .library(name: "ChainSwift", targets: ["ChainSwift"]),
+ ],
+
+ targets: [
+ .target(name: "ChainSwift", dependencies: []),
+ .testTarget(name: "ChainSwiftTests", dependencies: ["ChainSwift"]),
+ ]
+)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..753b103
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# ChainSwift
+
+A description of this package.
diff --git a/Sources/ChainSwift/ChainSwift.swift b/Sources/ChainSwift/ChainSwift.swift
new file mode 100644
index 0000000..9977595
--- /dev/null
+++ b/Sources/ChainSwift/ChainSwift.swift
@@ -0,0 +1,52 @@
+//
+// Chain.swift
+// ChainSwift
+//
+// Created by Orkhan Alikhanov on 5/29/21.
+//
+
+@dynamicMemberLookup
+public struct Chain {
+ public var ch: Base
+
+ init(_ base: Base) {
+ self.ch = base
+ }
+
+ public subscript(dynamicMember keyPath: WritableKeyPath) -> Callable {
+ Callable(ch, keyPath: keyPath)
+ }
+}
+
+public class Callable {
+ var base: Base
+ var keyPath: WritableKeyPath
+
+ init(_ base: Base, keyPath: WritableKeyPath) {
+ self.base = base
+ self.keyPath = keyPath
+ }
+
+ @discardableResult
+ public func callAsFunction(_ value: T) -> Chain {
+ base[keyPath: keyPath] = value
+ return Chain(base)
+ }
+}
+
+public protocol Chainable {
+ associatedtype ChainableBase
+
+ static var ch: Chain.Type { get }
+ var ch: Chain { get }
+}
+
+public extension Chainable {
+ static var ch: Chain.Type {
+ Chain.self
+ }
+
+ var ch: Chain {
+ Chain(self)
+ }
+}
diff --git a/Tests/ChainSwiftTests/ChainSwiftTests.swift b/Tests/ChainSwiftTests/ChainSwiftTests.swift
new file mode 100644
index 0000000..3174221
--- /dev/null
+++ b/Tests/ChainSwiftTests/ChainSwiftTests.swift
@@ -0,0 +1,86 @@
+import XCTest
+import ChainSwift
+
+enum MyEnum {
+ case value1
+ case value2
+}
+
+class MyClass {
+ var text = ""
+ var int = 0
+ var myEnum: MyEnum = .value1
+}
+
+struct MyStruct {
+ var text = ""
+ var int = 0
+ var myEnum: MyEnum = .value1
+}
+
+extension MyClass: Chainable { }
+extension MyStruct: Chainable { }
+
+
+/// Ensure compiles with NSObject
+import Foundation
+extension NSObject: Chainable { }
+
+final class ChainSwiftTests: XCTestCase {
+ func testClassWorks() {
+ let myClass = MyClass()
+
+ XCTAssertEqual(myClass.text, "")
+ XCTAssertEqual(myClass.int, 0)
+ XCTAssertEqual(myClass.myEnum, .value1)
+
+ myClass.ch
+ .text("It works")
+ .int(99)
+ .myEnum(.value2)
+
+ XCTAssertEqual(myClass.text, "It works")
+ XCTAssertEqual(myClass.int, 99)
+ XCTAssertEqual(myClass.myEnum, .value2)
+ }
+
+ func testTakingChValueWorks() {
+ let myClass = MyClass().ch
+ .text("It works")
+ .int(99)
+ .myEnum(.value2)
+ .ch
+
+ XCTAssertEqual(myClass.text, "It works")
+ XCTAssertEqual(myClass.int, 99)
+ XCTAssertEqual(myClass.myEnum, .value2)
+ }
+
+ func testStructWorks() {
+ let myStruct = MyStruct()
+
+ XCTAssertEqual(myStruct.text, "")
+ XCTAssertEqual(myStruct.int, 0)
+ XCTAssertEqual(myStruct.myEnum, .value1)
+
+ let updatedMyStruct = myStruct.ch
+ .text("It works")
+ .int(99)
+ .myEnum(.value2)
+ .ch
+
+ XCTAssertEqual(myStruct.text, "")
+ XCTAssertEqual(myStruct.int, 0)
+ XCTAssertEqual(myStruct.myEnum, .value1)
+
+ XCTAssertEqual(updatedMyStruct.text, "It works")
+ XCTAssertEqual(updatedMyStruct.int, 99)
+ XCTAssertEqual(updatedMyStruct.myEnum, .value2)
+ }
+
+ static var allTests = [
+ ("testClassWorks", testClassWorks),
+ ("testTakingChValueWorks", testTakingChValueWorks),
+ ("testStructWorks", testStructWorks),
+ ]
+}
diff --git a/Tests/ChainSwiftTests/XCTestManifests.swift b/Tests/ChainSwiftTests/XCTestManifests.swift
new file mode 100644
index 0000000..7e08ac4
--- /dev/null
+++ b/Tests/ChainSwiftTests/XCTestManifests.swift
@@ -0,0 +1,9 @@
+import XCTest
+
+#if !canImport(ObjectiveC)
+public func allTests() -> [XCTestCaseEntry] {
+ return [
+ testCase(ChainSwiftTests.allTests),
+ ]
+}
+#endif
diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift
new file mode 100644
index 0000000..03c7eef
--- /dev/null
+++ b/Tests/LinuxMain.swift
@@ -0,0 +1,7 @@
+import XCTest
+
+import ChainSwiftTests
+
+var tests = [XCTestCaseEntry]()
+tests += ChainSwiftTests.allTests()
+XCTMain(tests)