Browse Source

Initial commit

xcbosa mbp16 2 years ago
commit
da032cf867

+ 36 - 0
.gitignore

@@ -0,0 +1,36 @@
+# macOS
+.DS_Store
+
+# Xcode
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+
+# Bundler
+.bundle
+
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
+# 
+# Note: if you ignore the Pods directory, make sure to uncomment
+# `pod install` in .travis.yml
+#
+# Pods/

+ 14 - 0
.travis.yml

@@ -0,0 +1,14 @@
+# references:
+# * https://www.objc.io/issues/6-build-tools/travis-ci/
+# * https://github.com/supermarin/xcpretty#usage
+
+osx_image: xcode7.3
+language: objective-c
+# cache: cocoapods
+# podfile: Example/Podfile
+# before_install:
+# - gem install cocoapods # Since Travis is not always on latest version
+# - pod install --project-directory=Example
+script:
+- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/SwiftDynamic.xcworkspace -scheme SwiftDynamic-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
+- pod lib lint

+ 6 - 0
Example/Podfile

@@ -0,0 +1,6 @@
+use_frameworks!
+target 'SwiftDynamic_Tests' do
+  pod 'SwiftDynamic', :path => '../'
+  
+  
+end

+ 22 - 0
Example/Pods/Local Podspecs/SwiftDynamic.podspec.json

@@ -0,0 +1,22 @@
+{
+  "name": "SwiftDynamic",
+  "version": "0.1.0",
+  "summary": "A short description of SwiftDynamic.",
+  "description": "TODO: Add long description of the pod here.",
+  "homepage": "https://github.com/xcbosa mbp16/SwiftDynamic",
+  "license": {
+    "type": "MIT",
+    "file": "LICENSE"
+  },
+  "authors": {
+    "xcbosa mbp16": "xcbosa@forgetive.org"
+  },
+  "source": {
+    "git": "https://github.com/xcbosa mbp16/SwiftDynamic.git",
+    "tag": "0.1.0"
+  },
+  "platforms": {
+    "ios": "10.0"
+  },
+  "source_files": "SwiftDynamic/Classes/**/*"
+}

+ 377 - 0
Example/SwiftDynamic.xcodeproj/project.pbxproj

@@ -0,0 +1,377 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; };
+		607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; };
+		607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
+		607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
+		607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
+		607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		014F55EB44636E63A0CFBC13 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
+		607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
+		607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
+		607FACE51AFB9204008FA782 /* SwiftDynamic_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftDynamic_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = "<group>"; };
+		93CB2EF8596E6207F7A4A5C6 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; name = README.md; path = ../README.md; sourceTree = "<group>"; };
+		A22DA12C5E93F4E97D75541F /* SwiftDynamic.podspec */ = {isa = PBXFileReference; includeInIndex = 1; name = SwiftDynamic.podspec; path = ../SwiftDynamic.podspec; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		607FACE21AFB9204008FA782 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		607FACC71AFB9204008FA782 = {
+			isa = PBXGroup;
+			children = (
+				607FACF51AFB993E008FA782 /* Podspec Metadata */,
+				607FACE81AFB9204008FA782 /* Tests */,
+				607FACD11AFB9204008FA782 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		607FACD11AFB9204008FA782 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				607FACE51AFB9204008FA782 /* SwiftDynamic_Tests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		607FACE81AFB9204008FA782 /* Tests */ = {
+			isa = PBXGroup;
+			children = (
+				607FACEB1AFB9204008FA782 /* Tests.swift */,
+				607FACE91AFB9204008FA782 /* Supporting Files */,
+			);
+			path = Tests;
+			sourceTree = "<group>";
+		};
+		607FACE91AFB9204008FA782 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				607FACEA1AFB9204008FA782 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		607FACF51AFB993E008FA782 /* Podspec Metadata */ = {
+			isa = PBXGroup;
+			children = (
+				A22DA12C5E93F4E97D75541F /* SwiftDynamic.podspec */,
+				93CB2EF8596E6207F7A4A5C6 /* README.md */,
+				014F55EB44636E63A0CFBC13 /* LICENSE */,
+			);
+			name = "Podspec Metadata";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		607FACE41AFB9204008FA782 /* SwiftDynamic_Tests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftDynamic_Tests" */;
+			buildPhases = (
+				607FACE11AFB9204008FA782 /* Sources */,
+				607FACE21AFB9204008FA782 /* Frameworks */,
+				607FACE31AFB9204008FA782 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = SwiftDynamic_Tests;
+			productName = Tests;
+			productReference = 607FACE51AFB9204008FA782 /* SwiftDynamic_Tests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		607FACC81AFB9204008FA782 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 0830;
+				LastUpgradeCheck = 0830;
+				ORGANIZATIONNAME = CocoaPods;
+				TargetAttributes = {
+					607FACCF1AFB9204008FA782 = {
+						CreatedOnToolsVersion = 6.3.1;
+						LastSwiftMigration = 0900;
+					};
+					607FACE41AFB9204008FA782 = {
+						CreatedOnToolsVersion = 6.3.1;
+						LastSwiftMigration = 0900;
+						TestTargetID = 607FACCF1AFB9204008FA782;
+					};
+				};
+			};
+			buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "PROJECT" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 607FACC71AFB9204008FA782;
+			productRefGroup = 607FACD11AFB9204008FA782 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				607FACE41AFB9204008FA782 /* SwiftDynamic_Tests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		607FACE31AFB9204008FA782 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		607FACE11AFB9204008FA782 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				607FACEC1AFB9204008FA782 /* Tests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		607FACD91AFB9204008FA782 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				607FACDA1AFB9204008FA782 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				607FACDF1AFB9204008FA782 /* Base */,
+			);
+			name = LaunchScreen.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		607FACED1AFB9204008FA782 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		607FACEE1AFB9204008FA782 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.3;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		607FACF01AFB9204008FA782 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = SwiftDynamic/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				MODULE_NAME = ExampleApp;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
+			};
+			name = Debug;
+		};
+		607FACF11AFB9204008FA782 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = SwiftDynamic/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				MODULE_NAME = ExampleApp;
+				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
+			};
+			name = Release;
+		};
+		607FACF31AFB9204008FA782 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(PLATFORM_DIR)/Developer/Library/Frameworks",
+					"$(inherited)",
+				);
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = Tests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftDynamic_Example.app/SwiftDynamic_Example";
+			};
+			name = Debug;
+		};
+		607FACF41AFB9204008FA782 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(PLATFORM_DIR)/Developer/Library/Frameworks",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = Tests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_SWIFT3_OBJC_INFERENCE = Default;
+				SWIFT_VERSION = 4.0;
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftDynamic_Example.app/SwiftDynamic_Example";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "PROJECT" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				607FACED1AFB9204008FA782 /* Debug */,
+				607FACEE1AFB9204008FA782 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "SwiftDynamic_Tests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				607FACF31AFB9204008FA782 /* Debug */,
+				607FACF41AFB9204008FA782 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 607FACC81AFB9204008FA782 /* Project object */;
+}

