├── .gitignore ├── CarthageDemo ├── Cartfile ├── Cartfile.resolved ├── CarthageDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── CarthageDemo.xccheckout ├── CarthageDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── ViewController.h │ ├── ViewController.m │ └── main.m ├── CarthageDemoTests │ ├── CarthageDemoTests.m │ └── Info.plist └── README.md ├── LICENSE.md ├── README.md ├── Resources ├── built-frameworks.png ├── carthage-logo-colored.png ├── carthage-logo-gray.png ├── dependency-graph.dot ├── dependency-graph.pdf ├── generate-graphs ├── git-logo.png ├── link-frameworks.png ├── mantle-logo.png ├── me.jpg ├── nested-submodules.png ├── octocat.png ├── reactivecocoa-logo.png ├── release-with-binary.png ├── resolving-1.dot ├── resolving-1.pdf ├── resolving-2.dot ├── resolving-2.pdf ├── resolving-3.dot ├── resolving-3.pdf ├── resolving-4.dot ├── resolving-4.pdf ├── resolving-5.dot ├── resolving-5.pdf ├── run-script-phase.png └── xcode.png ├── Ruthlessly Simple Dependency Management with Carthage.md └── Ruthlessly Simple Dependency Management with Carthage.pdf /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /CarthageDemo/Cartfile: -------------------------------------------------------------------------------- 1 | github "Mantle/Mantle" ~> 1.5 2 | github "ReactiveCocoa/ReactiveCocoa" >= 2.4.7 3 | github "ReactiveCocoa/ReactiveCocoaLayout" == 0.5.2 4 | -------------------------------------------------------------------------------- /CarthageDemo/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "github/Archimedes" "1.1.4" 2 | github "Mantle/Mantle" "1.5.4" 3 | github "ReactiveCocoa/ReactiveCocoa" "v2.4.7" 4 | github "ReactiveCocoa/ReactiveCocoaLayout" "0.5.2" 5 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D0E2ACF21AB0DB6100104C94 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E2ACF11AB0DB6100104C94 /* main.m */; }; 11 | D0E2ACF51AB0DB6100104C94 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E2ACF41AB0DB6100104C94 /* AppDelegate.m */; }; 12 | D0E2ACF81AB0DB6100104C94 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E2ACF71AB0DB6100104C94 /* ViewController.m */; }; 13 | D0E2ACFB1AB0DB6100104C94 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D0E2ACF91AB0DB6100104C94 /* Main.storyboard */; }; 14 | D0E2ACFD1AB0DB6100104C94 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D0E2ACFC1AB0DB6100104C94 /* Images.xcassets */; }; 15 | D0E2AD001AB0DB6100104C94 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = D0E2ACFE1AB0DB6100104C94 /* LaunchScreen.xib */; }; 16 | D0E2AD0C1AB0DB6100104C94 /* CarthageDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = D0E2AD0B1AB0DB6100104C94 /* CarthageDemoTests.m */; }; 17 | D0E2AD231AB0DDD300104C94 /* Archimedes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E2AD151AB0DCA200104C94 /* Archimedes.framework */; }; 18 | D0E2AD241AB0DDD300104C94 /* Mantle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E2AD161AB0DCA200104C94 /* Mantle.framework */; }; 19 | D0E2AD251AB0DDD300104C94 /* ReactiveCocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E2AD171AB0DCA200104C94 /* ReactiveCocoa.framework */; }; 20 | D0E2AD261AB0DDD300104C94 /* ReactiveCocoaLayout.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D0E2AD181AB0DCA200104C94 /* ReactiveCocoaLayout.framework */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | D0E2AD061AB0DB6100104C94 /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = D0E2ACE41AB0DB6100104C94 /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = D0E2ACEB1AB0DB6100104C94; 29 | remoteInfo = CarthageDemo; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | D0E2ACEC1AB0DB6100104C94 /* CarthageDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CarthageDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | D0E2ACF01AB0DB6100104C94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | D0E2ACF11AB0DB6100104C94 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 37 | D0E2ACF31AB0DB6100104C94 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 38 | D0E2ACF41AB0DB6100104C94 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 39 | D0E2ACF61AB0DB6100104C94 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 40 | D0E2ACF71AB0DB6100104C94 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 41 | D0E2ACFA1AB0DB6100104C94 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | D0E2ACFC1AB0DB6100104C94 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 43 | D0E2ACFF1AB0DB6100104C94 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 44 | D0E2AD051AB0DB6100104C94 /* CarthageDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CarthageDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | D0E2AD0A1AB0DB6100104C94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 46 | D0E2AD0B1AB0DB6100104C94 /* CarthageDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CarthageDemoTests.m; sourceTree = ""; }; 47 | D0E2AD151AB0DCA200104C94 /* Archimedes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Archimedes.framework; path = Carthage/Build/iOS/Archimedes.framework; sourceTree = ""; }; 48 | D0E2AD161AB0DCA200104C94 /* Mantle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Mantle.framework; path = Carthage/Build/iOS/Mantle.framework; sourceTree = ""; }; 49 | D0E2AD171AB0DCA200104C94 /* ReactiveCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoa.framework; path = Carthage/Build/iOS/ReactiveCocoa.framework; sourceTree = ""; }; 50 | D0E2AD181AB0DCA200104C94 /* ReactiveCocoaLayout.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveCocoaLayout.framework; path = Carthage/Build/iOS/ReactiveCocoaLayout.framework; sourceTree = ""; }; 51 | /* End PBXFileReference section */ 52 | 53 | /* Begin PBXFrameworksBuildPhase section */ 54 | D0E2ACE91AB0DB6100104C94 /* Frameworks */ = { 55 | isa = PBXFrameworksBuildPhase; 56 | buildActionMask = 2147483647; 57 | files = ( 58 | D0E2AD231AB0DDD300104C94 /* Archimedes.framework in Frameworks */, 59 | D0E2AD241AB0DDD300104C94 /* Mantle.framework in Frameworks */, 60 | D0E2AD251AB0DDD300104C94 /* ReactiveCocoa.framework in Frameworks */, 61 | D0E2AD261AB0DDD300104C94 /* ReactiveCocoaLayout.framework in Frameworks */, 62 | ); 63 | runOnlyForDeploymentPostprocessing = 0; 64 | }; 65 | D0E2AD021AB0DB6100104C94 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | D0E2ACE31AB0DB6100104C94 = { 76 | isa = PBXGroup; 77 | children = ( 78 | D0E2AD151AB0DCA200104C94 /* Archimedes.framework */, 79 | D0E2AD161AB0DCA200104C94 /* Mantle.framework */, 80 | D0E2AD171AB0DCA200104C94 /* ReactiveCocoa.framework */, 81 | D0E2AD181AB0DCA200104C94 /* ReactiveCocoaLayout.framework */, 82 | D0E2ACEE1AB0DB6100104C94 /* CarthageDemo */, 83 | D0E2AD081AB0DB6100104C94 /* CarthageDemoTests */, 84 | D0E2ACED1AB0DB6100104C94 /* Products */, 85 | ); 86 | sourceTree = ""; 87 | }; 88 | D0E2ACED1AB0DB6100104C94 /* Products */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | D0E2ACEC1AB0DB6100104C94 /* CarthageDemo.app */, 92 | D0E2AD051AB0DB6100104C94 /* CarthageDemoTests.xctest */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | D0E2ACEE1AB0DB6100104C94 /* CarthageDemo */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | D0E2ACF31AB0DB6100104C94 /* AppDelegate.h */, 101 | D0E2ACF41AB0DB6100104C94 /* AppDelegate.m */, 102 | D0E2ACF61AB0DB6100104C94 /* ViewController.h */, 103 | D0E2ACF71AB0DB6100104C94 /* ViewController.m */, 104 | D0E2ACF91AB0DB6100104C94 /* Main.storyboard */, 105 | D0E2ACFC1AB0DB6100104C94 /* Images.xcassets */, 106 | D0E2ACFE1AB0DB6100104C94 /* LaunchScreen.xib */, 107 | D0E2ACEF1AB0DB6100104C94 /* Supporting Files */, 108 | ); 109 | path = CarthageDemo; 110 | sourceTree = ""; 111 | }; 112 | D0E2ACEF1AB0DB6100104C94 /* Supporting Files */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | D0E2ACF01AB0DB6100104C94 /* Info.plist */, 116 | D0E2ACF11AB0DB6100104C94 /* main.m */, 117 | ); 118 | name = "Supporting Files"; 119 | sourceTree = ""; 120 | }; 121 | D0E2AD081AB0DB6100104C94 /* CarthageDemoTests */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | D0E2AD0B1AB0DB6100104C94 /* CarthageDemoTests.m */, 125 | D0E2AD091AB0DB6100104C94 /* Supporting Files */, 126 | ); 127 | path = CarthageDemoTests; 128 | sourceTree = ""; 129 | }; 130 | D0E2AD091AB0DB6100104C94 /* Supporting Files */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | D0E2AD0A1AB0DB6100104C94 /* Info.plist */, 134 | ); 135 | name = "Supporting Files"; 136 | sourceTree = ""; 137 | }; 138 | /* End PBXGroup section */ 139 | 140 | /* Begin PBXNativeTarget section */ 141 | D0E2ACEB1AB0DB6100104C94 /* CarthageDemo */ = { 142 | isa = PBXNativeTarget; 143 | buildConfigurationList = D0E2AD0F1AB0DB6100104C94 /* Build configuration list for PBXNativeTarget "CarthageDemo" */; 144 | buildPhases = ( 145 | D0E2ACE81AB0DB6100104C94 /* Sources */, 146 | D0E2ACE91AB0DB6100104C94 /* Frameworks */, 147 | D0E2ACEA1AB0DB6100104C94 /* Resources */, 148 | D0E2AD271AB0DDFE00104C94 /* Run Script */, 149 | ); 150 | buildRules = ( 151 | ); 152 | dependencies = ( 153 | ); 154 | name = CarthageDemo; 155 | productName = CarthageDemo; 156 | productReference = D0E2ACEC1AB0DB6100104C94 /* CarthageDemo.app */; 157 | productType = "com.apple.product-type.application"; 158 | }; 159 | D0E2AD041AB0DB6100104C94 /* CarthageDemoTests */ = { 160 | isa = PBXNativeTarget; 161 | buildConfigurationList = D0E2AD121AB0DB6100104C94 /* Build configuration list for PBXNativeTarget "CarthageDemoTests" */; 162 | buildPhases = ( 163 | D0E2AD011AB0DB6100104C94 /* Sources */, 164 | D0E2AD021AB0DB6100104C94 /* Frameworks */, 165 | D0E2AD031AB0DB6100104C94 /* Resources */, 166 | ); 167 | buildRules = ( 168 | ); 169 | dependencies = ( 170 | D0E2AD071AB0DB6100104C94 /* PBXTargetDependency */, 171 | ); 172 | name = CarthageDemoTests; 173 | productName = CarthageDemoTests; 174 | productReference = D0E2AD051AB0DB6100104C94 /* CarthageDemoTests.xctest */; 175 | productType = "com.apple.product-type.bundle.unit-test"; 176 | }; 177 | /* End PBXNativeTarget section */ 178 | 179 | /* Begin PBXProject section */ 180 | D0E2ACE41AB0DB6100104C94 /* Project object */ = { 181 | isa = PBXProject; 182 | attributes = { 183 | LastUpgradeCheck = 0610; 184 | ORGANIZATIONNAME = Carthage; 185 | TargetAttributes = { 186 | D0E2ACEB1AB0DB6100104C94 = { 187 | CreatedOnToolsVersion = 6.1.1; 188 | }; 189 | D0E2AD041AB0DB6100104C94 = { 190 | CreatedOnToolsVersion = 6.1.1; 191 | TestTargetID = D0E2ACEB1AB0DB6100104C94; 192 | }; 193 | }; 194 | }; 195 | buildConfigurationList = D0E2ACE71AB0DB6100104C94 /* Build configuration list for PBXProject "CarthageDemo" */; 196 | compatibilityVersion = "Xcode 3.2"; 197 | developmentRegion = English; 198 | hasScannedForEncodings = 0; 199 | knownRegions = ( 200 | en, 201 | Base, 202 | ); 203 | mainGroup = D0E2ACE31AB0DB6100104C94; 204 | productRefGroup = D0E2ACED1AB0DB6100104C94 /* Products */; 205 | projectDirPath = ""; 206 | projectRoot = ""; 207 | targets = ( 208 | D0E2ACEB1AB0DB6100104C94 /* CarthageDemo */, 209 | D0E2AD041AB0DB6100104C94 /* CarthageDemoTests */, 210 | ); 211 | }; 212 | /* End PBXProject section */ 213 | 214 | /* Begin PBXResourcesBuildPhase section */ 215 | D0E2ACEA1AB0DB6100104C94 /* Resources */ = { 216 | isa = PBXResourcesBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | D0E2ACFB1AB0DB6100104C94 /* Main.storyboard in Resources */, 220 | D0E2AD001AB0DB6100104C94 /* LaunchScreen.xib in Resources */, 221 | D0E2ACFD1AB0DB6100104C94 /* Images.xcassets in Resources */, 222 | ); 223 | runOnlyForDeploymentPostprocessing = 0; 224 | }; 225 | D0E2AD031AB0DB6100104C94 /* Resources */ = { 226 | isa = PBXResourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | ); 230 | runOnlyForDeploymentPostprocessing = 0; 231 | }; 232 | /* End PBXResourcesBuildPhase section */ 233 | 234 | /* Begin PBXShellScriptBuildPhase section */ 235 | D0E2AD271AB0DDFE00104C94 /* Run Script */ = { 236 | isa = PBXShellScriptBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | ); 240 | inputPaths = ( 241 | "$(SRCROOT)/Carthage/Build/iOS/Archimedes.framework", 242 | "$(SRCROOT)/Carthage/Build/iOS/Mantle.framework", 243 | "$(SRCROOT)/Carthage/Build/iOS/ReactiveCocoa.framework", 244 | "$(SRCROOT)/Carthage/Build/iOS/ReactiveCocoaLayout.framework", 245 | ); 246 | name = "Run Script"; 247 | outputPaths = ( 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | shellPath = /bin/sh; 251 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 252 | }; 253 | /* End PBXShellScriptBuildPhase section */ 254 | 255 | /* Begin PBXSourcesBuildPhase section */ 256 | D0E2ACE81AB0DB6100104C94 /* Sources */ = { 257 | isa = PBXSourcesBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | D0E2ACF81AB0DB6100104C94 /* ViewController.m in Sources */, 261 | D0E2ACF51AB0DB6100104C94 /* AppDelegate.m in Sources */, 262 | D0E2ACF21AB0DB6100104C94 /* main.m in Sources */, 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | D0E2AD011AB0DB6100104C94 /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | D0E2AD0C1AB0DB6100104C94 /* CarthageDemoTests.m in Sources */, 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | /* End PBXSourcesBuildPhase section */ 275 | 276 | /* Begin PBXTargetDependency section */ 277 | D0E2AD071AB0DB6100104C94 /* PBXTargetDependency */ = { 278 | isa = PBXTargetDependency; 279 | target = D0E2ACEB1AB0DB6100104C94 /* CarthageDemo */; 280 | targetProxy = D0E2AD061AB0DB6100104C94 /* PBXContainerItemProxy */; 281 | }; 282 | /* End PBXTargetDependency section */ 283 | 284 | /* Begin PBXVariantGroup section */ 285 | D0E2ACF91AB0DB6100104C94 /* Main.storyboard */ = { 286 | isa = PBXVariantGroup; 287 | children = ( 288 | D0E2ACFA1AB0DB6100104C94 /* Base */, 289 | ); 290 | name = Main.storyboard; 291 | sourceTree = ""; 292 | }; 293 | D0E2ACFE1AB0DB6100104C94 /* LaunchScreen.xib */ = { 294 | isa = PBXVariantGroup; 295 | children = ( 296 | D0E2ACFF1AB0DB6100104C94 /* Base */, 297 | ); 298 | name = LaunchScreen.xib; 299 | sourceTree = ""; 300 | }; 301 | /* End PBXVariantGroup section */ 302 | 303 | /* Begin XCBuildConfiguration section */ 304 | D0E2AD0D1AB0DB6100104C94 /* Debug */ = { 305 | isa = XCBuildConfiguration; 306 | buildSettings = { 307 | ALWAYS_SEARCH_USER_PATHS = NO; 308 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 309 | CLANG_CXX_LIBRARY = "libc++"; 310 | CLANG_ENABLE_MODULES = YES; 311 | CLANG_ENABLE_OBJC_ARC = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_CONSTANT_CONVERSION = YES; 314 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 315 | CLANG_WARN_EMPTY_BODY = YES; 316 | CLANG_WARN_ENUM_CONVERSION = YES; 317 | CLANG_WARN_INT_CONVERSION = YES; 318 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 319 | CLANG_WARN_UNREACHABLE_CODE = YES; 320 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 321 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 322 | COPY_PHASE_STRIP = NO; 323 | ENABLE_STRICT_OBJC_MSGSEND = YES; 324 | GCC_C_LANGUAGE_STANDARD = gnu99; 325 | GCC_DYNAMIC_NO_PIC = NO; 326 | GCC_OPTIMIZATION_LEVEL = 0; 327 | GCC_PREPROCESSOR_DEFINITIONS = ( 328 | "DEBUG=1", 329 | "$(inherited)", 330 | ); 331 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 332 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 333 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 334 | GCC_WARN_UNDECLARED_SELECTOR = YES; 335 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 336 | GCC_WARN_UNUSED_FUNCTION = YES; 337 | GCC_WARN_UNUSED_VARIABLE = YES; 338 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 339 | MTL_ENABLE_DEBUG_INFO = YES; 340 | ONLY_ACTIVE_ARCH = YES; 341 | SDKROOT = iphoneos; 342 | TARGETED_DEVICE_FAMILY = "1,2"; 343 | }; 344 | name = Debug; 345 | }; 346 | D0E2AD0E1AB0DB6100104C94 /* Release */ = { 347 | isa = XCBuildConfiguration; 348 | buildSettings = { 349 | ALWAYS_SEARCH_USER_PATHS = NO; 350 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 351 | CLANG_CXX_LIBRARY = "libc++"; 352 | CLANG_ENABLE_MODULES = YES; 353 | CLANG_ENABLE_OBJC_ARC = YES; 354 | CLANG_WARN_BOOL_CONVERSION = YES; 355 | CLANG_WARN_CONSTANT_CONVERSION = YES; 356 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 357 | CLANG_WARN_EMPTY_BODY = YES; 358 | CLANG_WARN_ENUM_CONVERSION = YES; 359 | CLANG_WARN_INT_CONVERSION = YES; 360 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 361 | CLANG_WARN_UNREACHABLE_CODE = YES; 362 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 363 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 364 | COPY_PHASE_STRIP = YES; 365 | ENABLE_NS_ASSERTIONS = NO; 366 | ENABLE_STRICT_OBJC_MSGSEND = YES; 367 | GCC_C_LANGUAGE_STANDARD = gnu99; 368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 370 | GCC_WARN_UNDECLARED_SELECTOR = YES; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 375 | MTL_ENABLE_DEBUG_INFO = NO; 376 | SDKROOT = iphoneos; 377 | TARGETED_DEVICE_FAMILY = "1,2"; 378 | VALIDATE_PRODUCT = YES; 379 | }; 380 | name = Release; 381 | }; 382 | D0E2AD101AB0DB6100104C94 /* Debug */ = { 383 | isa = XCBuildConfiguration; 384 | buildSettings = { 385 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 386 | FRAMEWORK_SEARCH_PATHS = ( 387 | "$(inherited)", 388 | "$(PROJECT_DIR)/Carthage/Build/iOS", 389 | ); 390 | INFOPLIST_FILE = CarthageDemo/Info.plist; 391 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | }; 394 | name = Debug; 395 | }; 396 | D0E2AD111AB0DB6100104C94 /* Release */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 400 | FRAMEWORK_SEARCH_PATHS = ( 401 | "$(inherited)", 402 | "$(PROJECT_DIR)/Carthage/Build/iOS", 403 | ); 404 | INFOPLIST_FILE = CarthageDemo/Info.plist; 405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | }; 408 | name = Release; 409 | }; 410 | D0E2AD131AB0DB6100104C94 /* Debug */ = { 411 | isa = XCBuildConfiguration; 412 | buildSettings = { 413 | BUNDLE_LOADER = "$(TEST_HOST)"; 414 | FRAMEWORK_SEARCH_PATHS = ( 415 | "$(SDKROOT)/Developer/Library/Frameworks", 416 | "$(inherited)", 417 | ); 418 | GCC_PREPROCESSOR_DEFINITIONS = ( 419 | "DEBUG=1", 420 | "$(inherited)", 421 | ); 422 | INFOPLIST_FILE = CarthageDemoTests/Info.plist; 423 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 424 | PRODUCT_NAME = "$(TARGET_NAME)"; 425 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CarthageDemo.app/CarthageDemo"; 426 | }; 427 | name = Debug; 428 | }; 429 | D0E2AD141AB0DB6100104C94 /* Release */ = { 430 | isa = XCBuildConfiguration; 431 | buildSettings = { 432 | BUNDLE_LOADER = "$(TEST_HOST)"; 433 | FRAMEWORK_SEARCH_PATHS = ( 434 | "$(SDKROOT)/Developer/Library/Frameworks", 435 | "$(inherited)", 436 | ); 437 | INFOPLIST_FILE = CarthageDemoTests/Info.plist; 438 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 439 | PRODUCT_NAME = "$(TARGET_NAME)"; 440 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CarthageDemo.app/CarthageDemo"; 441 | }; 442 | name = Release; 443 | }; 444 | /* End XCBuildConfiguration section */ 445 | 446 | /* Begin XCConfigurationList section */ 447 | D0E2ACE71AB0DB6100104C94 /* Build configuration list for PBXProject "CarthageDemo" */ = { 448 | isa = XCConfigurationList; 449 | buildConfigurations = ( 450 | D0E2AD0D1AB0DB6100104C94 /* Debug */, 451 | D0E2AD0E1AB0DB6100104C94 /* Release */, 452 | ); 453 | defaultConfigurationIsVisible = 0; 454 | defaultConfigurationName = Release; 455 | }; 456 | D0E2AD0F1AB0DB6100104C94 /* Build configuration list for PBXNativeTarget "CarthageDemo" */ = { 457 | isa = XCConfigurationList; 458 | buildConfigurations = ( 459 | D0E2AD101AB0DB6100104C94 /* Debug */, 460 | D0E2AD111AB0DB6100104C94 /* Release */, 461 | ); 462 | defaultConfigurationIsVisible = 0; 463 | defaultConfigurationName = Release; 464 | }; 465 | D0E2AD121AB0DB6100104C94 /* Build configuration list for PBXNativeTarget "CarthageDemoTests" */ = { 466 | isa = XCConfigurationList; 467 | buildConfigurations = ( 468 | D0E2AD131AB0DB6100104C94 /* Debug */, 469 | D0E2AD141AB0DB6100104C94 /* Release */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | /* End XCConfigurationList section */ 475 | }; 476 | rootObject = D0E2ACE41AB0DB6100104C94 /* Project object */; 477 | } 478 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo.xcodeproj/project.xcworkspace/xcshareddata/CarthageDemo.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 53479702-DEE1-4B52-8023-F75C270A6425 9 | IDESourceControlProjectName 10 | CarthageDemo 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 84311B14F2987374E438BCE310AC5B7E31FCB061 14 | https://github.com/jspahrsummers/carthage-talk.git 15 | 16 | IDESourceControlProjectPath 17 | CarthageDemo/CarthageDemo.xcodeproj 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 84311B14F2987374E438BCE310AC5B7E31FCB061 21 | ../../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/jspahrsummers/carthage-talk.git 25 | IDESourceControlProjectVersion 26 | 111 27 | IDESourceControlProjectWCCIdentifier 28 | 84311B14F2987374E438BCE310AC5B7E31FCB061 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 84311B14F2987374E438BCE310AC5B7E31FCB061 36 | IDESourceControlWCCName 37 | carthage-talk 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // CarthageDemo 4 | // 5 | // Created by Justin Spahr-Summers on 2015-03-11. 6 | // Copyright (c) 2015 Carthage. 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 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // CarthageDemo 4 | // 5 | // Created by Justin Spahr-Summers on 2015-03-11. 6 | // Copyright (c) 2015 Carthage. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // 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. 25 | // 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. 26 | } 27 | 28 | - (void)applicationDidEnterBackground:(UIApplication *)application { 29 | // 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. 30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 31 | } 32 | 33 | - (void)applicationWillEnterForeground:(UIApplication *)application { 34 | // 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. 35 | } 36 | 37 | - (void)applicationDidBecomeActive:(UIApplication *)application { 38 | // 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. 39 | } 40 | 41 | - (void)applicationWillTerminate:(UIApplication *)application { 42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/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 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.carthage.$(PRODUCT_NAME:rfc1034identifier) 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 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // CarthageDemo 4 | // 5 | // Created by Justin Spahr-Summers on 2015-03-11. 6 | // Copyright (c) 2015 Carthage. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // CarthageDemo 4 | // 5 | // Created by Justin Spahr-Summers on 2015-03-11. 6 | // Copyright (c) 2015 Carthage. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)viewDidLoad { 18 | [super viewDidLoad]; 19 | // Do any additional setup after loading the view, typically from a nib. 20 | } 21 | 22 | - (void)didReceiveMemoryWarning { 23 | [super didReceiveMemoryWarning]; 24 | // Dispose of any resources that can be recreated. 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CarthageDemo 4 | // 5 | // Created by Justin Spahr-Summers on 2015-03-11. 6 | // Copyright (c) 2015 Carthage. 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 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemoTests/CarthageDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CarthageDemoTests.m 3 | // CarthageDemoTests 4 | // 5 | // Created by Justin Spahr-Summers on 2015-03-11. 6 | // Copyright (c) 2015 Carthage. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface CarthageDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation CarthageDemoTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /CarthageDemo/CarthageDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.carthage.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /CarthageDemo/README.md: -------------------------------------------------------------------------------- 1 | This is a demo project created for the presentation, intended to demonstrate how 2 | to install Carthage dependencies into an application. 3 | 4 | To see Carthage in action, simply install it: 5 | 6 | ```sh 7 | brew update 8 | brew install carthage 9 | ``` 10 | 11 | then bootstrap the demo: 12 | 13 | ```sh 14 | cd CarthageDemo 15 | carthage bootstrap 16 | ``` 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Presentation text copyright (c) 2015 Justin Spahr-Summers 2 | 3 | The presentation text is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruthlessly Simple Dependency Management with Carthage 2 | 3 | _[Presented and recorded](http://realm.io/news/swift-dependency-management-with-carthage/) at the [Swift Language User Group](http://www.meetup.com/swift-language), using [Deckset](http://www.decksetapp.com) theme “Sketchnote.”_ 4 | 5 | [Carthage](https://github.com/Carthage/Carthage) is a new dependency manager for Objective-C and Swift projects, intended to be the simplest way to add frameworks to a Cocoa application. Carthage works by delegating tasks to Xcode and Git, minimizing new concepts as much as possible, so you can continue to use the tools you’re already familiar with. 6 | 7 | This talk will explain what Carthage is and how to use it, then dive into the philosophy of ruthless simplicity that inspired the project. We’ll also compare and contrast Carthage with [CocoaPods](http://cocoapods.org), the original dependency manager for Cocoa. Finally, we’ll explore how Carthage is architected under-the-hood, and the benefits we’ve seen from writing it completely in Swift. 8 | -------------------------------------------------------------------------------- /Resources/built-frameworks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/built-frameworks.png -------------------------------------------------------------------------------- /Resources/carthage-logo-colored.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/carthage-logo-colored.png -------------------------------------------------------------------------------- /Resources/carthage-logo-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/carthage-logo-gray.png -------------------------------------------------------------------------------- /Resources/dependency-graph.dot: -------------------------------------------------------------------------------- 1 | digraph dependencygraph { 2 | bgcolor=transparent; 3 | 4 | center=1; 5 | node [shape=box, style=rounded, color=white, fontcolor=white, height=0.45, fontsize=18, fontname=Helvetica]; 6 | edge [color=white]; 7 | 8 | "GitHub for Mac" -> Mantle; 9 | "GitHub for Mac" -> Squirrel -> Mantle; 10 | "GitHub for Mac" -> OctoKit -> ReactiveCocoa; 11 | "GitHub for Mac" -> ReactiveCocoa; 12 | "GitHub for Mac" -> ReactiveViewModel -> ReactiveCocoa; 13 | OctoKit -> Mantle; 14 | Squirrel -> ReactiveCocoa; 15 | } 16 | -------------------------------------------------------------------------------- /Resources/dependency-graph.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/dependency-graph.pdf -------------------------------------------------------------------------------- /Resources/generate-graphs: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SCRIPT_DIR=$(dirname "$0") 4 | 5 | find "$SCRIPT_DIR" -name "*.dot" | while read line 6 | do 7 | dot -Tpdf "$line" > "${line%dot}pdf" 8 | done 9 | -------------------------------------------------------------------------------- /Resources/git-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/git-logo.png -------------------------------------------------------------------------------- /Resources/link-frameworks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/link-frameworks.png -------------------------------------------------------------------------------- /Resources/mantle-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/mantle-logo.png -------------------------------------------------------------------------------- /Resources/me.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/me.jpg -------------------------------------------------------------------------------- /Resources/nested-submodules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/nested-submodules.png -------------------------------------------------------------------------------- /Resources/octocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/octocat.png -------------------------------------------------------------------------------- /Resources/reactivecocoa-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/reactivecocoa-logo.png -------------------------------------------------------------------------------- /Resources/release-with-binary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/release-with-binary.png -------------------------------------------------------------------------------- /Resources/resolving-1.dot: -------------------------------------------------------------------------------- 1 | digraph dependencygraph { 2 | center=1; 3 | node [shape=box, style=rounded, color=black, fontcolor=black, height=0.45, fontsize=18, fontname=Helvetica]; 4 | edge [color=black]; 5 | 6 | Demo -> ReactiveCocoa [label=" 3.0 "]; 7 | Demo -> ReactiveCocoaLayout [label=" 0.5.2 "]; 8 | Demo -> Mantle [label=" 1.5.4 "]; 9 | } 10 | -------------------------------------------------------------------------------- /Resources/resolving-1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/resolving-1.pdf -------------------------------------------------------------------------------- /Resources/resolving-2.dot: -------------------------------------------------------------------------------- 1 | digraph dependencygraph { 2 | center=1; 3 | node [shape=box, style=rounded, color=black, fontcolor=black, height=0.45, fontsize=18, fontname=Helvetica]; 4 | edge [color=black]; 5 | 6 | Demo -> ReactiveCocoa [label=" 3.0 "]; 7 | Demo -> ReactiveCocoaLayout [label=" 0.5.2 "]; 8 | ReactiveCocoaLayout -> Archimedes [label=" 1.1.4 "]; 9 | ReactiveCocoaLayout -> ReactiveCocoa [label=" 2.4.7 "]; 10 | Demo -> Mantle [label=" 1.5.4 "]; 11 | } 12 | -------------------------------------------------------------------------------- /Resources/resolving-2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/resolving-2.pdf -------------------------------------------------------------------------------- /Resources/resolving-3.dot: -------------------------------------------------------------------------------- 1 | digraph dependencygraph { 2 | center=1; 3 | node [shape=box, style=rounded, color=black, fontcolor=black, height=0.45, fontsize=18, fontname=Helvetica]; 4 | edge [color=black]; 5 | 6 | Demo -> ReactiveCocoa [label=" 3.0 ", fontcolor=red, color=red]; 7 | Demo -> ReactiveCocoaLayout [label=" 0.5.2 "]; 8 | ReactiveCocoaLayout -> Archimedes [label=" 1.1.4 "]; 9 | ReactiveCocoaLayout -> ReactiveCocoa [label=" 2.4.7 ", fontcolor=red, color=red]; 10 | Demo -> Mantle [label=" 1.5.4 "]; 11 | } 12 | -------------------------------------------------------------------------------- /Resources/resolving-3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/resolving-3.pdf -------------------------------------------------------------------------------- /Resources/resolving-4.dot: -------------------------------------------------------------------------------- 1 | digraph dependencygraph { 2 | center=1; 3 | node [shape=box, style=rounded, color=black, fontcolor=black, height=0.45, fontsize=18, fontname=Helvetica]; 4 | edge [color=black]; 5 | 6 | Demo -> ReactiveCocoa [label=" 2.4.7 ", fontcolor=blue, color=blue]; 7 | Demo -> ReactiveCocoaLayout [label=" 0.5.2 "]; 8 | Demo -> Mantle [label=" 1.5.4 "]; 9 | } 10 | -------------------------------------------------------------------------------- /Resources/resolving-4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/resolving-4.pdf -------------------------------------------------------------------------------- /Resources/resolving-5.dot: -------------------------------------------------------------------------------- 1 | digraph dependencygraph { 2 | center=1; 3 | node [shape=box, style=rounded, color=black, fontcolor=black, height=0.45, fontsize=18, fontname=Helvetica]; 4 | edge [color=black]; 5 | 6 | Demo -> ReactiveCocoa [label=" 2.4.7 ", fontcolor=blue, color=blue]; 7 | Demo -> ReactiveCocoaLayout [label=" 0.5.2 "]; 8 | ReactiveCocoaLayout -> Archimedes [label=" 1.1.4 "]; 9 | ReactiveCocoaLayout -> ReactiveCocoa [label=" 2.4.7 ", fontcolor=blue, color=blue]; 10 | Demo -> Mantle [label=" 1.5.4 "]; 11 | } 12 | -------------------------------------------------------------------------------- /Resources/resolving-5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/resolving-5.pdf -------------------------------------------------------------------------------- /Resources/run-script-phase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/run-script-phase.png -------------------------------------------------------------------------------- /Resources/xcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Resources/xcode.png -------------------------------------------------------------------------------- /Ruthlessly Simple Dependency Management with Carthage.md: -------------------------------------------------------------------------------- 1 | # [fit] Ruthlessly Simple 2 | # [fit] Dependency Management 3 | ## with 4 | # [fit] Carthage 5 | 6 | ![fit](Resources/carthage-logo-gray.png) 7 | 8 | ^ Carthage is a dependency manager, intended to be simplest way to add frameworks to your Cocoa project. Carthage is also the first dependency manager to officially support Swift, and is written completely in Swift itself. 9 | 10 | ^ (logo by Reda Lemeden) 11 | 12 | --- 13 | 14 | # What 15 | # Why 16 | # How to use it 17 | # How it works 18 | 19 | ^ In this talk, I’ll explain exactly what Carthage is, _why_ it exists, how to use it, and then go into some of the inner workings as well. 20 | 21 | --- 22 | 23 | # [fit] Justin Spahr-Summers 24 | # @jspahrsummers 25 | 26 | ![inline fit](Resources/me.jpg) ![inline fit](Resources/octocat.png) ![inline fit](Resources/mantle-logo.png) ![inline fit](Resources/reactivecocoa-logo.png) 27 | 28 | ^ I’m a desktop developer at GitHub, primarily working on GitHub for Mac. Besides Carthage, I’ve also played a major role in Mantle and ReactiveCocoa, and several other open source libraries. 29 | 30 | --- 31 | 32 | # [fit] The Problem 33 | 34 | ^ One of the first things we learn as programmers is that we should reuse code. Why waste time and energy writing the same thing over and over? 35 | 36 | ^ Reusable code is put into _libraries_ or _frameworks_, which encapsulate components that may be useful in multiple other projects. 37 | 38 | --- 39 | 40 | # Dependencies in Cocoa **(historically)** 41 | 42 | 1. Copied source files 43 | 1. Zipped binaries 44 | 1. SVN externals, Git submodules 45 | 1. Git subtrees 46 | 47 | ^ Before CocoaPods and Carthage, there were these ways to distribute Cocoa libraries, all of them terrible. 48 | 49 | ^ Source files and binaries aren’t versioned, making it hard to avoid conflicts and stay up-to-date. On the other hand, the Subversion, Git, Mercurial, etc. options are often a pain to use in their own right. 50 | 51 | --- 52 | 53 | # Dependencies on other platforms 54 | 55 | 1. Dependency managers 56 | 57 | ^ Most modern platforms have dependency managers, usually created by the same team that designs the language. 58 | 59 | ^ A dependency manager helps you pick the right version for each library you want, and then sets up each library so you can use it. 60 | 61 | --- 62 | 63 | # [fit] _Our_ Problem 64 | 65 | ^ That’s why dependency management is important in general. But why Carthage? What specific problem were we trying to solve? 66 | 67 | --- 68 | 69 | > GitHub for Mac has what could be called “excessively nested submodules.” 70 | --Me, late 2014 71 | 72 | ![](Resources/nested-submodules.png) 73 | 74 | ^ For the longest time, GitHub for Mac imported dependencies exclusively through Git submodules and Xcode subprojects. This works well until two or more of your dependencies have a shared dependency. 75 | 76 | --- 77 | 78 | ![original fit](Resources/dependency-graph.pdf) 79 | 80 | ^ We used RAC and Mantle within several of our other libraries. Because the app must pick _one_ version of RAC to link, problems can arise if projects expect different versions of RAC. 81 | 82 | ^ Most dangerously, there’s often no indication of conflicts like these, unless you’re lucky and get a build failure. 83 | 84 | --- 85 | 86 | # [fit] Why not use 87 | # [fit] CocoaPods? 88 | 89 | ^ Although submodules are problematic in some ways, we also weren’t interested in using CocoaPods. 90 | 91 | --- 92 | 93 | # Podspecs 94 | 95 | ^ As library authors, we were frustrated with CocoaPods’ requirement that we add podspecs for all of our projects. This information already exists in Xcode and Git, so why should we duplicate it? 96 | 97 | --- 98 | 99 | # Less control 100 | 101 | ^ As users, we were frustrated with CocoaPods’ control of the project setup process. We know how to set up Xcode projects, and the automated setup takes away our flexibility. 102 | 103 | ^ CocoaPods does have a `--no-integrate` option, which stops it from manipulating your Xcode project, but you still have to use its generated Pods project. You don’t get the choice to integrate dependencies via their own Xcode projects, for example. 104 | 105 | --- 106 | 107 | # Centralized 108 | 109 | ^ We think CocoaPods’ centralized package list makes library authors’ jobs harder, because they’re now responsible for deploying to yet another place. 110 | 111 | ^ It also makes it harder to use library versions that haven’t been submitted, and to use libraries without explicit CocoaPods support. 112 | 113 | --- 114 | 115 | # :cry: 116 | 117 | ^ Of course, whether these are positives or negatives depend on what you want. If you’re happy with CocoaPods, continue to use it! 118 | 119 | ^ For us, it became clear that the existing solution wouldn’t work, and that we weren’t in a position to change it. We wanted a tool with a completely different philosophy. 120 | 121 | --- 122 | 123 | ## @robrix 124 | ## @mdiep 125 | ## @keithduncan 126 | ## @alanjrogers 127 | 128 | ^ During one of GitHub’s “Open Source Fridays,” these folks and I started discussing what a new dependency manager for Cocoa might look like. 129 | 130 | --- 131 | 132 | # Our goals 133 | # [fit] ![inline fit](Resources/git-logo.png) ⇄ ![inline fit](Resources/carthage-logo-colored.png) ⇆ ![inline fit](Resources/xcode.png) 134 | 135 | 1. Pick compatible versions for all dependencies 136 | 1. Check out dependencies with Git 137 | 1. Build frameworks with Xcode 138 | 139 | ^ We wanted a simple coordinator between Xcode and Git, plus a constraint solver for resolving dependency versions. 140 | 141 | ^ All the tool should do on its own is find the right version, and the rest should just be communicating with Xcode and Git. 142 | 143 | --- 144 | 145 | # [fit] Using Carthage 146 | 147 | ^ How do you actually set up an application project with Carthage? 148 | 149 | --- 150 | 151 | # Step 1: Create a Cartfile 152 | 153 | ``` 154 | github "Mantle/Mantle" ~> 1.5 155 | github "ReactiveCocoa/ReactiveCocoa" >= 2.4.7 156 | github "ReactiveCocoa/ReactiveCocoaLayout" == 0.5.2 157 | ``` 158 | 159 | ^ Let’s set up a project with Mantle, ReactiveCocoa, and the less well-known ReactiveCocoaLayout. (Explain each line of the Cartfile.) 160 | 161 | --- 162 | 163 | # Step 2: Run Carthage 164 | 165 | ``` 166 | $ carthage update 167 | *** Fetching Mantle 168 | *** Fetching ReactiveCocoa 169 | *** Fetching ReactiveCocoaLayout 170 | *** Fetching Archimedes 171 | *** Downloading Archimedes at "1.1.4" 172 | *** Downloading Mantle at "1.5.4" 173 | *** Downloading ReactiveCocoa at "v2.4.7" 174 | *** Downloading ReactiveCocoaLayout at "0.5.2" 175 | ``` 176 | 177 | ^ We’ll run `carthage update` to download and install all the dependencies. 178 | 179 | ^ Notice that it pulled in Archimedes, which is a dependency of ReactiveCocoaLayout, and that it picked the newest compatible version of Mantle available. 180 | 181 | --- 182 | 183 | # Step 3: Link Frameworks 184 | 185 | ![inline](Resources/built-frameworks.png) ![inline](Resources/link-frameworks.png) 186 | 187 | ^ Now that we have built frameworks on disk, we simply need to drag them into the “Linked Frameworks and Libraries” section of the app target. 188 | 189 | ^ On OS X, these can go directly in the “Embedded Binaries” section. 190 | 191 | --- 192 | 193 | # Step 4: Strip architectures (iOS only) 194 | 195 | ![inline](Resources/run-script-phase.png) 196 | 197 | ^ Unfortunately, there’s an App Store submission bug that disallows universal framework binaries, but Carthage offers a command to automatically remove the simulator bits. We can run that command from a Run Script build phase. 198 | 199 | ^ I’m hoping that Apple eventually fixes my radar and makes this step unnecessary. 200 | 201 | --- 202 | 203 | # That’s it! 204 | 205 | ^ We now have an app using all of those frameworks. Others using that project can get the same frameworks by running `carthage bootstrap`, and it’s easy to update to newer versions in the future by running `carthage update` again. 206 | 207 | --- 208 | 209 | # [fit] “Ruthlessly Simple” 210 | 211 | ^ These steps hint at the philosophy behind Carthage. 212 | 213 | ^ The prevailing design goal, overriding almost all others, is “ruthless simplicity.” We want a tool that is as simple as possible, and will try very hard to avoid features that add significant complexity. 214 | 215 | --- 216 | 217 | ## _Easy:_ familiar or approachable 218 | ## _Simple:_ fewer concepts and concerns[^1] 219 | 220 | [^1]: See Rich Hickey’s talk, [Simple Made Easy](http://www.infoq.com/presentations/Simple-Made-Easy) 221 | 222 | ^ Simple and easy are not the same thing. 223 | 224 | --- 225 | 226 | # CocoaPods is _easy_ 227 | # Carthage is _simple_ 228 | 229 | ^ CocoaPods is all about making it as easy as possible to find and use libraries, but it achieves those goals at the cost of complexity. It becomes _easier_ but less _simple_. 230 | 231 | --- 232 | 233 | # [fit] Simpler tools are… 234 | 235 | ^ With Carthage, we really wanted to focus more on simplicity, because we believe that the benefits are enormous. For example, simpler tools are… 236 | 237 | --- 238 | 239 | # Easier to _understand_ 240 | 241 | ^ Simplicity makes it easier to form a mental model of what’s going on. If something goes wrong, understanding what the tool does may help you resolve the issue. 242 | 243 | --- 244 | 245 | # Easier to _maintain_ 246 | # Easier to _contribute_ to 247 | 248 | ^ By keeping things simple, we don’t need to handle as many edge cases. And by integrating with other tools, like Xcode and Git, we delegate responsibility to them, resulting in less code (and less complexity) within Carthage. 249 | 250 | ^ It’s a lot easier to implement fixes or new features within a simple codebase than a complex one. 251 | 252 | --- 253 | 254 | # More _flexible_ 255 | # More _composable_ 256 | 257 | ^ It’s impossible for us to predict all the possible ways that someone might want to use our software. 258 | 259 | ^ If there’s a use case out there that we’re not anticipating, it’s a lot easier to bend Carthage to your will than to convince a complex tool to do exactly what you want. 260 | 261 | --- 262 | 263 | # [fit] _Enhanced_ when other tools improve 264 | 265 | ^ Wherever we hand off responsibility to Xcode and Git, we automatically benefit from improvements made to those tools, with little or no effort on our part. 266 | 267 | ^ On the other hand, the more we try to duplicate their functionality, the more fragile our system becomes, and the more we lose when those tools change. 268 | 269 | --- 270 | 271 | # [fit] Behind the Scenes 272 | 273 | ^ Now, onto the mechanics of how it works! 274 | 275 | --- 276 | 277 | ## _Parse_ the Cartfile 278 | ## _Resolve_ the dependency graph 279 | ## _Download_ all dependencies 280 | ## _Build_ each framework 281 | 282 | ^ These are the high-level steps that Carthage performs when you run `carthage update`. 283 | 284 | ^ Let’s dive into each step. 285 | 286 | --- 287 | 288 | # :mag: Parsing 289 | 290 | **Parse [OGDL](http://www.ogdl.org) into a list of dependencies** 291 | 292 | ``` 293 | github "ReactiveCocoa/ReactiveCocoa" >= 2.4.7 294 | ``` 295 | 296 | ^ Carthage files are written in a subset of the Ordered Graph Data Language, a dead simple language that’s useful for configuration files like this. 297 | 298 | --- 299 | 300 | # :mag: Parsing 301 | 302 | **Parse [OGDL](http://www.ogdl.org) into a list of dependencies** 303 | 304 | ``` 305 | github "ReactiveCocoa/ReactiveCocoa" >= 2.4.7 306 | ``` 307 | 308 | **Determine the type of each dependency** 309 | 310 | ``` 311 | github "ReactiveCocoa/ReactiveCocoa" 312 | ``` 313 | 314 | ^ Currently, dependencies can be GitHub repositories or arbitrary Git repositories. We may add other types in the future, but this covers most of the use cases already. 315 | 316 | --- 317 | 318 | # :mag: Parsing 319 | 320 | **Parse [OGDL](http://www.ogdl.org) into a list of dependencies** 321 | 322 | ``` 323 | github "ReactiveCocoa/ReactiveCocoa" >= 2.4.7 324 | ``` 325 | 326 | **Determine the type of each dependency** 327 | 328 | ``` 329 | github "ReactiveCocoa/ReactiveCocoa" 330 | ``` 331 | 332 | **Parse any [Semantic Version](http://semver.org)** 333 | 334 | ``` 335 | >= 2.4.7 336 | ``` 337 | 338 | ^ Determine what the version number is, if any, and what it means: “equal to”, “compatible with”, or “at least.” If there’s no version number, it means “pick latest.” You can also specify a branch, tag, or commit by name. 339 | 340 | --- 341 | 342 | # :recycle: Resolving 343 | 344 | _CarthageDemo/Cartfile:_ 345 | 346 | ``` 347 | github "Mantle/Mantle" ~> 1.5 348 | github "ReactiveCocoa/ReactiveCocoa" >= 2.4.7 349 | github "ReactiveCocoa/ReactiveCocoaLayout" == 0.5.2 350 | ``` 351 | 352 | ^ Now we know the dependencies we want, so it’s time to create a directed acyclic graph representing the versions of and relationships between the dependencies. 353 | 354 | --- 355 | 356 | # :recycle: Resolving 357 | 358 | **Create a graph** of the latest dependency versions 359 | 360 | ![inline](Resources/resolving-1.pdf) 361 | 362 | ^ We start by trying the latest allowed version for every dependency. 99% of the time, these are the versions will end up in the final graph. 363 | 364 | --- 365 | 366 | # :recycle: Resolving 367 | 368 | **Insert dependency Cartfiles** into the graph 369 | 370 | ![inline](Resources/resolving-2.pdf) 371 | 372 | ^ Now we need to go to each dependency’s repository _at the version we picked_, and look for a Cartfile there. 373 | 374 | ^ If we find one, we need to include those dependencies in the graph, and pick some candidate versions for those nested dependencies too. 375 | 376 | --- 377 | 378 | # :recycle: Resolving 379 | 380 | **If requirements conflict**, throw out the graph 381 | 382 | ![inline](Resources/resolving-3.pdf) 383 | 384 | ^ Okay, now we have a graph with some possible versions locked in. 385 | 386 | ^ Unfortunately, we picked ReactiveCocoa 3.0, while ReactiveCocoaLayout only allows 2.4.7! When this happens, the graph is _inconsistent_ and must be thrown out, because we can’t satisfy both at the same time. 387 | 388 | --- 389 | 390 | # :recycle: Resolving 391 | 392 | **Try a new graph** with the next possible version 393 | 394 | ![inline](Resources/resolving-4.pdf) 395 | 396 | ^ If this happens, and the graph gets thrown out, we decrement one of the version numbers and start over. 397 | 398 | --- 399 | 400 | # :recycle: Resolving 401 | 402 | **Repeat** until a valid graph is found 403 | 404 | ![inline](Resources/resolving-5.pdf) 405 | 406 | ^ We keep doing that until we find a set of versions that are all compatible with each other. 407 | 408 | ^ This is inefficient in the worst case, but performs surprisingly well in practice. Part of it is because we throw out graphs the moment they become invalid, and part of it is because we automatically terminate when we find the first valid solution. 409 | 410 | --- 411 | 412 | # :floppy_disk: Downloading 413 | 414 | 1. **Fetch the repository** into Carthage’s cache 415 | 416 | ^ Carthage maintains a global cache of dependency repositories, so you don’t have to download the same thing ten times across all of your projects. 417 | 418 | ^ So, the first step of downloading a dependency is making sure that cache is up-to-date. 419 | 420 | --- 421 | 422 | # :floppy_disk: Downloading 423 | 424 | 1. **Fetch the repository** into Carthage’s cache 425 | 1. **Copy the right version** into Carthage/Checkouts 426 | 427 | ^ Then, we use `git checkout` to copy the repository files (without the Git metadata) into the project’s Carthage/Checkouts folder. 428 | 429 | --- 430 | 431 | # :hammer: Building 432 | 433 | 1. **Symlink Carthage/Build** into the dependency folder 434 | 435 | ^ Once every dependency has been downloaded, we link all the Carthage/Build folders together. This ensures that any frameworks we build can be used by the _later_ projects we build. 436 | 437 | --- 438 | 439 | # :hammer: Building 440 | 441 | 1. **Symlink Carthage/Build** into the dependency folder 442 | 1. **List framework schemes** from the `.xcodeproj` 443 | 444 | ^ In the root-most Xcode project, we use `xcodebuild -list` to find all schemes that build a dynamic framework. Static libraries and other products are ignored. 445 | 446 | --- 447 | 448 | # :hammer: Building 449 | 450 | 1. **Symlink Carthage/Build** into the dependency folder 451 | 1. **List framework schemes** from the `.xcodeproj` 452 | 1. **Build each scheme** for all supported architectures 453 | 454 | ^ Now we can use `xcodebuild` to actually build. By looking at the target platform for the scheme, we determine which architectures to build for. 455 | 456 | --- 457 | 458 | # :hammer: Building 459 | 460 | 1. **Symlink Carthage/Build** into the dependency folder 461 | 1. **List framework schemes** from the `.xcodeproj` 462 | 1. **Build each scheme** for all supported architectures 463 | 1. **Combine multiple architectures** with `lipo` 464 | 465 | ^ Once all of the architectures have been built, we combine the products into one binary using the `lipo` tool. 466 | 467 | --- 468 | 469 | # :hammer: Building 470 | 471 | 1. **Symlink Carthage/Build** into the dependency folder 472 | 1. **List framework schemes** from the `.xcodeproj` 473 | 1. **Build each scheme** for all supported architectures 474 | 1. **Combine multiple architectures** with `lipo` 475 | 1. **Copy the built frameworks** into Carthage/Build 476 | 477 | ^ The final universal framework then gets copied into the Carthage/Build folder, for the parent project to find. 478 | 479 | --- 480 | 481 | # :sparkles: BONUS: Prebuilt binaries! :sparkles: 482 | 483 | ![inline](Resources/release-with-binary.png) 484 | 485 | ``` 486 | *** Downloading ReactiveCocoa at "v2.4.7" 487 | ``` 488 | 489 | ^ You may have noticed earlier that Carthage didn’t actually _build_ the dependencies listed in the Cartfile. That’s because each of these projects have GitHub Releases with binaries attached. 490 | 491 | ^ When possible, Carthage will download binaries instead of building from scratch, saving you time. On GitHub for Mac, this cut build times by almost 70%, from 9.5 minutes to about 3! 492 | 493 | --- 494 | 495 | # [fit] CarthageKit 496 | 497 | ^ Most of Carthage’s core behavior is actually implemented as a framework of its own, CarthageKit. This increases modularity and makes testing easier, but it’s also intended to make Carthage integration easier. 498 | 499 | ^ If you want to build a tool on top of Carthage, or compatible with Carthage, CarthageKit is your friend. Using CarthageKit is a great way to add features without increasing the complexity of Carthage itself. 500 | 501 | --- 502 | 503 | # [fit] Technical Choices 504 | 505 | ^ I’d also like to explain just a few of the technical choices we made while building Carthage. 506 | 507 | --- 508 | 509 | # Dynamic frameworks vs. static libraries 510 | 511 | - iOS 8+ only 512 | - Can include resources 513 | - Self-contained and ready-to-use 514 | - Avoid duplicate symbol errors 515 | - **Required for Swift** 516 | 517 | ^ Carthage uses frameworks and not static libraries, because frameworks come with significant advantages—like being completely self-contained. 518 | 519 | ^ Built frameworks can be distributed as-is, without any auxiliary files necessary, which is huge for the Carthage use case. 520 | 521 | --- 522 | 523 | # Swift vs. Objective-C 524 | 525 | * Type safety 526 | * Value types _(especially enums)_ 527 | * Much faster to write 528 | * Better modularization 529 | * The Next Big Thing™ 530 | 531 | ^ Carthage and CarthageKit are written 100% in Swift. Although Swift definitely involves its share of obstacles, we saw huge benefits from using it instead of Objective-C. These are just some of them. 532 | 533 | --- 534 | 535 | # ReactiveCocoa 536 | 537 | * Simplifies networking (with the GitHub API) 538 | * Simplifies the dependency resolver 539 | * Simplifies shelling out, via [ReactiveTask](https://github.com/Carthage/ReactiveTask) 540 | * Carthage helps test RAC 3.0 in the real world 541 | 542 | ^ ReactiveCocoa is a framework for programming with “streams of values over time,” which are known as signals. We use ReactiveCocoa extensively in Carthage, which turns out to be an especially great application for it, because of all the inherently stream-based stuff we need to do (like shell tasks and networking). 543 | 544 | ^ We also wanted a way to test RAC’s Swift API (part of 3.0) in the real world, to make sure it works well and is pleasant to use. Carthage is probably the biggest user of that API, and, as a result, RAC 3.0 is now getting close to a release. 545 | 546 | --- 547 | 548 | # [fit] 1.0 549 | 550 | ^ I’d like to finish by talking about why Carthage isn’t at 1.0 yet, and what it will take to get there. 551 | 552 | --- 553 | 554 | # Per-project settings 555 | 556 | ^ Carthage supports flags for changing the default workflow; for example, to only build for one platform, to add dependencies as submodules instead of copying their contents, or to skip the build step entirely. 557 | 558 | ^ Right now, all of those flags need to be specified on the command line. By 1.0, we want to allow these options to live in Cartfile.private, so all users of the project follow the same configuration automatically. 559 | 560 | --- 561 | 562 | # Review CarthageKit API 563 | # Review command line flags 564 | 565 | ^ The other major part of 1.0 will be carefully reviewing anything that we can’t really break after release, and making any breaking changes necessary ahead of time. 566 | 567 | ^ CarthageKit and the CLI are the main public-facing components where we’ll have to maintain backwards compatibility. 568 | 569 | --- 570 | 571 | # Profit!!! 💸 572 | 573 | ^ 1.0 is really important to me, and I know it will make Carthage more “real” to many folks. We have an 0.7 release to do, and then 1.0 is up right after that, so it shouldn’t be too long now. Keep an eye out! 574 | 575 | --- 576 | 577 | # What 578 | # Why 579 | # How to use it 580 | # How it works 581 | 582 | ^ So, to recap, we covered what Carthage is and how to use it, why we built it and what problem it’s solving, and how it works behind the scenes. 583 | 584 | ^ Ultimately, we’ve just scratched the surface, but hopefully this has helped create a clear picture of the project and its goals, as well as the ways in which it’s similar to and different from CocoaPods. 585 | 586 | --- 587 | 588 | # Questions? Comments? 589 | 590 | Slides and notes, plus my demo project, are available at: 591 | [https://github.com/jspahrsummers/carthage-talk](https://github.com/jspahrsummers/carthage-talk) 592 | 593 | Thanks to everyone who reviewed this presentation, and to Realm for inviting me to speak![^2] 594 | 595 | [^2]: :heart: Matt Diephouse, Rob Rix, Alan Rogers, Keith Duncan, Nacho Soto, Tom Brow, James Lawton, Arwa Jumkawala, JP Simard, Tim Anglade, Brendan Forster, Benjamin Encz, Robert Böhnke, and you! 596 | -------------------------------------------------------------------------------- /Ruthlessly Simple Dependency Management with Carthage.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspahrsummers/carthage-talk/816a204f73167f1594666f245cc49604ad5cc22b/Ruthlessly Simple Dependency Management with Carthage.pdf --------------------------------------------------------------------------------