├── .swift-version ├── DeluxeInjection └── Classes │ ├── .gitkeep │ ├── DIInjectPlugin.h │ ├── DeluxeInjection.h │ ├── DIDeluxeInjectionPlugin.h │ ├── DIImperativePlugin.h │ ├── DIAssociate.h │ ├── DILazy.h │ ├── DIImperative.h │ ├── DIForceInject.h │ ├── DILazy.m │ ├── DIAssociate.m │ ├── DIForceInject.m │ ├── DIImperative.m │ ├── DIDefaults.h │ ├── DIInject.h │ ├── DIDeluxeInjection.h │ ├── DIDefaults.m │ └── DIInject.m ├── _Pods.xcodeproj ├── codecov.yml ├── images ├── AI.png ├── FI.png ├── LI.png └── Relax.png ├── Example ├── Tests │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── Tests-Prefix.pch │ ├── AbstractTests.h │ ├── Tests-Info.plist │ ├── AbstractTests.m │ ├── DIDeallocTests.m │ ├── Benchmarks.m │ ├── DIClassPropertyTests.m │ ├── DILazyTests.m │ ├── DIDefaultsTests.m │ ├── DIImperativeTests.m │ └── DIInjectTests.m ├── DeluxeInjection │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── DIAppDelegate.h │ ├── DeluxeInjection-Prefix.pch │ ├── main.m │ ├── DeluxeInjection-Info.plist │ └── DIAppDelegate.m ├── Pods │ ├── Target Support Files │ │ ├── DeluxeInjection │ │ │ ├── DeluxeInjection.modulemap │ │ │ ├── DeluxeInjection-dummy.m │ │ │ ├── DeluxeInjection-prefix.pch │ │ │ ├── DeluxeInjection.xcconfig │ │ │ ├── DeluxeInjection-umbrella.h │ │ │ └── Info.plist │ │ ├── RuntimeRoutines │ │ │ ├── RuntimeRoutines.modulemap │ │ │ ├── RuntimeRoutines-dummy.m │ │ │ ├── RuntimeRoutines-prefix.pch │ │ │ ├── RuntimeRoutines-umbrella.h │ │ │ ├── RuntimeRoutines.xcconfig │ │ │ └── Info.plist │ │ ├── Pods-DeluxeInjection_Tests │ │ │ ├── Pods-DeluxeInjection_Tests-acknowledgements.markdown │ │ │ ├── Pods-DeluxeInjection_Tests.modulemap │ │ │ ├── Pods-DeluxeInjection_Tests-dummy.m │ │ │ ├── Pods-DeluxeInjection_Tests-umbrella.h │ │ │ ├── Pods-DeluxeInjection_Tests.debug.xcconfig │ │ │ ├── Pods-DeluxeInjection_Tests.release.xcconfig │ │ │ ├── Info.plist │ │ │ ├── Pods-DeluxeInjection_Tests-acknowledgements.plist │ │ │ ├── Pods-DeluxeInjection_Tests-resources.sh │ │ │ └── Pods-DeluxeInjection_Tests-frameworks.sh │ │ └── Pods-DeluxeInjection_Example │ │ │ ├── Pods-DeluxeInjection_Example.modulemap │ │ │ ├── Pods-DeluxeInjection_Example-dummy.m │ │ │ ├── Pods-DeluxeInjection_Example-umbrella.h │ │ │ ├── Pods-DeluxeInjection_Example.debug.xcconfig │ │ │ ├── Pods-DeluxeInjection_Example.release.xcconfig │ │ │ ├── Info.plist │ │ │ ├── Pods-DeluxeInjection_Example-acknowledgements.markdown │ │ │ ├── Pods-DeluxeInjection_Example-acknowledgements.plist │ │ │ ├── Pods-DeluxeInjection_Example-resources.sh │ │ │ └── Pods-DeluxeInjection_Example-frameworks.sh │ ├── Manifest.lock │ ├── Local Podspecs │ │ └── DeluxeInjection.podspec.json │ ├── RuntimeRoutines │ │ ├── LICENSE │ │ ├── README.md │ │ └── Pod │ │ │ └── Classes │ │ │ ├── RuntimeRoutines.h │ │ │ └── RuntimeRoutines.m │ └── Pods.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ └── DeluxeInjection.xcscheme ├── Podfile ├── DeluxeInjection.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── DeluxeInjection-Example.xcscheme ├── DeluxeInjection.xcworkspace │ └── contents.xcworkspacedata └── Podfile.lock ├── .slather.yml ├── .travis.yml ├── .gitignore ├── DeluxeInjection.podspec ├── LICENSE └── README.md /.swift-version: -------------------------------------------------------------------------------- 1 | 2.3 2 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "Example" 3 | -------------------------------------------------------------------------------- /images/AI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k06a/DeluxeInjection/HEAD/images/AI.png -------------------------------------------------------------------------------- /images/FI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k06a/DeluxeInjection/HEAD/images/FI.png -------------------------------------------------------------------------------- /images/LI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k06a/DeluxeInjection/HEAD/images/LI.png -------------------------------------------------------------------------------- /images/Relax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/k06a/DeluxeInjection/HEAD/images/Relax.png -------------------------------------------------------------------------------- /Example/Tests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/DeluxeInjection/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // The contents of this file are implicitly included at the beginning of every test case source file. 2 | 3 | #ifdef __OBJC__ 4 | 5 | 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/DeluxeInjection/DeluxeInjection.modulemap: -------------------------------------------------------------------------------- 1 | framework module DeluxeInjection { 2 | umbrella header "DeluxeInjection-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RuntimeRoutines/RuntimeRoutines.modulemap: -------------------------------------------------------------------------------- 1 | framework module RuntimeRoutines { 2 | umbrella header "RuntimeRoutines-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'DeluxeInjection_Example' do 4 | pod 'DeluxeInjection', :path => '../' 5 | 6 | target 'DeluxeInjection_Tests' do 7 | inherit! :search_paths 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/DeluxeInjection/DeluxeInjection-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_DeluxeInjection : NSObject 3 | @end 4 | @implementation PodsDummy_DeluxeInjection 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RuntimeRoutines/RuntimeRoutines-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_RuntimeRoutines : NSObject 3 | @end 4 | @implementation PodsDummy_RuntimeRoutines 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_DeluxeInjection_Tests { 2 | umbrella header "Pods-DeluxeInjection_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/DeluxeInjection.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_DeluxeInjection_Example { 2 | umbrella header "Pods-DeluxeInjection_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_DeluxeInjection_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_DeluxeInjection_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_DeluxeInjection_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_DeluxeInjection_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Tests/AbstractTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // AbstractTests.h 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 08.07.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AbstractTests : XCTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/DeluxeInjection/DeluxeInjection-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RuntimeRoutines/RuntimeRoutines-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/DeluxeInjection.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/DeluxeInjection/DIAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIAppDelegate.h 3 | // DeluxeInjection 4 | // 5 | // Created by Anton Bukov on 05/23/2016. 6 | // Copyright (c) 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface DIAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /.slather.yml: -------------------------------------------------------------------------------- 1 | # .slather.yml 2 | 3 | #ci_service: travis_ci 4 | coverage_service: coveralls 5 | workspace: Example/DeluxeInjection.xcworkspace 6 | xcodeproj: Example/DeluxeInjection.xcodeproj 7 | scheme: DeluxeInjection-Example 8 | #source_directory: DeluxeInjection/Classes 9 | build_directory: /Users/travis/Library/Developer/Xcode/DerivedData/ 10 | binary_basename: DeluxeInjection_Example 11 | -------------------------------------------------------------------------------- /Example/DeluxeInjection/DeluxeInjection-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | @import UIKit; 15 | @import Foundation; 16 | #endif 17 | -------------------------------------------------------------------------------- /Example/DeluxeInjection/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // DeluxeInjection 4 | // 5 | // Created by Anton Bukov on 05/23/2016. 6 | // Copyright (c) 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | #import "DIAppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) 13 | { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([DIAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RuntimeRoutines/RuntimeRoutines-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "RuntimeRoutines.h" 14 | 15 | FOUNDATION_EXPORT double RuntimeRoutinesVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char RuntimeRoutinesVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_DeluxeInjection_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_DeluxeInjection_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - DeluxeInjection (0.8.6): 3 | - RuntimeRoutines (~> 0.3.2) 4 | - RuntimeRoutines (0.3.2) 5 | 6 | DEPENDENCIES: 7 | - DeluxeInjection (from `../`) 8 | 9 | EXTERNAL SOURCES: 10 | DeluxeInjection: 11 | :path: ../ 12 | 13 | SPEC CHECKSUMS: 14 | DeluxeInjection: 4e36645d049b1b8b947959a7930778b8e1581b14 15 | RuntimeRoutines: 9253ad1e61a0e2af32cf2ce37393e9185003b94f 16 | 17 | PODFILE CHECKSUM: 81c90c6c2ec9754827499be4f3481cecc117ffe0 18 | 19 | COCOAPODS: 1.4.0 20 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_DeluxeInjection_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_DeluxeInjection_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - DeluxeInjection (0.8.6): 3 | - RuntimeRoutines (~> 0.3.2) 4 | - RuntimeRoutines (0.3.2) 5 | 6 | DEPENDENCIES: 7 | - DeluxeInjection (from `../`) 8 | 9 | EXTERNAL SOURCES: 10 | DeluxeInjection: 11 | :path: ../ 12 | 13 | SPEC CHECKSUMS: 14 | DeluxeInjection: 4e36645d049b1b8b947959a7930778b8e1581b14 15 | RuntimeRoutines: 9253ad1e61a0e2af32cf2ce37393e9185003b94f 16 | 17 | PODFILE CHECKSUM: 81c90c6c2ec9754827499be4f3481cecc117ffe0 18 | 19 | COCOAPODS: 1.4.0 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | language: objective-c 6 | osx_image: xcode8 7 | 8 | before_install: 9 | - gem update cocoapods --pre --no-ri --no-rdoc 10 | - pod repo update 11 | 12 | script: 13 | - set -o pipefail && xcodebuild test -workspace Example/DeluxeInjection.xcworkspace -scheme DeluxeInjection-Example -destination 'platform=iOS Simulator,name=iPhone 6' ONLY_ACTIVE_ARCH=YES | xcpretty 14 | - pod lib lint --verbose 15 | 16 | after_success: 17 | - bash <(curl -s https://codecov.io/bash) 18 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RuntimeRoutines/RuntimeRoutines.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/RuntimeRoutines 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/DeluxeInjection/DeluxeInjection.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | # Pods/ 34 | -------------------------------------------------------------------------------- /Example/Tests/Tests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection" "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection/DeluxeInjection.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines/RuntimeRoutines.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/DeluxeInjection/DeluxeInjection-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "DeluxeInjection.h" 14 | #import "DIAssociate.h" 15 | #import "DIDefaults.h" 16 | #import "DIDeluxeInjection.h" 17 | #import "DIDeluxeInjectionPlugin.h" 18 | #import "DIForceInject.h" 19 | #import "DIImperative.h" 20 | #import "DIImperativePlugin.h" 21 | #import "DIInject.h" 22 | #import "DIInjectPlugin.h" 23 | #import "DILazy.h" 24 | 25 | FOUNDATION_EXPORT double DeluxeInjectionVersionNumber; 26 | FOUNDATION_EXPORT const unsigned char DeluxeInjectionVersionString[]; 27 | 28 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection" "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection/DeluxeInjection.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines/RuntimeRoutines.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection" "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection/DeluxeInjection.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines/RuntimeRoutines.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "DeluxeInjection" -framework "RuntimeRoutines" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection" "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/DeluxeInjection/DeluxeInjection.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/RuntimeRoutines/RuntimeRoutines.framework/Headers" 5 | OTHER_LDFLAGS = $(inherited) -framework "DeluxeInjection" -framework "RuntimeRoutines" 6 | PODS_BUILD_DIR = ${BUILD_DIR} 7 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 9 | PODS_ROOT = ${SRCROOT}/Pods 10 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIInjectPlugin.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIImperativePlugin.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIInject.h" 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | @interface DIImperativeInjector (Plugin) 25 | 26 | - (instancetype)skipDIInjectProtocolFilter; 27 | 28 | @end 29 | 30 | NS_ASSUME_NONNULL_END 31 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/DeluxeInjection/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.8.6 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/RuntimeRoutines/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.3.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Tests/AbstractTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // AbstractTests.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 08.07.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AbstractTests.h" 12 | 13 | @implementation AbstractTests 14 | 15 | static dispatch_group_t runTestsSequentially; 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | static dispatch_once_t onceToken; 21 | dispatch_once(&onceToken, ^{ 22 | runTestsSequentially = dispatch_group_create(); 23 | }); 24 | 25 | dispatch_group_enter(runTestsSequentially); 26 | } 27 | 28 | - (void)tearDown { 29 | [super tearDown]; 30 | 31 | XCTAssertTrue([DeluxeInjection injectedClasses].count == 0, 32 | @"Some injections still exists: %@", 33 | [DeluxeInjection debugDescription]); 34 | 35 | dispatch_group_leave(runTestsSequentially); 36 | } 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/DeluxeInjection.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DeluxeInjection", 3 | "version": "0.8.6", 4 | "summary": "Simplest Objective-C Dependency Injection (DI:syringe:) implementation ever", 5 | "description": "DeluxeInjection allows you simply inject any property of any class by defining value or\ngetter of this property with a block. This should be the simplest DI library ever.", 6 | "homepage": "https://github.com/k06a/DeluxeInjection", 7 | "license": "MIT", 8 | "authors": { 9 | "Anton Bukov": "k06aaa@gmail.com" 10 | }, 11 | "source": { 12 | "git": "https://github.com/k06a/DeluxeInjection.git", 13 | "tag": "0.8.6" 14 | }, 15 | "social_media_url": "https://twitter.com/k06a", 16 | "platforms": { 17 | "ios": "9.0", 18 | "osx": "10.8", 19 | "watchos": "2.0", 20 | "tvos": "9.0" 21 | }, 22 | "source_files": "DeluxeInjection/Classes/**/*", 23 | "dependencies": { 24 | "RuntimeRoutines": [ 25 | "~> 0.3.2" 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Tests/DIDeallocTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIDeallocTests.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 23.10.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import "AbstractTests.h" 10 | 11 | #import 12 | 13 | @interface DIDeallocTests_Class : NSObject 14 | 15 | @property (strong, nonatomic) NSString *string; 16 | 17 | @end 18 | 19 | @implementation DIDeallocTests_Class 20 | 21 | @dynamic string; 22 | 23 | - (void)dealloc { 24 | NSAssert(self.string, @""); 25 | } 26 | 27 | @end 28 | 29 | // 30 | 31 | @interface DIDeallocTests : AbstractTests 32 | 33 | @end 34 | 35 | @implementation DIDeallocTests 36 | 37 | - (void)tearDown { 38 | [DeluxeInjection rejectLazy]; 39 | 40 | [super tearDown]; 41 | } 42 | 43 | - (void)testAssociatedInDealloc { 44 | [DeluxeInjection injectLazy]; 45 | 46 | __unused DIDeallocTests_Class *obj = [DIDeallocTests_Class new]; 47 | NSAssert(obj.string, @""); 48 | obj.string = nil; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DeluxeInjection.h: -------------------------------------------------------------------------------- 1 | // 2 | // DeluxeInjection.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #ifndef __DELUXEINJECTION__ 21 | #define __DELUXEINJECTION__ 22 | 23 | #import "DIDeluxeInjection.h" 24 | 25 | #import "DIForceInject.h" 26 | #import "DIInject.h" 27 | #import "DILazy.h" 28 | #import "DIDefaults.h" 29 | #import "DIAssociate.h" 30 | 31 | #import "DIImperative.h" 32 | 33 | #endif // __DELUXEINJECTION__ 34 | -------------------------------------------------------------------------------- /DeluxeInjection.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "DeluxeInjection" 3 | s.version = "0.8.6" 4 | s.summary = "Simplest Objective-C Dependency Injection (DI:syringe:) implementation ever" 5 | 6 | s.description = <<-DESC 7 | DeluxeInjection allows you simply inject any property of any class by defining value or 8 | getter of this property with a block. This should be the simplest DI library ever. 9 | DESC 10 | 11 | s.homepage = "https://github.com/k06a/DeluxeInjection" 12 | s.license = 'MIT' 13 | s.author = { "Anton Bukov" => "k06aaa@gmail.com" } 14 | s.source = { :git => "https://github.com/k06a/DeluxeInjection.git", :tag => s.version.to_s } 15 | s.social_media_url = 'https://twitter.com/k06a' 16 | 17 | s.ios.deployment_target = '9.0' 18 | s.osx.deployment_target = '10.8' 19 | s.watchos.deployment_target = '2.0' 20 | s.tvos.deployment_target = '9.0' 21 | 22 | s.source_files = 'DeluxeInjection/Classes/**/*' 23 | s.dependency 'RuntimeRoutines', '~> 0.3.2' 24 | end 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Anton Bukov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Example/Pods/RuntimeRoutines/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Anton Bukov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Example/Tests/Benchmarks.m: -------------------------------------------------------------------------------- 1 | // 2 | // Benchmarks.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 30.05.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AbstractTests.h" 12 | 13 | @interface Benchmarks : AbstractTests 14 | 15 | @end 16 | 17 | @implementation Benchmarks 18 | 19 | - (void)testInject { 20 | [self measureBlock:^{ 21 | [DeluxeInjection inject:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 22 | return [DeluxeInjection doNotInject]; 23 | }]; 24 | }]; 25 | } 26 | 27 | - (void)testForceInject { 28 | [self measureBlock:^{ 29 | [DeluxeInjection forceInject:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 30 | return [DeluxeInjection doNotInject]; 31 | }]; 32 | }]; 33 | } 34 | 35 | - (void)testImperative { 36 | [self measureBlock:^{ 37 | [DeluxeInjection imperative:^(DIImperative *lets) { 38 | [lets skipAsserts]; 39 | }]; 40 | }]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIDeluxeInjectionPlugin.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIDeluxeInjectionPlugin.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import 21 | 22 | #import "DIDeluxeInjection.h" 23 | 24 | NS_ASSUME_NONNULL_BEGIN 25 | 26 | @interface DeluxeInjection (Plugin) 27 | 28 | + (void)inject:(DIPropertyBlock)block conformingProtocols:(NSArray * _Nullable)protocols; 29 | + (void)reject:(DIPropertyFilter)block conformingProtocols:(NSArray * _Nullable)protocols; 30 | 31 | + (void)inject:(Class)klass property:(objc_property_t)property getterBlock:(DIGetter)getterBlock setterBlock:(DISetter)setterBlock; 32 | 33 | + (void)reject:(Class)klass property:(objc_property_t)property; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /Example/Pods/RuntimeRoutines/README.md: -------------------------------------------------------------------------------- 1 | # RuntimeRoutines 2 | 3 | [![CI Status](http://img.shields.io/travis/ML-Works/RuntimeRoutines.svg?style=flat)](https://travis-ci.org/ML-Works/RuntimeRoutines) 4 | [![Version](https://img.shields.io/cocoapods/v/RuntimeRoutines.svg?style=flat)](http://cocoapods.org/pods/RuntimeRoutines) 5 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 6 | [![License](https://img.shields.io/cocoapods/l/RuntimeRoutines.svg?style=flat)](http://cocoapods.org/pods/RuntimeRoutines) 7 | [![Platform](https://img.shields.io/cocoapods/p/RuntimeRoutines.svg?style=flat)](http://cocoapods.org/pods/RuntimeRoutines) 8 | 9 | ## Usage 10 | 11 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 12 | 13 | ## Requirements 14 | 15 | ## Installation 16 | 17 | RuntimeRoutines is available through [CocoaPods](http://cocoapods.org). To install 18 | it, simply add the following line to your Podfile: 19 | 20 | ```ruby 21 | pod 'RuntimeRoutines' 22 | ``` 23 | 24 | Or, if you’re using [Carthage](https://github.com/Carthage/Carthage), simply add RuntimeRoutines to your `Cartfile`: 25 | 26 | ``` 27 | github "ML-Works/RuntimeRoutines" 28 | ``` 29 | 30 | ## Author 31 | 32 | Anton Bukov, k06a@mlworks.com 33 | 34 | ## License 35 | 36 | RuntimeRoutines is available under the MIT license. See the LICENSE file for more info. 37 | -------------------------------------------------------------------------------- /Example/Tests/DIClassPropertyTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIClassPropertyTests.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 24.09.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import "AbstractTests.h" 10 | 11 | #import 12 | 13 | // 14 | 15 | @interface DIClassPropertyTests_Example : NSObject 16 | 17 | @property (strong, class) NSString *classProperty; 18 | 19 | @end 20 | 21 | @implementation DIClassPropertyTests_Example 22 | 23 | @dynamic /*(class)*/ classProperty; 24 | 25 | static NSUInteger setterCallsCount = 0; 26 | 27 | + (void)setClassProperty:(NSString *)classProperty { 28 | setterCallsCount++; 29 | } 30 | 31 | @end 32 | 33 | // 34 | 35 | @interface DIClassPropertyTests : AbstractTests 36 | 37 | @end 38 | 39 | @implementation DIClassPropertyTests 40 | 41 | - (void)testClassProperty { 42 | [DeluxeInjection injectAssociate]; 43 | 44 | NSString *answer = @"test"; 45 | XCTAssertEqual(setterCallsCount, 0); 46 | 47 | DIClassPropertyTests_Example.classProperty = answer; 48 | XCTAssertEqual(setterCallsCount, 1); 49 | XCTAssertEqualObjects(DIClassPropertyTests_Example.classProperty, answer); 50 | 51 | DIClassPropertyTests_Example.classProperty = nil; 52 | XCTAssertEqual(setterCallsCount, 2); 53 | XCTAssertNil(DIClassPropertyTests_Example.classProperty); 54 | 55 | [DeluxeInjection rejectAssociate]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIImperativePlugin.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIImperativePlugin.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIImperative.h" 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | @interface DIPropertyHolder : NSObject 25 | 26 | @property (assign, nonatomic) Class targetClass; 27 | @property (assign, nonatomic) Class propertyClass; 28 | @property (strong, nonatomic) NSString *propertyName; 29 | @property (strong, nonatomic) NSSet *propertyProtocols; 30 | @property (assign, nonatomic) SEL getter; 31 | @property (assign, nonatomic) SEL setter; 32 | @property (assign, nonatomic) BOOL wasInjectedGetter; 33 | @property (assign, nonatomic) BOOL wasInjectedSetter; 34 | 35 | @end 36 | 37 | // 38 | 39 | @interface DIImperative (Plugin) 40 | 41 | @property (strong, nonatomic) NSMutableDictionary *> *byClass; 42 | @property (strong, nonatomic) NSMutableDictionary *> *byProtocol; 43 | 44 | @end 45 | 46 | NS_ASSUME_NONNULL_END 47 | -------------------------------------------------------------------------------- /Example/DeluxeInjection/DeluxeInjection-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /Example/Tests/DILazyTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DILazyTests.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 08.07.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import "AbstractTests.h" 10 | 11 | #import 12 | 13 | // 14 | 15 | @interface DILazyTests_Class : NSObject 16 | 17 | @property (strong, nonatomic) NSMutableArray *lazyArray; 18 | @property (strong, nonatomic) NSMutableDictionary *lazyDict; 19 | 20 | @end 21 | 22 | @implementation DILazyTests_Class 23 | 24 | @end 25 | 26 | // 27 | 28 | @interface DILazyTests : AbstractTests 29 | 30 | @end 31 | 32 | @implementation DILazyTests 33 | 34 | - (void)tearDown { 35 | [DeluxeInjection rejectLazy]; 36 | 37 | [super tearDown]; 38 | } 39 | 40 | - (void)testLazy { 41 | [DeluxeInjection injectLazy]; 42 | 43 | DILazyTests_Class *test = [[DILazyTests_Class alloc] init]; 44 | 45 | [test.lazyArray addObject:@"object"]; 46 | test.lazyDict[@"key"] = @"value"; 47 | XCTAssertTrue([test.lazyArray isKindOfClass:[NSMutableArray class]]); 48 | XCTAssertTrue([test.lazyDict isKindOfClass:[NSMutableDictionary class]]); 49 | XCTAssertTrue(test.lazyArray.count == 1); 50 | XCTAssertTrue(test.lazyDict.count == 1); 51 | 52 | test.lazyArray = nil; 53 | test.lazyDict = nil; 54 | XCTAssertTrue([test.lazyArray isKindOfClass:[NSMutableArray class]]); 55 | XCTAssertTrue([test.lazyDict isKindOfClass:[NSMutableDictionary class]]); 56 | XCTAssertTrue(test.lazyArray.count == 0); 57 | XCTAssertTrue(test.lazyDict.count == 0); 58 | 59 | } 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIAssociate.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIAssociate.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjection.h" 21 | #import "DIImperative.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @protocol DIAssociate 26 | 27 | @end 28 | 29 | @interface NSObject (DIAssociate) 30 | 31 | @end 32 | 33 | @interface DeluxeInjection (DIAssociate) 34 | 35 | /** 36 | * Inject properties marked with \c protocol using block: \code 37 | *return _ivar 38 | *\endcode 39 | */ 40 | + (void)injectAssociate; 41 | 42 | /** 43 | * Reject all injections marked explicitly with \c protocol. 44 | */ 45 | + (void)rejectAssociate; 46 | 47 | @end 48 | 49 | // 50 | 51 | @interface DIImperative (DIAssociate) 52 | 53 | /** 54 | * Inject properties marked with \c protocol using block: \code 55 | *return _ivar 56 | *\endcode 57 | */ 58 | - (void)injectAssociate; 59 | 60 | /** 61 | * Reject all injections marked explicitly with \c protocol. 62 | */ 63 | - (void)rejectAssociate; 64 | 65 | @end 66 | 67 | NS_ASSUME_NONNULL_END 68 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DILazy.h: -------------------------------------------------------------------------------- 1 | // 2 | // DILazy.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjection.h" 21 | #import "DIImperative.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @protocol DILazy 26 | 27 | @end 28 | 29 | @interface NSObject (DILazy) 30 | 31 | @end 32 | 33 | // 34 | 35 | @interface DeluxeInjection (DILazy) 36 | 37 | /** 38 | * Inject properties marked with \c protocol using block: \code 39 | *if (_ivar == nil) 40 | * _ivar = [[class alloc] init]; 41 | *return _ivar 42 | *\endcode 43 | */ 44 | + (void)injectLazy; 45 | 46 | /** 47 | * Reject all injections marked explicitly with \c protocol. 48 | */ 49 | + (void)rejectLazy; 50 | 51 | @end 52 | 53 | // 54 | 55 | @interface DIImperative (DILazy) 56 | 57 | /** 58 | * Inject properties marked with \c protocol using block: \code 59 | *if (_ivar == nil) 60 | * _ivar = [[class alloc] init]; 61 | *return _ivar 62 | *\endcode 63 | */ 64 | - (void)injectLazy; 65 | 66 | /** 67 | * Reject all injections marked explicitly with \c protocol. 68 | */ 69 | - (void)rejectLazy; 70 | 71 | @end 72 | 73 | NS_ASSUME_NONNULL_END 74 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIImperative.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIImperative.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjection.h" 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | @interface DIImperative : NSObject 25 | 26 | /** 27 | * Register plugin protocol to be scanned while injection 28 | * 29 | * @param pluginProtocol Protocol what properties can be injected 30 | */ 31 | + (void)registerPluginProtocol:(Protocol *)pluginProtocol; 32 | 33 | /** 34 | * Debug method to skip asserts 35 | */ 36 | - (void)skipAsserts; 37 | 38 | @end 39 | 40 | // 41 | 42 | /** 43 | * Block to apply imperative injections 44 | * 45 | * @param lets Object to apply imperative injections 46 | */ 47 | typedef void (^DIImperativeBlock)(DIImperative *lets); 48 | 49 | @interface DeluxeInjection (DIImperative) 50 | 51 | /** 52 | * Method to apply imperative injections inside block. All properties 53 | * marked with \c DIInject protocol should be injected at least once. 54 | * Properties who will be injected several times will be logged to \c NSLog(). 55 | * 56 | * @param block Block to apply imperative injections 57 | */ 58 | + (void)imperative:(DIImperativeBlock)block; 59 | 60 | @end 61 | 62 | NS_ASSUME_NONNULL_END 63 | -------------------------------------------------------------------------------- /Example/Tests/DIDefaultsTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIDefaultsTests.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 08.07.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import "AbstractTests.h" 10 | 11 | #import 12 | 13 | // 14 | 15 | @interface DIDefaultsTests_Class : NSObject 16 | 17 | @property (strong, nonatomic) NSNumber *defaultsNumber; 18 | @property (strong, nonatomic) NSString *defaultsString; 19 | 20 | @end 21 | 22 | @implementation DIDefaultsTests_Class 23 | 24 | @end 25 | 26 | // 27 | 28 | @interface DIDefaultsTests : AbstractTests 29 | 30 | @end 31 | 32 | @implementation DIDefaultsTests 33 | 34 | - (void)tearDown { 35 | [DeluxeInjection rejectDefaults]; 36 | 37 | [super tearDown]; 38 | } 39 | 40 | - (void)testDefaults { 41 | id answer1 = @777; 42 | id answer2 = @"abc"; 43 | 44 | NSString *key1 = NSStringFromSelector(@selector(defaultsNumber)); 45 | NSString *key2 = NSStringFromSelector(@selector(defaultsString)); 46 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:key1]; 47 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:key2]; 48 | 49 | [DeluxeInjection injectDefaults]; 50 | 51 | DIDefaultsTests_Class *test = [[DIDefaultsTests_Class alloc] init]; 52 | 53 | XCTAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:key1]); 54 | XCTAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:key2]); 55 | 56 | test.defaultsNumber = answer1; 57 | test.defaultsString = answer2; 58 | XCTAssertEqualObjects(answer1, [[NSUserDefaults standardUserDefaults] objectForKey:key1]); 59 | XCTAssertEqualObjects(answer2, [[NSUserDefaults standardUserDefaults] objectForKey:key2]); 60 | 61 | test.defaultsNumber = nil; 62 | test.defaultsString = nil; 63 | XCTAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:key1]); 64 | XCTAssertNil([[NSUserDefaults standardUserDefaults] objectForKey:key2]); 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Example/DeluxeInjection/DIAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIAppDelegate.m 3 | // DeluxeInjection 4 | // 5 | // Created by Anton Bukov on 05/23/2016. 6 | // Copyright (c) 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import "DIAppDelegate.h" 10 | 11 | @implementation DIAppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | return YES; 16 | } 17 | 18 | - (void)applicationWillResignActive:(UIApplication *)application 19 | { 20 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 21 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 22 | } 23 | 24 | - (void)applicationDidEnterBackground:(UIApplication *)application 25 | { 26 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 28 | } 29 | 30 | - (void)applicationWillEnterForeground:(UIApplication *)application 31 | { 32 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | - (void)applicationDidBecomeActive:(UIApplication *)application 36 | { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | - (void)applicationWillTerminate:(UIApplication *)application 41 | { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIForceInject.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIForceInject.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjection.h" 21 | 22 | @interface DeluxeInjection (DIForceInject) 23 | 24 | /** 25 | * Force inject \b values into class properties \b not marked explicitly with any of \c protocol. 26 | * 27 | * @param block Block to be called on every injection into instance. Block should return objects to be injected. Return value will be used each time instance variable is \c nil. 28 | */ 29 | + (void)forceInject:(DIPropertyGetter)block; 30 | 31 | /** 32 | * Force inject \b getters into class properties \b not marked explicitly with any of \c protocol. 33 | * 34 | * @param block Block to be called once for every marked property of all classes. Block should return \c DIGetter block to be called as getter for each object on injection or \c nil if no property injection required for this property. 35 | */ 36 | + (void)forceInjectBlock:(DIPropertyGetterBlock)block; 37 | 38 | /** 39 | * Reject some injections not marked with any of \c protocol. 40 | * 41 | * @param block Block to determine which injections to reject, will be called for all previously injected properties \b not marked explicitly with any of \c protocol. Returns \c BOOL which means \c YES to reject and \NO \b not to reject. 42 | */ 43 | + (void)forceReject:(DIPropertyFilter)block; 44 | 45 | /** 46 | * Reject all injections \b not marked with any of \c protocol. 47 | */ 48 | + (void)forceRejectAll; 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DILazy.m: -------------------------------------------------------------------------------- 1 | // 2 | // DILazy.m 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjectionPlugin.h" 21 | #import "DIInjectPlugin.h" 22 | #import "DILazy.h" 23 | 24 | @implementation DeluxeInjection (DILazy) 25 | 26 | + (void)load { 27 | [DIImperative registerPluginProtocol:@protocol(DILazy)]; 28 | } 29 | 30 | + (void)injectLazy { 31 | [self inject:^NSArray *(Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 32 | NSAssert(propertyClass, @"DILazy can not be applied to unknown class (id)"); 33 | return @[DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 34 | return [[propertyClass alloc] init]; 35 | }), [DeluxeInjection doNotInject]]; 36 | } conformingProtocols:@[@protocol(DILazy)]]; 37 | } 38 | 39 | + (void)rejectLazy { 40 | [self reject:^BOOL(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 41 | return YES; 42 | } conformingProtocols:@[@protocol(DILazy)]]; 43 | } 44 | 45 | @end 46 | 47 | // 48 | 49 | @implementation DIImperative (DILazy) 50 | 51 | - (void)injectLazy { 52 | [[[[self inject] byPropertyProtocol:@protocol(DILazy)] getterBlock:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols, id target, id *ivar, DIOriginalGetter originalGetter) { 53 | NSAssert(propertyClass, @"DILazy can not be applied to unknown class (id)"); 54 | if (*ivar == nil) { 55 | *ivar = [[propertyClass alloc] init]; 56 | } 57 | return *ivar; 58 | }] skipDIInjectProtocolFilter]; 59 | } 60 | 61 | - (void)rejectLazy { 62 | [[[self reject] byPropertyProtocol:@protocol(DILazy)] skipDIInjectProtocolFilter]; 63 | } 64 | 65 | @end 66 | 67 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## DeluxeInjection 5 | 6 | Copyright (c) 2016 Anton Bukov 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | 27 | ## RuntimeRoutines 28 | 29 | Copyright (c) 2016 Anton Bukov 30 | 31 | Permission is hereby granted, free of charge, to any person obtaining a copy 32 | of this software and associated documentation files (the "Software"), to deal 33 | in the Software without restriction, including without limitation the rights 34 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 35 | copies of the Software, and to permit persons to whom the Software is 36 | furnished to do so, subject to the following conditions: 37 | 38 | The above copyright notice and this permission notice shall be included in 39 | all copies or substantial portions of the Software. 40 | 41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 42 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 43 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 44 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 45 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 46 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 47 | THE SOFTWARE. 48 | 49 | Generated by CocoaPods - https://cocoapods.org 50 | -------------------------------------------------------------------------------- /Example/Pods/RuntimeRoutines/Pod/Classes/RuntimeRoutines.h: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeRoutines.h 3 | // MachineLearningWorks 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | #pragma mark - Class 25 | 26 | void RRClassEnumerateAllClasses(BOOL includeMetaClasses, void (^block)(Class klass)); 27 | void RRClassEnumerateSubclasses(Class parentclass, BOOL includeMetaClasses, void (^block)(Class klass)); 28 | void RRClassEnumerateMethods(Class klass, void (^block)(Method method)); 29 | void RRClassEnumerateProperties(Class klass, void (^block)(objc_property_t property)); 30 | void RRClassEnumeratePropertiesWithSuperclassesProperties(Class klass, void (^block)(objc_property_t property)); 31 | void RRClassEnumerateIvars(Class klass, void (^block)(Ivar ivar)); 32 | void RRClassEnumerateProtocols(Class klass, void (^block)(Protocol *protocol)); 33 | void RRClassEnumerateProtocolsWithSuperprotocols(Class klass, void (^block)(Protocol *protocol)); 34 | objc_property_t RRClassGetPropertyByName(Class klass, NSString *propertyName); 35 | 36 | #pragma mark - Protocol 37 | 38 | void RRProtocolEnumerateSuperprotocols(Protocol *protocol, void (^block)(Protocol *superprotocol)); 39 | void RRProtocolEnumerateProperties(Protocol *protocol, BOOL required, BOOL instance, void (^block)(objc_property_t property)); 40 | 41 | #pragma mark - Property 42 | 43 | NSString *RRPropertyGetAttribute(objc_property_t property, char *attrribute); 44 | BOOL RRPropertyGetIsWeak(objc_property_t property); 45 | SEL _Nullable RRPropertyGetGetterIfExist(objc_property_t property); 46 | SEL _Nullable RRPropertyGetSetterIfExist(objc_property_t property); 47 | SEL RRPropertyGetGetter(objc_property_t property); 48 | SEL RRPropertyGetSetter(objc_property_t property); 49 | void RRPropertyGetClassAndProtocols(objc_property_t property, void (^block)(Class _Nullable klass, NSSet *protocols)); 50 | objc_AssociationPolicy RRPropertyGetAssociationPolicy(objc_property_t property); 51 | 52 | #pragma mark - Method 53 | 54 | NSString *RRMethodGetReturnType(Method method); 55 | NSUInteger RRMethodGetArgumentsCount(Method method); 56 | NSString *RRMethodGetArgumentType(Method method, NSUInteger index); 57 | 58 | NS_ASSUME_NONNULL_END 59 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/DeluxeInjection.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 67 | 68 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIAssociate.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIAssociate.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIAssociate.h" 21 | #import "DIDeluxeInjectionPlugin.h" 22 | #import "DIInjectPlugin.h" 23 | 24 | @implementation DeluxeInjection (DIAssociate) 25 | 26 | + (void)load { 27 | [DIImperative registerPluginProtocol:@protocol(DIAssociate)]; 28 | } 29 | 30 | + (void)injectAssociate { 31 | [self inject:^NSArray *(Class targetClass, SEL getter, SEL setter, 32 | NSString *propertyName, Class propertyClass, 33 | NSSet *propertyProtocols) { 34 | return @[ 35 | DIGetterMake(^id(id target, SEL cmd, id *ivar) { 36 | return *ivar; 37 | }), 38 | DISetterWithOriginalMake(^(id target, SEL cmd, id *ivar, id value, DIOriginalSetter originalSetter) { 39 | *ivar = value; 40 | }) 41 | ]; 42 | } conformingProtocols:@[ @protocol(DIAssociate) ]]; 43 | } 44 | 45 | + (void)rejectAssociate { 46 | [self reject:^BOOL(Class targetClass, NSString *propertyName, 47 | Class propertyClass, 48 | NSSet *propertyProtocols) { 49 | return YES; 50 | } conformingProtocols:@[ @protocol(DIAssociate) ]]; 51 | } 52 | 53 | @end 54 | 55 | // 56 | 57 | @implementation DIImperative (DIAssociate) 58 | 59 | - (void)injectAssociate { 60 | [[[[[self inject] byPropertyProtocol:@protocol(DIAssociate)] 61 | getterBlock:^id(Class targetClass, SEL getter, NSString *propertyName, 62 | Class propertyClass, NSSet *propertyProtocols, 63 | id target, id *ivar, DIOriginalGetter originalGetter) { 64 | return *ivar; 65 | }] setterBlock:^(Class targetClass, SEL setter, NSString *propertyName, 66 | Class propertyClass, 67 | NSSet *propertyProtocols, id target, 68 | id *ivar, id value, DIOriginalSetter originalSetter) { 69 | *ivar = value; 70 | }] skipDIInjectProtocolFilter]; 71 | } 72 | 73 | - (void)rejectAssociate { 74 | [[[self reject] byPropertyProtocol:@protocol(DIAssociate)] 75 | skipDIInjectProtocolFilter]; 76 | } 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /Example/Tests/DIImperativeTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIImperativeTests.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 08.07.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import "AbstractTests.h" 10 | 11 | #import 12 | 13 | // 14 | 15 | @protocol DIImperativeTests_Protocol 16 | 17 | @end 18 | 19 | @interface DIImperativeTests_Class : NSObject 20 | 21 | @property (strong, nonatomic) NSMutableArray *classObject; 22 | @property (strong, nonatomic) id protocolObject; 23 | @property (strong, nonatomic) NSMutableArray *forceClassObject; 24 | @property (strong, nonatomic) id forceProtocolObject; 25 | 26 | @property (strong, nonatomic) NSMutableArray *dynamicClassObject; 27 | @property (strong, nonatomic) id dynamicProtocolObject; 28 | @property (weak, nonatomic) NSString *dynamicWeakObject; 29 | 30 | @property (strong, nonatomic) NSMutableArray *lazyArray; 31 | @property (strong, nonatomic) NSMutableDictionary *lazyDict; 32 | 33 | @property (strong, nonatomic) NSNumber *defaultsNumber; 34 | @property (strong, nonatomic) NSString *defaultsString; 35 | 36 | @end 37 | 38 | @implementation DIImperativeTests_Class 39 | 40 | @dynamic dynamicClassObject; 41 | @dynamic dynamicProtocolObject; 42 | @dynamic dynamicWeakObject; 43 | 44 | @end 45 | 46 | // 47 | 48 | @interface DIImperativeTests_Class (DIImperativeTests_Category) 49 | 50 | @property (strong, nonatomic) NSArray *DIImperativeTests_dynamicCategoryProperty; 51 | 52 | @end 53 | 54 | @implementation DIImperativeTests_Class (DIImperativeTests_Category) 55 | 56 | @dynamic DIImperativeTests_dynamicCategoryProperty; 57 | 58 | @end 59 | 60 | // 61 | 62 | @interface DIImperativeTests : AbstractTests 63 | 64 | @end 65 | 66 | @implementation DIImperativeTests 67 | 68 | - (void)tearDown { 69 | [DeluxeInjection imperative:^(DIImperative *lets){ 70 | [lets rejectAll]; 71 | 72 | [lets skipAsserts]; 73 | }]; 74 | 75 | [super tearDown]; 76 | } 77 | 78 | - (void)testInjectImperative { 79 | id answer1 = @[@1,@2,@3]; 80 | id answer2 = @"abc"; 81 | 82 | [DeluxeInjection imperative:^(DIImperative *lets){ 83 | [[[[lets inject] 84 | byPropertyClass:[NSMutableArray class]] 85 | filterContainerClass:[DIImperativeTests_Class class]] 86 | getterValue:[answer1 mutableCopy]]; 87 | 88 | [[[[lets inject] 89 | byPropertyClass:[NSArray class]] 90 | filterContainerClass:[DIImperativeTests_Class class]] 91 | getterValue:answer1]; 92 | 93 | [[[[lets inject] 94 | byPropertyProtocol:@protocol(DIImperativeTests_Protocol)] 95 | filterContainerClass:[DIImperativeTests_Class class]] 96 | getterValue:answer2]; 97 | 98 | [lets skipAsserts]; 99 | }]; 100 | 101 | DIImperativeTests_Class *test = [[DIImperativeTests_Class alloc] init]; 102 | 103 | XCTAssertEqualObjects(test.classObject, answer1); 104 | XCTAssertTrue([test.classObject isKindOfClass:[NSMutableArray class]]); 105 | XCTAssertEqualObjects(test.protocolObject, answer2); 106 | XCTAssertEqual(test.DIImperativeTests_dynamicCategoryProperty, answer1); 107 | } 108 | 109 | @end 110 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Copyright (c) 2016 Anton Bukov <k06aaa@gmail.com> 18 | 19 | Permission is hereby granted, free of charge, to any person obtaining a copy 20 | of this software and associated documentation files (the "Software"), to deal 21 | in the Software without restriction, including without limitation the rights 22 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 | copies of the Software, and to permit persons to whom the Software is 24 | furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in 27 | all copies or substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 | THE SOFTWARE. 36 | 37 | License 38 | MIT 39 | Title 40 | DeluxeInjection 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Copyright (c) 2016 Anton Bukov <k06aaa@gmail.com> 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in 56 | all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 64 | THE SOFTWARE. 65 | 66 | License 67 | MIT 68 | Title 69 | RuntimeRoutines 70 | Type 71 | PSGroupSpecifier 72 | 73 | 74 | FooterText 75 | Generated by CocoaPods - https://cocoapods.org 76 | Title 77 | 78 | Type 79 | PSGroupSpecifier 80 | 81 | 82 | StringsTable 83 | Acknowledgements 84 | Title 85 | Acknowledgements 86 | 87 | 88 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIForceInject.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIForceInject.m 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import 21 | 22 | #import "DIInject.h" 23 | #import "DILazy.h" 24 | #import "DIDefaults.h" 25 | #import "DIAssociate.h" 26 | 27 | #import "DIDeluxeInjectionPlugin.h" 28 | #import "DIForceInject.h" 29 | 30 | static NSSet *excudeProtocols() { 31 | static NSSet *excudeProtocols; 32 | static dispatch_once_t onceToken; 33 | dispatch_once(&onceToken, ^{ 34 | excudeProtocols = [NSSet setWithArray:@[ 35 | @protocol(DIInject), 36 | @protocol(DILazy), 37 | @protocol(DIAssociate), 38 | @protocol(DIDefaults), 39 | @protocol(DIDefaultsSync), 40 | @protocol(DIDefaultsArchived), 41 | @protocol(DIDefaultsArchivedSync), 42 | ]]; 43 | }); 44 | return excudeProtocols; 45 | } 46 | 47 | @implementation DeluxeInjection (DIForceInject) 48 | 49 | + (void)forceInject:(DIPropertyGetter)block { 50 | [self inject:^NSArray* (Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 51 | if ([propertyProtocols intersectsSet:excudeProtocols()]) { 52 | return nil; 53 | } 54 | 55 | id value = block(targetClass, getter, propertyName, propertyClass, propertyProtocols); 56 | if (value == [DeluxeInjection doNotInject]) { 57 | return nil; 58 | } 59 | 60 | objc_property_t property = RRClassGetPropertyByName(targetClass, propertyName); 61 | if (RRPropertyGetIsWeak(property)) { 62 | __weak id weakValue = value; 63 | return @[DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 64 | return weakValue; 65 | }), [DeluxeInjection doNotInject]]; 66 | } else { 67 | return @[DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 68 | return value; 69 | }), [DeluxeInjection doNotInject]]; 70 | } 71 | } conformingProtocols:nil]; 72 | } 73 | 74 | + (void)forceInjectBlock:(DIPropertyGetterBlock)block { 75 | [self inject:^NSArray *(Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 76 | if ([propertyProtocols intersectsSet:excudeProtocols()]) { 77 | return nil; 78 | } 79 | return @[(id)block(targetClass, getter, propertyName, propertyClass, propertyProtocols) ?: (id)[DeluxeInjection doNotInject], [DeluxeInjection doNotInject]]; 80 | } conformingProtocols:nil]; 81 | } 82 | 83 | + (void)forceReject:(DIPropertyFilter)block { 84 | [self reject:^BOOL(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 85 | if ([propertyProtocols intersectsSet:excudeProtocols()]) { 86 | return NO; 87 | } 88 | return block(targetClass, propertyName, propertyClass, propertyProtocols); 89 | } conformingProtocols:nil]; 90 | } 91 | 92 | + (void)forceRejectAll { 93 | [self reject:^BOOL(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 94 | if ([propertyProtocols intersectsSet:excudeProtocols()]) { 95 | return NO; 96 | } 97 | return YES; 98 | } conformingProtocols:nil]; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /Example/DeluxeInjection.xcodeproj/xcshareddata/xcschemes/DeluxeInjection-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 67 | 73 | 74 | 75 | 76 | 77 | 78 | 84 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIImperative.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIImperative.m 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjectionPlugin.h" 21 | #import "DIInject.h" 22 | 23 | #import "DIImperative.h" 24 | #import "DIImperativePlugin.h" 25 | 26 | // 27 | 28 | @implementation DIPropertyHolder 29 | 30 | @end 31 | 32 | // 33 | 34 | @interface DIImperative () 35 | 36 | @property (strong, nonatomic) NSMutableDictionary *> *byClass; 37 | @property (strong, nonatomic) NSMutableDictionary *> *byProtocol; 38 | @property (assign, nonatomic) BOOL shouldSkipAsserts; 39 | 40 | @end 41 | 42 | // 43 | 44 | static NSMutableArray *DIImperativeProtocols; 45 | 46 | @implementation DIImperative 47 | 48 | + (void)registerPluginProtocol:(Protocol *)pluginProtocol { 49 | static dispatch_once_t onceToken; 50 | dispatch_once(&onceToken, ^{ 51 | DIImperativeProtocols = [NSMutableArray array]; 52 | }); 53 | [DIImperativeProtocols addObject:pluginProtocol]; 54 | } 55 | 56 | - (instancetype)init { 57 | self = [super init]; 58 | if (self) { 59 | _byClass = [NSMutableDictionary dictionary]; 60 | _byProtocol = [NSMutableDictionary dictionary]; 61 | 62 | [DeluxeInjection inject:^NSArray * (Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 63 | 64 | DIPropertyHolder *holder = [[DIPropertyHolder alloc] init]; 65 | holder.targetClass = targetClass; 66 | holder.getter = getter; 67 | holder.setter = setter; 68 | holder.propertyName = propertyName; 69 | holder.propertyClass = propertyClass; 70 | holder.propertyProtocols = propertyProtocols; 71 | 72 | if (propertyClass) { 73 | if (self->_byClass[(id)propertyClass] == nil) { 74 | self->_byClass[(id)propertyClass] = [NSMutableArray array]; 75 | } 76 | [self->_byClass[(id)propertyClass] addObject:holder]; 77 | } 78 | 79 | for (Protocol *protocol in propertyProtocols) { 80 | NSValue *key = [NSValue valueWithPointer:(__bridge void *)(protocol)]; 81 | if (self->_byProtocol[key] == nil) { 82 | self->_byProtocol[key] = [NSMutableArray array]; 83 | } 84 | [self->_byProtocol[key] addObject:holder]; 85 | } 86 | 87 | return @[[DeluxeInjection doNotInject], 88 | [DeluxeInjection doNotInject]]; 89 | } conformingProtocols:DIImperativeProtocols]; 90 | } 91 | return self; 92 | } 93 | 94 | - (void)skipAsserts { 95 | self.shouldSkipAsserts = YES; 96 | } 97 | 98 | - (void)checkAllInjected { 99 | if (self.shouldSkipAsserts) { 100 | return; 101 | } 102 | 103 | for (Class klass in self.byClass) { 104 | for (DIPropertyHolder *holder in self.byClass[klass]) { 105 | NSString *problemDescription = [NSString stringWithFormat:@"Missing injection by class to %@.%@", holder.targetClass, NSStringFromSelector(holder.getter)]; 106 | NSAssert(holder.wasInjectedGetter || holder.wasInjectedSetter, problemDescription); 107 | if (!holder.wasInjectedGetter && !holder.wasInjectedSetter) { 108 | NSLog(@"Warning: %@", problemDescription); 109 | } 110 | } 111 | } 112 | 113 | for (NSValue *key in self.byProtocol) { 114 | for (DIPropertyHolder *holder in self.byProtocol[key]) { 115 | NSString *problemDescription = [NSString stringWithFormat:@"Missing injection by protocol to %@.%@", holder.targetClass, NSStringFromSelector(holder.getter)]; 116 | NSAssert(holder.wasInjectedGetter || holder.wasInjectedSetter, problemDescription); 117 | if (!holder.wasInjectedGetter && !holder.wasInjectedSetter) { 118 | NSLog(@"Warning: %@", problemDescription); 119 | } 120 | } 121 | } 122 | } 123 | 124 | @end 125 | 126 | // 127 | 128 | @implementation DeluxeInjection (DIImperative) 129 | 130 | + (void)imperative:(void (^)(DIImperative *lets))block; { 131 | DIImperative *di = [[DIImperative alloc] init]; 132 | @autoreleasepool { 133 | block(di); 134 | } 135 | [di checkAllInjected]; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 12 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 13 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 14 | 15 | case "${TARGETED_DEVICE_FAMILY}" in 16 | 1,2) 17 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 18 | ;; 19 | 1) 20 | TARGET_DEVICE_ARGS="--target-device iphone" 21 | ;; 22 | 2) 23 | TARGET_DEVICE_ARGS="--target-device ipad" 24 | ;; 25 | 3) 26 | TARGET_DEVICE_ARGS="--target-device tv" 27 | ;; 28 | 4) 29 | TARGET_DEVICE_ARGS="--target-device watch" 30 | ;; 31 | *) 32 | TARGET_DEVICE_ARGS="--target-device mac" 33 | ;; 34 | esac 35 | 36 | install_resource() 37 | { 38 | if [[ "$1" = /* ]] ; then 39 | RESOURCE_PATH="$1" 40 | else 41 | RESOURCE_PATH="${PODS_ROOT}/$1" 42 | fi 43 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 44 | cat << EOM 45 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 46 | EOM 47 | exit 1 48 | fi 49 | case $RESOURCE_PATH in 50 | *.storyboard) 51 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 52 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 53 | ;; 54 | *.xib) 55 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 56 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 57 | ;; 58 | *.framework) 59 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 60 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 61 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 62 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 63 | ;; 64 | *.xcdatamodel) 65 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 66 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 67 | ;; 68 | *.xcdatamodeld) 69 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 70 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 71 | ;; 72 | *.xcmappingmodel) 73 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 74 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 75 | ;; 76 | *.xcassets) 77 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 78 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 79 | ;; 80 | *) 81 | echo "$RESOURCE_PATH" || true 82 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 83 | ;; 84 | esac 85 | } 86 | 87 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 88 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 89 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 90 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 91 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 92 | fi 93 | rm -f "$RESOURCES_TO_COPY" 94 | 95 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 96 | then 97 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 98 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 99 | while read line; do 100 | if [[ $line != "${PODS_ROOT}*" ]]; then 101 | XCASSET_FILES+=("$line") 102 | fi 103 | done <<<"$OTHER_XCASSETS" 104 | 105 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 106 | fi 107 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIDefaults.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIDefaults.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjection.h" 21 | #import "DIImperative.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @protocol DIDefaults 26 | 27 | @end 28 | 29 | @protocol DIDefaultsSync 30 | 31 | @end 32 | 33 | @protocol DIDefaultsArchived 34 | 35 | @end 36 | 37 | @protocol DIDefaultsArchivedSync 38 | 39 | @end 40 | 41 | @interface NSObject (DIDefaults) 42 | 43 | @end 44 | 45 | /** 46 | * Block to define custom key for properties to store in NSUserDefaults 47 | * 48 | * @param targetClass Class to be injected/rejected 49 | * @param propertyName Property name to be injected/rejected 50 | * @param propertyClass Class of property to be injected/rejected, \c nil in case of \c id 51 | * @param propertyProtocols Set of property protocols including all superprotocols 52 | * 53 | * @return Key to store in \c NSUserDefaults for propertyName of \c targetClass or \c nil to use \c propertyName 54 | */ 55 | typedef NSString * _Nullable (^DIDefaultsKeyBlock)(Class targetClass, 56 | NSString *propertyName, 57 | Class _Nullable propertyClass, 58 | NSSet *propertyProtocols); 59 | 60 | /** 61 | * Block to define custom NSUserDefaults to use 62 | * 63 | * @param targetClass Class to be injected/rejected 64 | * @param propertyName Property name to be injected/rejected 65 | * @param propertyClass Class of property to be injected/rejected, \c nil in case of \c id 66 | * @param propertyProtocols Set of property protocols including all superprotocols 67 | * 68 | * @return NSUserDefaults instance for propertyName of \c targetClass or \c nil to use \c [NSUserDefaults \c standardUserDefaults] 69 | */ 70 | typedef NSUserDefaults * _Nullable (^DIUserDefaultsBlock)(Class targetClass, 71 | NSString *propertyName, 72 | Class _Nullable propertyClass, 73 | NSSet *propertyProtocols); 74 | 75 | @interface DeluxeInjection (DIDefaults) 76 | 77 | /** 78 | * Inject properties marked with \c , \c , 79 | * \c and \c protocol using NSUserDefaults access 80 | */ 81 | + (void)injectDefaults; 82 | 83 | /** 84 | * Inject properties marked with \c , \c , 85 | * \c and \c protocol 86 | * using NSUserDefaults access with custom key provided by block 87 | * 88 | * @param keyBlock Block to provide key for property 89 | */ 90 | + (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock; 91 | 92 | /** 93 | * Inject properties marked with \c , \c , 94 | * \c and \c protocol 95 | * using NSUserDefaults access with custom key provided by block 96 | * 97 | * @param defaultsBlock Block to provide NSUserDefaults instance 98 | */ 99 | + (void)injectDefaultsWithDefaultsBlock:(DIUserDefaultsBlock)defaultsBlock; 100 | 101 | /** 102 | * Inject properties marked with \c , \c , 103 | * \c and \c protocol 104 | * using NSUserDefaults access with custom key provided by block 105 | * 106 | * @param keyBlock Block to provide key for property 107 | * @param defaultsBlock Block to provide NSUserDefaults instance 108 | */ 109 | + (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock defaultsBlock:(DIUserDefaultsBlock)defaultsBlock; 110 | 111 | /** 112 | * Reject all injections marked explicitly with \c , \c , 113 | * \c and \c protocol. 114 | */ 115 | + (void)rejectDefaults; 116 | 117 | @end 118 | 119 | // 120 | 121 | @interface DIImperative (DIDefaults) 122 | 123 | /** 124 | * Inject properties marked with \c , \c , 125 | * \c and \c protocol using NSUserDefaults access 126 | */ 127 | - (void)injectDefaults; 128 | 129 | /** 130 | * Inject properties marked with \c , \c , 131 | * \c and \c protocol 132 | * using NSUserDefaults access with custom key provided by block 133 | * 134 | * @param keyBlock Block to provide key for property 135 | */ 136 | - (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock; 137 | 138 | /** 139 | * Inject properties marked with \c , \c , 140 | * \c and \c protocol 141 | * using NSUserDefaults access with custom key provided by block 142 | * 143 | * @param defaultsBlock Block to provide NSUserDefaults instance 144 | */ 145 | - (void)injectDefaultsWithDefaultsBlock:(DIUserDefaultsBlock)defaultsBlock; 146 | 147 | 148 | /** 149 | * Inject properties marked with \c , \c , 150 | * \c and \c protocol 151 | * using NSUserDefaults access with custom key provided by block 152 | * 153 | * @param keyBlock Block to provide key for property 154 | * @param defaultsBlock Block to provide NSUserDefaults instance 155 | */ 156 | - (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock defaultsBlock:(DIUserDefaultsBlock)defaultsBlock; 157 | 158 | /** 159 | * Reject all injections marked explicitly with \c , 160 | * \c , \c and \c protocol. 161 | */ 162 | - (void)rejectDefaults; 163 | 164 | @end 165 | 166 | NS_ASSUME_NONNULL_END 167 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Tests/Pods-DeluxeInjection_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # Used as a return value for each invocation of `strip_invalid_archs` function. 10 | STRIP_BINARY_RETVAL=0 11 | 12 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 13 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 14 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 15 | 16 | # Copies and strips a vendored framework 17 | install_framework() 18 | { 19 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 20 | local source="${BUILT_PRODUCTS_DIR}/$1" 21 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 22 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 23 | elif [ -r "$1" ]; then 24 | local source="$1" 25 | fi 26 | 27 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 28 | 29 | if [ -L "${source}" ]; then 30 | echo "Symlinked..." 31 | source="$(readlink "${source}")" 32 | fi 33 | 34 | # Use filter instead of exclude so missing patterns don't throw errors. 35 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 36 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 37 | 38 | local basename 39 | basename="$(basename -s .framework "$1")" 40 | binary="${destination}/${basename}.framework/${basename}" 41 | if ! [ -r "$binary" ]; then 42 | binary="${destination}/${basename}" 43 | fi 44 | 45 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 46 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 47 | strip_invalid_archs "$binary" 48 | fi 49 | 50 | # Resign the code if required by the build settings to avoid unstable apps 51 | code_sign_if_enabled "${destination}/$(basename "$1")" 52 | 53 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 54 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 55 | local swift_runtime_libs 56 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 57 | for lib in $swift_runtime_libs; do 58 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 59 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 60 | code_sign_if_enabled "${destination}/${lib}" 61 | done 62 | fi 63 | } 64 | 65 | # Copies and strips a vendored dSYM 66 | install_dsym() { 67 | local source="$1" 68 | if [ -r "$source" ]; then 69 | # Copy the dSYM into a the targets temp dir. 70 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 71 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 72 | 73 | local basename 74 | basename="$(basename -s .framework.dSYM "$source")" 75 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 76 | 77 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 78 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 79 | strip_invalid_archs "$binary" 80 | fi 81 | 82 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 83 | # Move the stripped file into its final destination. 84 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 85 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 86 | else 87 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 88 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 89 | fi 90 | fi 91 | } 92 | 93 | # Signs a framework with the provided identity 94 | code_sign_if_enabled() { 95 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 96 | # Use the current code_sign_identitiy 97 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 98 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 99 | 100 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 101 | code_sign_cmd="$code_sign_cmd &" 102 | fi 103 | echo "$code_sign_cmd" 104 | eval "$code_sign_cmd" 105 | fi 106 | } 107 | 108 | # Strip invalid architectures 109 | strip_invalid_archs() { 110 | binary="$1" 111 | # Get architectures for current target binary 112 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 113 | # Intersect them with the architectures we are building for 114 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 115 | # If there are no archs supported by this binary then warn the user 116 | if [[ -z "$intersected_archs" ]]; then 117 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 118 | STRIP_BINARY_RETVAL=0 119 | return 120 | fi 121 | stripped="" 122 | for arch in $binary_archs; do 123 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 124 | # Strip non-valid architectures in-place 125 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 126 | stripped="$stripped $arch" 127 | fi 128 | done 129 | if [[ "$stripped" ]]; then 130 | echo "Stripped $binary of architectures:$stripped" 131 | fi 132 | STRIP_BINARY_RETVAL=1 133 | } 134 | 135 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 136 | wait 137 | fi 138 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-DeluxeInjection_Example/Pods-DeluxeInjection_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | # Used as a return value for each invocation of `strip_invalid_archs` function. 10 | STRIP_BINARY_RETVAL=0 11 | 12 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 13 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 14 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 15 | 16 | # Copies and strips a vendored framework 17 | install_framework() 18 | { 19 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 20 | local source="${BUILT_PRODUCTS_DIR}/$1" 21 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 22 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 23 | elif [ -r "$1" ]; then 24 | local source="$1" 25 | fi 26 | 27 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 28 | 29 | if [ -L "${source}" ]; then 30 | echo "Symlinked..." 31 | source="$(readlink "${source}")" 32 | fi 33 | 34 | # Use filter instead of exclude so missing patterns don't throw errors. 35 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 36 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 37 | 38 | local basename 39 | basename="$(basename -s .framework "$1")" 40 | binary="${destination}/${basename}.framework/${basename}" 41 | if ! [ -r "$binary" ]; then 42 | binary="${destination}/${basename}" 43 | fi 44 | 45 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 46 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 47 | strip_invalid_archs "$binary" 48 | fi 49 | 50 | # Resign the code if required by the build settings to avoid unstable apps 51 | code_sign_if_enabled "${destination}/$(basename "$1")" 52 | 53 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 54 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 55 | local swift_runtime_libs 56 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 57 | for lib in $swift_runtime_libs; do 58 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 59 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 60 | code_sign_if_enabled "${destination}/${lib}" 61 | done 62 | fi 63 | } 64 | 65 | # Copies and strips a vendored dSYM 66 | install_dsym() { 67 | local source="$1" 68 | if [ -r "$source" ]; then 69 | # Copy the dSYM into a the targets temp dir. 70 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 71 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 72 | 73 | local basename 74 | basename="$(basename -s .framework.dSYM "$source")" 75 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 76 | 77 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 78 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 79 | strip_invalid_archs "$binary" 80 | fi 81 | 82 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 83 | # Move the stripped file into its final destination. 84 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 85 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 86 | else 87 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 88 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 89 | fi 90 | fi 91 | } 92 | 93 | # Signs a framework with the provided identity 94 | code_sign_if_enabled() { 95 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 96 | # Use the current code_sign_identitiy 97 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 98 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements '$1'" 99 | 100 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 101 | code_sign_cmd="$code_sign_cmd &" 102 | fi 103 | echo "$code_sign_cmd" 104 | eval "$code_sign_cmd" 105 | fi 106 | } 107 | 108 | # Strip invalid architectures 109 | strip_invalid_archs() { 110 | binary="$1" 111 | # Get architectures for current target binary 112 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 113 | # Intersect them with the architectures we are building for 114 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 115 | # If there are no archs supported by this binary then warn the user 116 | if [[ -z "$intersected_archs" ]]; then 117 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 118 | STRIP_BINARY_RETVAL=0 119 | return 120 | fi 121 | stripped="" 122 | for arch in $binary_archs; do 123 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 124 | # Strip non-valid architectures in-place 125 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 126 | stripped="$stripped $arch" 127 | fi 128 | done 129 | if [[ "$stripped" ]]; then 130 | echo "Stripped $binary of architectures:$stripped" 131 | fi 132 | STRIP_BINARY_RETVAL=1 133 | } 134 | 135 | 136 | if [[ "$CONFIGURATION" == "Debug" ]]; then 137 | install_framework "${BUILT_PRODUCTS_DIR}/DeluxeInjection/DeluxeInjection.framework" 138 | install_framework "${BUILT_PRODUCTS_DIR}/RuntimeRoutines/RuntimeRoutines.framework" 139 | fi 140 | if [[ "$CONFIGURATION" == "Release" ]]; then 141 | install_framework "${BUILT_PRODUCTS_DIR}/DeluxeInjection/DeluxeInjection.framework" 142 | install_framework "${BUILT_PRODUCTS_DIR}/RuntimeRoutines/RuntimeRoutines.framework" 143 | fi 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIInject.h: -------------------------------------------------------------------------------- 1 | // 2 | // DIInject.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjection.h" 21 | #import "DIImperative.h" 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @protocol DIInject 26 | 27 | @end 28 | 29 | @interface NSObject (DIInject) 30 | 31 | @end 32 | 33 | @interface DeluxeInjection (DIInject) 34 | 35 | /** 36 | * Inject \b values into class properties marked explicitly with \c protocol. 37 | * 38 | * @param block Block to be called on every injection into instance. Will be called during getter call each time instance variable is \c nil. Block should return objects to be injected. 39 | */ 40 | + (void)inject:(DIPropertyGetter)block; 41 | 42 | /** 43 | * Inject \b getters into class properties marked explicitly with \c protocol. 44 | * 45 | * @param block Block to be called once for every marked property of all classes. Block should return \c DIGetter block to be called as getter for each object on injection or \c nil if no injection required for this property. 46 | */ 47 | + (void)injectBlock:(DIPropertyGetterBlock)block; 48 | 49 | /** 50 | * Reject some injections marked explicitly with \c protocol. 51 | * 52 | * @param block Block to determine which injections to reject, will be called for all previously injected properties. Returns \c BOOL which means to reject or \b not to reject. 53 | */ 54 | + (void)reject:(DIPropertyFilter)block; 55 | 56 | /** 57 | * Reject all injections marked explicitly with \c protocol. 58 | */ 59 | + (void)rejectAll; 60 | 61 | @end 62 | 63 | // 64 | 65 | /** 66 | * Block to filter properties to be injected 67 | * 68 | * @param targetClass Class to be injected 69 | * @param getter Selector of getter method 70 | * @param propertyName Property name to be injected 71 | * @param propertyClass Class of property to be injected, \c nil in case of \c id 72 | * @param propertyProtocols Set of property protocols including all superprotocols 73 | * 74 | * @return \c YES to inject property, \c NO to skip injection 75 | */ 76 | typedef BOOL (^DIPropertyFilterBlock)(Class targetClass, 77 | SEL getter, 78 | NSString * propertyName, 79 | Class _Nullable propertyClass, 80 | NSSet *propertyProtocols); 81 | 82 | /** 83 | * Block to be injected for property getter 84 | * 85 | * @param targetClass Class to be injected 86 | * @param getter Property getter selector 87 | * @param propertyName Injected property name 88 | * @param propertyClass Class of injected property, \c nil in case of \c id 89 | * @param propertyProtocols Set of property protocols including all superprotocols 90 | * @param target Receiver of selector 91 | * @param ivar Pointer to instance variable 92 | * @param originalGetter Original setter pointer if exists 93 | * 94 | * @return Injected value or \c nil 95 | */ 96 | typedef id _Nullable (^DIImperativeGetter)(Class targetClass, 97 | SEL getter, 98 | NSString *propertyName, 99 | Class _Nullable propertyClass, 100 | NSSet *propertyProtocols, 101 | id target, 102 | id _Nullable * _Nonnull ivar, 103 | DIOriginalGetter _Nullable originalGetter); 104 | 105 | /** 106 | * Block to be injected for property setter 107 | * 108 | * @param targetClass Class to be injected 109 | * @param setter Property setter selector 110 | * @param propertyName Injected property name 111 | * @param propertyClass Class of injected property, \c nil in case of \c id 112 | * @param propertyProtocols Set of property protocols including all superprotocols 113 | * @param target Receiver of selector 114 | * @param ivar Pointer to instance variable 115 | * @param value New value to assign inside setter 116 | * @param originalSetter Original setter pointer if exists 117 | */ 118 | typedef void (^DIImperativeSetter)(Class targetClass, 119 | SEL setter, 120 | NSString *propertyName, 121 | Class _Nullable propertyClass, 122 | NSSet *propertyProtocols, 123 | id target, 124 | id _Nullable * _Nonnull ivar, 125 | id _Nullable value, 126 | DIOriginalSetter _Nullable originalSetter); 127 | 128 | /** 129 | * Helper methods to create DIImperativeGetter and DIImperativeSetter with Xcode autocomplete :) 130 | */ 131 | DIImperativeGetter DIImperativeGetterMake(DIImperativeGetter getter); 132 | DIImperativeSetter DIImperativeSetterMake(DIImperativeSetter setter); 133 | 134 | 135 | /** 136 | * Helper methods to create DIImperativeGetter and DIImperativeSetter with DIGetter and DISetter 137 | */ 138 | DIImperativeGetter DIImperativeGetterFromGetter(DIGetter getter); 139 | DIImperativeSetter DIImperativeSetterFromSetter(DISetter getter); 140 | 141 | // 142 | 143 | @interface DIImperativeInjector : NSObject 144 | 145 | #pragma mark - Property injection type 146 | 147 | /** 148 | * Set property \c klass to be injected 149 | * 150 | * @param klass Property class 151 | */ 152 | - (instancetype)byPropertyClass:(Class)klass; 153 | 154 | /** 155 | * Set property \c protocol to be injected 156 | * 157 | * @param protocol Protocol of property 158 | */ 159 | - (instancetype)byPropertyProtocol:(Protocol *)protocol; 160 | 161 | /** 162 | * Set value to be injected 163 | * 164 | * @param getterValue Value to be injected 165 | */ 166 | - (instancetype)getterValue:(nullable id)getterValue; 167 | 168 | /** 169 | * Set value to be injected by lazy block 170 | * 171 | * @param lazyBlock Block to be called on first access only 172 | */ 173 | - (instancetype)getterValueLazy:(id(^)(void))lazyBlock; 174 | 175 | /** 176 | * Set value to be injected by class 177 | * 178 | * @param lazyClass Class value to be injected with alloc-init 179 | */ 180 | - (instancetype)getterValueLazyByClass:(Class)lazyClass; 181 | 182 | #pragma mark - Property injection value or blocks 183 | 184 | /** 185 | * Set getter block to be injected 186 | * 187 | * @param getterBlock Value block to be injected 188 | */ 189 | - (instancetype)getterBlock:(DIImperativeGetter)getterBlock; 190 | 191 | /** 192 | * Set setter block to be injected 193 | * 194 | * @param setterBlock Value block to be injected 195 | */ 196 | - (instancetype)setterBlock:(DIImperativeSetter)setterBlock; 197 | 198 | #pragma mark - Property injection filtering 199 | 200 | /** 201 | * Set filter block for conditional injection 202 | * 203 | * @param filterBlock Block which define what properties can be injected 204 | */ 205 | - (instancetype)filterBlock:(DIPropertyFilterBlock)filterBlock; 206 | 207 | /** 208 | * Set filter class for conditional injection 209 | * 210 | * @param filterContainerClass Class which sublasses properties can be injected 211 | */ 212 | - (instancetype)filterContainerClass:(Class)filterContainerClass; 213 | 214 | @end 215 | 216 | // 217 | 218 | @interface DIImperative (DIInject) 219 | 220 | /** 221 | * Create injector object 222 | * 223 | * @return injector object 224 | */ 225 | - (DIImperativeInjector *)inject; 226 | 227 | /** 228 | * Create rejector object 229 | * 230 | * @return rejector object 231 | */ 232 | - (DIImperativeInjector *)reject; 233 | 234 | /** 235 | * Reject all injected properties marked with \c DIInject 236 | */ 237 | - (void)rejectAll; 238 | 239 | @end 240 | 241 | NS_ASSUME_NONNULL_END 242 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIDeluxeInjection.h: -------------------------------------------------------------------------------- 1 | // 2 | // DeluxeInjection.h 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import 21 | 22 | NS_ASSUME_NONNULL_BEGIN 23 | 24 | #pragma mark - Block types 25 | 26 | typedef id _Nullable (*DIOriginalGetter)(id target, SEL cmd); 27 | typedef void (*DIOriginalSetter)(id target, SEL cmd, id _Nullable value); 28 | 29 | /** 30 | * Block to be injected instead of property getter 31 | * 32 | * @param target Receiver of selector 33 | * @param cmd Selector name 34 | * @param ivar Pointer to instance variable 35 | * @param originalGetter Original getter pointer if exists 36 | * 37 | * @return Injected value or \c [DeluxeInjection \c doNotInject] instance to not inject this property 38 | */ 39 | typedef id _Nullable (^DIGetter)(id target, SEL cmd, id _Nullable * _Nonnull ivar, DIOriginalGetter _Nullable originalGetter); 40 | typedef id _Nullable (^DIGetterWithoutOriginal)(id target, SEL cmd, id _Nullable * _Nonnull ivar); 41 | typedef id _Nullable (^DIGetterWithoutIvar)(id target, SEL cmd); 42 | 43 | /** 44 | * Block to be injected instead of property setter 45 | * 46 | * @param target Receiver of selector 47 | * @param cmd Selector name 48 | * @param ivar Pointer to instance variable 49 | * @param value New value to assign inside setter 50 | * @param originalSetter Original setter pointer if exists 51 | */ 52 | typedef void (^DISetter)(id target, SEL cmd, id _Nullable * _Nonnull ivar, id value, DIOriginalSetter _Nullable originalSetter); 53 | typedef void (^DISetterWithoutOriginal)(id target, SEL cmd, id _Nullable * _Nonnull ivar, id value); 54 | typedef void (^DISetterWithoutIvar)(id target, SEL cmd, id value); 55 | 56 | /** 57 | * Block to be injected for property 58 | * 59 | * @param targetClass Class to be injected 60 | * @param propertyName Injected property name 61 | * @param propertyClass Class of injected property, \c nil in case of \c id 62 | * @param propertyProtocols Set of property protocols including all superprotocols 63 | * 64 | * @return Injected value or \c nil 65 | */ 66 | typedef id _Nullable (^DIPropertyGetter)(Class targetClass, 67 | SEL getter, 68 | NSString *propertyName, 69 | Class _Nullable propertyClass, 70 | NSSet *propertyProtocols); 71 | 72 | /** 73 | * Block to get injectable block as getter for property 74 | * 75 | * @param targetClass Class to be injected 76 | * @param getter Selector of getter method 77 | * @param propertyName Property name to be injected 78 | * @param propertyClass Class of property to be injected, \c nil in case of \c id 79 | * @param propertyProtocols Set of property protocols including all superprotocols 80 | * 81 | * @return Injectable block \c DIGetter or \c nil for skipping injection 82 | */ 83 | typedef DIGetter _Nullable (^DIPropertyGetterBlock)(Class targetClass, 84 | SEL getter, 85 | NSString *propertyName, 86 | Class _Nullable propertyClass, 87 | NSSet *propertyProtocols); 88 | 89 | /** 90 | * Block to get injectable block as setter for property 91 | * 92 | * @param targetClass Class to be injected 93 | * @param setter Selector of setter method 94 | * @param propertyName Property name to be injected 95 | * @param propertyClass Class of property to be injected, \c nil in case of \c id 96 | * @param propertyProtocols Set of property protocols including all superprotocols 97 | * 98 | * @return Injectable block \c DISetter or \c [DeluxeInjecion \c doNotInject] for skipping injection 99 | */ 100 | typedef DISetter _Nullable (^DIPropertySetterBlock)(Class targetClass, 101 | SEL setter, 102 | NSString *propertyName, 103 | Class _Nullable propertyClass, 104 | NSSet *propertyProtocols); 105 | 106 | /** 107 | * Block to get injectable block as setter for property 108 | * 109 | * @param targetClass Class to be injected 110 | * @param getter Selector of getter method 111 | * @param setter Selector of setter method 112 | * @param propertyName Property name to be injected 113 | * @param propertyClass Class of property to be injected, \c nil in case of \c id 114 | * @param propertyProtocols Set of property protocols including all superprotocols 115 | * 116 | * @return Array of getter and settor injectable blocks or \c nil or \c [DeluxeInjecion \c doNotInject] for skipping injection 117 | */ 118 | typedef NSArray *_Nullable (^DIPropertyBlock)(Class targetClass, 119 | SEL getter, 120 | SEL setter, 121 | NSString *propertyName, 122 | Class _Nullable propertyClass, 123 | NSSet *propertyProtocols); 124 | 125 | /** 126 | * Block to filter properties to be injected or not 127 | * 128 | * @param targetClass Class to be injected/rejected 129 | * @param propertyName Property name to be injected/rejected 130 | * @param propertyClass Class of property to be injected/rejected, \c nil in case of \c id 131 | * @param propertyProtocols Set of property protocols including all superprotocols 132 | * 133 | * @return \c YES to inject/reject \c propertyName of \c targetClass or NO to not inject/reject 134 | */ 135 | typedef BOOL (^DIPropertyFilter)(Class targetClass, 136 | NSString *propertyName, 137 | Class _Nullable propertyClass, 138 | NSSet *propertyProtocols); 139 | 140 | /** 141 | * Helper methods to create DIGetter and DISetter with Xcode autocomplete :) 142 | */ 143 | DIGetter DIGetterMake(DIGetterWithoutOriginal getter); 144 | DISetter DISetterMake(DISetterWithoutOriginal setter); 145 | DIGetter DIGetterWithOriginalMake(DIGetter getter); 146 | DISetter DISetterWithOriginalMake(DISetter setter); 147 | 148 | /** 149 | * Transforms getter block without \c ivar argument to block with \c ivar argument this way: 150 | * \code 151 | *return ^id(id target, id *ivar) { 152 | * if (*ivar == nil) { 153 | * *ivar = getter(target); 154 | * } 155 | * return *ivar; 156 | *}; 157 | * \endcode 158 | * 159 | * @param getter Block without \c ivar argument 160 | * 161 | * @return Block with \c ivar argument 162 | */ 163 | DIGetter DIGetterIfIvarIsNil(DIGetterWithoutIvar getter); 164 | 165 | /** 166 | * Works the same way as \c DIGetterIfIvarIsNil 167 | * but returns new value once per target per injection 168 | */ 169 | DIGetter DIGetterIfIvarIsNilOnce(DIGetterWithoutIvar getter); 170 | 171 | /** 172 | * Helper to call supers getter method 173 | * 174 | * @param target Target to call 175 | * @param klass Class of current getter implementation 176 | * @param getter Selector to call 177 | * 178 | * @return Return value be supers getter 179 | */ 180 | id DIGetterSuperCall(id target, Class klass, SEL getter); 181 | 182 | /** 183 | * Helper to call supers setter method 184 | * 185 | * @param target Target to call 186 | * @param klass Class of current setter implementation 187 | * @param setter Selector to call 188 | */ 189 | void DISetterSuperCall(id target, Class klass, SEL setter, id value); 190 | 191 | #pragma mark - Main injection class 192 | 193 | @interface DeluxeInjection : NSObject 194 | 195 | /** 196 | * Shared instance to show which injections to skip as return value in \c inject: and \c forceInject: methods 197 | * 198 | * @return Share instance of helper class 199 | */ 200 | + (id)doNotInject; 201 | 202 | /** 203 | * Check if \c getter or \c setter of \c class was injected 204 | * 205 | * @param klass Class of property to check 206 | * @param selector Selector to check 207 | * 208 | * @return \c YES if injected, otherwise \c NO 209 | */ 210 | + (BOOL)checkInjected:(Class)klass selector:(SEL)selector; 211 | 212 | /** 213 | * Get array of classes with some properties injected 214 | * 215 | * @return Array of \c Class objects 216 | */ 217 | + (NSArray *)injectedClasses; 218 | 219 | /** 220 | * Get array of selectors of injected properties 221 | * 222 | * @param klass Class of properties 223 | * 224 | * @return Array of \c NSStrings, should be transformed with NSSelectorFromString 225 | */ 226 | + (NSArray *)injectedSelectorsForClass:(Class)klass; 227 | 228 | /** 229 | * Overriden \c debugDescription method to see tree of classes and injected properties 230 | * 231 | * @return String with injections info 232 | */ 233 | + (NSString *)debugDescription; 234 | 235 | @end 236 | 237 | NS_ASSUME_NONNULL_END 238 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIDefaults.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIDefaults.m 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "DIDeluxeInjectionPlugin.h" 21 | #import "DIInjectPlugin.h" 22 | #import "DIDefaults.h" 23 | 24 | @implementation NSObject (DIDefaults) 25 | 26 | @end 27 | 28 | @implementation DeluxeInjection (DIDefaults) 29 | 30 | #pragma mark - Private 31 | 32 | + (void)load { 33 | [DIImperative registerPluginProtocol:@protocol(DIDefaults)]; 34 | [DIImperative registerPluginProtocol:@protocol(DIDefaultsSync)]; 35 | [DIImperative registerPluginProtocol:@protocol(DIDefaultsArchived)]; 36 | [DIImperative registerPluginProtocol:@protocol(DIDefaultsArchivedSync)]; 37 | } 38 | 39 | #pragma mark - Public 40 | 41 | + (void)injectDefaults { 42 | [self injectDefaultsWithKeyBlock:^NSString *(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 43 | return propertyName; 44 | }]; 45 | } 46 | 47 | + (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock { 48 | [self injectDefaultsWithKeyBlock:keyBlock defaultsBlock:^NSUserDefaults *(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 49 | return [NSUserDefaults standardUserDefaults]; 50 | }]; 51 | } 52 | 53 | + (void)injectDefaultsWithDefaultsBlock:(DIUserDefaultsBlock)defaultsBlock { 54 | [self injectDefaultsWithKeyBlock:^NSString *(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 55 | return propertyName; 56 | } defaultsBlock:defaultsBlock]; 57 | } 58 | 59 | + (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock defaultsBlock:(DIUserDefaultsBlock)defaultsBlock { 60 | NSMutableSet *defaultsProtocols = [NSMutableSet setWithArray:@[ 61 | @protocol(DIDefaults), 62 | @protocol(DIDefaultsSync), 63 | @protocol(DIDefaultsArchived), 64 | @protocol(DIDefaultsArchivedSync), 65 | ]]; 66 | 67 | [self inject:^NSArray *(Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 68 | 69 | NSMutableSet *protocolsCopy = [defaultsProtocols mutableCopy]; 70 | [protocolsCopy intersectSet:propertyProtocols]; 71 | Protocol *protocol = protocolsCopy.anyObject; 72 | NSValue *protocolKey = [NSValue valueWithPointer:(__bridge void *)protocol]; 73 | 74 | BOOL withSync = [@{ 75 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaults)] : @NO, 76 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsSync)] : @YES, 77 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchived)] : @NO, 78 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchivedSync)] : @YES, 79 | }[protocolKey] boolValue]; 80 | 81 | BOOL withArchive = [@{ 82 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaults)] : @NO, 83 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsSync)] : @NO, 84 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchived)] : @YES, 85 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchivedSync)] : @YES, 86 | }[protocolKey] boolValue]; 87 | 88 | NSString *key = keyBlock(targetClass, propertyName, propertyClass, propertyProtocols) ?: propertyName; 89 | NSUserDefaults *defaults = defaultsBlock(targetClass, propertyName, propertyClass, propertyProtocols) ?: [NSUserDefaults standardUserDefaults]; 90 | return @[DIGetterMake(^id _Nullable(id target, SEL cmd, id *ivar) { 91 | if (withSync) { 92 | [defaults synchronize]; 93 | } 94 | id value = [defaults objectForKey:key]; 95 | if (withArchive && value) { 96 | return [NSKeyedUnarchiver unarchiveObjectWithData:value]; 97 | } 98 | return value; 99 | }), DISetterWithOriginalMake(^(id target, SEL cmd, id *ivar, id value, void (*originalSetter)(id, SEL, id)) { 100 | if (withArchive && value) { 101 | value = [NSKeyedArchiver archivedDataWithRootObject:value]; 102 | } 103 | [defaults setObject:value forKey:key]; 104 | if (withSync) { 105 | [defaults synchronize]; 106 | } 107 | })]; 108 | } conformingProtocols:defaultsProtocols.allObjects]; 109 | } 110 | 111 | + (void)rejectDefaults { 112 | [self reject:^BOOL(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 113 | return YES; 114 | } conformingProtocols:@[ @protocol(DIDefaults), 115 | @protocol(DIDefaultsSync), 116 | @protocol(DIDefaultsArchived), 117 | @protocol(DIDefaultsArchivedSync) ]]; 118 | } 119 | 120 | @end 121 | 122 | // 123 | 124 | @implementation DIImperative (DIDefaults) 125 | 126 | - (void)injectDefaults { 127 | [self injectDefaultsWithKeyBlock:^NSString *(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 128 | return propertyName; 129 | } defaultsBlock:^NSUserDefaults *(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 130 | return [NSUserDefaults standardUserDefaults]; 131 | }]; 132 | } 133 | 134 | - (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock { 135 | [self injectDefaultsWithKeyBlock:keyBlock defaultsBlock:^NSUserDefaults *(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 136 | return [NSUserDefaults standardUserDefaults]; 137 | }]; 138 | } 139 | 140 | - (void)injectDefaultsWithDefaultsBlock:(DIUserDefaultsBlock)defaultsBlock { 141 | [self injectDefaultsWithKeyBlock:^NSString * _Nullable(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 142 | return propertyName; 143 | } defaultsBlock:defaultsBlock]; 144 | } 145 | 146 | 147 | - (void)injectDefaultsWithKeyBlock:(DIDefaultsKeyBlock)keyBlock defaultsBlock:(DIUserDefaultsBlock)defaultsBlock { 148 | for (Protocol *protocol in @[ @protocol(DIDefaults), 149 | @protocol(DIDefaultsSync), 150 | @protocol(DIDefaultsArchived), 151 | @protocol(DIDefaultsArchivedSync) ]) { 152 | NSValue *protocolKey = [NSValue valueWithPointer:(__bridge void *)protocol]; 153 | 154 | BOOL withSync = [@{ 155 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaults)] : @NO, 156 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsSync)] : @YES, 157 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchived)] : @NO, 158 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchivedSync)] : @YES, 159 | }[protocolKey] boolValue]; 160 | 161 | BOOL withArchive = [@{ 162 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaults)] : @NO, 163 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsSync)] : @NO, 164 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchived)] : @YES, 165 | [NSValue valueWithPointer:(__bridge void *)@protocol(DIDefaultsArchivedSync)] : @YES, 166 | }[protocolKey] boolValue]; 167 | 168 | [[[[[self inject] byPropertyProtocol:protocol] getterBlock:^id _Nullable(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols, id target, id *ivar, DIOriginalGetter originalGetter) { 169 | NSUserDefaults *defaults = defaultsBlock(targetClass, propertyName, propertyClass, propertyProtocols) ?: [NSUserDefaults standardUserDefaults]; 170 | NSString *key = keyBlock(targetClass, propertyName, propertyClass, propertyProtocols) ?: propertyName; 171 | if (withSync) { 172 | [defaults synchronize]; 173 | } 174 | id value = [defaults objectForKey:key]; 175 | if (withArchive && value) { 176 | return [NSKeyedUnarchiver unarchiveObjectWithData:value]; 177 | } 178 | return value; 179 | }] setterBlock:^(Class targetClass, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols, id target, id *ivar, id value, DIOriginalSetter originalSetter) { 180 | NSUserDefaults *defaults = defaultsBlock(targetClass, propertyName, propertyClass, propertyProtocols) ?: [NSUserDefaults standardUserDefaults]; 181 | NSString *key = keyBlock(targetClass, propertyName, propertyClass, propertyProtocols) ?: propertyName; 182 | if (withArchive && value) { 183 | value = [NSKeyedArchiver archivedDataWithRootObject:value]; 184 | } 185 | [defaults setObject:value forKey:key]; 186 | if (withSync) { 187 | [defaults synchronize]; 188 | } 189 | }] skipDIInjectProtocolFilter]; 190 | } 191 | } 192 | 193 | - (void)rejectDefaults { 194 | for (Protocol *protocol in @[ @protocol(DIDefaults), 195 | @protocol(DIDefaultsSync), 196 | @protocol(DIDefaultsArchived), 197 | @protocol(DIDefaultsArchivedSync) ]) { 198 | [[[self reject] byPropertyProtocol:protocol] skipDIInjectProtocolFilter]; 199 | } 200 | } 201 | 202 | @end 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeluxeInjection :syringe: 2 | 3 | [![CI Status](http://img.shields.io/travis/k06a/DeluxeInjection.svg?style=flat)](https://travis-ci.org/k06a/DeluxeInjection) 4 | [![Test Coverage](http://codecov.io/github/k06a/DeluxeInjection/coverage.svg?branch=master)](http://codecov.io/github/k06a/DeluxeInjection?branch=master) 5 | [![Version](https://img.shields.io/cocoapods/v/DeluxeInjection.svg?style=flat)](http://cocoapods.org/pods/DeluxeInjection) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | [![License](https://img.shields.io/cocoapods/l/DeluxeInjection.svg?style=flat)](http://cocoapods.org/pods/DeluxeInjection) 8 | [![Platform](https://img.shields.io/cocoapods/p/DeluxeInjection.svg?style=flat)](http://cocoapods.org/pods/DeluxeInjection) 9 | 10 | [![Apps Using](https://img.shields.io/cocoapods/at/DeluxeInjection.svg?label=Apps%20Using%20DeluxeInjection&colorB=28B9FE)](http://cocoapods.org/pods/DeluxeInjection) 11 | [![Downloads](https://img.shields.io/cocoapods/dt/DeluxeInjection.svg?label=Total%20Downloads&colorB=28B9FE)](http://cocoapods.org/pods/DeluxeInjection) 12 | 13 | 14 | 15 | ## Features 16 | 17 | 1. Auto-injection as first-class feature 18 | 2. Objective-C **class** properties support 19 | 3. Both **value-** and **getter-/setter-** injection supported 20 | 4. Inject both **ivar**-backed and `@dynamic` properties (over association) 21 | 5. Easily access **ivar** and original method implementation inside injected method 22 | 23 | ## Table of contents 24 | 25 | 1. [Concepts](#concepts) 26 | 2. [Auto Injection](#auto-injection) 27 | 3. [Lazies Injection](#lazies-injection) 28 | 4. [Settings Injection](#settings-injection) 29 | 5. [Performance and Testing](#performance-and-testing) 30 | 6. [Installation](#installation) 31 | 32 | ## Concepts 33 | 34 | The main concept of DeluxeInjection library is explicit injection of marked properties. Properties can be marked with protocols, thats why integral (non-object) properties are not supported. 35 | 36 | DeluxeInjection have minimal API and most interesting features are implemented like separated plugins. You can easily develop your own plugins, just looking at some existing: `DIInject`, `DILazy`, `DIDefaults`, `DIAssociate`. 37 | 38 | Due DeluxeInjection architecture most plugins works by enumeration all properties of all classes. Thats not very optimal to use with several plugins, thats why `DIImperative` plugin was implemented, and all other plugins now support `DIImperative` plugin. It collects all injectable properties of all classes and provide you a block to apply all necessary injections in imperative format. This plugin is used to be default usage of DeluxeInjection. 39 | 40 | ## Auto Injection 41 | 42 | 43 | 44 | Here is basic example of injection value by class and protocol and getter injection: 45 | ```objective-c 46 | @interface MyClass : NSObject 47 | 48 | @property (nonatomic) Settings *settings; 49 | @property (nonatomic) id *analytics; 50 | @property (nonatomic) NSMutableArray *items; 51 | 52 | @end 53 | ``` 54 | 55 | Create some instances to be injected: 56 | ```objective-c 57 | Settings *settings = [UserDefaultsSetting new]; 58 | Analytics *analytics = [CountlyAnalytics new]; 59 | ``` 60 | 61 | Inject instances and blocks to properties: 62 | ```objective-c 63 | [DeluxeInjection imperative:^(DIImperative *lets) { 64 | 65 | // Inject value by class 66 | [[[lets inject] byPropertyClass:[Settings class]] 67 | getterValue:settings]; 68 | 69 | // Inject value by property protocol 70 | [[[lets inject] byPropertyProtocol:[Analytics class]] 71 | getterValue:analytics]; 72 | 73 | // Inject getter by property class 74 | [[[lets inject] byPropertyClass:[NSMutableArray class]] 75 | getterBlock:DIImperativeGetterFromGetter(DIGetterMake(^id (id target, id *ivar) { 76 | if (*ivar == nil) { 77 | *ivar = [NSMutableArray array]; 78 | } 79 | return *ivar; 80 | }))]; 81 | }]; 82 | ``` 83 | 84 | You can inject value, getter and setter blocks. There are some functions to simplify getter (same for setters plus additional argument `value`) block definitions: 85 | - `DIGetterMake` with block arguments: target and \*ivar 86 | - `DIGetterIfIvarIsNil` with block arguments: target 87 | - `DIGetterWithOriginalMake` with block arguments: target, \*ivar and original getter pointer 88 | 89 | ## Lazies Injection 90 | 91 | 92 | 93 | Do you really like this boilerplate? 94 | 95 | ```objective-c 96 | @interface SomeClass : SomeSuperclass 97 | 98 | @property (nonatomic) NSMutableArray *items; 99 | 100 | @end 101 | 102 | @implementation SomeClass 103 | 104 | - (NSMutableArray *)items { 105 | if (_items == nil) { 106 | _items = [NSMutableArray array]; 107 | } 108 | return items; 109 | } 110 | 111 | @end 112 | ``` 113 | 114 | Just one keyword will do this for you: 115 | 116 | ```objective-c 117 | @interface SomeClass : SomeSuperclass 118 | 119 | @property (nonatomic) NSMutableArray *items; 120 | 121 | @end 122 | ``` 123 | 124 | Of course this will work for generic types: 125 | 126 | ```objective-c 127 | @property (nonatomic) NSMutableArray *items; 128 | @property (nonatomic) NSMutableDictionary *items; 129 | ``` 130 | 131 | This all will be injected after calling `injectLazy`: 132 | 133 | ```objective-c 134 | [DeluxeInjection imperative:^(DIImperative *lets) { 135 | ... 136 | [lets injectLazy]; 137 | ... 138 | }]; 139 | ``` 140 | 141 | ## Settings Injection 142 | 143 | Wanna achieve this behavior with less boilerplate code? 144 | 145 | ```objective-c 146 | @interface SomeClass : SomeSuperclass 147 | 148 | @property (nonatomic) NSString *username; 149 | 150 | @end 151 | 152 | @implementation SomeClass 153 | 154 | - (NSString *)username { 155 | return [[NSUserDefaults standardUserDefaults] stringForKey:@"username"]; 156 | } 157 | 158 | - (void)setUsername:(NSString *)username { 159 | return [[NSUserDefaults standardUserDefaults] setValue:username forKey:@"username"]; 160 | } 161 | 162 | @end 163 | ``` 164 | 165 | Just use `` or `` protocols on property declaration: 166 | 167 | ```objective-c 168 | @interface SomeClass : SomeSuperclass 169 | 170 | @property (nonatomic) NSString *username; 171 | 172 | @end 173 | ``` 174 | 175 | Injections are made by calling `injectDefaults`: 176 | 177 | ```objective-c 178 | [DeluxeInjection imperative:^(DIImperative *lets) { 179 | ... 180 | [lets injectDefaults]; 181 | ... 182 | }]; 183 | ``` 184 | 185 | If something can not be stored directly to NSUserDefault it can be archived, just use `DIDefaultsArchived` or `DIDefaultsArchivedSync` protocols. 186 | 187 | There are some extended versions of `injectDefaults` methods to provide key generator and use different `NSUserDefaults` instance: 188 | - `injectDefaultsWithKeyBlock:` 189 | - `injectDefaultsWithDefaultsBlock:` 190 | - `injectDefaultsWithKeyBlock:injectDefaultsWithKeyBlock:` 191 | 192 | ## Force injection 193 | 194 | 195 | 196 | **!!!Warning `DIForceInject` plugin can't be used in `DIImperative` manner and should be used in separate way.** 197 | 198 | You can force inject any property of any class: 199 | 200 | ```objective-c 201 | @interface TestClass : SomeSuperclass 202 | 203 | @property (nonatomic) Network *network; 204 | 205 | @end 206 | ``` 207 | 208 | Even without any protocol specification using `forceInject:` method: 209 | ```objective-c 210 | Network *network = [Network alloc] initWithSettings: ... ]; 211 | [DeluxeInjection forceInject:^id(Class targetClass, 212 | NSString *propertyName, 213 | Class propertyClass, 214 | NSSet *protocols) { 215 | 216 | if ([target isKindOfClass:[TestClass class]] && 217 | propertyClass == [Network class]) { 218 | 219 | return network; 220 | } 221 | return [DeluxeInjection doNotInject]; // Special value to skip injection 222 | }]; 223 | ``` 224 | 225 | Specified block will be called for all properties of all classes (exclude properties conforming any `DI***` protocol) and you should determine which value to inject in this property, or not inject at all. You are also able to use method `forceInjectBlock:` to return `DIGetter` block instead of value to provide injected getter. 226 | 227 | ## Performance and Testing 228 | 229 | Single time enumeration of 100.000 properties in 40.000 classes with injecting 150 properties tooks 0.082 sec on my `iPhone 6s` in `DEBUG` configuration. Performance will not decrease in future versions, it is one of first-class feature of the library to be super-performant. You can find some performance test and other tests in Example project. I am planning to add as many tests as possible to detect all possible problems. May be you wanna help me with tests? 230 | 231 | ## Installation 232 | 233 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 234 | 235 | DeluxeInjection is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile: 236 | 237 | ```ruby 238 | pod 'DeluxeInjection' 239 | ``` 240 | 241 | Or, if you’re using [Carthage](https://github.com/Carthage/Carthage), simply add DeluxeInjection to your `Cartfile`: 242 | 243 | ``` 244 | github "k06a/DeluxeInjection" 245 | ``` 246 | 247 | ## Author 248 | 249 | Anton Bukov 250 | k06aaa@gmail.com 251 | https://twitter.com/k06a 252 | 253 | ## License 254 | 255 | DeluxeInjection is available under the MIT license. See the LICENSE file for more info. 256 | 257 | ## Contribution 258 | 259 | – Wanna really help to project?
260 | – Help me to add more test and divide them by plugins. 261 | 262 | – Found any bugs?
263 | – Feel free to open issue or talk to me directly [@k06a](https://twitter.com/k06a)! 264 | 265 | Contribution workflow:
266 | 1. Fork repository
267 | 2. Create new branch from master
268 | 3. Commit to your newly created branch
269 | 4. Open Pull Request and we will talk :) 270 | -------------------------------------------------------------------------------- /Example/Pods/RuntimeRoutines/Pod/Classes/RuntimeRoutines.m: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeRoutines.m 3 | // MachineLearningWorks 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import "RuntimeRoutines.h" 21 | 22 | void RRClassEnumerateAllClasses(BOOL includeMetaClasses, void (^block)(Class klass)) { 23 | RRClassEnumerateSubclasses([NSObject class], includeMetaClasses, block); 24 | } 25 | 26 | void RRClassEnumerateSubclasses(Class parentclass, BOOL includeMetaClasses, void (^block)(Class klass)) { 27 | Class *classes = objc_copyClassList(NULL); 28 | for (Class *cursor = classes; classes && *cursor; cursor++) { 29 | // Filter only NSObject subclasses 30 | Class superclass = *cursor; 31 | while (superclass && superclass != parentclass) { 32 | superclass = class_getSuperclass(superclass); 33 | } 34 | if (!superclass) { 35 | continue; 36 | } 37 | 38 | block(*cursor); 39 | if (includeMetaClasses) { 40 | block(objc_getMetaClass(class_getName(*cursor))); 41 | } 42 | } 43 | 44 | free(classes); 45 | } 46 | 47 | void RRClassEnumerateMethods(Class klass, void (^block)(Method method)) { 48 | Method *methods = class_copyMethodList(klass, NULL); 49 | for (Method *cursor = methods; methods && *cursor; cursor++) { 50 | block(*cursor); 51 | } 52 | free(methods); 53 | } 54 | 55 | void RRClassEnumerateProperties(Class klass, void (^block)(objc_property_t property)) { 56 | objc_property_t *properties = class_copyPropertyList(klass, NULL); 57 | for (objc_property_t *cursor = properties; properties && *cursor; cursor++) { 58 | block(*cursor); 59 | } 60 | free(properties); 61 | } 62 | 63 | void RRClassEnumeratePropertiesWithSuperclassesProperties(Class klass, void (^block)(objc_property_t property)) { 64 | NSMutableSet *calledProperties = [NSMutableSet new]; 65 | 66 | for (Class currentKlass = klass; currentKlass; currentKlass = [currentKlass superclass]) { 67 | RRClassEnumerateProperties(currentKlass, ^(objc_property_t property) { 68 | NSString *name = [[NSString alloc] initWithUTF8String:property_getName(property)]; 69 | if ([calledProperties containsObject:name]) { 70 | return; 71 | } 72 | 73 | [calledProperties addObject:name]; 74 | block(property); 75 | }); 76 | } 77 | } 78 | 79 | void RRClassEnumerateIvars(Class klass, void (^block)(Ivar ivar)) { 80 | Ivar *ivars = class_copyIvarList(klass, NULL); 81 | for (Ivar *cursor = ivars; ivars && *cursor; cursor++) { 82 | block(*cursor); 83 | } 84 | free(ivars); 85 | } 86 | 87 | void RRClassEnumerateProtocols(Class klass, void (^block)(Protocol *protocol)) { 88 | __unsafe_unretained Protocol **protocols = class_copyProtocolList(klass, NULL); 89 | for (__unsafe_unretained Protocol **cursor = protocols; protocols && *cursor; cursor++) { 90 | block(*cursor); 91 | } 92 | free(protocols); 93 | } 94 | 95 | void RRClassEnumerateProtocolsWithSuperprotocols(Class klass, void (^block)(Protocol *protocol)) { 96 | NSMutableSet *protocols = [NSMutableSet set]; 97 | RRClassEnumerateProtocols(klass, ^(Protocol *protocol) { 98 | if (![protocols containsObject:protocol]) { 99 | block(protocol); 100 | [protocols addObject:protocol]; 101 | } 102 | 103 | RRProtocolEnumerateSuperprotocols(protocol, ^(Protocol *superprotocol) { 104 | if (![protocols containsObject:superprotocol]) { 105 | block(superprotocol); 106 | [protocols addObject:superprotocol]; 107 | } 108 | }); 109 | }); 110 | } 111 | 112 | objc_property_t RRClassGetPropertyByName(Class klass, NSString *propertyName) { 113 | return class_getProperty(klass, propertyName.UTF8String); 114 | } 115 | 116 | void RRProtocolEnumerateSuperprotocols(Protocol *protocol, void (^block)(Protocol *superprotocol)) { 117 | __unsafe_unretained Protocol **protocols = protocol_copyProtocolList(protocol, NULL); 118 | for (__unsafe_unretained Protocol **cursor = protocols; protocols && *cursor; cursor++) { 119 | block(*cursor); 120 | } 121 | free(protocols); 122 | } 123 | 124 | void RRProtocolEnumerateProperties(Protocol *protocol, BOOL required, BOOL instance, void (^block)(objc_property_t property)) { 125 | objc_property_t *properties = protocol_copyPropertyList(protocol, NULL); 126 | for (objc_property_t *cursor = properties; properties && *cursor; cursor++) { 127 | objc_property_t property = protocol_getProperty(protocol, property_getName(*cursor), required, instance); 128 | if (property) { 129 | block(property); 130 | } 131 | } 132 | free(properties); 133 | } 134 | 135 | NSString *RRPropertyGetAttribute(objc_property_t property, char *attrribute) { 136 | char *value = property_copyAttributeValue(property, attrribute); 137 | if (value) { 138 | return [[NSString alloc] initWithBytesNoCopy:value length:strlen(value) encoding:NSUTF8StringEncoding freeWhenDone:YES]; 139 | } 140 | return nil; 141 | } 142 | 143 | BOOL RRPropertyGetIsWeak(objc_property_t property) { 144 | return RRPropertyGetAttribute(property, "W") != nil; 145 | } 146 | 147 | SEL RRPropertyGetGetterIfExist(objc_property_t property) { 148 | char *value = property_copyAttributeValue(property, "G"); 149 | if (value) { 150 | SEL sel = NSSelectorFromString([NSString stringWithUTF8String:value]); 151 | free(value); 152 | return sel; 153 | } 154 | return nil; 155 | } 156 | 157 | SEL RRPropertyGetSetterIfExist(objc_property_t property) { 158 | char *value = property_copyAttributeValue(property, "S"); 159 | if (value) { 160 | SEL sel = NSSelectorFromString([NSString stringWithUTF8String:value]); 161 | free(value); 162 | return sel; 163 | } 164 | return nil; 165 | } 166 | 167 | SEL RRPropertyGetGetter(objc_property_t property) { 168 | return RRPropertyGetGetterIfExist(property) ?: NSSelectorFromString([NSString stringWithUTF8String:property_getName(property)]); 169 | } 170 | 171 | SEL RRPropertyGetSetter(objc_property_t property) { 172 | SEL sel = RRPropertyGetSetterIfExist(property); 173 | if (sel) { 174 | return sel; 175 | } 176 | NSString *str = [NSString stringWithUTF8String:property_getName(property)]; 177 | str = [NSString stringWithFormat:@"set%@%@:", [[str substringToIndex:1] uppercaseString], [str substringFromIndex:1]]; 178 | return NSSelectorFromString(str); 179 | } 180 | 181 | void RRPropertyGetClassAndProtocols(objc_property_t property, void (^block)(Class klass, NSSet *protocols)) { 182 | char *value = property_copyAttributeValue(property, "T"); 183 | NSString *type = [[NSString alloc] initWithBytesNoCopy:value length:(value ? strlen(value) : 0) encoding:NSUTF8StringEncoding freeWhenDone:YES]; 184 | 185 | if ([type rangeOfString:@"@\""].location == 0) { 186 | type = [type substringWithRange:NSMakeRange(2, type.length - 3)]; 187 | } 188 | 189 | Class klass = nil; 190 | NSUInteger location = [type rangeOfString:@"<"].location; 191 | if (location != 0 && location != NSNotFound) { 192 | klass = NSClassFromString([type substringToIndex:location]); 193 | type = [type substringFromIndex:location]; 194 | } else { 195 | klass = NSClassFromString(type); 196 | } 197 | 198 | NSMutableSet *protocols = [NSMutableSet set]; 199 | while ((location = [type rangeOfString:@">"].location) != NSNotFound) { 200 | NSString *protocolStr = [type substringWithRange:NSMakeRange(1, location - 1)]; 201 | Protocol *protocol = NSProtocolFromString(protocolStr); 202 | if (protocol) { 203 | [protocols addObject:protocol]; 204 | RRProtocolEnumerateSuperprotocols(protocol, ^(Protocol *superprotocol) { 205 | [protocols addObject:superprotocol]; 206 | }); 207 | } 208 | type = [type substringFromIndex:location + 1]; 209 | } 210 | 211 | block(klass, protocols); 212 | } 213 | 214 | objc_AssociationPolicy RRPropertyGetAssociationPolicy(objc_property_t property) { 215 | if (RRPropertyGetAttribute(property, "N") != nil) { 216 | if (RRPropertyGetAttribute(property, "W")) { 217 | return OBJC_ASSOCIATION_RETAIN; // Weaks are supported over boxing 218 | } 219 | if (RRPropertyGetAttribute(property, "C") != nil) { 220 | return OBJC_ASSOCIATION_COPY_NONATOMIC; 221 | } 222 | if (RRPropertyGetAttribute(property, "&") != nil) { 223 | return OBJC_ASSOCIATION_RETAIN_NONATOMIC; 224 | } 225 | } 226 | else { 227 | if (RRPropertyGetAttribute(property, "W")) { 228 | return OBJC_ASSOCIATION_RETAIN; // Weaks are supported over boxing 229 | } 230 | if (RRPropertyGetAttribute(property, "C") != nil) { 231 | return OBJC_ASSOCIATION_COPY; 232 | } 233 | if (RRPropertyGetAttribute(property, "&") != nil) { 234 | return OBJC_ASSOCIATION_RETAIN; 235 | } 236 | } 237 | return OBJC_ASSOCIATION_ASSIGN; 238 | } 239 | 240 | NSString *RRMethodGetReturnType(Method method) { 241 | char *returnType = method_copyReturnType(method); 242 | return [[NSString alloc] initWithBytesNoCopy:returnType length:(returnType ? strlen(returnType) : 0) encoding:NSUTF8StringEncoding freeWhenDone:YES]; 243 | } 244 | 245 | NSUInteger RRMethodGetArgumentsCount(Method method) { 246 | return method_getNumberOfArguments(method) - 2; 247 | } 248 | 249 | NSString *RRMethodGetArgumentType(Method method, NSUInteger index) { 250 | char *argumentType = method_copyArgumentType(method, (unsigned int)(index + 2)); 251 | return [[NSString alloc] initWithBytesNoCopy:argumentType length:(argumentType ? strlen(argumentType) : 0) encoding:NSUTF8StringEncoding freeWhenDone:YES]; 252 | } 253 | -------------------------------------------------------------------------------- /DeluxeInjection/Classes/DIInject.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIInject.m 3 | // DeluxeInjection 4 | // 5 | // Copyright (c) 2016 Anton Bukov 6 | // 7 | // Licensed under the MIT License (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://opensource.org/licenses/MIT 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // 19 | 20 | #import 21 | 22 | #import "DIDeluxeInjectionPlugin.h" 23 | #import "DIImperativePlugin.h" 24 | 25 | #import "DIInjectPlugin.h" 26 | 27 | // 28 | 29 | DIImperativeGetter DIImperativeGetterMake(DIImperativeGetter getter) { 30 | return getter; 31 | } 32 | 33 | DIImperativeSetter DIImperativeSetterMake(DIImperativeSetter setter) { 34 | return setter; 35 | } 36 | 37 | DIImperativeGetter DIImperativeGetterFromGetter(DIGetter di_getter) { 38 | return ^id _Nullable(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols, id target, id *ivar, DIOriginalGetter originalGetter) { 39 | return di_getter(target, getter, ivar, originalGetter); 40 | }; 41 | } 42 | 43 | DIImperativeSetter DIImperativeSetterFromSetter(DISetter di_setter) { 44 | return ^(Class targetClass, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols, id target, id *ivar, id value, DIOriginalSetter originalSetter) { 45 | return di_setter(target, setter, ivar, value, originalSetter); 46 | }; 47 | } 48 | 49 | // 50 | 51 | @implementation DeluxeInjection (DIInject) 52 | 53 | + (void)load { 54 | [DIImperative registerPluginProtocol:@protocol(DIInject)]; 55 | } 56 | 57 | + (void)inject:(DIPropertyGetter)block { 58 | [self inject:^NSArray * (Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 59 | id value = block(targetClass, getter, propertyName, propertyClass, propertyProtocols); 60 | if (value == [DeluxeInjection doNotInject]) { 61 | return nil; 62 | } 63 | 64 | objc_property_t property = RRClassGetPropertyByName(targetClass, propertyName); 65 | if (RRPropertyGetIsWeak(property)) { 66 | __weak id weakValue = value; 67 | return @[DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 68 | return weakValue; 69 | }), [DeluxeInjection doNotInject]]; 70 | } else { 71 | return @[DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 72 | return value; 73 | }), [DeluxeInjection doNotInject]]; 74 | } 75 | } conformingProtocols:@[@protocol(DIInject)]]; 76 | } 77 | 78 | + (void)injectBlock:(DIPropertyGetterBlock)block { 79 | [self inject:^NSArray *(Class targetClass, SEL getter, SEL setter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 80 | return @[(id)block(targetClass, getter, propertyName, propertyClass, propertyProtocols) ?: (id)[DeluxeInjection doNotInject], [DeluxeInjection doNotInject]]; 81 | } conformingProtocols:@[@protocol(DIInject)]]; 82 | } 83 | 84 | + (void)reject:(DIPropertyFilter)block { 85 | [self reject:block conformingProtocols:@[@protocol(DIInject)]]; 86 | } 87 | 88 | + (void)rejectAll { 89 | [self reject:^BOOL(Class targetClass, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 90 | return YES; 91 | } conformingProtocols:@[@protocol(DIInject)]]; 92 | } 93 | 94 | @end 95 | 96 | // 97 | 98 | @interface DIImperativeInjector () 99 | 100 | @property (assign, nonatomic) BOOL shouldSkipDIInjectProtocolFilter; 101 | @property (assign, nonatomic) BOOL resolved; 102 | @property (assign, nonatomic) BOOL injector; 103 | @property (weak, nonatomic) DIImperative *lets; 104 | @property (assign, nonatomic) Class savedPropertyClass; 105 | @property (assign, nonatomic) Protocol *savedPropertyProtocol; 106 | @property (copy, nonatomic) DIImperativeGetter savedGetterBlock; 107 | @property (copy, nonatomic) DIImperativeSetter savedSetterBlock; 108 | @property (copy, nonatomic) DIPropertyFilterBlock savedFilterBlock; 109 | 110 | @end 111 | 112 | @implementation DIImperativeInjector 113 | 114 | - (instancetype)skipDIInjectProtocolFilter { 115 | self.shouldSkipDIInjectProtocolFilter = YES; 116 | return self; 117 | } 118 | 119 | - (instancetype)byPropertyClass:(Class)klass { 120 | NSAssert(self.savedPropertyClass == nil, @"You should call byPropertyClass: only once"); 121 | NSAssert(self.savedPropertyProtocol == nil, @"You should not call byPropertyClass: after calling byPropertyProtocol:"); 122 | self.savedPropertyClass = klass; 123 | return self; 124 | } 125 | 126 | - (instancetype)byPropertyProtocol:(Protocol *)protocol { 127 | NSAssert(self.savedPropertyProtocol == nil, @"You should call byPropertyProtocol: only once"); 128 | NSAssert(self.savedPropertyClass == nil, @"You should not call byPropertyProtocol: after calling byPropertyClass:"); 129 | self.savedPropertyProtocol = protocol; 130 | return self; 131 | } 132 | 133 | - (instancetype)getterValue:(id)getterValue { 134 | return [self getterBlock:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols, id target, id *ivar, DIOriginalGetter originalGetter) { 135 | if (*ivar == nil) { 136 | *ivar = getterValue; 137 | } 138 | return *ivar; 139 | }]; 140 | } 141 | 142 | - (instancetype)getterValueLazy:(id(^)(void))lazyBlock { 143 | __block id(^lazyBlockCopy)(void) = [lazyBlock copy]; 144 | __block id lazyValue = nil; 145 | __block dispatch_once_t onceToken = 0; 146 | [self getterBlock:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols, id target, id *ivar, DIOriginalGetter originalGetter) { 147 | if (*ivar == nil) { 148 | dispatch_once(&onceToken, ^{ 149 | lazyValue = lazyBlockCopy(); 150 | lazyBlockCopy = nil; 151 | }); 152 | *ivar = lazyValue; 153 | } 154 | return *ivar; 155 | }]; 156 | return self; 157 | } 158 | 159 | - (instancetype)getterValueLazyByClass:(Class)lazyClass { 160 | return [self getterValueLazy:^id { 161 | return [[lazyClass alloc] init]; 162 | }]; 163 | } 164 | 165 | - (instancetype)getterBlock:(DIImperativeGetter)getterBlock { 166 | NSAssert(self.savedGetterBlock == nil, @"You should call getterValue: or getterBlock: only once"); 167 | self.savedGetterBlock = getterBlock; 168 | return self; 169 | } 170 | 171 | - (instancetype)setterBlock:(DIImperativeSetter)setterBlock { 172 | NSAssert(self.savedSetterBlock == nil, @"You should call setterBlock: only once"); 173 | self.savedSetterBlock = setterBlock; 174 | return self; 175 | } 176 | 177 | - (instancetype)filterBlock:(DIPropertyFilterBlock)filterBlock { 178 | NSAssert(self.savedFilterBlock == nil, @"You should call filterContainerClass: or filterBlock: only once"); 179 | self.savedFilterBlock = filterBlock; 180 | return self; 181 | } 182 | 183 | - (instancetype)filterContainerClass:(Class)filterContainerClass { 184 | return [self filterBlock:^BOOL(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 185 | return [targetClass isSubclassOfClass:filterContainerClass]; 186 | }]; 187 | } 188 | 189 | - (void)resolve { 190 | if (self.resolved) { 191 | return; 192 | } 193 | 194 | if (self.injector) { 195 | NSAssert(self.savedGetterBlock || self.savedSetterBlock, @"You should call getterValue: or getterBlock: or setterBlock:"); 196 | } else { 197 | NSAssert(self.savedGetterBlock == nil && self.savedSetterBlock == nil, @"You should NOT call getterValue: or getterBlock: or setterBlock: when trying to reject"); 198 | } 199 | 200 | NSValue *key = [NSValue valueWithPointer:(__bridge void *)(self.savedPropertyProtocol)]; 201 | NSArray *holders = self.savedPropertyClass ? self.lets.byClass[self.savedPropertyClass] : self.lets.byProtocol[key]; 202 | 203 | for (DIPropertyHolder *holder in holders) { 204 | if (!self.shouldSkipDIInjectProtocolFilter && ![holder.propertyProtocols containsObject:@protocol(DIInject)]) { 205 | continue; 206 | } 207 | if (self.savedFilterBlock && !self.savedFilterBlock(holder.targetClass, holder.getter, holder.propertyName, holder.propertyClass, holder.propertyProtocols)) { 208 | continue; 209 | } 210 | if (self.injector) { 211 | if (self.savedGetterBlock || self.savedSetterBlock) { 212 | if (self.savedGetterBlock && holder.wasInjectedGetter) { 213 | NSLog(@"Warning: Reinjecting property getter [%@ %@]", holder.targetClass, NSStringFromSelector(holder.getter)); 214 | } 215 | if (self.savedSetterBlock && holder.wasInjectedSetter) { 216 | NSLog(@"Warning: Reinjecting property setter [%@ %@]", holder.targetClass, NSStringFromSelector(holder.setter)); 217 | } 218 | DIImperativeGetter savedGetterBlock = [self.savedGetterBlock copy]; 219 | DIImperativeSetter savedSetterBlock = [self.savedSetterBlock copy]; 220 | objc_property_t property = RRClassGetPropertyByName(holder.targetClass, holder.propertyName); 221 | [DeluxeInjection inject:holder.targetClass property:property getterBlock:!savedGetterBlock ? nil : ^id(id target, SEL cmd, id *ivar, DIOriginalGetter originalGetter) { 222 | return savedGetterBlock(holder.targetClass, holder.getter, holder.propertyName, holder.propertyClass, holder.propertyProtocols, target, ivar, originalGetter); 223 | } setterBlock:!savedSetterBlock ? nil : ^void(id target, SEL cmd, id *ivar, id value, DIOriginalSetter originalSetter) { 224 | return savedSetterBlock(holder.targetClass, holder.setter, holder.propertyName, holder.propertyClass, holder.propertyProtocols, target, ivar, value, originalSetter); 225 | }]; 226 | holder.wasInjectedGetter = (self.savedGetterBlock != nil); 227 | holder.wasInjectedSetter = (self.savedSetterBlock != nil); 228 | } 229 | } 230 | else { 231 | objc_property_t property = class_getProperty(holder.targetClass, holder.propertyName.UTF8String); 232 | [DeluxeInjection reject:holder.targetClass property:property]; 233 | holder.wasInjectedGetter = NO; 234 | holder.wasInjectedSetter = NO; 235 | } 236 | } 237 | 238 | self.resolved = YES; 239 | } 240 | 241 | - (void)dealloc { 242 | [self resolve]; 243 | } 244 | 245 | @end 246 | 247 | // 248 | 249 | @implementation DIImperative (DIInject) 250 | 251 | - (DIImperativeInjector *)inject { 252 | DIImperativeInjector *injector = [[DIImperativeInjector alloc] init]; 253 | injector.lets = self; 254 | injector.injector = YES; 255 | return injector; 256 | } 257 | 258 | - (DIImperativeInjector *)reject { 259 | DIImperativeInjector *rejector = [[DIImperativeInjector alloc] init]; 260 | rejector.lets = self; 261 | rejector.injector = NO; 262 | return rejector; 263 | } 264 | 265 | - (void)rejectAll { 266 | [[self reject] byPropertyProtocol:@protocol(DIInject)]; 267 | } 268 | 269 | @end 270 | -------------------------------------------------------------------------------- /Example/Tests/DIInjectTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // DIInjectTests.m 3 | // DeluxeInjection 4 | // 5 | // Created by Антон Буков on 07.07.16. 6 | // Copyright © 2016 Anton Bukov. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AbstractTests.h" 12 | 13 | // 14 | 15 | @protocol DIInjectTests_Protocol 16 | 17 | @end 18 | 19 | @interface DIInjectTests_Class : NSObject 20 | 21 | @property (strong, nonatomic) NSMutableArray *classObject; 22 | @property (strong, nonatomic) id protocolObject; 23 | 24 | @property (strong, nonatomic) NSMutableArray *dynamicClassObject; 25 | @property (strong, nonatomic) id dynamicProtocolObject; 26 | @property (weak, nonatomic) NSString *dynamicWeakObject; 27 | 28 | @end 29 | 30 | @implementation DIInjectTests_Class 31 | 32 | @dynamic dynamicClassObject; 33 | @dynamic dynamicProtocolObject; 34 | @dynamic dynamicWeakObject; 35 | 36 | @end 37 | 38 | // 39 | 40 | @interface NSObject (DIInjectTests_Category) 41 | 42 | @property (strong, nonatomic) NSArray *DIInjectTests_dynamicCategoryProperty; 43 | 44 | @end 45 | 46 | @implementation NSObject (DIInjectTests_Category) 47 | 48 | @dynamic DIInjectTests_dynamicCategoryProperty; 49 | 50 | @end 51 | 52 | // 53 | 54 | @interface DIInjectTests : AbstractTests 55 | 56 | @end 57 | 58 | @implementation DIInjectTests 59 | 60 | - (void)tearDown { 61 | [DeluxeInjection rejectAll]; 62 | 63 | [super tearDown]; 64 | } 65 | 66 | - (void)testInjectByClass { 67 | NSArray *answer1 = @[ @1, @2, @3 ]; 68 | NSArray *answer2 = @[ @4, @5, @6 ]; 69 | 70 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(classObject)]); 71 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(protocolObject)]); 72 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setClassObject:)]); 73 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setProtocolObject:)]); 74 | 75 | [DeluxeInjection inject:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 76 | if (targetClass == [DIInjectTests_Class class] && propertyClass == [NSMutableArray class]) { 77 | return [answer1 mutableCopy]; 78 | } 79 | return [DeluxeInjection doNotInject]; 80 | }]; 81 | 82 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(classObject)]); 83 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(protocolObject)]); 84 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setClassObject:)]); 85 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setProtocolObject:)]); 86 | 87 | DIInjectTests_Class *test = [[DIInjectTests_Class alloc] init]; 88 | 89 | XCTAssertEqualObjects(test.classObject, answer1); 90 | test.classObject = [answer2 mutableCopy]; 91 | XCTAssertEqualObjects(test.classObject, answer2); 92 | test.classObject = nil; 93 | XCTAssertEqualObjects(test.classObject, answer1); 94 | } 95 | 96 | - (void)testInjectByProtocol { 97 | id answer1 = @777; 98 | id answer2 = @666; 99 | 100 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(classObject)]); 101 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(protocolObject)]); 102 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setClassObject:)]); 103 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setProtocolObject:)]); 104 | 105 | [DeluxeInjection inject:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 106 | if (targetClass == [DIInjectTests_Class class] && [propertyProtocols containsObject:@protocol(DIInjectTests_Protocol)]) { 107 | return answer1; 108 | } 109 | return [DeluxeInjection doNotInject]; 110 | }]; 111 | 112 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(classObject)]); 113 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(protocolObject)]); 114 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setClassObject:)]); 115 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setProtocolObject:)]); 116 | 117 | DIInjectTests_Class *test = [[DIInjectTests_Class alloc] init]; 118 | 119 | XCTAssertEqualObjects(test.protocolObject, answer1); 120 | test.protocolObject = answer2; 121 | XCTAssertEqualObjects(test.protocolObject, answer2); 122 | test.protocolObject = nil; 123 | XCTAssertEqualObjects(test.protocolObject, answer1); 124 | } 125 | 126 | - (void)testInjectBlock { 127 | NSArray *answer1 = @[ @1, @2, @3 ]; 128 | NSArray *answer2 = @[ @4, @5, @6 ]; 129 | 130 | [DeluxeInjection injectBlock:^DIGetter(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 131 | if (targetClass == [DIInjectTests_Class class] && propertyClass == [NSMutableArray class]) { 132 | return DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 133 | return [answer1 mutableCopy]; 134 | }); 135 | } 136 | return nil; 137 | }]; 138 | 139 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(classObject)]); 140 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(protocolObject)]); 141 | 142 | DIInjectTests_Class *test = [[DIInjectTests_Class alloc] init]; 143 | 144 | XCTAssertEqualObjects(test.classObject, answer1); 145 | test.classObject = [answer2 mutableCopy]; 146 | XCTAssertEqualObjects(test.classObject, answer2); 147 | test.classObject = nil; 148 | XCTAssertEqualObjects(test.classObject, answer1); 149 | } 150 | 151 | - (void)testRejectAll { 152 | NSArray *answer1 = @[ @1, @2, @3 ]; 153 | NSArray *answer2 = @[ @4, @5, @6 ]; 154 | NSString *answer3 = @"hello"; 155 | NSString *answer4 = @"bye"; 156 | 157 | [DeluxeInjection injectBlock:^DIGetter(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 158 | if (targetClass == [DIInjectTests_Class class] && propertyClass == [NSString class]) { 159 | return DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 160 | return answer3; 161 | }); 162 | } 163 | if (targetClass == [DIInjectTests_Class class] && propertyClass == [NSMutableArray class]) { 164 | return DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 165 | return [answer1 mutableCopy]; 166 | }); 167 | } 168 | if (targetClass == [DIInjectTests_Class class] && [propertyProtocols containsObject:@protocol(DIInjectTests_Protocol)]) { 169 | return DIGetterIfIvarIsNil(^id(id target, SEL cmd) { 170 | return [answer1 mutableCopy]; 171 | }); 172 | } 173 | return nil; 174 | }]; 175 | 176 | DIInjectTests_Class *test = [[DIInjectTests_Class alloc] init]; 177 | 178 | XCTAssertEqualObjects(test.dynamicClassObject, answer1); 179 | XCTAssertEqualObjects(test.dynamicProtocolObject, answer1); 180 | XCTAssertEqualObjects(test.dynamicWeakObject, answer3); 181 | test.dynamicClassObject = [answer2 mutableCopy]; 182 | test.dynamicProtocolObject = [answer2 mutableCopy]; 183 | test.dynamicWeakObject = answer4; 184 | XCTAssertEqualObjects(test.dynamicClassObject, answer2); 185 | XCTAssertEqualObjects(test.dynamicProtocolObject, answer2); 186 | XCTAssertEqualObjects(test.dynamicWeakObject, answer4); 187 | test.dynamicClassObject = nil; 188 | test.dynamicProtocolObject = nil; 189 | test.dynamicWeakObject = nil; 190 | XCTAssertEqualObjects(test.dynamicClassObject, answer1); 191 | XCTAssertEqualObjects(test.dynamicProtocolObject, answer1); 192 | XCTAssertEqualObjects(test.dynamicWeakObject, answer3); 193 | 194 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(classObject)]); 195 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(protocolObject)]); 196 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(dynamicClassObject)]); 197 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(dynamicProtocolObject)]); 198 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(dynamicWeakObject)]); 199 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setDynamicClassObject:)]); 200 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setDynamicProtocolObject:)]); 201 | XCTAssertTrue([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setDynamicWeakObject:)]); 202 | 203 | [DeluxeInjection rejectAll]; 204 | 205 | XCTAssertThrows(test.dynamicClassObject); 206 | XCTAssertThrows(test.dynamicProtocolObject); 207 | XCTAssertThrows(test.dynamicWeakObject); 208 | XCTAssertThrows(test.dynamicClassObject = nil); 209 | XCTAssertThrows(test.dynamicProtocolObject = nil); 210 | XCTAssertThrows(test.dynamicWeakObject = nil); 211 | 212 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(classObject)]); 213 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(protocolObject)]); 214 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(dynamicClassObject)]); 215 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(dynamicProtocolObject)]); 216 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(dynamicWeakObject)]); 217 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setDynamicClassObject:)]); 218 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setDynamicProtocolObject:)]); 219 | XCTAssertFalse([DeluxeInjection checkInjected:[DIInjectTests_Class class] selector:@selector(setDynamicWeakObject:)]); 220 | } 221 | 222 | - (void)testInjectDynamic { 223 | id answer1 = @777; 224 | id answer2 = @666; 225 | 226 | [DeluxeInjection inject:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *protocols) { 227 | if (targetClass == [DIInjectTests_Class class] && [protocols containsObject:@protocol(DIInjectTests_Protocol)]) { 228 | return answer1; 229 | } 230 | return [DeluxeInjection doNotInject]; 231 | }]; 232 | 233 | DIInjectTests_Class *test = [[DIInjectTests_Class alloc] init]; 234 | XCTAssertEqualObjects(test.dynamicProtocolObject, answer1); 235 | test.dynamicProtocolObject = answer2; 236 | XCTAssertEqualObjects(test.dynamicProtocolObject, answer2); 237 | test.dynamicProtocolObject = nil; 238 | XCTAssertEqualObjects(test.dynamicProtocolObject, answer1); 239 | } 240 | 241 | - (void)testDynamicWeak { 242 | __weak id weakAnswer = nil; 243 | DIInjectTests_Class *test = [[DIInjectTests_Class alloc] init]; 244 | @autoreleasepool { 245 | id answer1 = [@[ @1, @2, @3, @4, @5 ] mutableCopy]; 246 | 247 | [DeluxeInjection inject:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *protocols) { 248 | if (targetClass == [DIInjectTests_Class class] && [propertyName isEqualToString:NSStringFromSelector(@selector(dynamicWeakObject))]) { 249 | return answer1; 250 | } 251 | return [DeluxeInjection doNotInject]; 252 | }]; 253 | 254 | weakAnswer = answer1; 255 | test.dynamicWeakObject = answer1; 256 | XCTAssertEqualObjects(weakAnswer, answer1); 257 | XCTAssertEqualObjects(test.dynamicWeakObject, answer1); 258 | } 259 | XCTAssertNil(weakAnswer); 260 | XCTAssertNil(test.dynamicWeakObject); 261 | } 262 | 263 | - (void)testInjectToCategory { 264 | NSArray *answer1 = @[ @1, @2, @3 ]; 265 | NSArray *answer2 = @[ @4, @5, @6 ]; 266 | 267 | [DeluxeInjection inject:^id(Class targetClass, SEL getter, NSString *propertyName, Class propertyClass, NSSet *propertyProtocols) { 268 | if ([propertyName isEqualToString:NSStringFromSelector(@selector(DIInjectTests_dynamicCategoryProperty))]) { 269 | return answer1; 270 | } 271 | return [DeluxeInjection doNotInject]; 272 | }]; 273 | 274 | NSObject *test = [[NSObject alloc] init]; 275 | 276 | XCTAssertEqualObjects(test.DIInjectTests_dynamicCategoryProperty, answer1); 277 | test.DIInjectTests_dynamicCategoryProperty = answer2; 278 | XCTAssertEqualObjects(test.DIInjectTests_dynamicCategoryProperty, answer2); 279 | test.DIInjectTests_dynamicCategoryProperty = nil; 280 | XCTAssertEqualObjects(test.DIInjectTests_dynamicCategoryProperty, answer1); 281 | } 282 | 283 | @end 284 | --------------------------------------------------------------------------------