+ 7 - 0
Example/SwiftDynamic.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:SwiftDynamic.xcodeproj">
+   </FileRef>
+</Workspace>

+ 117 - 0
Example/SwiftDynamic.xcodeproj/xcshareddata/xcschemes/SwiftDynamic-Example.xcscheme

@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0900"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "607FACCF1AFB9204008FA782"
+               BuildableName = "SwiftDynamic_Example.app"
+               BlueprintName = "SwiftDynamic_Example"
+               ReferencedContainer = "container:SwiftDynamic.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "607FACE41AFB9204008FA782"
+               BuildableName = "SwiftDynamic_Tests.xctest"
+               BlueprintName = "SwiftDynamic_Tests"
+               ReferencedContainer = "container:SwiftDynamic.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "607FACE41AFB9204008FA782"
+               BuildableName = "SwiftDynamic_Tests.xctest"
+               BlueprintName = "SwiftDynamic_Tests"
+               ReferencedContainer = "container:SwiftDynamic.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "607FACCF1AFB9204008FA782"
+            BuildableName = "SwiftDynamic_Example.app"
+            BlueprintName = "SwiftDynamic_Example"
+            ReferencedContainer = "container:SwiftDynamic.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "607FACCF1AFB9204008FA782"
+            BuildableName = "SwiftDynamic_Example.app"
+            BlueprintName = "SwiftDynamic_Example"
+            ReferencedContainer = "container:SwiftDynamic.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "607FACCF1AFB9204008FA782"
+            BuildableName = "SwiftDynamic_Example.app"
+            BlueprintName = "SwiftDynamic_Example"
+            ReferencedContainer = "container:SwiftDynamic.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 24 - 0
Example/Tests/Info.plist

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>

+ 28 - 0
Example/Tests/Tests.swift

@@ -0,0 +1,28 @@
+import XCTest
+import SwiftDynamic
+
+class Tests: XCTestCase {
+    
+    override func setUp() {
+        super.setUp()
+        // Put setup code here. This method is called before the invocation of each test method in the class.
+    }
+    
+    override func tearDown() {
+        // Put teardown code here. This method is called after the invocation of each test method in the class.
+        super.tearDown()
+    }
+    
+    func testExample() {
+        // This is an example of a functional test case.
+        XCTAssert(true, "Pass")
+    }
+    
+    func testPerformanceExample() {
+        // This is an example of a performance test case.
+        self.measure() {
+            // Put the code you want to measure the time of here.
+        }
+    }
+    
+}

+ 19 - 0
LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2023 xcbosa mbp16 <xcbosa@forgetive.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

+ 29 - 0
README.md

