├── .gitignore ├── .swift-version ├── CTMediator.podspec ├── CTMediator.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── casa.xcuserdatad │ └── xcschemes │ ├── CTMediator.xcscheme │ └── xcschememanagement.plist ├── CTMediator ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── image.imageset │ │ ├── Contents.json │ │ └── image.png │ └── noImage.imageset │ │ ├── Contents.json │ │ └── noImage.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── CTMediator │ ├── CTMediator+HandyTools.h │ ├── CTMediator+HandyTools.m │ ├── CTMediator.h │ └── CTMediator.m ├── Categories │ └── ModuleA │ │ ├── CTMediator+CTMediatorModuleAActions.h │ │ └── CTMediator+CTMediatorModuleAActions.m ├── DemoModule │ ├── Actions │ │ ├── Target_A.h │ │ └── Target_A.m │ ├── DemoModuleADetailViewController.h │ └── DemoModuleADetailViewController.m ├── Info.plist ├── TableViewController │ ├── TableViewController.h │ └── TableViewController.m ├── ViewController.h ├── ViewController.m └── main.m ├── CTMediatorUITests ├── CTMediatorUITests.m └── Info.plist ├── FILE_LICENSE ├── Podfile ├── readme.md └── upload.sh /.gitignore: -------------------------------------------------------------------------------- 1 | CTMediator.xcworkspace/ 2 | Podfile.lock 3 | Pods/ 4 | CTMediator.xcodeproj/project.xcworkspace/xcuserdata/ 5 | .DS_Store 6 | *.swp 7 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /CTMediator.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint CTMediator.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | 11 | # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 12 | # 13 | # These will help people to find your library, and whilst it 14 | # can feel like a chore to fill in it's definitely to your advantage. The 15 | # summary should be tweet-length, and the description more in depth. 16 | # 17 | 18 | s.name = "CTMediator" 19 | s.version = "48" 20 | s.summary = "CTMediator." 21 | 22 | # This description is used to generate tags and improve search results. 23 | # * Think: What does it do? Why did you write it? What is the focus? 24 | # * Try to keep it short, snappy and to the point. 25 | # * Write the description between the DESC delimiters below. 26 | # * Finally, don't worry about the indent, CocoaPods strips it! 27 | s.description = "this is CTMediator" 28 | s.homepage = "https://github.com/casatwy/CTMediator" 29 | # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" 30 | 31 | 32 | # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 33 | # 34 | # Licensing your code is important. See http://choosealicense.com for more info. 35 | # CocoaPods will detect a license file if there is a named LICENSE* 36 | # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. 37 | # 38 | 39 | # s.license = "MIT (example)" 40 | s.license = { :type => "MIT", :file => "FILE_LICENSE" } 41 | 42 | 43 | # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 44 | # 45 | # Specify the authors of the library, with email addresses. Email addresses 46 | # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also 47 | # accepts just a name if you'd rather not provide an email address. 48 | # 49 | # Specify a social_media_url where others can refer to, for example a twitter 50 | # profile URL. 51 | # 52 | 53 | s.author = { "Casa Taloyum" => "casatwy@msn.com" } 54 | # Or just: s.author = "Casa Taloyum" 55 | # s.authors = { "Casa Taloyum" => "casatwy@msn.com" } 56 | # s.social_media_url = "http://twitter.com/Casa Taloyum" 57 | 58 | # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 59 | # 60 | # If this Pod runs only on iOS or OS X, then specify the platform and 61 | # the deployment target. You can optionally include the target after the platform. 62 | # 63 | 64 | # s.platform = :ios 65 | # s.platform = :ios, "7.0" 66 | 67 | # When using multiple platforms 68 | s.ios.deployment_target = "7.0" 69 | s.osx.deployment_target = "10.12" 70 | # s.watchos.deployment_target = "2.0" 71 | # s.tvos.deployment_target = "9.0" 72 | 73 | 74 | # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 75 | # 76 | # Specify the location from where the source should be retrieved. 77 | # Supports git, hg, bzr, svn and HTTP. 78 | # 79 | 80 | s.source = { :git => "https://github.com/casatwy/CTMediator.git", :tag => s.version.to_s } 81 | 82 | 83 | # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 84 | # 85 | # CocoaPods is smart about how it includes source code. For source files 86 | # giving a folder will include any swift, h, m, mm, c & cpp files. 87 | # For header files it will include any header in the folder. 88 | # Not including the public_header_files will make all headers public. 89 | # 90 | 91 | s.source_files = "CTMediator/CTMediator/**/*.{h,m}" 92 | # s.exclude_files = "Classes/Exclude" 93 | 94 | # s.public_header_files = "Classes/**/*.h" 95 | 96 | 97 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 98 | # 99 | # A list of resources included with the Pod. These are copied into the 100 | # target bundle with a build phase script. Anything else will be cleaned. 101 | # You can preserve files from being cleaned, please don't preserve 102 | # non-essential files like tests, examples and documentation. 103 | # 104 | 105 | # s.resource = "icon.png" 106 | # s.resources = "Resources/*.png" 107 | 108 | # s.preserve_paths = "FilesToSave", "MoreFilesToSave" 109 | 110 | 111 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 112 | # 113 | # Link your library with frameworks, or libraries. Libraries do not include 114 | # the lib prefix of their name. 115 | # 116 | 117 | # s.framework = "UIKit" 118 | s.frameworks = "Foundation", "CoreGraphics" 119 | 120 | # s.library = "iconv" 121 | # s.libraries = "iconv", "xml2" 122 | 123 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 124 | # 125 | # If your library depends on compiler flags you can set them in the xcconfig hash 126 | # where they will only apply to your library. If you depend on other Podspecs 127 | # you can include multiple dependencies to ensure it works. 128 | 129 | # s.requires_arc = true 130 | 131 | # s.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 132 | # s.dependency "JSONKit", "~> 1.4" 133 | # s.dependency "CTHandyCategories" 134 | 135 | end 136 | -------------------------------------------------------------------------------- /CTMediator.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4A5CE4FF1C9531C9006AEDB9 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5CE4FE1C9531C9006AEDB9 /* main.m */; }; 11 | 4A5CE5021C9531C9006AEDB9 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5CE5011C9531C9006AEDB9 /* AppDelegate.m */; }; 12 | 4A5CE5051C9531C9006AEDB9 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5CE5041C9531C9006AEDB9 /* ViewController.m */; }; 13 | 4A5CE5081C9531C9006AEDB9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4A5CE5061C9531C9006AEDB9 /* Main.storyboard */; }; 14 | 4A5CE50A1C9531C9006AEDB9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4A5CE5091C9531C9006AEDB9 /* Assets.xcassets */; }; 15 | 4A5CE50D1C9531C9006AEDB9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4A5CE50B1C9531C9006AEDB9 /* LaunchScreen.storyboard */; }; 16 | 4A5CE51A1C95322F006AEDB9 /* CTMediator.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5CE5191C95322F006AEDB9 /* CTMediator.m */; }; 17 | 4A5CE5251C953D7D006AEDB9 /* DemoModuleADetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5CE5241C953D7D006AEDB9 /* DemoModuleADetailViewController.m */; }; 18 | 4A5CE52B1C9547B2006AEDB9 /* Target_A.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A5CE52A1C9547B2006AEDB9 /* Target_A.m */; }; 19 | 4A85F5541DA92AFC00DB882C /* CTMediator+CTMediatorModuleAActions.m in Sources */ = {isa = PBXBuildFile; fileRef = 4A85F5531DA92AFC00DB882C /* CTMediator+CTMediatorModuleAActions.m */; }; 20 | 4AC176591DE163B600566741 /* CTMediatorUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AC176581DE163B600566741 /* CTMediatorUITests.m */; }; 21 | 4AD2E20524174B1900F8B53A /* CTMediator+HandyTools.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AD2E20424174B1900F8B53A /* CTMediator+HandyTools.m */; }; 22 | 4AED66AE1DB895D900C13EDC /* TableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4AED66AD1DB895D900C13EDC /* TableViewController.m */; }; 23 | 6A1C7FA419878A7AFA10DB7C /* libPods-CTMediator.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CDD4389F5AEA1D8F6AA0C9F /* libPods-CTMediator.a */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXContainerItemProxy section */ 27 | 4AC1765B1DE163B600566741 /* PBXContainerItemProxy */ = { 28 | isa = PBXContainerItemProxy; 29 | containerPortal = 4A5CE4F21C9531C9006AEDB9 /* Project object */; 30 | proxyType = 1; 31 | remoteGlobalIDString = 4A5CE4F91C9531C9006AEDB9; 32 | remoteInfo = CTMediator; 33 | }; 34 | /* End PBXContainerItemProxy section */ 35 | 36 | /* Begin PBXFileReference section */ 37 | 1CDD4389F5AEA1D8F6AA0C9F /* libPods-CTMediator.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CTMediator.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 2D583B83854D11618B8F5E65 /* Pods-CTMediator.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTMediator.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CTMediator/Pods-CTMediator.debug.xcconfig"; sourceTree = ""; }; 39 | 4A5CE4FA1C9531C9006AEDB9 /* CTMediator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CTMediator.app; sourceTree = BUILT_PRODUCTS_DIR; }; 40 | 4A5CE4FE1C9531C9006AEDB9 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 41 | 4A5CE5001C9531C9006AEDB9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 42 | 4A5CE5011C9531C9006AEDB9 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 43 | 4A5CE5031C9531C9006AEDB9 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 44 | 4A5CE5041C9531C9006AEDB9 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 45 | 4A5CE5071C9531C9006AEDB9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 46 | 4A5CE5091C9531C9006AEDB9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 47 | 4A5CE50C1C9531C9006AEDB9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 48 | 4A5CE50E1C9531C9006AEDB9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 49 | 4A5CE5181C95322F006AEDB9 /* CTMediator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTMediator.h; sourceTree = ""; }; 50 | 4A5CE5191C95322F006AEDB9 /* CTMediator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTMediator.m; sourceTree = ""; }; 51 | 4A5CE5231C953D7D006AEDB9 /* DemoModuleADetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoModuleADetailViewController.h; sourceTree = ""; }; 52 | 4A5CE5241C953D7D006AEDB9 /* DemoModuleADetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoModuleADetailViewController.m; sourceTree = ""; }; 53 | 4A5CE5291C9547B2006AEDB9 /* Target_A.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Target_A.h; sourceTree = ""; }; 54 | 4A5CE52A1C9547B2006AEDB9 /* Target_A.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Target_A.m; sourceTree = ""; }; 55 | 4A85F5521DA92AFC00DB882C /* CTMediator+CTMediatorModuleAActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CTMediator+CTMediatorModuleAActions.h"; sourceTree = ""; }; 56 | 4A85F5531DA92AFC00DB882C /* CTMediator+CTMediatorModuleAActions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "CTMediator+CTMediatorModuleAActions.m"; sourceTree = ""; }; 57 | 4AC176561DE163B600566741 /* CTMediatorUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CTMediatorUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 4AC176581DE163B600566741 /* CTMediatorUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTMediatorUITests.m; sourceTree = ""; }; 59 | 4AC1765A1DE163B600566741 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 60 | 4AD2E20324174B1900F8B53A /* CTMediator+HandyTools.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CTMediator+HandyTools.h"; sourceTree = ""; }; 61 | 4AD2E20424174B1900F8B53A /* CTMediator+HandyTools.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CTMediator+HandyTools.m"; sourceTree = ""; }; 62 | 4AED66AC1DB895D900C13EDC /* TableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TableViewController.h; sourceTree = ""; }; 63 | 4AED66AD1DB895D900C13EDC /* TableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TableViewController.m; sourceTree = ""; }; 64 | E9C114EF1101F17773DD4798 /* Pods-CTMediator.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CTMediator.release.xcconfig"; path = "Pods/Target Support Files/Pods-CTMediator/Pods-CTMediator.release.xcconfig"; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 4A5CE4F71C9531C9006AEDB9 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | 6A1C7FA419878A7AFA10DB7C /* libPods-CTMediator.a in Frameworks */, 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 4AC176531DE163B600566741 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | 4A5CE4F11C9531C9006AEDB9 = { 87 | isa = PBXGroup; 88 | children = ( 89 | 4A5CE4FC1C9531C9006AEDB9 /* CTMediator */, 90 | 4AC176571DE163B600566741 /* CTMediatorUITests */, 91 | 4A5CE4FB1C9531C9006AEDB9 /* Products */, 92 | B79600ED5E76116124B15CEE /* Pods */, 93 | 9449B9483205DCF29E913981 /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 4A5CE4FB1C9531C9006AEDB9 /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 4A5CE4FA1C9531C9006AEDB9 /* CTMediator.app */, 101 | 4AC176561DE163B600566741 /* CTMediatorUITests.xctest */, 102 | ); 103 | name = Products; 104 | sourceTree = ""; 105 | }; 106 | 4A5CE4FC1C9531C9006AEDB9 /* CTMediator */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 4AED66AB1DB895C600C13EDC /* TableViewController */, 110 | 4A85F5501DA92AFC00DB882C /* Categories(实际应用中,这是一个单独的repo,所用需要调度其他模块的人,只需要依赖这个repo。这个repo由target-action维护者维护) */, 111 | 4A5CE5141C9531E9006AEDB9 /* CTMediator(这也是单独的repo,完整的中间件就这100行代码) */, 112 | 4A5CE5211C953D5A006AEDB9 /* DemoModule(target-action所在的模块,也就是提供服务的模块,这也是单独的repo,但无需被其他人依赖,其他人通过category调用到这里的功能) */, 113 | 4A5CE5001C9531C9006AEDB9 /* AppDelegate.h */, 114 | 4A5CE5011C9531C9006AEDB9 /* AppDelegate.m */, 115 | 4A5CE5031C9531C9006AEDB9 /* ViewController.h */, 116 | 4A5CE5041C9531C9006AEDB9 /* ViewController.m */, 117 | 4A5CE5061C9531C9006AEDB9 /* Main.storyboard */, 118 | 4A5CE5091C9531C9006AEDB9 /* Assets.xcassets */, 119 | 4A5CE50B1C9531C9006AEDB9 /* LaunchScreen.storyboard */, 120 | 4A5CE50E1C9531C9006AEDB9 /* Info.plist */, 121 | 4A5CE4FD1C9531C9006AEDB9 /* Supporting Files */, 122 | ); 123 | path = CTMediator; 124 | sourceTree = ""; 125 | }; 126 | 4A5CE4FD1C9531C9006AEDB9 /* Supporting Files */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 4A5CE4FE1C9531C9006AEDB9 /* main.m */, 130 | ); 131 | name = "Supporting Files"; 132 | sourceTree = ""; 133 | }; 134 | 4A5CE5141C9531E9006AEDB9 /* CTMediator(这也是单独的repo,完整的中间件就这100行代码) */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 4A5CE5181C95322F006AEDB9 /* CTMediator.h */, 138 | 4A5CE5191C95322F006AEDB9 /* CTMediator.m */, 139 | 4AD2E20324174B1900F8B53A /* CTMediator+HandyTools.h */, 140 | 4AD2E20424174B1900F8B53A /* CTMediator+HandyTools.m */, 141 | ); 142 | name = "CTMediator(这也是单独的repo,完整的中间件就这100行代码)"; 143 | path = CTMediator; 144 | sourceTree = ""; 145 | }; 146 | 4A5CE5211C953D5A006AEDB9 /* DemoModule(target-action所在的模块,也就是提供服务的模块,这也是单独的repo,但无需被其他人依赖,其他人通过category调用到这里的功能) */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 4A5CE5221C953D5A006AEDB9 /* Actions */, 150 | 4A5CE5231C953D7D006AEDB9 /* DemoModuleADetailViewController.h */, 151 | 4A5CE5241C953D7D006AEDB9 /* DemoModuleADetailViewController.m */, 152 | ); 153 | name = "DemoModule(target-action所在的模块,也就是提供服务的模块,这也是单独的repo,但无需被其他人依赖,其他人通过category调用到这里的功能)"; 154 | path = DemoModule; 155 | sourceTree = ""; 156 | }; 157 | 4A5CE5221C953D5A006AEDB9 /* Actions */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 4A5CE5291C9547B2006AEDB9 /* Target_A.h */, 161 | 4A5CE52A1C9547B2006AEDB9 /* Target_A.m */, 162 | ); 163 | path = Actions; 164 | sourceTree = ""; 165 | }; 166 | 4A85F5501DA92AFC00DB882C /* Categories(实际应用中,这是一个单独的repo,所用需要调度其他模块的人,只需要依赖这个repo。这个repo由target-action维护者维护) */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 4A85F5511DA92AFC00DB882C /* ModuleA */, 170 | ); 171 | name = "Categories(实际应用中,这是一个单独的repo,所用需要调度其他模块的人,只需要依赖这个repo。这个repo由target-action维护者维护)"; 172 | path = Categories; 173 | sourceTree = ""; 174 | }; 175 | 4A85F5511DA92AFC00DB882C /* ModuleA */ = { 176 | isa = PBXGroup; 177 | children = ( 178 | 4A85F5521DA92AFC00DB882C /* CTMediator+CTMediatorModuleAActions.h */, 179 | 4A85F5531DA92AFC00DB882C /* CTMediator+CTMediatorModuleAActions.m */, 180 | ); 181 | path = ModuleA; 182 | sourceTree = ""; 183 | }; 184 | 4AC176571DE163B600566741 /* CTMediatorUITests */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | 4AC176581DE163B600566741 /* CTMediatorUITests.m */, 188 | 4AC1765A1DE163B600566741 /* Info.plist */, 189 | ); 190 | path = CTMediatorUITests; 191 | sourceTree = ""; 192 | }; 193 | 4AED66AB1DB895C600C13EDC /* TableViewController */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 4AED66AC1DB895D900C13EDC /* TableViewController.h */, 197 | 4AED66AD1DB895D900C13EDC /* TableViewController.m */, 198 | ); 199 | path = TableViewController; 200 | sourceTree = ""; 201 | }; 202 | 9449B9483205DCF29E913981 /* Frameworks */ = { 203 | isa = PBXGroup; 204 | children = ( 205 | 1CDD4389F5AEA1D8F6AA0C9F /* libPods-CTMediator.a */, 206 | ); 207 | name = Frameworks; 208 | sourceTree = ""; 209 | }; 210 | B79600ED5E76116124B15CEE /* Pods */ = { 211 | isa = PBXGroup; 212 | children = ( 213 | 2D583B83854D11618B8F5E65 /* Pods-CTMediator.debug.xcconfig */, 214 | E9C114EF1101F17773DD4798 /* Pods-CTMediator.release.xcconfig */, 215 | ); 216 | name = Pods; 217 | sourceTree = ""; 218 | }; 219 | /* End PBXGroup section */ 220 | 221 | /* Begin PBXNativeTarget section */ 222 | 4A5CE4F91C9531C9006AEDB9 /* CTMediator */ = { 223 | isa = PBXNativeTarget; 224 | buildConfigurationList = 4A5CE5111C9531C9006AEDB9 /* Build configuration list for PBXNativeTarget "CTMediator" */; 225 | buildPhases = ( 226 | 9F6811B15C0C49802EEE481B /* [CP] Check Pods Manifest.lock */, 227 | 0DEB0101A9257C7385F33550 /* [CP] Check Pods Manifest.lock */, 228 | 4A5CE4F61C9531C9006AEDB9 /* Sources */, 229 | 4A5CE4F71C9531C9006AEDB9 /* Frameworks */, 230 | 4A5CE4F81C9531C9006AEDB9 /* Resources */, 231 | ); 232 | buildRules = ( 233 | ); 234 | dependencies = ( 235 | ); 236 | name = CTMediator; 237 | productName = CTMediator; 238 | productReference = 4A5CE4FA1C9531C9006AEDB9 /* CTMediator.app */; 239 | productType = "com.apple.product-type.application"; 240 | }; 241 | 4AC176551DE163B600566741 /* CTMediatorUITests */ = { 242 | isa = PBXNativeTarget; 243 | buildConfigurationList = 4AC1765F1DE163B600566741 /* Build configuration list for PBXNativeTarget "CTMediatorUITests" */; 244 | buildPhases = ( 245 | 4AC176521DE163B600566741 /* Sources */, 246 | 4AC176531DE163B600566741 /* Frameworks */, 247 | 4AC176541DE163B600566741 /* Resources */, 248 | ); 249 | buildRules = ( 250 | ); 251 | dependencies = ( 252 | 4AC1765C1DE163B600566741 /* PBXTargetDependency */, 253 | ); 254 | name = CTMediatorUITests; 255 | productName = CTMediatorUITests; 256 | productReference = 4AC176561DE163B600566741 /* CTMediatorUITests.xctest */; 257 | productType = "com.apple.product-type.bundle.ui-testing"; 258 | }; 259 | /* End PBXNativeTarget section */ 260 | 261 | /* Begin PBXProject section */ 262 | 4A5CE4F21C9531C9006AEDB9 /* Project object */ = { 263 | isa = PBXProject; 264 | attributes = { 265 | LastUpgradeCheck = 0930; 266 | ORGANIZATIONNAME = casa; 267 | TargetAttributes = { 268 | 4A5CE4F91C9531C9006AEDB9 = { 269 | CreatedOnToolsVersion = 7.2.1; 270 | DevelopmentTeam = THS9SRQVES; 271 | }; 272 | 4AC176551DE163B600566741 = { 273 | CreatedOnToolsVersion = 8.1; 274 | DevelopmentTeam = THS9SRQVES; 275 | ProvisioningStyle = Automatic; 276 | TestTargetID = 4A5CE4F91C9531C9006AEDB9; 277 | }; 278 | }; 279 | }; 280 | buildConfigurationList = 4A5CE4F51C9531C9006AEDB9 /* Build configuration list for PBXProject "CTMediator" */; 281 | compatibilityVersion = "Xcode 3.2"; 282 | developmentRegion = English; 283 | hasScannedForEncodings = 0; 284 | knownRegions = ( 285 | English, 286 | en, 287 | Base, 288 | ); 289 | mainGroup = 4A5CE4F11C9531C9006AEDB9; 290 | productRefGroup = 4A5CE4FB1C9531C9006AEDB9 /* Products */; 291 | projectDirPath = ""; 292 | projectRoot = ""; 293 | targets = ( 294 | 4A5CE4F91C9531C9006AEDB9 /* CTMediator */, 295 | 4AC176551DE163B600566741 /* CTMediatorUITests */, 296 | ); 297 | }; 298 | /* End PBXProject section */ 299 | 300 | /* Begin PBXResourcesBuildPhase section */ 301 | 4A5CE4F81C9531C9006AEDB9 /* Resources */ = { 302 | isa = PBXResourcesBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | 4A5CE50D1C9531C9006AEDB9 /* LaunchScreen.storyboard in Resources */, 306 | 4A5CE50A1C9531C9006AEDB9 /* Assets.xcassets in Resources */, 307 | 4A5CE5081C9531C9006AEDB9 /* Main.storyboard in Resources */, 308 | ); 309 | runOnlyForDeploymentPostprocessing = 0; 310 | }; 311 | 4AC176541DE163B600566741 /* Resources */ = { 312 | isa = PBXResourcesBuildPhase; 313 | buildActionMask = 2147483647; 314 | files = ( 315 | ); 316 | runOnlyForDeploymentPostprocessing = 0; 317 | }; 318 | /* End PBXResourcesBuildPhase section */ 319 | 320 | /* Begin PBXShellScriptBuildPhase section */ 321 | 0DEB0101A9257C7385F33550 /* [CP] Check Pods Manifest.lock */ = { 322 | isa = PBXShellScriptBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | ); 326 | inputPaths = ( 327 | ); 328 | name = "[CP] Check Pods Manifest.lock"; 329 | outputPaths = ( 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | shellPath = /bin/sh; 333 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 334 | showEnvVarsInLog = 0; 335 | }; 336 | 9F6811B15C0C49802EEE481B /* [CP] Check Pods Manifest.lock */ = { 337 | isa = PBXShellScriptBuildPhase; 338 | buildActionMask = 2147483647; 339 | files = ( 340 | ); 341 | inputPaths = ( 342 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 343 | "${PODS_ROOT}/Manifest.lock", 344 | ); 345 | name = "[CP] Check Pods Manifest.lock"; 346 | outputPaths = ( 347 | "$(DERIVED_FILE_DIR)/Pods-CTMediator-checkManifestLockResult.txt", 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | shellPath = /bin/sh; 351 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 352 | showEnvVarsInLog = 0; 353 | }; 354 | /* End PBXShellScriptBuildPhase section */ 355 | 356 | /* Begin PBXSourcesBuildPhase section */ 357 | 4A5CE4F61C9531C9006AEDB9 /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | 4A5CE5051C9531C9006AEDB9 /* ViewController.m in Sources */, 362 | 4A5CE5021C9531C9006AEDB9 /* AppDelegate.m in Sources */, 363 | 4A85F5541DA92AFC00DB882C /* CTMediator+CTMediatorModuleAActions.m in Sources */, 364 | 4A5CE4FF1C9531C9006AEDB9 /* main.m in Sources */, 365 | 4A5CE5251C953D7D006AEDB9 /* DemoModuleADetailViewController.m in Sources */, 366 | 4A5CE51A1C95322F006AEDB9 /* CTMediator.m in Sources */, 367 | 4AD2E20524174B1900F8B53A /* CTMediator+HandyTools.m in Sources */, 368 | 4A5CE52B1C9547B2006AEDB9 /* Target_A.m in Sources */, 369 | 4AED66AE1DB895D900C13EDC /* TableViewController.m in Sources */, 370 | ); 371 | runOnlyForDeploymentPostprocessing = 0; 372 | }; 373 | 4AC176521DE163B600566741 /* Sources */ = { 374 | isa = PBXSourcesBuildPhase; 375 | buildActionMask = 2147483647; 376 | files = ( 377 | 4AC176591DE163B600566741 /* CTMediatorUITests.m in Sources */, 378 | ); 379 | runOnlyForDeploymentPostprocessing = 0; 380 | }; 381 | /* End PBXSourcesBuildPhase section */ 382 | 383 | /* Begin PBXTargetDependency section */ 384 | 4AC1765C1DE163B600566741 /* PBXTargetDependency */ = { 385 | isa = PBXTargetDependency; 386 | target = 4A5CE4F91C9531C9006AEDB9 /* CTMediator */; 387 | targetProxy = 4AC1765B1DE163B600566741 /* PBXContainerItemProxy */; 388 | }; 389 | /* End PBXTargetDependency section */ 390 | 391 | /* Begin PBXVariantGroup section */ 392 | 4A5CE5061C9531C9006AEDB9 /* Main.storyboard */ = { 393 | isa = PBXVariantGroup; 394 | children = ( 395 | 4A5CE5071C9531C9006AEDB9 /* Base */, 396 | ); 397 | name = Main.storyboard; 398 | sourceTree = ""; 399 | }; 400 | 4A5CE50B1C9531C9006AEDB9 /* LaunchScreen.storyboard */ = { 401 | isa = PBXVariantGroup; 402 | children = ( 403 | 4A5CE50C1C9531C9006AEDB9 /* Base */, 404 | ); 405 | name = LaunchScreen.storyboard; 406 | sourceTree = ""; 407 | }; 408 | /* End PBXVariantGroup section */ 409 | 410 | /* Begin XCBuildConfiguration section */ 411 | 4A5CE50F1C9531C9006AEDB9 /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | ALWAYS_SEARCH_USER_PATHS = NO; 415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 416 | CLANG_CXX_LIBRARY = "libc++"; 417 | CLANG_ENABLE_MODULES = YES; 418 | CLANG_ENABLE_OBJC_ARC = YES; 419 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 420 | CLANG_WARN_BOOL_CONVERSION = YES; 421 | CLANG_WARN_COMMA = YES; 422 | CLANG_WARN_CONSTANT_CONVERSION = YES; 423 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 424 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 425 | CLANG_WARN_EMPTY_BODY = YES; 426 | CLANG_WARN_ENUM_CONVERSION = YES; 427 | CLANG_WARN_INFINITE_RECURSION = YES; 428 | CLANG_WARN_INT_CONVERSION = YES; 429 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 430 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 431 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 432 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 433 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 434 | CLANG_WARN_STRICT_PROTOTYPES = YES; 435 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 436 | CLANG_WARN_UNREACHABLE_CODE = YES; 437 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 438 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 439 | COPY_PHASE_STRIP = NO; 440 | DEBUG_INFORMATION_FORMAT = dwarf; 441 | ENABLE_STRICT_OBJC_MSGSEND = YES; 442 | ENABLE_TESTABILITY = YES; 443 | GCC_C_LANGUAGE_STANDARD = gnu99; 444 | GCC_DYNAMIC_NO_PIC = NO; 445 | GCC_NO_COMMON_BLOCKS = YES; 446 | GCC_OPTIMIZATION_LEVEL = 0; 447 | GCC_PREPROCESSOR_DEFINITIONS = ( 448 | "DEBUG=1", 449 | "$(inherited)", 450 | ); 451 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 452 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 453 | GCC_WARN_UNDECLARED_SELECTOR = YES; 454 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 455 | GCC_WARN_UNUSED_FUNCTION = YES; 456 | GCC_WARN_UNUSED_VARIABLE = YES; 457 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 458 | MTL_ENABLE_DEBUG_INFO = YES; 459 | ONLY_ACTIVE_ARCH = YES; 460 | SDKROOT = iphoneos; 461 | TARGETED_DEVICE_FAMILY = "1,2"; 462 | }; 463 | name = Debug; 464 | }; 465 | 4A5CE5101C9531C9006AEDB9 /* Release */ = { 466 | isa = XCBuildConfiguration; 467 | buildSettings = { 468 | ALWAYS_SEARCH_USER_PATHS = NO; 469 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 470 | CLANG_CXX_LIBRARY = "libc++"; 471 | CLANG_ENABLE_MODULES = YES; 472 | CLANG_ENABLE_OBJC_ARC = YES; 473 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 474 | CLANG_WARN_BOOL_CONVERSION = YES; 475 | CLANG_WARN_COMMA = YES; 476 | CLANG_WARN_CONSTANT_CONVERSION = YES; 477 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 478 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 479 | CLANG_WARN_EMPTY_BODY = YES; 480 | CLANG_WARN_ENUM_CONVERSION = YES; 481 | CLANG_WARN_INFINITE_RECURSION = YES; 482 | CLANG_WARN_INT_CONVERSION = YES; 483 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 484 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 485 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 486 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 487 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 488 | CLANG_WARN_STRICT_PROTOTYPES = YES; 489 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 490 | CLANG_WARN_UNREACHABLE_CODE = YES; 491 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 492 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 493 | COPY_PHASE_STRIP = NO; 494 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 495 | ENABLE_NS_ASSERTIONS = NO; 496 | ENABLE_STRICT_OBJC_MSGSEND = YES; 497 | GCC_C_LANGUAGE_STANDARD = gnu99; 498 | GCC_NO_COMMON_BLOCKS = YES; 499 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 500 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 501 | GCC_WARN_UNDECLARED_SELECTOR = YES; 502 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 503 | GCC_WARN_UNUSED_FUNCTION = YES; 504 | GCC_WARN_UNUSED_VARIABLE = YES; 505 | IPHONEOS_DEPLOYMENT_TARGET = 9.2; 506 | MTL_ENABLE_DEBUG_INFO = NO; 507 | SDKROOT = iphoneos; 508 | TARGETED_DEVICE_FAMILY = "1,2"; 509 | VALIDATE_PRODUCT = YES; 510 | }; 511 | name = Release; 512 | }; 513 | 4A5CE5121C9531C9006AEDB9 /* Debug */ = { 514 | isa = XCBuildConfiguration; 515 | baseConfigurationReference = 2D583B83854D11618B8F5E65 /* Pods-CTMediator.debug.xcconfig */; 516 | buildSettings = { 517 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 518 | DEVELOPMENT_TEAM = THS9SRQVES; 519 | INFOPLIST_FILE = CTMediator/Info.plist; 520 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 521 | PRODUCT_BUNDLE_IDENTIFIER = casa.CTMediator; 522 | PRODUCT_NAME = "$(TARGET_NAME)"; 523 | }; 524 | name = Debug; 525 | }; 526 | 4A5CE5131C9531C9006AEDB9 /* Release */ = { 527 | isa = XCBuildConfiguration; 528 | baseConfigurationReference = E9C114EF1101F17773DD4798 /* Pods-CTMediator.release.xcconfig */; 529 | buildSettings = { 530 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 531 | DEVELOPMENT_TEAM = THS9SRQVES; 532 | INFOPLIST_FILE = CTMediator/Info.plist; 533 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 534 | PRODUCT_BUNDLE_IDENTIFIER = casa.CTMediator; 535 | PRODUCT_NAME = "$(TARGET_NAME)"; 536 | }; 537 | name = Release; 538 | }; 539 | 4AC1765D1DE163B600566741 /* Debug */ = { 540 | isa = XCBuildConfiguration; 541 | buildSettings = { 542 | CLANG_ANALYZER_NONNULL = YES; 543 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 544 | CLANG_WARN_INFINITE_RECURSION = YES; 545 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 546 | DEVELOPMENT_TEAM = THS9SRQVES; 547 | INFOPLIST_FILE = CTMediatorUITests/Info.plist; 548 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 549 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 550 | PRODUCT_BUNDLE_IDENTIFIER = casa.CTMediatorUITests; 551 | PRODUCT_NAME = "$(TARGET_NAME)"; 552 | TEST_TARGET_NAME = CTMediator; 553 | }; 554 | name = Debug; 555 | }; 556 | 4AC1765E1DE163B600566741 /* Release */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | CLANG_ANALYZER_NONNULL = YES; 560 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 561 | CLANG_WARN_INFINITE_RECURSION = YES; 562 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 563 | DEVELOPMENT_TEAM = THS9SRQVES; 564 | INFOPLIST_FILE = CTMediatorUITests/Info.plist; 565 | IPHONEOS_DEPLOYMENT_TARGET = 10.1; 566 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 567 | PRODUCT_BUNDLE_IDENTIFIER = casa.CTMediatorUITests; 568 | PRODUCT_NAME = "$(TARGET_NAME)"; 569 | TEST_TARGET_NAME = CTMediator; 570 | }; 571 | name = Release; 572 | }; 573 | /* End XCBuildConfiguration section */ 574 | 575 | /* Begin XCConfigurationList section */ 576 | 4A5CE4F51C9531C9006AEDB9 /* Build configuration list for PBXProject "CTMediator" */ = { 577 | isa = XCConfigurationList; 578 | buildConfigurations = ( 579 | 4A5CE50F1C9531C9006AEDB9 /* Debug */, 580 | 4A5CE5101C9531C9006AEDB9 /* Release */, 581 | ); 582 | defaultConfigurationIsVisible = 0; 583 | defaultConfigurationName = Release; 584 | }; 585 | 4A5CE5111C9531C9006AEDB9 /* Build configuration list for PBXNativeTarget "CTMediator" */ = { 586 | isa = XCConfigurationList; 587 | buildConfigurations = ( 588 | 4A5CE5121C9531C9006AEDB9 /* Debug */, 589 | 4A5CE5131C9531C9006AEDB9 /* Release */, 590 | ); 591 | defaultConfigurationIsVisible = 0; 592 | defaultConfigurationName = Release; 593 | }; 594 | 4AC1765F1DE163B600566741 /* Build configuration list for PBXNativeTarget "CTMediatorUITests" */ = { 595 | isa = XCConfigurationList; 596 | buildConfigurations = ( 597 | 4AC1765D1DE163B600566741 /* Debug */, 598 | 4AC1765E1DE163B600566741 /* Release */, 599 | ); 600 | defaultConfigurationIsVisible = 0; 601 | defaultConfigurationName = Release; 602 | }; 603 | /* End XCConfigurationList section */ 604 | }; 605 | rootObject = 4A5CE4F21C9531C9006AEDB9 /* Project object */; 606 | } 607 | -------------------------------------------------------------------------------- /CTMediator.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CTMediator.xcodeproj/xcuserdata/casa.xcuserdatad/xcschemes/CTMediator.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /CTMediator.xcodeproj/xcuserdata/casa.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CTMediator.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4A5CE4F91C9531C9006AEDB9 16 | 17 | primary 18 | 19 | 20 | 4AC176551DE163B600566741 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CTMediator/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /CTMediator/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "CTMediator.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | return YES; 22 | } 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // 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. 26 | // 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. 27 | } 28 | 29 | - (void)applicationDidEnterBackground:(UIApplication *)application { 30 | // 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. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | - (void)applicationWillEnterForeground:(UIApplication *)application { 35 | // 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. 36 | } 37 | 38 | - (void)applicationDidBecomeActive:(UIApplication *)application { 39 | // 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. 40 | } 41 | 42 | - (void)applicationWillTerminate:(UIApplication *)application { 43 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 44 | } 45 | 46 | - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options 47 | { 48 | return [[[CTMediator sharedInstance] performActionWithUrl:url completion:nil] boolValue]; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /CTMediator/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /CTMediator/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /CTMediator/Assets.xcassets/image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "image.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CTMediator/Assets.xcassets/image.imageset/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casatwy/CTMediator/942e9d8bf43b03f3eb0241b339c488d3e3427571/CTMediator/Assets.xcassets/image.imageset/image.png -------------------------------------------------------------------------------- /CTMediator/Assets.xcassets/noImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "noImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CTMediator/Assets.xcassets/noImage.imageset/noImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casatwy/CTMediator/942e9d8bf43b03f3eb0241b339c488d3e3427571/CTMediator/Assets.xcassets/noImage.imageset/noImage.png -------------------------------------------------------------------------------- /CTMediator/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /CTMediator/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /CTMediator/CTMediator/CTMediator+HandyTools.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTMediator+HandyTools.h 3 | // CTMediator 4 | // 5 | // Created by casa on 2020/3/10. 6 | // Copyright © 2020 casa. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IOS 10 | 11 | #import "CTMediator.h" 12 | #import 13 | 14 | NS_ASSUME_NONNULL_BEGIN 15 | 16 | @interface CTMediator (HandyTools) 17 | 18 | - (UIViewController * _Nullable)topViewController NS_EXTENSION_UNAVAILABLE_IOS("not available on iOS (App Extension)"); 19 | 20 | - (UIWindow * _Nullable)keyWindow NS_EXTENSION_UNAVAILABLE_IOS("not available on iOS (App Extension)"); 21 | 22 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated NS_EXTENSION_UNAVAILABLE_IOS("not available on iOS (App Extension)"); 23 | 24 | - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)animated completion:(void (^ _Nullable )(void))completion NS_EXTENSION_UNAVAILABLE_IOS("not available on iOS (App Extension)"); 25 | 26 | @end 27 | 28 | NS_ASSUME_NONNULL_END 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /CTMediator/CTMediator/CTMediator+HandyTools.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTMediator+HandyTools.m 3 | // CTMediator 4 | // 5 | // Created by casa on 2020/3/10. 6 | // Copyright © 2020 casa. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IOS 10 | 11 | #import "CTMediator+HandyTools.h" 12 | 13 | @implementation CTMediator (HandyTools) 14 | 15 | - (UIWindow *)keyWindow { 16 | if (@available(iOS 13, *)) { 17 | NSArray *windows = [[UIApplication sharedApplication] windows]; 18 | for (UIWindow *window in windows) { 19 | if (window.isKeyWindow) { 20 | return window; 21 | } 22 | } 23 | }else { 24 | return [UIApplication sharedApplication].keyWindow; 25 | } 26 | 27 | return nil; 28 | } 29 | 30 | - (UIViewController *)topViewController 31 | { 32 | UIViewController *topController = [self keyWindow].rootViewController; 33 | 34 | while (topController.presentedViewController) { 35 | topController = topController.presentedViewController; 36 | } 37 | 38 | return topController; 39 | } 40 | 41 | - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated 42 | { 43 | UINavigationController *navigationController = (UINavigationController *)[self topViewController]; 44 | 45 | if ([navigationController isKindOfClass:[UINavigationController class]] == NO) { 46 | if ([navigationController isKindOfClass:[UITabBarController class]]) { 47 | UITabBarController *tabbarController = (UITabBarController *)navigationController; 48 | navigationController = tabbarController.selectedViewController; 49 | if ([navigationController isKindOfClass:[UINavigationController class]] == NO) { 50 | navigationController = tabbarController.selectedViewController.navigationController; 51 | } 52 | } else { 53 | navigationController = navigationController.navigationController; 54 | } 55 | } 56 | 57 | if ([navigationController isKindOfClass:[UINavigationController class]]) { 58 | [navigationController pushViewController:viewController animated:animated]; 59 | } 60 | } 61 | 62 | - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)animated completion:(void (^ _Nullable)(void))completion 63 | { 64 | UIViewController *viewController = [self topViewController]; 65 | if ([viewController isKindOfClass:[UINavigationController class]]) { 66 | UINavigationController *navigationController = (UINavigationController *)viewController; 67 | viewController = navigationController.topViewController; 68 | } 69 | 70 | if ([viewController isKindOfClass:[UIAlertController class]]) { 71 | UIViewController *viewControllerToUse = viewController.presentingViewController; 72 | [viewController dismissViewControllerAnimated:false completion:nil]; 73 | viewController = viewControllerToUse; 74 | } 75 | 76 | if (viewController) { 77 | [viewController presentViewController:viewControllerToPresent animated:animated completion:completion]; 78 | } 79 | } 80 | 81 | @end 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /CTMediator/CTMediator/CTMediator.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTMediator.h 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString * _Nonnull const kCTMediatorParamsKeySwiftTargetModuleName; 12 | 13 | @interface CTMediator : NSObject 14 | 15 | + (instancetype _Nonnull)sharedInstance; 16 | 17 | // 远程App调用入口 18 | - (id _Nullable)performActionWithUrl:(NSURL * _Nullable)url completion:(void(^_Nullable)(NSDictionary * _Nullable info))completion; 19 | // 本地组件调用入口 20 | - (id _Nullable )performTarget:(NSString * _Nullable)targetName action:(NSString * _Nullable)actionName params:(NSDictionary * _Nullable)params shouldCacheTarget:(BOOL)shouldCacheTarget; 21 | - (void)releaseCachedTargetWithFullTargetName:(NSString * _Nullable)fullTargetName; 22 | // 检查 是否有引源码 23 | - (BOOL)check:(NSString * _Nullable)targetName moduleName:(NSString * _Nullable)moduleName; 24 | @end 25 | 26 | // 简化调用单例的函数 27 | CTMediator* _Nonnull CT(void); 28 | -------------------------------------------------------------------------------- /CTMediator/CTMediator/CTMediator.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTMediator.m 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "CTMediator.h" 10 | #import 11 | #import 12 | 13 | NSString * const kCTMediatorParamsKeySwiftTargetModuleName = @"kCTMediatorParamsKeySwiftTargetModuleName"; 14 | 15 | @interface CTMediator () 16 | 17 | @property (nonatomic, strong) NSMutableDictionary *cachedTarget; 18 | 19 | @end 20 | 21 | @implementation CTMediator 22 | 23 | #pragma mark - public methods 24 | + (instancetype)sharedInstance 25 | { 26 | static CTMediator *mediator; 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | mediator = [[CTMediator alloc] init]; 30 | [mediator cachedTarget]; // 同时把cachedTarget初始化,避免多线程重复初始化 31 | }); 32 | return mediator; 33 | } 34 | 35 | /* 36 | scheme://[target]/[action]?[params] 37 | 38 | url sample: 39 | aaa://targetA/actionB?id=1234 40 | */ 41 | 42 | - (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion 43 | { 44 | if (url == nil||![url isKindOfClass:[NSURL class]]) { 45 | return nil; 46 | } 47 | 48 | NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; 49 | NSURLComponents *urlComponents = [[NSURLComponents alloc] initWithString:url.absoluteString]; 50 | // 遍历所有参数 51 | [urlComponents.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { 52 | if (obj.value&&obj.name) { 53 | [params setObject:obj.value forKey:obj.name]; 54 | } 55 | }]; 56 | 57 | // 这里这么写主要是出于安全考虑,防止黑客通过远程方式调用本地模块。这里的做法足以应对绝大多数场景,如果要求更加严苛,也可以做更加复杂的安全逻辑。 58 | NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""]; 59 | if ([actionName hasPrefix:@"native"]) { 60 | return @(NO); 61 | } 62 | 63 | // 这个demo针对URL的路由处理非常简单,就只是取对应的target名字和method名字,但这已经足以应对绝大部份需求。如果需要拓展,可以在这个方法调用之前加入完整的路由逻辑 64 | id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO]; 65 | if (completion) { 66 | if (result) { 67 | completion(@{@"result":result}); 68 | } else { 69 | completion(nil); 70 | } 71 | } 72 | return result; 73 | } 74 | 75 | - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget 76 | { 77 | if (targetName == nil || actionName == nil) { 78 | return nil; 79 | } 80 | 81 | NSString *swiftModuleName = params[kCTMediatorParamsKeySwiftTargetModuleName]; 82 | 83 | // generate target 84 | NSString *targetClassString = nil; 85 | if (swiftModuleName.length > 0) { 86 | targetClassString = [NSString stringWithFormat:@"%@.Target_%@", swiftModuleName, targetName]; 87 | } else { 88 | targetClassString = [NSString stringWithFormat:@"Target_%@", targetName]; 89 | } 90 | NSObject *target = [self safeFetchCachedTarget:targetClassString]; 91 | if (target == nil) { 92 | Class targetClass = NSClassFromString(targetClassString); 93 | target = [[targetClass alloc] init]; 94 | } 95 | 96 | // generate action 97 | NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; 98 | SEL action = NSSelectorFromString(actionString); 99 | 100 | if (target == nil) { 101 | // 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的 102 | [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params]; 103 | return nil; 104 | } 105 | 106 | if (shouldCacheTarget) { 107 | [self safeSetCachedTarget:target key:targetClassString]; 108 | } 109 | 110 | if ([target respondsToSelector:action]) { 111 | return [self safePerformAction:action target:target params:params]; 112 | } else { 113 | // 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理 114 | SEL action = NSSelectorFromString(@"notFound:"); 115 | if ([target respondsToSelector:action]) { 116 | return [self safePerformAction:action target:target params:params]; 117 | } else { 118 | // 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。 119 | [self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params]; 120 | @synchronized (self) { 121 | [self.cachedTarget removeObjectForKey:targetClassString]; 122 | } 123 | return nil; 124 | } 125 | } 126 | } 127 | 128 | - (void)releaseCachedTargetWithFullTargetName:(NSString *)fullTargetName 129 | { 130 | /* 131 | fullTargetName在oc环境下,就是Target_XXXX。要带上Target_前缀。在swift环境下,就是XXXModule.Target_YYY。不光要带上Target_前缀,还要带上模块名。 132 | */ 133 | if (fullTargetName == nil) { 134 | return; 135 | } 136 | @synchronized (self) { 137 | [self.cachedTarget removeObjectForKey:fullTargetName]; 138 | } 139 | } 140 | 141 | - (BOOL)check:(NSString * _Nullable)targetName moduleName:(NSString * _Nullable)moduleName{ 142 | if (moduleName.length > 0) { 143 | return NSClassFromString([NSString stringWithFormat:@"%@.Target_%@", moduleName, targetName]) != nil; 144 | } else { 145 | return NSClassFromString([NSString stringWithFormat:@"Target_%@", targetName]) != nil; 146 | } 147 | } 148 | 149 | #pragma mark - private methods 150 | - (void)NoTargetActionResponseWithTargetString:(NSString *)targetString selectorString:(NSString *)selectorString originParams:(NSDictionary *)originParams 151 | { 152 | SEL action = NSSelectorFromString(@"Action_response:"); 153 | NSObject *target = [[NSClassFromString(@"Target_NoTargetAction") alloc] init]; 154 | 155 | NSMutableDictionary *params = [[NSMutableDictionary alloc] init]; 156 | params[@"originParams"] = originParams; 157 | params[@"targetString"] = targetString; 158 | params[@"selectorString"] = selectorString; 159 | 160 | [self safePerformAction:action target:target params:params]; 161 | } 162 | 163 | - (id)safePerformAction:(SEL)action target:(NSObject *)target params:(NSDictionary *)params 164 | { 165 | NSMethodSignature* methodSig = [target methodSignatureForSelector:action]; 166 | if(methodSig == nil) { 167 | return nil; 168 | } 169 | const char* retType = [methodSig methodReturnType]; 170 | 171 | if (strcmp(retType, @encode(void)) == 0) { 172 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 173 | [invocation setArgument:¶ms atIndex:2]; 174 | [invocation setSelector:action]; 175 | [invocation setTarget:target]; 176 | [invocation invoke]; 177 | return nil; 178 | } 179 | 180 | if (strcmp(retType, @encode(NSInteger)) == 0) { 181 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 182 | [invocation setArgument:¶ms atIndex:2]; 183 | [invocation setSelector:action]; 184 | [invocation setTarget:target]; 185 | [invocation invoke]; 186 | NSInteger result = 0; 187 | [invocation getReturnValue:&result]; 188 | return @(result); 189 | } 190 | 191 | if (strcmp(retType, @encode(BOOL)) == 0) { 192 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 193 | [invocation setArgument:¶ms atIndex:2]; 194 | [invocation setSelector:action]; 195 | [invocation setTarget:target]; 196 | [invocation invoke]; 197 | BOOL result = 0; 198 | [invocation getReturnValue:&result]; 199 | return @(result); 200 | } 201 | 202 | if (strcmp(retType, @encode(CGFloat)) == 0) { 203 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 204 | [invocation setArgument:¶ms atIndex:2]; 205 | [invocation setSelector:action]; 206 | [invocation setTarget:target]; 207 | [invocation invoke]; 208 | CGFloat result = 0; 209 | [invocation getReturnValue:&result]; 210 | return @(result); 211 | } 212 | 213 | if (strcmp(retType, @encode(NSUInteger)) == 0) { 214 | NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig]; 215 | [invocation setArgument:¶ms atIndex:2]; 216 | [invocation setSelector:action]; 217 | [invocation setTarget:target]; 218 | [invocation invoke]; 219 | NSUInteger result = 0; 220 | [invocation getReturnValue:&result]; 221 | return @(result); 222 | } 223 | 224 | #pragma clang diagnostic push 225 | #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 226 | return [target performSelector:action withObject:params]; 227 | #pragma clang diagnostic pop 228 | } 229 | 230 | #pragma mark - getters and setters 231 | - (NSMutableDictionary *)cachedTarget 232 | { 233 | if (_cachedTarget == nil) { 234 | _cachedTarget = [[NSMutableDictionary alloc] init]; 235 | } 236 | return _cachedTarget; 237 | } 238 | 239 | - (NSObject *)safeFetchCachedTarget:(NSString *)key { 240 | @synchronized (self) { 241 | return self.cachedTarget[key]; 242 | } 243 | } 244 | 245 | - (void)safeSetCachedTarget:(NSObject *)target key:(NSString *)key { 246 | @synchronized (self) { 247 | self.cachedTarget[key] = target; 248 | } 249 | } 250 | 251 | 252 | @end 253 | 254 | CTMediator* _Nonnull CT(void){ 255 | return [CTMediator sharedInstance]; 256 | }; 257 | -------------------------------------------------------------------------------- /CTMediator/Categories/ModuleA/CTMediator+CTMediatorModuleAActions.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTMediator+CTMediatorModuleAActions.h 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "CTMediator.h" 10 | #import 11 | 12 | @interface CTMediator (CTMediatorModuleAActions) 13 | 14 | - (UIViewController *)CTMediator_viewControllerForDetail; 15 | 16 | - (void)CTMediator_showAlertWithMessage:(NSString *)message cancelAction:(void(^)(NSDictionary *info))cancelAction confirmAction:(void(^)(NSDictionary *info))confirmAction; 17 | 18 | - (void)CTMediator_presentImage:(UIImage *)image; 19 | 20 | - (UITableViewCell *)CTMediator_tableViewCellWithIdentifier:(NSString *)identifier tableView:(UITableView *)tableView; 21 | 22 | - (void)CTMediator_configTableViewCell:(UITableViewCell *)cell withTitle:(NSString *)title atIndexPath:(NSIndexPath *)indexPath; 23 | 24 | - (void)CTMediator_cleanTableViewCellTarget; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /CTMediator/Categories/ModuleA/CTMediator+CTMediatorModuleAActions.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTMediator+CTMediatorModuleAActions.m 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "CTMediator+CTMediatorModuleAActions.h" 10 | 11 | NSString * const kCTMediatorTargetA = @"A"; 12 | 13 | NSString * const kCTMediatorActionNativeFetchDetailViewController = @"nativeFetchDetailViewController"; 14 | NSString * const kCTMediatorActionNativePresentImage = @"nativePresentImage"; 15 | NSString * const kCTMediatorActionNativeNoImage = @"nativeNoImage"; 16 | NSString * const kCTMediatorActionShowAlert = @"showAlert"; 17 | NSString * const kCTMediatorActionCell = @"cell"; 18 | NSString * const kCTMediatorActionConfigCell = @"configCell"; 19 | 20 | @implementation CTMediator (CTMediatorModuleAActions) 21 | 22 | - (UIViewController *)CTMediator_viewControllerForDetail 23 | { 24 | UIViewController *viewController = [self performTarget:kCTMediatorTargetA 25 | action:kCTMediatorActionNativeFetchDetailViewController 26 | params:@{@"key":@"value"} 27 | shouldCacheTarget:NO 28 | ]; 29 | if ([viewController isKindOfClass:[UIViewController class]]) { 30 | // view controller 交付出去之后,可以由外界选择是push还是present 31 | return viewController; 32 | } else { 33 | // 这里处理异常场景,具体如何处理取决于产品 34 | return [[UIViewController alloc] init]; 35 | } 36 | } 37 | 38 | - (void)CTMediator_presentImage:(UIImage *)image 39 | { 40 | if (image) { 41 | [self performTarget:kCTMediatorTargetA 42 | action:kCTMediatorActionNativePresentImage 43 | params:@{@"image":image} 44 | shouldCacheTarget:NO]; 45 | } else { 46 | // 这里处理image为nil的场景,如何处理取决于产品 47 | [self performTarget:kCTMediatorTargetA 48 | action:kCTMediatorActionNativeNoImage 49 | params:@{@"image":[UIImage imageNamed:@"noImage"]} 50 | shouldCacheTarget:NO]; 51 | } 52 | } 53 | 54 | - (void)CTMediator_showAlertWithMessage:(NSString *)message cancelAction:(void(^)(NSDictionary *info))cancelAction confirmAction:(void(^)(NSDictionary *info))confirmAction 55 | { 56 | NSMutableDictionary *paramsToSend = [[NSMutableDictionary alloc] init]; 57 | if (message) { 58 | paramsToSend[@"message"] = message; 59 | } 60 | if (cancelAction) { 61 | paramsToSend[@"cancelAction"] = cancelAction; 62 | } 63 | if (confirmAction) { 64 | paramsToSend[@"confirmAction"] = confirmAction; 65 | } 66 | [self performTarget:kCTMediatorTargetA 67 | action:kCTMediatorActionShowAlert 68 | params:paramsToSend 69 | shouldCacheTarget:NO]; 70 | } 71 | 72 | - (UITableViewCell *)CTMediator_tableViewCellWithIdentifier:(NSString *)identifier tableView:(UITableView *)tableView 73 | { 74 | return [self performTarget:kCTMediatorTargetA 75 | action:kCTMediatorActionCell 76 | params:@{ 77 | @"identifier":identifier, 78 | @"tableView":tableView 79 | } 80 | shouldCacheTarget:YES]; 81 | } 82 | 83 | - (void)CTMediator_configTableViewCell:(UITableViewCell *)cell withTitle:(NSString *)title atIndexPath:(NSIndexPath *)indexPath 84 | { 85 | [self performTarget:kCTMediatorTargetA 86 | action:kCTMediatorActionConfigCell 87 | params:@{ 88 | @"cell":cell, 89 | @"title":title, 90 | @"indexPath":indexPath 91 | } 92 | shouldCacheTarget:YES]; 93 | } 94 | 95 | - (void)CTMediator_cleanTableViewCellTarget 96 | { 97 | NSString *fullTargetName = [NSString stringWithFormat:@"Target_%@", kCTMediatorTargetA]; 98 | [self releaseCachedTargetWithFullTargetName:fullTargetName]; 99 | } 100 | 101 | @end 102 | -------------------------------------------------------------------------------- /CTMediator/DemoModule/Actions/Target_A.h: -------------------------------------------------------------------------------- 1 | // 2 | // Target_A.h 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface Target_A : NSObject 13 | 14 | - (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params; 15 | - (id)Action_nativePresentImage:(NSDictionary *)params; 16 | - (id)Action_showAlert:(NSDictionary *)params; 17 | 18 | // 容错 19 | - (id)Action_nativeNoImage:(NSDictionary *)params; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CTMediator/DemoModule/Actions/Target_A.m: -------------------------------------------------------------------------------- 1 | // 2 | // Target_A.m 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "Target_A.h" 10 | #import "DemoModuleADetailViewController.h" 11 | 12 | typedef void (^CTUrlRouterCallbackBlock)(NSDictionary *info); 13 | 14 | @implementation Target_A 15 | 16 | - (UIViewController *)Action_nativeFetchDetailViewController:(NSDictionary *)params 17 | { 18 | // 因为action是从属于ModuleA的,所以action直接可以使用ModuleA里的所有声明 19 | DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init]; 20 | viewController.valueLabel.text = params[@"key"]; 21 | return viewController; 22 | } 23 | 24 | - (id)Action_nativePresentImage:(NSDictionary *)params 25 | { 26 | DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init]; 27 | viewController.valueLabel.text = @"this is image"; 28 | viewController.imageView.image = params[@"image"]; 29 | [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil]; 30 | return nil; 31 | } 32 | 33 | - (id)Action_showAlert:(NSDictionary *)params 34 | { 35 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 36 | CTUrlRouterCallbackBlock callback = params[@"cancelAction"]; 37 | if (callback) { 38 | callback(@{@"alertAction":action}); 39 | } 40 | }]; 41 | 42 | UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"confirm" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { 43 | CTUrlRouterCallbackBlock callback = params[@"confirmAction"]; 44 | if (callback) { 45 | callback(@{@"alertAction":action}); 46 | } 47 | }]; 48 | 49 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"alert from Module A" message:params[@"message"] preferredStyle:UIAlertControllerStyleAlert]; 50 | [alertController addAction:cancelAction]; 51 | [alertController addAction:confirmAction]; 52 | [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil]; 53 | return nil; 54 | } 55 | 56 | - (id)Action_nativeNoImage:(NSDictionary *)params 57 | { 58 | DemoModuleADetailViewController *viewController = [[DemoModuleADetailViewController alloc] init]; 59 | viewController.valueLabel.text = @"no image"; 60 | viewController.imageView.image = [UIImage imageNamed:@"noImage"]; 61 | [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:viewController animated:YES completion:nil]; 62 | 63 | return nil; 64 | } 65 | 66 | - (UITableViewCell *)Action_cell:(NSDictionary *)params 67 | { 68 | UITableView *tableView = params[@"tableView"]; 69 | NSString *identifier = params[@"identifier"]; 70 | 71 | // 这里的TableViewCell的类型可以是自定义的,我这边偷懒就不自定义了。 72 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; 73 | if (cell == nil) { 74 | cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; 75 | } 76 | return cell; 77 | } 78 | 79 | - (id)Action_configCell:(NSDictionary *)params 80 | { 81 | NSString *title = params[@"title"]; 82 | NSIndexPath *indexPath = params[@"indexPath"]; 83 | UITableViewCell *cell = params[@"cell"]; 84 | 85 | // 这里的TableViewCell的类型可以是自定义的,我这边偷懒就不自定义了。 86 | cell.textLabel.text = [NSString stringWithFormat:@"%@,%ld", title, (long)indexPath.row]; 87 | 88 | // if ([cell isKindOfClass:[XXXXXCell class]]) { 89 | // 正常情况下在这里做特定cell的赋值,上面就简单写了 90 | // } 91 | 92 | return nil; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /CTMediator/DemoModule/DemoModuleADetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoModuleADetailViewController.h 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DemoModuleADetailViewController : UIViewController 12 | 13 | @property (nonatomic, strong, readonly) UILabel *valueLabel; 14 | @property (nonatomic, strong, readonly) UIImageView *imageView; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /CTMediator/DemoModule/DemoModuleADetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoModuleADetailViewController.m 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "DemoModuleADetailViewController.h" 10 | #import 11 | 12 | @interface DemoModuleADetailViewController () 13 | 14 | @property (nonatomic, strong, readwrite) UILabel *valueLabel; 15 | @property (nonatomic, strong, readwrite) UIImageView *imageView; 16 | 17 | @property (nonatomic, strong) UIButton *returnButton; 18 | 19 | @end 20 | 21 | @implementation DemoModuleADetailViewController 22 | 23 | #pragma mark - life cycle 24 | - (void)viewDidLoad { 25 | [super viewDidLoad]; 26 | self.view.backgroundColor = [UIColor grayColor]; 27 | 28 | [self.view addSubview:self.valueLabel]; 29 | [self.view addSubview:self.imageView]; 30 | [self.view addSubview:self.returnButton]; 31 | } 32 | 33 | - (void)viewWillLayoutSubviews 34 | { 35 | [super viewWillLayoutSubviews]; 36 | 37 | [self.valueLabel sizeToFit]; 38 | [self.valueLabel topInContainer:70 shouldResize:NO]; 39 | [self.valueLabel centerXEqualToView:self.view]; 40 | 41 | self.imageView.ct_size = CGSizeMake(100, 100); 42 | [self.imageView centerEqualToView:self.view]; 43 | 44 | self.returnButton.ct_size = CGSizeMake(100, 100); 45 | [self.returnButton bottomInContainer:0 shouldResize:NO]; 46 | [self.returnButton centerXEqualToView:self.view]; 47 | } 48 | 49 | #pragma mark - event response 50 | - (void)didTappedReturnButton:(UIButton *)button 51 | { 52 | if (self.navigationController == nil) { 53 | [self dismissViewControllerAnimated:YES completion:nil]; 54 | } else { 55 | [self.navigationController popViewControllerAnimated:YES]; 56 | } 57 | } 58 | 59 | #pragma mark - getters and setters 60 | - (UILabel *)valueLabel 61 | { 62 | if (_valueLabel == nil) { 63 | _valueLabel = [[UILabel alloc] init]; 64 | _valueLabel.font = [UIFont systemFontOfSize:30]; 65 | _valueLabel.textColor = [UIColor blackColor]; 66 | } 67 | return _valueLabel; 68 | } 69 | 70 | - (UIImageView *)imageView 71 | { 72 | if (_imageView == nil) { 73 | _imageView = [[UIImageView alloc] init]; 74 | _imageView.contentMode = UIViewContentModeScaleAspectFit; 75 | } 76 | return _imageView; 77 | } 78 | 79 | - (UIButton *)returnButton 80 | { 81 | if (_returnButton == nil) { 82 | _returnButton = [UIButton buttonWithType:UIButtonTypeCustom]; 83 | [_returnButton addTarget:self action:@selector(didTappedReturnButton:) forControlEvents:UIControlEventTouchUpInside]; 84 | [_returnButton setTitle:@"return" forState:UIControlStateNormal]; 85 | [_returnButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; 86 | } 87 | return _returnButton; 88 | } 89 | 90 | @end 91 | -------------------------------------------------------------------------------- /CTMediator/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 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /CTMediator/TableViewController/TableViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.h 3 | // CTMediator 4 | // 5 | // Created by casa on 2016/10/20. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TableViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CTMediator/TableViewController/TableViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.m 3 | // CTMediator 4 | // 5 | // Created by casa on 2016/10/20. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "TableViewController.h" 10 | #import 11 | #import "CTMediator+CTMediatorModuleAActions.h" 12 | 13 | @interface TableViewController () 14 | 15 | @property (nonatomic, strong) UITableView *tableView; 16 | @property (nonatomic, strong) UIButton *closeButton; 17 | 18 | @end 19 | 20 | @implementation TableViewController 21 | 22 | #pragma mark - life cycle 23 | - (void)viewDidLoad { 24 | [super viewDidLoad]; 25 | 26 | [self.view addSubview:self.tableView]; 27 | [self.view addSubview:self.closeButton]; 28 | } 29 | 30 | - (void)viewWillLayoutSubviews 31 | { 32 | [super viewWillLayoutSubviews]; 33 | 34 | [self.tableView fillWidth]; 35 | [self.tableView topInContainer:0 shouldResize:YES]; 36 | [self.tableView bottomInContainer:50 shouldResize:YES]; 37 | 38 | [self.closeButton fillWidth]; 39 | [self.closeButton top:0 FromView:self.tableView]; 40 | [self.closeButton bottomInContainer:0 shouldResize:YES]; 41 | } 42 | 43 | - (void)dealloc 44 | { 45 | // 在Controller被回收的时候,把相关的target也回收掉 46 | [[CTMediator sharedInstance] CTMediator_cleanTableViewCellTarget]; 47 | } 48 | 49 | #pragma mark - UITableViewDelegate 50 | - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath 51 | { 52 | // 通过Mediator来获取cell实例,由于target已经被cache了,高频调用不是问题。 53 | [[CTMediator sharedInstance] CTMediator_configTableViewCell:cell withTitle:@"cell title" atIndexPath:indexPath]; 54 | } 55 | 56 | #pragma mark - UITableViewDataSource 57 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 58 | { 59 | return 100; 60 | } 61 | 62 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 63 | { 64 | // 通过mediator来配置cell实例,由于target已经被cache了,高频调用不是问题。 65 | return [[CTMediator sharedInstance] CTMediator_tableViewCellWithIdentifier:@"cell" tableView:tableView]; 66 | } 67 | 68 | #pragma mark - event response 69 | - (void)didTappedCloseButton:(UIButton *)button 70 | { 71 | [self dismissViewControllerAnimated:YES completion:nil]; 72 | } 73 | 74 | #pragma mark - getters and setters 75 | - (UITableView *)tableView 76 | { 77 | if (_tableView == nil) { 78 | _tableView = [[UITableView alloc] init]; 79 | _tableView.delegate = self; 80 | _tableView.dataSource = self; 81 | } 82 | return _tableView; 83 | } 84 | 85 | - (UIButton *)closeButton 86 | { 87 | if (_closeButton == nil) { 88 | _closeButton = [UIButton buttonWithType:UIButtonTypeCustom]; 89 | [_closeButton setTitle:@"Close" forState:UIControlStateNormal]; 90 | [_closeButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal]; 91 | [_closeButton addTarget:self action:@selector(didTappedCloseButton:) forControlEvents:UIControlEventTouchUpInside]; 92 | _closeButton.backgroundColor = [UIColor grayColor]; 93 | } 94 | return _closeButton; 95 | } 96 | 97 | @end 98 | -------------------------------------------------------------------------------- /CTMediator/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /CTMediator/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import 11 | #import "CTMediator+CTMediatorModuleAActions.h" 12 | #import "TableViewController.h" 13 | 14 | NSString * const kCellIdentifier = @"kCellIdentifier"; 15 | 16 | @interface ViewController () 17 | 18 | @property (nonatomic, strong) UITableView *tableView; 19 | @property (nonatomic, strong) NSArray *dataSource; 20 | 21 | @end 22 | 23 | @implementation ViewController 24 | 25 | #pragma mark - life cycle 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | [self.view addSubview:self.tableView]; 30 | } 31 | 32 | - (void)viewWillLayoutSubviews 33 | { 34 | [super viewWillLayoutSubviews]; 35 | [self.tableView fill]; 36 | } 37 | 38 | #pragma mark - UITableViewDataSource 39 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 40 | { 41 | return self.dataSource.count; 42 | } 43 | 44 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 45 | { 46 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier]; 47 | cell.textLabel.text = self.dataSource[indexPath.row]; 48 | return cell; 49 | } 50 | 51 | #pragma mark - UITableViewDelegate 52 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 53 | { 54 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 55 | 56 | if (indexPath.row == 0) { 57 | UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail]; 58 | 59 | // 获得view controller之后,在这种场景下,到底push还是present,其实是要由使用者决定的,mediator只要给出view controller的实例就好了 60 | [self presentViewController:viewController animated:YES completion:nil]; 61 | } 62 | 63 | if (indexPath.row == 1) { 64 | UIViewController *viewController = [[CTMediator sharedInstance] CTMediator_viewControllerForDetail]; 65 | [self.navigationController pushViewController:viewController animated:YES]; 66 | } 67 | 68 | if (indexPath.row == 2) { 69 | // 这种场景下,很明显是需要被present的,所以不必返回实例,mediator直接present了 70 | [[CTMediator sharedInstance] CTMediator_presentImage:[UIImage imageNamed:@"image"]]; 71 | } 72 | 73 | if (indexPath.row == 3) { 74 | // 这种场景下,参数有问题,因此需要在流程中做好处理 75 | [[CTMediator sharedInstance] CTMediator_presentImage:nil]; 76 | } 77 | 78 | if (indexPath.row == 4) { 79 | [[CTMediator sharedInstance] CTMediator_showAlertWithMessage:@"casa" cancelAction:nil confirmAction:^(NSDictionary *info) { 80 | // 做你想做的事 81 | }]; 82 | } 83 | 84 | if (indexPath.row == 5) { 85 | TableViewController *tableViewController = [[TableViewController alloc] init]; 86 | [self presentViewController:tableViewController animated:YES completion:nil]; 87 | } 88 | 89 | if (indexPath.row == 6) { 90 | [[CTMediator sharedInstance] performTarget:@"InvalidTarget" action:@"InvalidAction" params:nil shouldCacheTarget:NO]; 91 | } 92 | } 93 | 94 | #pragma mark - getters and setters 95 | - (UITableView *)tableView 96 | { 97 | if (_tableView == nil) { 98 | _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; 99 | _tableView.delegate = self; 100 | _tableView.dataSource = self; 101 | 102 | [_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellIdentifier]; 103 | } 104 | return _tableView; 105 | } 106 | 107 | - (NSArray *)dataSource 108 | { 109 | if (_dataSource == nil) { 110 | _dataSource = @[@"present detail view controller", 111 | @"push detail view controller", 112 | @"present image", 113 | @"present image when error", 114 | @"show alert", 115 | @"table view cell", 116 | @"No Target-Action response" 117 | ]; 118 | } 119 | return _dataSource; 120 | } 121 | @end 122 | -------------------------------------------------------------------------------- /CTMediator/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CTMediator 4 | // 5 | // Created by casa on 16/3/13. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CTMediatorUITests/CTMediatorUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTMediatorUITests.m 3 | // CTMediatorUITests 4 | // 5 | // Created by casa on 2016/11/20. 6 | // Copyright © 2016年 casa. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CTMediatorUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation CTMediatorUITests 16 | 17 | - (void)setUp { 18 | [super setUp]; 19 | 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | 22 | // In UI tests it is usually best to stop immediately when a failure occurs. 23 | self.continueAfterFailure = YES; 24 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 25 | [[[XCUIApplication alloc] init] launch]; 26 | 27 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 28 | } 29 | 30 | - (void)tearDown { 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | [super tearDown]; 33 | } 34 | 35 | - (void)testPresentDetailViewController 36 | { 37 | XCUIApplication *app = [[XCUIApplication alloc] init]; 38 | [app.tables.staticTexts[@"present detail view controller"] tap]; 39 | [app.buttons[@"return"] tap]; 40 | XCTAssertTrue(app.exists); 41 | } 42 | 43 | - (void)testCrashTableViewController 44 | { 45 | XCUIApplication *app = [[XCUIApplication alloc] init]; 46 | XCUIElement *cellToCrash = app.tables.staticTexts[@"table view cell"]; 47 | [cellToCrash tap]; 48 | XCTAssertTrue(app.exists); 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /CTMediatorUITests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /FILE_LICENSE: -------------------------------------------------------------------------------- 1 | MIT 2 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '8.0' 3 | # Uncomment this line if you're using Swift 4 | # use_frameworks! 5 | 6 | target 'CTMediator' do 7 | 8 | pod "HandyFrame" 9 | # pod "CTHandyCategories" 10 | 11 | end 12 | 13 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | CTMediator 2 | ========== 3 | 4 | objective-c: 5 | 6 | ``` 7 | pod "CTMediator" 8 | ``` 9 | 10 | swift: 11 | 12 | ``` 13 | use_frameworks! 14 | pod "CTMediator" 15 | ``` 16 | 17 | --- 18 | 19 | 20 | `CTMediator` helps you to divide your project into multi-project, and use `Target-Action` pattern to let subprojects to communicate with each other. *with no regist process!* 21 | 22 | you can check demo for more details: 23 | 24 | [Origin project](https://github.com/ModulizationDemo/MainProject) 25 | 26 | [Modulized Main Project with CTMediator](https://github.com/ModulizationDemo/ModulizedMainProject) 27 | 28 | [Demos](https://github.com/ModulizationDemo) 29 | 30 | [Swift Demo](https://github.com/ModulizationDemo/SwfitDemo) 31 | 32 | add private repo before you run demos: 33 | 34 | ``` 35 | pod repo add PrivatePods https://github.com/ModulizationDemo/PrivatePods.git 36 | ``` 37 | 38 | remember to run `pod install` before you run the demo! 39 | 40 | --- 41 | 42 | [Origin project](https://github.com/ModulizationDemo/MainProject) 43 | 44 | [Modulized Main Project with CTMediator](https://github.com/ModulizationDemo/ModulizedMainProject) 45 | 46 | [Demos](https://github.com/ModulizationDemo) 47 | 48 | [Swift Demo](https://github.com/ModulizationDemo/SwfitDemo) 49 | 50 | 跑demo时先添加私有库: 51 | 52 | ``` 53 | pod repo add PrivatePods https://github.com/ModulizationDemo/PrivatePods.git 54 | ``` 55 | 56 | 然后就可以`pod install`了 57 | 58 | --- 59 | 60 | [iOS应用架构谈 组件化方案](http://casatwy.com/iOS-Modulization.html) 61 | 62 | [在现有工程中实施基于CTMediator的组件化方案](http://casatwy.com/modulization_in_action.html) 63 | 64 | [CTMediator的Swift应用](https://casatwy.com/CTMediator_in_Swift.html) 65 | 66 | --- 67 | 68 | 本工程其实也是个Demo 69 | 70 | `Category`目录在实际工程中是单独的一个repo,调用者通过依赖category这个repo来完成功能调度。一般来说是每一个业务对应一个category的repo。因此调用者需要调度哪个业务,就依赖哪个业务的category。category这个repo由对应提供服务的业务来维护。 71 | 72 | `CTMediator`目录在实际工程中也是一个单独的repo,仅用于存放中间件。被每一个业务线各自维护的category repo所依赖。 73 | 74 | `DemoModule`目录是实际提供服务的业务,这个在实际工程中也是一个单独的repo。这个repo不被任何人所依赖,这个repo通过target-action来提供被调度的功能,然后由category repo通过runtime调度。 75 | 76 | --- 77 | 78 | `CTMediator` helps you to divide your project into multi-project, and use `Target-Action` pattern to let subprojects to communicate with each other. 79 | 80 | you can check demo for more details: 81 | 82 | [Demo](https://github.com/ModulizationDemo) 83 | 84 | [Swift Demo](https://github.com/ModulizationDemo/SwfitDemo) 85 | -------------------------------------------------------------------------------- /upload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git stash 4 | git pull origin master --tags 5 | git stash pop 6 | 7 | VersionString=`grep -E 's.version.*=' CTMediator.podspec` 8 | VersionNumber=`tr -cd 0-9 <<<"$VersionString"` 9 | NewVersionNumber=$(($VersionNumber + 1)) 10 | LineNumber=`grep -nE 's.version.*=' CTMediator.podspec | cut -d : -f1` 11 | 12 | git add . 13 | git commit -am modification 14 | git pull origin master --tags 15 | 16 | sed -i "" "${LineNumber}s/${VersionNumber}/${NewVersionNumber}/g" CTMediator.podspec 17 | 18 | echo "current version is ${VersionNumber}, new version is ${NewVersionNumber}" 19 | 20 | git commit -am ${NewVersionNumber} 21 | git tag ${NewVersionNumber} 22 | git push origin master --tags 23 | pod trunk push ./CTMediator.podspec --verbose --use-libraries --allow-warnings 24 | --------------------------------------------------------------------------------