@@ -0,0 +1,29 @@
+# SwiftDynamic
+
+[![CI Status](https://img.shields.io/travis/xcbosa mbp16/SwiftDynamic.svg?style=flat)](https://travis-ci.org/xcbosa mbp16/SwiftDynamic)
+[![Version](https://img.shields.io/cocoapods/v/SwiftDynamic.svg?style=flat)](https://cocoapods.org/pods/SwiftDynamic)
+[![License](https://img.shields.io/cocoapods/l/SwiftDynamic.svg?style=flat)](https://cocoapods.org/pods/SwiftDynamic)
+[![Platform](https://img.shields.io/cocoapods/p/SwiftDynamic.svg?style=flat)](https://cocoapods.org/pods/SwiftDynamic)
+
+## Example
+
+To run the example project, clone the repo, and run `pod install` from the Example directory first.
+
+## Requirements
+
+## Installation
+
+SwiftDynamic is available through [CocoaPods](https://cocoapods.org). To install
+it, simply add the following line to your Podfile:
+
+```ruby
+pod 'SwiftDynamic'
+```
+
+## Author
+
+xcbosa mbp16, xcbosa@forgetive.org
+
+## License
+
+SwiftDynamic is available under the MIT license. See the LICENSE file for more info.

+ 43 - 0
SwiftDynamic.podspec

@@ -0,0 +1,43 @@
+#
+# Be sure to run `pod lib lint SwiftDynamic.podspec' to ensure this is a
+# valid spec before submitting.
+#
+# Any lines starting with a # are optional, but their use is encouraged
+# To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html
+#
+
+Pod::Spec.new do |s|
+  s.name             = 'SwiftDynamic'
+  s.version          = '0.1.0'
+  s.summary          = 'Swift Dynamic Callee: Use to call dynamic callable API of Objective-C'
+
+# This description is used to generate tags and improve search results.
+#   * Think: What does it do? Why did you write it? What is the focus?
+#   * Try to keep it short, snappy and to the point.
+#   * Write the description between the DESC delimiters below.
+#   * Finally, don't worry about the indent, CocoaPods strips it!
+
+  s.description      = <<-DESC
+TODO: Add long description of the pod here.
+                       DESC
+
+  s.homepage         = 'https://git.forgetive.org/XCAppCollection/SwiftDynamic'
+  # s.screenshots     = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
+  s.license          = { :type => 'MIT', :file => 'LICENSE' }
+  s.author           = { 'xcbosa mbp16' => 'xcbosa@forgetive.org' }
+  s.source           = { :git => 'https://git.forgetive.org/XCAppCollection/SwiftDynamic.git', :tag => s.version.to_s }
+  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
+
+  s.ios.deployment_target = '14.0'
+  s.swift_version = '5.0'
+
+  s.source_files = 'SwiftDynamic/Classes/**/*'
+  
+  # s.resource_bundles = {
+  #   'SwiftDynamic' => ['SwiftDynamic/Assets/*.png']
+  # }
+
+  # s.public_header_files = 'Pod/Classes/**/*.h'
+  # s.frameworks = 'UIKit', 'MapKit'
+  # s.dependency 'AFNetworking', '~> 2.3'
+end

+ 0 - 0
SwiftDynamic/Assets/.gitkeep


+ 0 - 0
SwiftDynamic/Classes/.gitkeep


+ 316 - 0
SwiftDynamic/Classes/Dynamic.swift

@@ -0,0 +1,316 @@
+//
+//  Dynamic
+//  Created by Mhd Hejazi on 4/15/20.
+//  Copyright © 2020 Samabox. All rights reserved.
+//
+
+import Foundation
+
+public typealias ObjC = Dynamic
+
+@dynamicCallable
+@dynamicMemberLookup
+public class Dynamic: CustomDebugStringConvertible, Loggable {
+    public static var loggingEnabled: Bool = false {
+        didSet {
+            Invocation.loggingEnabled = loggingEnabled
+        }
+    }
+    var loggingEnabled: Bool { Self.loggingEnabled }
+
+    public static let `nil` = Dynamic(nil)
+
+    private let object: AnyObject?
+    private let memberName: String?
+    private var invocation: Invocation?
+    private var error: Error?
+
+    public var isError: Bool { error != nil || object is Error }
+    public var debugDescription: String { object?.debugDescription ?? "<nil>" }
+
+    public init(_ object: Any?, memberName: String? = nil) {
+        self.object = object as AnyObject?
+        self.memberName = memberName
+
+        log(.end).log(.start)
+        log("# Dynamic")
+        log("Object:", object ?? "<nil>").log("Member:", memberName ?? "<nil>")
+    }
+
+    public init(className: String) {
+        self.object = NSClassFromString(className)
+        self.memberName = nil
+
+        log(.end).log(.start)
+        log("# Dynamic")
+        log("Class:", className)
+    }
+
+    public static subscript(dynamicMember className: String) -> Dynamic {
+        Dynamic(className: className)
+    }
+
+    public subscript(dynamicMember member: String) -> Dynamic {
+        get {
+            getProperty(member)
+        }
+        set {
+            self[dynamicMember: member] = newValue.resolve()
+        }
+    }
+
+    public subscript<T>(dynamicMember member: String) -> T? {
+        get {
+            self[dynamicMember: member].unwrap()
+        }
+        set {
+            setProperty(member, value: newValue)
+        }
+    }
+
+    @discardableResult
+    public func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs<String, Any?>) -> Dynamic {
+        /// Constructors
+        if object is AnyClass, memberName == nil {
+            if pairs.isEmpty {
+                return self.`init`.dynamicallyCall(withKeywordArguments: pairs)
+            } else {
+                return self.`initWith`.dynamicallyCall(withKeywordArguments: pairs)
+            }
+        }
+
+        guard let name = memberName else { return self }
+
+        let selector = name + pairs.reduce("") { result, pair in
+            if result.isEmpty {
+                return (pair.key.first?.uppercased() ?? "") + pair.key.dropFirst() + ":"
+            } else {
+                return result + (pair.key + ":")
+            }
+        }
+        callMethod(selector, with: pairs.map { $0.value })
+        return self
+    }
+
+    @discardableResult
+    public func dynamicallyCall<T>(withKeywordArguments pairs: KeyValuePairs<String, Any?>) -> T? {
+        let result: Dynamic = dynamicallyCall(withKeywordArguments: pairs)
+        return result.unwrap()
+    }
+
+    private func getProperty(_ name: String) -> Dynamic {
+        log("Get:", "\(object?.debugDescription ?? "").\(name)")
+
+        let resolved = resolve()
+        if resolved is Error {
+            return self
+        }
+
+        if resolved == nil {
+            return Self.nil
+        }
+
+        log(.end)
+
+        return Dynamic(resolved, memberName: name)
+    }
+
+    private func setProperty<T>(_ name: String, value: T?) {
+        log("Set:", "\(object?.debugDescription ?? "").\(name)")
+
+        let resolved = resolve()
+        log(.end)
+
+        var setterName: String
+        if name.count > 2, name.hasPrefix("is"), name[name.index(name.startIndex, offsetBy: 2)].isUppercase {
+            setterName = "set" + name.dropFirst(2)
+        } else {
+            setterName = "set" + (name.first?.uppercased() ?? "") + name.dropFirst()
+        }
+
+        Dynamic(resolved, memberName: setterName)(value)
+    }
+
+    private func callMethod(_ selector: String, with arguments: [Any?] = []) {
+        guard var target = object as? NSObject, !isError else { return }
+        log("Call: [\(type(of: target)) \(selector)]")
+
+        var invocation: Invocation
+
+        /// Call `alloc()` before `init()`
+        if target is AnyClass, selector.hasPrefix("init") {
+            guard let allocated = allocate(type: target) else { return }
+            target = allocated
+        }
+
+        do {
+            invocation = try Invocation(target: target, selector: NSSelectorFromString(selector))
+        } catch {
+            print("WARNING: Trying to access an unrecognized member: \(type(of: target)).\(selector)")
+            self.error = error
+            return
+        }
+
+        self.invocation = invocation
+
+        for index in 0..<invocation.numberOfArguments - 2 {
+            var argument = arguments[index]
+
+            if let dynamicArgument = argument as? Dynamic {
+                argument = dynamicArgument.asObject
+            }
+
+            argument = TypeMapping.convertToObjCType(arguments[index]) ?? argument
+            invocation.setArgument(argument, at: index + 2)
+        }
+
+        invocation.invoke()
+    }
+
+    private func allocate(type: NSObject) -> NSObject? {
+        do {
+            let invocation = try Invocation(target: type, selector: NSSelectorFromString("alloc"))
+            invocation.invoke()
+            return invocation.returnedObject as? NSObject
+        } catch {
+            self.error = error
+            return nil
+        }
+    }
+
+    private func resolve() -> AnyObject? {
+        /// This is a class. Return it.
+        if object is AnyClass && memberName == nil {
+            return object
+        }
+
+        guard let object = object else {
+            return nil
+        }
+
+        /// This is a method we have called before. Return the result.
+        if let result = invocation?.returnedObject {
+            return result
+        }
+
+        /// This is an error caused by a previous call. Just pass it.
+        if object is Error {
+            return object
+        }
+        if error != nil {
+            return error as AnyObject?
+        }
+
+        /// This is a wrapped object. Return it.
+        guard let name = memberName else {
+            return object
+        }
+
+        /// This is a wrapped object with a member name. Return the member.
+        if invocation?.isInvoked != true {
+            callMethod(name)
+        }
+
+        return invocation?.returnedObject ?? error as AnyObject?
+    }
+
+    @available(*, unavailable, message: "Call init() directly from the class name.")
+    public func alloc() {}
+}
+
+extension Dynamic {
+    public var asAnyObject: AnyObject? {
+        let result = resolve()
+        log(.end)
+        return result
+    }
+
+    public var asValue: NSValue? {
+        if let object = resolve() {
+            log(.end)
+            return NSValue(nonretainedObject: object)
+        }
+
+        log(.end)
+
+        guard let invocation = invocation,
+            let returnType = invocation.returnType,
+            invocation.returnsAny else { return nil }
+
+        let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: invocation.returnLength)
+        defer { buffer.deallocate() }
+        buffer.initialize(repeating: 0, count: invocation.returnLength)
+
+        invocation.getReturnValue(result: &buffer.pointee)
+
+        let value = NSValue(bytes: buffer, objCType: UnsafePointer<Int8>(returnType))
+        return value
+    }
+
+    public var asObject: NSObject? { asAnyObject as? NSObject }
+    public var asArray: NSArray? { asAnyObject as? NSArray }
+    public var asDictionary: NSDictionary? { asAnyObject as? NSDictionary }
+    public var asString: String? { asAnyObject?.description }
+    public var asInt8: Int8? { unwrap() }
+    public var asUInt8: UInt8? { unwrap() }
+    public var asInt16: Int16? { unwrap() }
+    public var asUInt16: UInt16? { unwrap() }
+    public var asInt32: Int32? { unwrap() }
+    public var asUInt32: UInt32? { unwrap() }
+    public var asInt64: Int64? { unwrap() }
+    public var asUInt64: UInt64? { unwrap() }
+    public var asFloat: Float? { unwrap() }
+    public var asDouble: Double? { unwrap() }
+    public var asBool: Bool? { unwrap() }
+    public var asInt: Int? { unwrap() }
+    public var asUInt: UInt? { unwrap() }
+    public var asSelector: Selector? { unwrap() }
+
+    public func asInferred<T>() -> T? { unwrap() }
+
+    private func unwrap<T>() -> T? {
+        guard let value = asValue else { return nil }
+        guard let invocation = invocation else {
+            if let result = object as? T {
+                return result
+            }
+            return nil
+        }
+
+        let encoding = invocation.returnTypeString
+        if encoding == "^v" || encoding == "@" {
+            return value.nonretainedObjectValue as? T
+        }
+
+        var storedSize = 0
+        var storedAlignment = 0
+        NSGetSizeAndAlignment(invocation.returnType!, &storedSize, &storedAlignment)
+        guard MemoryLayout<T>.size == storedSize && MemoryLayout<T>.alignment == storedAlignment else {
+            return nil
+        }
+
+        let buffer = UnsafeMutablePointer<T>.allocate(capacity: 1)
+        defer { buffer.deallocate() }
+        value.getValue(buffer)
+
+        return buffer.pointee
+    }
+}
+
+#if canImport(UIKit)
+import UIKit
+
+extension Dynamic {
+    public var asCGPoint: CGPoint? { unwrap() }
+    public var asCGVector: CGVector? { unwrap() }
+    public var asCGSize: CGSize? { unwrap() }
+    public var asCGRect: CGRect? { unwrap() }
+    public var asCGAffineTransform: CGAffineTransform? { unwrap() }
+    public var asUIEdgeInsets: UIEdgeInsets? { unwrap() }
+    public var asUIOffset: UIOffset? { unwrap() }
+
+    #if !os(watchOS)
+    public var asCATransform3D: CATransform3D? { unwrap() }
+    #endif
+}
+#endif

+ 237 - 0
SwiftDynamic/Classes/Invocation.swift

@@ -0,0 +1,237 @@
+//
+//  Dynamic
+//  Created by Mhd Hejazi on 4/15/20.
+//  Copyright © 2020 Samabox. All rights reserved.
+//
+
+import Foundation
+
+class Invocation: Loggable {
+    public static var loggingEnabled: Bool = false
+    var loggingEnabled: Bool { Self.loggingEnabled }
+
+    private let target: NSObject
+    private let selector: Selector
+
+    var invocation: NSObject?
+
+    var numberOfArguments: Int = 0
+    var returnLength: Int = 0
+    var returnType: UnsafePointer<CChar>?
+    var returnTypeString: String? {
+        guard let returnType = returnType else { return nil }
+        return String(cString: returnType)
+    }
+    var returnsObject: Bool {
+        /// `@` is the type encoding for an object
+        returnTypeString == "@"
+    }
+    var returnsAny: Bool {
+        /// `v` is the type encoding for Void
+        returnTypeString != "v"
+    }
+    lazy var returnedObject: AnyObject? = {
+        returnedObjectValue()
+    }()
+    private(set) var isInvoked: Bool = false
+
+    init(target: NSObject, selector: Selector) throws {
+        self.target = target
+        self.selector = selector
+
+        log(.start)
+        log("# Invocation")
+        log("[\(type(of: target)) \(selector)]")
+        log("Selector:", selector)
+
+        try initialize()
+    }
+
+    private func initialize() throws {
+        /// `NSMethodSignature *methodSignature = [target methodSignatureForSelector: selector]`
+        let methodSignature: NSObject
+        do {
+            let selector = NSSelectorFromString("methodSignatureForSelector:")
+            let signature = (@convention(c)(NSObject, Selector, Selector) -> Any).self
+            let method = unsafeBitCast(target.method(for: selector), to: signature)
+            guard let result = method(target, selector, self.selector) as? NSObject else {
+                let error = InvocationError.unrecognizedSelector(type(of: target), self.selector)
+                log("ERROR:", error)
+                throw error
+            }
+            methodSignature = result
+        }
+
+        /// `numberOfArguments = methodSignature.numberOfArguments`
+        self.numberOfArguments = methodSignature.value(forKeyPath: "numberOfArguments") as? Int ?? 0
+        log("NumberOfArguments:", numberOfArguments)
+
+        /// `methodReturnLength = methodSignature.methodReturnLength`
+        self.returnLength = methodSignature.value(forKeyPath: "methodReturnLength") as? Int ?? 0
+        log("ReturnLength:", returnLength)
+
+        /// `methodReturnType = methodSignature.methodReturnType`
+        let methodReturnType: UnsafePointer<CChar>
+        do {
+            let selector = NSSelectorFromString("methodReturnType")
+            let signature = (@convention(c)(NSObject, Selector) -> UnsafePointer<CChar>).self
+            let method = unsafeBitCast(methodSignature.method(for: selector), to: signature)
+            methodReturnType = method(methodSignature, selector)
+        }
+        self.returnType = methodReturnType
+        log("ReturnType:", self.returnTypeString ?? "?")
+
+        /// `NSInvocation *invocation = [NSInvocation invocationWithMethodSignature: methodSignature]`
+        let invocation: NSObject
+        do {
+            let NSInvocation = NSClassFromString("NSInvocation") as AnyObject
+            let selector = NSSelectorFromString("invocationWithMethodSignature:")
+            let signature = (@convention(c)(AnyObject, Selector, AnyObject) -> AnyObject).self
+            let method = unsafeBitCast(NSInvocation.method(for: selector), to: signature)
+            guard let result = method(NSInvocation, selector, methodSignature) as? NSObject else {
+                let error = InvocationError.unrecognizedSelector(type(of: target), self.selector)
+                log("ERROR:", error)
+                throw error
+            }
+            invocation = result
+        }
+        self.invocation = invocation
+
+        /// `invocation.selector = selector`
+        do {
+            let selector = NSSelectorFromString("setSelector:")
+            let signature = (@convention(c)(NSObject, Selector, Selector) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            method(invocation, selector, self.selector)
+        }
+
+        /// `[invocation retainArguments]`
+        do {
+            let selector = NSSelectorFromString("retainArguments")
+            let signature = (@convention(c)(NSObject, Selector) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            method(invocation, selector)
+        }
+    }
+
+    func setArgument(_ argument: Any?, at index: NSInteger) {
+        guard let invocation = invocation else { return }
+
+        log("Argument #\(index - 1):", argument ?? "<nil>")
+
+        /// `[invocation setArgument:&argument atIndex:i + 2]`
+        let selector = NSSelectorFromString("setArgument:atIndex:")
+        let signature = (@convention(c)(NSObject, Selector, UnsafeRawPointer, Int) -> Void).self
+        let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+
+        if let valueArgument = argument as? NSValue {
+            /// Get the type byte size
+            let typeSize = UnsafeMutablePointer<Int>.allocate(capacity: 1)
+            defer { typeSize.deallocate() }
+            NSGetSizeAndAlignment(valueArgument.objCType, typeSize, nil)
+
+            /// Get the actual value
+            let buffer = UnsafeMutablePointer<Int8>.allocate(capacity: typeSize.pointee)
+            defer { buffer.deallocate() }
+            valueArgument.getValue(buffer)
+
+            method(invocation, selector, buffer, index)
+        } else {
+            withUnsafePointer(to: argument) { pointer in
+                method(invocation, selector, pointer, index)
+            }
+        }
+    }
+
+    func invoke() {
+        guard let invocation = invocation, !isInvoked else { return }
+
+        log("Invoking...")
+
+        isInvoked = true
+
+        /// `[invocation invokeWithTarget: target]`
+        do {
+            let selector = NSSelectorFromString("invokeWithTarget:")
+            let signature = (@convention(c)(NSObject, Selector, AnyObject) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            method(invocation, selector, target)
+        }
+
+        log(.end)
+    }
+
+    func getReturnValue<T>(result: inout T) {
+        guard let invocation = invocation else { return }
+
+        /// `[invocation getReturnValue: returnValue]`
+        do {
+            let selector = NSSelectorFromString("getReturnValue:")
+            let signature = (@convention(c)(NSObject, Selector, UnsafeMutableRawPointer) -> Void).self
+            let method = unsafeBitCast(invocation.method(for: selector), to: signature)
+            withUnsafeMutablePointer(to: &result) { pointer in
+                method(invocation, selector, pointer)
+            }
+        }
+
+        if NSStringFromSelector(self.selector) == "alloc" {
+            log("getReturnValue() -> <alloc>")
+        } else {
+            log("getReturnValue() ->", result)
+        }
+    }
+
+    private func returnedObjectValue() -> AnyObject? {
+        guard returnsObject, returnLength > 0 else {
+            return nil
+        }
+
+        var result: AnyObject?
+
+        getReturnValue(result: &result)
+
+        guard let object = result else {
+            return nil
+        }
+
+        /// Take the ownership of the initialized objects to ensure they're deallocated properly.
+        if isRetainingMethod() {
+            return Unmanaged.passRetained(object).takeRetainedValue()
+        }
+
+        /// `NSInvocation.getReturnValue()` doesn't give us the ownership of the returned object, but the compiler
+        /// tries to release this object anyway. So, we are retaining it to balance with the compiler's release.
+        return Unmanaged.passRetained(object).takeUnretainedValue()
+    }
+
+    private func isRetainingMethod() -> Bool {
+        /// Refer to: https://bit.ly/308okXm
+        let selector = NSStringFromSelector(self.selector)
+        return selector == "alloc" ||
+            selector.hasPrefix("new") ||
+            selector.hasPrefix("copy") ||
+            selector.hasPrefix("mutableCopy")
+    }
+}
+
+public enum InvocationError: CustomNSError {
+    case unrecognizedSelector(_ classType: AnyClass, _ selector: Selector)
+
+    public static var errorDomain: String { String(describing: Invocation.self) }
+
+    public var errorCode: Int {
+        switch self {
+        case .unrecognizedSelector:
+            return 404
+        }
+    }
+
+    public var errorUserInfo: [String: Any] {
+        var message: String
+        switch self {
+        case .unrecognizedSelector(let classType, let selector):
+            message = "'\(String(describing: classType))' doesn't recognize selector '\(selector)'"
+        }
+        return [NSLocalizedDescriptionKey: message]
+    }
+}

+ 106 - 0
SwiftDynamic/Classes/Logger.swift

@@ -0,0 +1,106 @@
+//
+//  Dynamic
+//  Created by Mhd Hejazi on 4/15/20.
+//  Copyright © 2020 Samabox. All rights reserved.
+//
+
+import Foundation
+
+protocol Loggable: AnyObject {
+    var loggingEnabled: Bool { get }
+}
+
+extension Loggable {
+    var loggingEnabled: Bool { false }
+    var logUsingPrint: Bool { true }
+
+    @discardableResult
+    func log(_ items: Any...) -> Logger {
+        guard loggingEnabled else { return Logger.dummy }
+        return Logger.logger(for: self).log(items)
+    }
+
+    @discardableResult
+    func log(_ group: Logger.Group) -> Logger {
+        guard loggingEnabled else { return Logger.dummy }
+        return Logger.logger(for: self).log(group)
+    }
+}
+
+class Logger {
+    enum Group {
+        case start, end
+    }
+
+    static let dummy = DummyLogger()
+    static var enabled = true
+
+    private static var loggers: [ObjectIdentifier: Logger] = [:]
+    private static var level: Int = 0
+
+    static func logger(for object: AnyObject) -> Logger {
+        let id = ObjectIdentifier(object)
+        if let logger = Self.loggers[id] {
+            return logger
+        }
+
+        let logger = Logger()
+        Self.loggers[id] = logger
+
+        return logger
+    }
+
+    @discardableResult
+    func log(_ items: Any..., withBullet: Bool = true) -> Logger {
+        log(items, withBullet: withBullet)
+    }
+
+    @discardableResult
+    func log(_ items: [Any], withBullet: Bool = true) -> Logger {
+        guard Self.enabled else { return self }
+
+        let message = items.lazy.map { String(describing: $0) }.joined(separator: " ")
+        var indent = String(repeating: " ╷  ", count: Self.level)
+        if !indent.isEmpty, withBullet {
+            indent = indent.dropLast(2) + "‣ "
+        }
+        print(indent + message)
+        return self
+    }
+
+    @discardableResult
+    func log(_ group: Group) -> Logger {
+        switch group {
+        case .start: logGroupStart()
+        case .end: logGroupEnd()
+        }
+        return self
+    }
+
+    private func logGroupStart() {
+        guard Self.enabled else { return }
+
+        log([" ╭╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴"], withBullet: false)
+        Self.level += 1
+    }
+
+    private func logGroupEnd() {
+        guard Self.enabled else { return }
+
+        guard Self.level > 0 else { return }
+        Self.level -= 1
+        log([" ╰╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴╴"], withBullet: false)
+    }
+}
+
+class DummyLogger: Logger {
+    @discardableResult
+    override func log(_ items: [Any], withBullet: Bool = true) -> Logger {
+        self
+    }
+
+    @discardableResult
+    override func log(_ group: Group) -> Logger {
+        self
+    }
+}

+ 125 - 0
SwiftDynamic/Classes/TypeMapping.swift

@@ -0,0 +1,125 @@
+//
+//  Dynamic
+//  Created by Mhd Hejazi on 4/18/20.
+//  Copyright © 2020 Samabox. All rights reserved.
+//
+
+// swiftlint:disable cyclomatic_complexity syntactic_sugar
+
+import Foundation
+
+/// The type mapping table can be found here:
+///   https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/working_with_foundation_types
+class TypeMapping {
+    private static let typePairs: [(swiftType: Any.Type, objCType: AnyObject.Type)] = [
+        (Array<Any>.self, NSArray.self),
+        (Calendar.self, NSCalendar.self),
+        (CharacterSet.self, NSCharacterSet.self),
+        (Data.self, NSData.self),
+        (DateComponents.self, NSDateComponents.self),
+        (DateInterval.self, NSDateInterval.self),
+        (Date.self, NSDate.self),
+        (Decimal.self, NSDecimalNumber.self),
+        (Dictionary<AnyHashable, Any>.self, NSDictionary.self),
+        (IndexPath.self, NSIndexPath.self),
+        (IndexSet.self, NSIndexSet.self),
+        (Locale.self, NSLocale.self),
+        (Notification.self, NSNotification.self),
+        (PersonNameComponents.self, NSPersonNameComponents.self),
+        (Set<AnyHashable>.self, NSSet.self),
+        (String.self, NSString.self),
+        (TimeZone.self, NSTimeZone.self),
+        (URL.self, NSURL.self),
+        (URLComponents.self, NSURLComponents.self),
+        (URLQueryItem.self, NSURLQueryItem.self),
+        (URLRequest.self, NSURLRequest.self),
+        (UUID.self, NSUUID.self)
+    ]
+
+    private static let swiftToObjCTypes: [ObjectIdentifier: AnyObject.Type] = {
+        let pairs = typePairs.map {
+            (ObjectIdentifier($0.swiftType), $0.objCType)
+        }
+        return [ObjectIdentifier: AnyObject.Type](uniqueKeysWithValues: pairs)
+    }()
+
+    private static let objCToSwiftTypes: [ObjectIdentifier: Any.Type] = {
+        let pairs = typePairs.map {
+            (ObjectIdentifier($0.objCType), $0.swiftType)
+        }
+        return [ObjectIdentifier: Any.Type](uniqueKeysWithValues: pairs)
+    }()
+
+    static func swiftType(for type: Any.Type) -> Any.Type? {
+        objCToSwiftTypes[ObjectIdentifier(type)]
+    }
+
+    static func objCType(for type: Any.Type) -> Any.Type? {
+        swiftToObjCTypes[ObjectIdentifier(type)]
+    }
+
+    static func mappedType(for type: Any.Type) -> Any.Type? {
+        swiftType(for: type) ?? objCType(for: type)
+    }
+
+    static func convertToObjCType(_ object: Any?) -> Any? {
+        switch object {
+        case is Array<Any>: return object as? NSArray
+        case is Calendar: return object as? NSCalendar
+        case is CharacterSet: return object as? NSCharacterSet
+        case is Data: return object as? NSData
+        case is DateComponents: return object as? NSDateComponents
+        case is DateInterval: return object as? NSDateInterval
+        case is Date: return object as? NSDate
+        case is Decimal: return object as? NSDecimalNumber
+        case is Dictionary<AnyHashable, Any>: return object as? NSDictionary
+        case is IndexPath: return object as? NSIndexPath
+        case is IndexSet: return object as? NSIndexSet
+        case is Locale: return object as? NSLocale
+        case is Notification: return object as? NSNotification
+        case is PersonNameComponents: return object as? NSPersonNameComponents
+        case is Set<AnyHashable>: return object as? NSSet
+        case is String: return object as? NSString
+        case is TimeZone: return object as? NSTimeZone
+        case is URL: return object as? NSURL
+        case is URLComponents: return object as? NSURLComponents
+        case is URLQueryItem: return object as? NSURLQueryItem
+        case is URLRequest: return object as? NSURLRequest
+        case is UUID: return object as? NSUUID
+        default: return nil
+        }
+    }
+
+    static func convertToSwiftType(_ object: Any?) -> Any? {
+        switch object {
+        case is NSArray: return object as? Array<Any>
+        case is NSCalendar: return object as? Calendar
+        case is NSCharacterSet: return object as? CharacterSet
+        case is NSData: return object as? Data
+        case is NSDateComponents: return object as? DateComponents
+        case is NSDateInterval: return object as? DateInterval
+        case is NSDate: return object as? Date
+        case is NSDecimalNumber: return object as? Decimal
+        case is NSDictionary: return object as? Dictionary<AnyHashable, Any>
+        case is NSIndexPath: return object as? IndexPath
+        case is NSIndexSet: return object as? IndexSet
+        case is NSLocale: return object as? Locale
+        case is NSMeasurement: return object as? Measurement
+        case is NSNotification: return object as? Notification
+        case is NSPersonNameComponents: return object as? PersonNameComponents
+        case is NSSet: return object as? Set<AnyHashable>
+        case is NSString: return object as? String
+        case is NSTimeZone: return object as? TimeZone
+        case is NSURL: return object as? URL
+        case is NSURLComponents: return object as? URLComponents
+        case is NSURLQueryItem: return object as? URLQueryItem
+        case is NSURLRequest: return object as? URLRequest
+        case is NSUUID: return object as? UUID
+        default: return nil
+        }
+    }
+
+    static func convertType(of object: Any?) -> Any? {
+        convertToObjCType(object) ?? convertToSwiftType(object)
+    }
+}

+ 1 - 0
_Pods.xcodeproj

@@ -0,0 +1 @@
+Example/Pods/Pods.xcodeproj