Commit c9ac09dd authored by Kyle Fuller's avatar Kyle Fuller

Merge pull request #2835 from CocoaPods/swift

Support for Frameworks / Swift
parents 36ca914e c5a799f3
...@@ -6,8 +6,31 @@ To install release candidates run `[sudo] gem install cocoapods --pre` ...@@ -6,8 +6,31 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
## Master ## Master
##### Highlighted Enhancement That Needs Testing
* Support Frameworks & Swift: CocoaPods now recognizes Swift source files and
builds dynamic frameworks when necessary. A project can explicitly
opt-in via `use_frameworks!` in the Podfile, or if any dependency contains
Swift code, all pods for that target will be integrated as frameworks.
As a pod author, you can change the module name of the built framework by
specifying a `module_name` in the podspec. The built frameworks are embedded into
the host application with a new shell script build phase in the user's
project allowing configuration-dependent pods.
[Marius Rackwitz](https://github.com/mrackwitz)
[#2835](https://github.com/CocoaPods/CocoaPods/issues/2835)
##### Breaking ##### Breaking
* Bundle Resources into Frameworks: Previously all resources were compiled and
copied into the `mainBundle`. Now Pods have to use
`[NSBundle bundleForClass:<#Class from Pod#>]` to access provided resources
relative to the bundle.
[Boris Bügling](https://github.com/neonichu)
[#2835](https://github.com/CocoaPods/CocoaPods/issues/2730)
* Only the hooks specified by usage of the `plugin` directive of the `Podfile` * Only the hooks specified by usage of the `plugin` directive of the `Podfile`
will be run. Additionally, plugins that depend on hooks will have to update to will be run. Additionally, plugins that depend on hooks will have to update to
specify their 'plugin name' when registering the hooks in order to make it specify their 'plugin name' when registering the hooks in order to make it
......
...@@ -7,7 +7,7 @@ GIT ...@@ -7,7 +7,7 @@ GIT
GIT GIT
remote: https://github.com/CocoaPods/Core.git remote: https://github.com/CocoaPods/Core.git
revision: c9c91c5296067ebf5af122ed87580e9f0479b47c revision: a01ba8e384324922bd173a415330b0a95fb13c5d
branch: master branch: master
specs: specs:
cocoapods-core (0.35.0) cocoapods-core (0.35.0)
......
![CocoaPods Logo](https://raw.github.com/CocoaPods/shared_resources/master/assets/cocoapods-banner-readme.png) ![CocoaPods Logo](https://raw.github.com/CocoaPods/shared_resources/master/assets/cocoapods-banner-readme.png)
### CocoaPods: The Objective-C dependency manager ### CocoaPods: The Cocoa dependency manager
[![Build Status](http://img.shields.io/travis/CocoaPods/CocoaPods/master.svg?style=flat)](https://travis-ci.org/CocoaPods/CocoaPods) [![Build Status](http://img.shields.io/travis/CocoaPods/CocoaPods/master.svg?style=flat)](https://travis-ci.org/CocoaPods/CocoaPods)
[![Gem Version](http://img.shields.io/gem/v/cocoapods.svg?style=flat)](http://badge.fury.io/rb/cocoapods) [![Gem Version](http://img.shields.io/gem/v/cocoapods.svg?style=flat)](http://badge.fury.io/rb/cocoapods)
......
...@@ -225,26 +225,39 @@ begin ...@@ -225,26 +225,39 @@ begin
desc "Build all examples" desc "Build all examples"
task :build do task :build do
Dir.chdir("examples/AFNetworking Example") do Bundler.require 'xcodeproj', :development
puts "Installing Pods" Dir['examples/*'].each do |dir|
# pod_command = ENV['FROM_GEM'] ? 'sandbox-pod' : 'bundle exec ../../bin/sandbox-pod' Dir.chdir(dir) do
# TODO: The sandbox is blocking local git repos making bundler crash puts "Example: #{dir}"
pod_command = ENV['FROM_GEM'] ? 'sandbox-pod' : 'bundle exec ../../bin/pod'
puts " Installing Pods"
execute_command "rm -rf Pods" # pod_command = ENV['FROM_GEM'] ? 'sandbox-pod' : 'bundle exec ../../bin/sandbox-pod'
execute_command "#{pod_command} install --verbose --no-repo-update" # TODO: The sandbox is blocking local git repos making bundler crash
pod_command = ENV['FROM_GEM'] ? 'sandbox-pod' : 'bundle exec ../../bin/pod'
puts "Building example: AFNetworking Mac Example"
execute_command "xcodebuild -workspace 'AFNetworking Examples.xcworkspace' -scheme 'AFNetworking Example' clean build" execute_command "rm -rf Pods"
execute_command "#{pod_command} install --verbose --no-repo-update"
puts "Building example: AFNetworking iOS Example"
xcode_version = `xcodebuild -version`.scan(/Xcode (.*)\n/).first.first workspace_path = 'Examples.xcworkspace'
major_version = xcode_version.split('.').first.to_i workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
# Specifically build against the simulator SDK so we don't have to deal with code signing. workspace.schemes.each do |scheme_name, project_path|
if major_version > 5 next if scheme_name == 'Pods'
execute_command "xcodebuild -workspace 'AFNetworking Examples.xcworkspace' -scheme 'AFNetworking iOS Example' clean build ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,name=iPhone 6'" puts " Building scheme: #{scheme_name}"
else
execute_command "xcodebuild -workspace 'AFNetworking Examples.xcworkspace' -scheme 'AFNetworking iOS Example' clean build ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,name=iPhone Retina (4-inch)'" project = Xcodeproj::Project.open(project_path)
target = project.targets.first
case target
when :osx
execute_command "xcodebuild -workspace '#{workspace_path}' -scheme '#{scheme_name}' clean build"
when :ios
xcode_version = `xcodebuild -version`.scan(/Xcode (.*)\n/).first.first
major_version = xcode_version.split('.').first.to_i
# Specifically build against the simulator SDK so we don't have to deal with code signing.
simulator_name = major_version > 5 ? 'iPhone 6' : 'iPhone Retina (4-inch)'
execute_command "xcodebuild -workspace '#{workspace_path}' -scheme '#{scheme_name}' clean build ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,name=#{simulator_name}"
end
end
end end
end end
end end
......
...@@ -11,7 +11,7 @@ Gem::Specification.new do |s| ...@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
s.homepage = "https://github.com/CocoaPods/CocoaPods" s.homepage = "https://github.com/CocoaPods/CocoaPods"
s.authors = ["Eloy Duran", "Fabio Pelosin"] s.authors = ["Eloy Duran", "Fabio Pelosin"]
s.summary = "An Objective-C library package manager." s.summary = "The Cocoa library package manager."
s.description = "CocoaPods manages library dependencies for your Xcode project.\n\n" \ s.description = "CocoaPods manages library dependencies for your Xcode project.\n\n" \
"You specify the dependencies for your project in one easy text file. " \ "You specify the dependencies for your project in one easy text file. " \
"CocoaPods resolves dependencies between libraries, fetches source " \ "CocoaPods resolves dependencies between libraries, fetches source " \
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
26E82B9975F54DA9BAC1E0C9 /* libPods-AFNetworking Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6935B1E417A24F0E958977ED /* libPods-AFNetworking Example.a */; }; 1A2265312C5CBC89CD477BF8 /* libPods-AFNetworking Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 53BDE93051BED183E9F4D921 /* libPods-AFNetworking Example.a */; };
B304CCE8177D58DD00F4FC85 /* adn.cer in Resources */ = {isa = PBXBuildFile; fileRef = B304CCE7177D58DD00F4FC85 /* adn.cer */; }; B304CCE8177D58DD00F4FC85 /* adn.cer in Resources */ = {isa = PBXBuildFile; fileRef = B304CCE7177D58DD00F4FC85 /* adn.cer */; };
F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8129BFF1591061B009BFE23 /* Cocoa.framework */; }; F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8129BFF1591061B009BFE23 /* Cocoa.framework */; };
F8129C321591073C009BFE23 /* AFAppDotNetAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C251591073C009BFE23 /* AFAppDotNetAPIClient.m */; }; F8129C321591073C009BFE23 /* AFAppDotNetAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C251591073C009BFE23 /* AFAppDotNetAPIClient.m */; };
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
3C79D6B3AA6A1DBBAE48A944 /* Pods-AFNetworking Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking Example/Pods-AFNetworking Example.debug.xcconfig"; sourceTree = "<group>"; }; 3C79D6B3AA6A1DBBAE48A944 /* Pods-AFNetworking Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking Example/Pods-AFNetworking Example.debug.xcconfig"; sourceTree = "<group>"; };
6935B1E417A24F0E958977ED /* libPods-AFNetworking Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AFNetworking Example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 53BDE93051BED183E9F4D921 /* libPods-AFNetworking Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AFNetworking Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
A67B777556EF5D95EAFA5CF8 /* Pods-AFNetworking Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking Example/Pods-AFNetworking Example.release.xcconfig"; sourceTree = "<group>"; }; A67B777556EF5D95EAFA5CF8 /* Pods-AFNetworking Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking Example/Pods-AFNetworking Example.release.xcconfig"; sourceTree = "<group>"; };
B304CCE7177D58DD00F4FC85 /* adn.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = adn.cer; sourceTree = SOURCE_ROOT; }; B304CCE7177D58DD00F4FC85 /* adn.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = adn.cer; sourceTree = SOURCE_ROOT; };
F8129BFB1591061B009BFE23 /* AFNetworking Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AFNetworking Example.app"; sourceTree = BUILT_PRODUCTS_DIR; }; F8129BFB1591061B009BFE23 /* AFNetworking Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "AFNetworking Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */, F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */,
26E82B9975F54DA9BAC1E0C9 /* libPods-AFNetworking Example.a in Frameworks */, 1A2265312C5CBC89CD477BF8 /* libPods-AFNetworking Example.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
children = ( children = (
F8129BFF1591061B009BFE23 /* Cocoa.framework */, F8129BFF1591061B009BFE23 /* Cocoa.framework */,
F8129C011591061B009BFE23 /* Other Frameworks */, F8129C011591061B009BFE23 /* Other Frameworks */,
6935B1E417A24F0E958977ED /* libPods-AFNetworking Example.a */, 53BDE93051BED183E9F4D921 /* libPods-AFNetworking Example.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -169,7 +169,7 @@ ...@@ -169,7 +169,7 @@
F8129BF21591061B009BFE23 /* Project object */ = { F8129BF21591061B009BFE23 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 0460; LastUpgradeCheck = 0610;
}; };
buildConfigurationList = F8129BF51591061B009BFE23 /* Build configuration list for PBXProject "AFNetworking Mac Example" */; buildConfigurationList = F8129BF51591061B009BFE23 /* Build configuration list for PBXProject "AFNetworking Mac Example" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
...@@ -253,13 +253,16 @@ ...@@ -253,13 +253,16 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES;
...@@ -272,7 +275,9 @@ ...@@ -272,7 +275,9 @@
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7; MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
...@@ -284,20 +289,25 @@ ...@@ -284,20 +289,25 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES; GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7; MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx; SDKROOT = macosx;
...@@ -313,6 +323,7 @@ ...@@ -313,6 +323,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Prefix.pch; GCC_PREFIX_HEADER = Prefix.pch;
INFOPLIST_FILE = "Mac-Info.plist"; INFOPLIST_FILE = "Mac-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app; WRAPPER_EXTENSION = app;
}; };
...@@ -327,6 +338,7 @@ ...@@ -327,6 +338,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Prefix.pch; GCC_PREFIX_HEADER = Prefix.pch;
INFOPLIST_FILE = "Mac-Info.plist"; INFOPLIST_FILE = "Mac-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app; WRAPPER_EXTENSION = app;
}; };
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0500" LastUpgradeVersion = "0610"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
2982AD3217107C0000FFF048 /* adn.cer in Resources */ = {isa = PBXBuildFile; fileRef = 2982AD3117107C0000FFF048 /* adn.cer */; }; 2982AD3217107C0000FFF048 /* adn.cer in Resources */ = {isa = PBXBuildFile; fileRef = 2982AD3117107C0000FFF048 /* adn.cer */; };
E5D2E13908F241ABAC3B554B /* libPods-AFNetworking iOS Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D87F8FEDE4A4313B0D579A3 /* libPods-AFNetworking iOS Example.a */; }; 9A67BEDAF4D3529C5380A3E2 /* libPods-AFNetworking iOS Example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DB1F7D464FF374F6931E274F /* libPods-AFNetworking iOS Example.a */; };
F8129C7415910C37009BFE23 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C7215910C37009BFE23 /* AppDelegate.m */; }; F8129C7415910C37009BFE23 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C7215910C37009BFE23 /* AppDelegate.m */; };
F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */; }; F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */; };
F88812F016C533D6003C8B8C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E469E013957DF100DB05C8 /* Security.framework */; }; F88812F016C533D6003C8B8C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E469E013957DF100DB05C8 /* Security.framework */; };
...@@ -35,11 +35,9 @@ ...@@ -35,11 +35,9 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
2982AD3117107C0000FFF048 /* adn.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = adn.cer; sourceTree = SOURCE_ROOT; }; 2982AD3117107C0000FFF048 /* adn.cer */ = {isa = PBXFileReference; lastKnownFileType = file; path = adn.cer; sourceTree = SOURCE_ROOT; };
50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; }; 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
5628C4E1DE6DDC806B14CF8D /* Pods-AFNetworking iOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking iOS Example.release.xcconfig"; sourceTree = "<group>"; };
7669B6F3267891B0317A0167 /* Pods-AFNetworking iOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking iOS Example/Pods-AFNetworking iOS Example.debug.xcconfig"; sourceTree = "<group>"; }; 7669B6F3267891B0317A0167 /* Pods-AFNetworking iOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking iOS Example/Pods-AFNetworking iOS Example.debug.xcconfig"; sourceTree = "<group>"; };
9D87F8FEDE4A4313B0D579A3 /* libPods-AFNetworking iOS Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AFNetworking iOS Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
BEA3D3BDD12996573240F05F /* Pods-AFNetworking iOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking iOS Example/Pods-AFNetworking iOS Example.release.xcconfig"; sourceTree = "<group>"; }; BEA3D3BDD12996573240F05F /* Pods-AFNetworking iOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking iOS Example/Pods-AFNetworking iOS Example.release.xcconfig"; sourceTree = "<group>"; };
C3B414D5F7FCC5379A284B4E /* Pods-AFNetworking iOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AFNetworking iOS Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AFNetworking iOS Example.debug.xcconfig"; sourceTree = "<group>"; }; DB1F7D464FF374F6931E274F /* libPods-AFNetworking iOS Example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AFNetworking iOS Example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
F8129C3815910830009BFE23 /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = SOURCE_ROOT; }; F8129C3815910830009BFE23 /* Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Prefix.pch; sourceTree = SOURCE_ROOT; };
F8129C7215910C37009BFE23 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; }; F8129C7215910C37009BFE23 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = SOURCE_ROOT; };
F8129C7315910C37009BFE23 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; }; F8129C7315910C37009BFE23 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = SOURCE_ROOT; };
...@@ -83,7 +81,7 @@ ...@@ -83,7 +81,7 @@
F8E469DF13957DD500DB05C8 /* CoreLocation.framework in Frameworks */, F8E469DF13957DD500DB05C8 /* CoreLocation.framework in Frameworks */,
F8D0701B14310F4A00653FD3 /* SystemConfiguration.framework in Frameworks */, F8D0701B14310F4A00653FD3 /* SystemConfiguration.framework in Frameworks */,
F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */, F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */,
E5D2E13908F241ABAC3B554B /* libPods-AFNetworking iOS Example.a in Frameworks */, 9A67BEDAF4D3529C5380A3E2 /* libPods-AFNetworking iOS Example.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -93,8 +91,6 @@ ...@@ -93,8 +91,6 @@
9CE17E7C344B0E75BC0723EA /* Pods */ = { 9CE17E7C344B0E75BC0723EA /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
C3B414D5F7FCC5379A284B4E /* Pods-AFNetworking iOS Example.debug.xcconfig */,
5628C4E1DE6DDC806B14CF8D /* Pods-AFNetworking iOS Example.release.xcconfig */,
7669B6F3267891B0317A0167 /* Pods-AFNetworking iOS Example.debug.xcconfig */, 7669B6F3267891B0317A0167 /* Pods-AFNetworking iOS Example.debug.xcconfig */,
BEA3D3BDD12996573240F05F /* Pods-AFNetworking iOS Example.release.xcconfig */, BEA3D3BDD12996573240F05F /* Pods-AFNetworking iOS Example.release.xcconfig */,
); );
...@@ -169,7 +165,7 @@ ...@@ -169,7 +165,7 @@
F8E469641395739D00DB05C8 /* UIKit.framework */, F8E469641395739D00DB05C8 /* UIKit.framework */,
F8E469661395739D00DB05C8 /* Foundation.framework */, F8E469661395739D00DB05C8 /* Foundation.framework */,
F8E469681395739D00DB05C8 /* CoreGraphics.framework */, F8E469681395739D00DB05C8 /* CoreGraphics.framework */,
9D87F8FEDE4A4313B0D579A3 /* libPods-AFNetworking iOS Example.a */, DB1F7D464FF374F6931E274F /* libPods-AFNetworking iOS Example.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -383,7 +379,7 @@ ...@@ -383,7 +379,7 @@
GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNUSED_PARAMETER = NO; GCC_WARN_UNUSED_PARAMETER = NO;
INFOPLIST_FILE = "iOS-Info.plist"; INFOPLIST_FILE = "iOS-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = ""; WARNING_CFLAGS = "";
WRAPPER_EXTENSION = app; WRAPPER_EXTENSION = app;
...@@ -404,7 +400,7 @@ ...@@ -404,7 +400,7 @@
GCC_WARN_SIGN_COMPARE = YES; GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNUSED_PARAMETER = NO; GCC_WARN_UNUSED_PARAMETER = NO;
INFOPLIST_FILE = "iOS-Info.plist"; INFOPLIST_FILE = "iOS-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
WARNING_CFLAGS = ""; WARNING_CFLAGS = "";
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0500" LastUpgradeVersion = "0610"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#if __IPHONE_OS_VERSION_MIN_REQUIRED #if __IPHONE_OS_VERSION_MIN_REQUIRED
#import "GlobalTimelineViewController.h" #import "GlobalTimelineViewController.h"
#import <AFNetworking/AFNetworkActivityIndicatorManager.h>
#import "AFNetworkActivityIndicatorManager.h" #import "AFNetworkActivityIndicatorManager.h"
@implementation AppDelegate @implementation AppDelegate
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
xcodeproj "AFNetworking Mac Example.xcodeproj" xcodeproj "AFNetworking Mac Example.xcodeproj"
#### ####
workspace 'AFNetworking Examples.xcworkspace' workspace 'Examples.xcworkspace'
target "AFNetworking Example" do target "AFNetworking Example" do
platform :osx, '10.8' platform :osx, '10.8'
...@@ -11,7 +11,7 @@ target "AFNetworking Example" do ...@@ -11,7 +11,7 @@ target "AFNetworking Example" do
end end
target "AFNetworking iOS Example" do target "AFNetworking iOS Example" do
platform :ios, '7.0' platform :ios, '8.0'
xcodeproj "AFNetworking iOS Example.xcodeproj" xcodeproj "AFNetworking iOS Example.xcodeproj"
pod "AFNetworking", "1.3.3" pod "AFNetworking", "1.3.3"
end end
Pod::Spec.new do |s|
s.name = 'Alamofire'
s.version = '0.0.1'
s.license = 'MIT'
s.summary = 'Elegant HTTP Networking in Swift'
s.homepage = 'https://github.com/Alamofire/Alamofire'
s.social_media_url = 'http://twitter.com/mattt'
s.authors = { 'Mattt Thompson' => 'm@mattt.me' }
s.source = { :git => 'https://github.com/Alamofire/Alamofire.git', :branch => 'master' }
s.requires_arc = true
s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10'
s.source_files = 'Source/*.swift'
end
// AppDelegate.swift
//
// Copyright (c) 2014 Alamofire (http://alamofire.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
var window: UIWindow?
// MARK: - UIApplicationDelegate
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
let splitViewController = self.window!.rootViewController as UISplitViewController
let navigationController = splitViewController.viewControllers.last as UINavigationController
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
return true
}
// MARK: - UISplitViewControllerDelegate
func splitViewController(splitViewController: UISplitViewController!, collapseSecondaryViewController secondaryViewController:UIViewController!, ontoPrimaryViewController primaryViewController:UIViewController!) -> Bool {
if let secondaryAsNavController = secondaryViewController as? UINavigationController {
if let topAsDetailController = secondaryAsNavController.topViewController as? DetailViewController {
return topAsDetailController.request == nil
}
}
return false
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="H1p-Uh-vWS">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
</dependencies>
<scenes>
<!--Master-->
<scene sceneID="pY4-Hu-kfo">
<objects>
<navigationController title="Master" id="RMx-3f-FxP" sceneMemberID="viewController">
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="Pmd-2v-anx">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="7bK-jq-Zjz" kind="relationship" relationship="rootViewController" id="tsl-Nk-0bq"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="8fS-aE-onr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-38" y="-630"/>
</scene>
<!--Split View Controller-->
<scene sceneID="Nki-YV-4Qg">
<objects>
<splitViewController id="H1p-Uh-vWS" sceneMemberID="viewController">
<toolbarItems/>
<connections>
<segue destination="RMx-3f-FxP" kind="relationship" relationship="masterViewController" id="BlO-5A-QYV"/>
<segue destination="vC3-pB-5Vb" kind="relationship" relationship="detailViewController" id="Tll-UG-LXB"/>
</connections>
</splitViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="cZU-Oi-B1e" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-687" y="-322"/>
</scene>
<!--Master-->
<scene sceneID="smW-Zh-WAh">
<objects>
<tableViewController title="Master" clearsSelectionOnViewWillAppear="NO" id="7bK-jq-Zjz" customClass="MasterViewController" customModule="iOS_Example" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="r7i-6Z-zg0">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection headerTitle="Data" id="8cQ-ii-Dz7">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="Arm-wq-HPj" style="IBUITableViewCellStyleDefault" id="WCw-Qf-5nD">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WCw-Qf-5nD" id="37f-cq-3Eg">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="GET Request" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Arm-wq-HPj">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="GET" id="1CM-sC-jmU"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="kv2-3k-J9w" style="IBUITableViewCellStyleDefault" id="Bba-qf-fdr">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Bba-qf-fdr" id="U5U-eQ-rXg">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="POST Request" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="kv2-3k-J9w">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="POST" id="qtJ-N8-1Yf"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="gGF-bI-vhg" style="IBUITableViewCellStyleDefault" id="QdK-ID-hbj">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="QdK-ID-hbj" id="JxT-sx-VDC">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="PUT Request" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="gGF-bI-vhg">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="PUT" id="ATO-2Y-gmc"/>
</connections>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="blue" accessoryType="disclosureIndicator" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="jsW-Zp-fZf" style="IBUITableViewCellStyleDefault" id="Mmy-5X-lLC">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Mmy-5X-lLC" id="0eC-Qc-fq1">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="DELETE Request" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="jsW-Zp-fZf">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<connections>
<segue destination="vC3-pB-5Vb" kind="showDetail" identifier="DELETE" id="FoU-a6-nga"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Upload" id="HR4-nh-1dM">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="tFv-wn-QTG" style="IBUITableViewCellStyleDefault" id="cNY-Vx-8u5">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="cNY-Vx-8u5" id="yYR-YT-Fse">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="Upload Data" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tFv-wn-QTG">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="Ii5-Vh-zHX" style="IBUITableViewCellStyleDefault" id="xgg-BT-6Gr">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="xgg-BT-6Gr" id="2eT-D2-puB">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="Upload File" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Ii5-Vh-zHX">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="gzG-92-x5R" style="IBUITableViewCellStyleDefault" id="jNV-fh-84v">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jNV-fh-84v" id="52u-Fk-l5Q">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="Upload Stream" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="gzG-92-x5R">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Download" id="7nc-cQ-nUY">
<cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="Gr3-i1-tdE" style="IBUITableViewCellStyleDefault" id="khw-Sk-LOc">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="khw-Sk-LOc" id="HO7-NM-pbP">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="Download Request" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Gr3-i1-tdE">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="Cell" textLabel="fmT-qD-bf2" style="IBUITableViewCellStyleDefault" id="8hK-B8-VMy">
<rect key="frame" x="0.0" y="86" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="8hK-B8-VMy" id="bfY-jx-lTE">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" text="Download Resume Data" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="fmT-qD-bf2">
<rect key="frame" x="15" y="0.0" width="290" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="20"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="highlightedColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</label>
</subviews>
</tableViewCellContentView>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="7bK-jq-Zjz" id="Gho-Na-rnu"/>
<outlet property="delegate" destination="7bK-jq-Zjz" id="RA6-mI-bju"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="Zdf-7t-Un8"/>
<connections>
<outlet property="titleImageView" destination="9c8-WZ-jVF" id="jvG-Sa-nSG"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Rux-fX-hf1" sceneMemberID="firstResponder"/>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Logo" id="9c8-WZ-jVF">
<rect key="frame" x="0.0" y="0.0" width="250" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</imageView>
</objects>
<point key="canvasLocation" x="604" y="-630"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="r7l-gg-dq7">
<objects>
<navigationController id="vC3-pB-5Vb" sceneMemberID="viewController">
<navigationBar key="navigationBar" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="DjV-YW-jjY">
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="hay-0o-6iw" kind="relationship" relationship="rootViewController" id="bmM-Kc-6qk"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="SLD-UC-DBI" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-38" y="30"/>
</scene>
<!--Detail View Controller-->
<scene sceneID="Vi6-fp-rZb">
<objects>
<tableViewController id="hay-0o-6iw" customClass="DetailViewController" customModule="iOS_Example" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="10" sectionFooterHeight="10" id="uo9-Sd-Gpr">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Header" textLabel="CVi-D2-cds" detailTextLabel="Umi-gS-7r0" style="IBUITableViewCellStyleValue1" id="tsM-dO-McZ">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="tsM-dO-McZ" id="LQw-cm-GmU">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Accept" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="CVi-D2-cds">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="&quot;text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&quot;" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Umi-gS-7r0">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Body" textLabel="m1f-qP-FlJ" rowHeight="119" style="IBUITableViewCellStyleDefault" id="N0t-UM-UX3">
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="N0t-UM-UX3" id="AxK-Aj-qDS">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="m1f-qP-FlJ">
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<fontDescription key="fontDescription" name="Courier" family="Courier" pointSize="12"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="hay-0o-6iw" id="U5L-JB-xfD"/>
<outlet property="delegate" destination="hay-0o-6iw" id="N4n-xl-uSD"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="ovN-S0-oAU"/>
<refreshControl key="refreshControl" opaque="NO" multipleTouchEnabled="YES" contentMode="center" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" id="e6J-W5-mRs">
<autoresizingMask key="autoresizingMask"/>
</refreshControl>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vwc-ms-sMM" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="604" y="30"/>
</scene>
</scenes>
<resources>
<image name="Logo" width="250" height="40"/>
</resources>
<inferredMetricsTieBreakers>
<segue reference="FoU-a6-nga"/>
</inferredMetricsTieBreakers>
<color key="tintColor" red="0.84313732385635376" green="0.25882354378700256" blue="0.11372549831867218" alpha="1" colorSpace="deviceRGB"/>
</document>
// DetailViewController.swift
//
// Copyright (c) 2014 Alamofire (http://alamofire.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import UIKit
import Alamofire
class DetailViewController: UITableViewController {
enum Sections: Int {
case Headers, Body
}
var request: Alamofire.Request? {
didSet {
oldValue?.cancel()
self.title = self.request?.description
self.refreshControl?.endRefreshing()
self.headers.removeAll()
self.body = nil
self.elapsedTime = nil
}
}
var headers: [String: String] = [:]
var body: String?
var elapsedTime: NSTimeInterval?
override func awakeFromNib() {
super.awakeFromNib()
self.refreshControl?.addTarget(self, action: "refresh", forControlEvents: .ValueChanged)
}
// MARK: - UIViewController
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.refresh()
}
// MARK: - IBAction
@IBAction func refresh() {
if self.request == nil {
return
}
self.refreshControl?.beginRefreshing()
let start = CACurrentMediaTime()
self.request?.responseString { (request, response, body, error) in
let end = CACurrentMediaTime()
self.elapsedTime = end - start
for (field, value) in response!.allHeaderFields {
self.headers["\(field)"] = "\(value)"
}
self.body = body
self.tableView.reloadData()
self.refreshControl?.endRefreshing()
}
}
// MARK: UITableViewDataSource
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch Sections(rawValue: section)! {
case .Headers:
return self.headers.count
case .Body:
return self.body == nil ? 0 : 1
default:
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
switch Sections(rawValue: indexPath.section)! {
case .Headers:
let cell = self.tableView.dequeueReusableCellWithIdentifier("Header") as UITableViewCell
let field = self.headers.keys.array.sorted(<)[indexPath.row]
let value = self.headers[field]
cell.textLabel.text = field
cell.detailTextLabel!.text = value
return cell
case .Body:
let cell = self.tableView.dequeueReusableCellWithIdentifier("Body") as UITableViewCell
cell.textLabel.text = self.body
return cell
}
}
// MARK: UITableViewDelegate
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String {
if self.tableView(tableView, numberOfRowsInSection: section) == 0 {
return ""
}
switch Sections(rawValue: section)! {
case .Headers:
return "Headers"
case .Body:
return "Body"
}
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
switch Sections(rawValue: indexPath.section)! {
case .Body:
return 300
default:
return tableView.rowHeight
}
}
override func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String {
if Sections(rawValue: section)! == .Body && self.elapsedTime != nil {
let numberFormatter = NSNumberFormatter()
numberFormatter.numberStyle = .DecimalStyle
return "Elapsed Time: \(numberFormatter.stringFromNumber(self.elapsedTime!)) sec"
}
return ""
}
}
// MasterViewController.swift
//
// Copyright (c) 2014 Alamofire (http://alamofire.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import Alamofire
enum HTTPBinRoute: URLStringConvertible {
case Method(Alamofire.Method)
case BasicAuth(String, String)
var URLString: String {
let baseURLString = "http://httpbin.org/"
let path: String = {
switch self {
case .Method(let method):
return "/\(method.rawValue.lowercaseString)"
case .BasicAuth(let user, let password):
return "/basic-auth/\(user)/\(password)"
}
}()
return NSURL(string: path, relativeToURL: NSURL(string: baseURLString))!.absoluteString!
}
}
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"subtype" : "retina4",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "landscape",
"idiom" : "ipad",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "Logo.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "Logo@2x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.alamofire.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Alamofire</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarTintParameters</key>
<dict>
<key>UINavigationBar</key>
<dict>
<key>Style</key>
<string>UIBarStyleDefault</string>
<key>Translucent</key>
<false/>
</dict>
</dict>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
// MasterViewController.swift
//
// Copyright (c) 2014 Alamofire (http://alamofire.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import UIKit
import Alamofire
class MasterViewController: UITableViewController {
@IBOutlet weak var titleImageView: UIImageView!
var detailViewController: DetailViewController? = nil
var objects = NSMutableArray()
override func awakeFromNib() {
super.awakeFromNib()
self.navigationItem.titleView = self.titleImageView
}
// MARK: - UIViewController
override func viewDidLoad() {
super.viewDidLoad()
if let split = self.splitViewController {
let controllers = split.viewControllers
self.detailViewController = controllers.last?.topViewController as? DetailViewController
}
}
// MARK: - UIStoryboardSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let detailViewController = segue.destinationViewController.topViewController as? DetailViewController {
func requestForSegue(segue: UIStoryboardSegue) -> Request? {
switch segue.identifier as String! {
case "GET":
return Alamofire.request(.GET, "http://httpbin.org/get")
case "POST":
return Alamofire.request(.POST, "http://httpbin.org/post")
case "PUT":
return Alamofire.request(.PUT, "http://httpbin.org/put")
case "DELETE":
return Alamofire.request(.DELETE, "http://httpbin.org/delete")
default:
return nil
}
}
if let request = requestForSegue(segue) {
detailViewController.request = request
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:iOS Example.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
Copyright (c) 2014 Alamofire (http://alamofire.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
source 'https://github.com/CocoaPods/Specs.git'
workspace 'Examples.xcworkspace'
xcodeproj 'iOS Example.xcodeproj'
target 'iOS Example' do
platform :ios, '8.0'
use_frameworks!
pod 'Alamofire', :path => '.'
end
// Alamofire.h
//
// Copyright (c) 2014 Alamofire (http://alamofire.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
FOUNDATION_EXPORT double AlamofireVersionNumber;
FOUNDATION_EXPORT const unsigned char AlamofireVersionString[];
// Alamofire.swift
//
// Copyright (c) 2014 Alamofire (http://alamofire.org)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/// Alamofire errors
public let AlamofireErrorDomain = "com.alamofire.error"
/**
HTTP method definitions.
See http://tools.ietf.org/html/rfc7231#section-4.3
*/
public enum Method: String {
case OPTIONS = "OPTIONS"
case GET = "GET"
case HEAD = "HEAD"
case POST = "POST"
case PUT = "PUT"
case PATCH = "PATCH"
case DELETE = "DELETE"
case TRACE = "TRACE"
case CONNECT = "CONNECT"
}
/**
Used to specify the way in which a set of parameters are applied to a URL request.
*/
public enum ParameterEncoding {
/**
A query string to be set as or appended to any existing URL query for `GET`, `HEAD`, and `DELETE` requests, or set as the body for requests with any other HTTP method. The `Content-Type` HTTP header field of an encoded request with HTTP body is set to `application/x-www-form-urlencoded`. Since there is no published specification for how to encode collection types, the convention of appending `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for nested dictionary values (`foo[bar]=baz`).
*/
case URL
/**
Uses `NSJSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
*/
case JSON
/**
Uses `NSPropertyListSerialization` to create a plist representation of the parameters object, according to the associated format and write options values, which is set as the body of the request. The `Content-Type` HTTP header field of an encoded request is set to `application/x-plist`.
*/
case PropertyList(NSPropertyListFormat, NSPropertyListWriteOptions)
/**
Uses the associated closure value to construct a new request given an existing request and parameters.
*/
case Custom((URLRequestConvertible, [String: AnyObject]?) -> (NSURLRequest, NSError?))
/**
Creates a URL request by encoding parameters and applying them onto an existing request.
:param: URLRequest The request to have parameters applied
:param: parameters The parameters to apply
:returns: A tuple containing the constructed request and the error that occurred during parameter encoding, if any.
*/
public func encode(URLRequest: URLRequestConvertible, parameters: [String: AnyObject]?) -> (NSURLRequest, NSError?) {
if parameters == nil {
return (URLRequest.URLRequest, nil)
}
var mutableURLRequest: NSMutableURLRequest! = URLRequest.URLRequest.mutableCopy() as NSMutableURLRequest
var error: NSError? = nil
switch self {
case .URL:
func query(parameters: [String: AnyObject]) -> String {
var components: [(String, String)] = []
for key in sorted(Array(parameters.keys), <) {
let value: AnyObject! = parameters[key]
components += queryComponents(key, value)
}
return join("&", components.map{"\($0)=\($1)"} as [String])
}
func encodesParametersInURL(method: Method) -> Bool {
switch method {
case .GET, .HEAD, .DELETE:
return true
default:
return false
}
}
let method = Method(rawValue: mutableURLRequest.HTTPMethod)
if method != nil && encodesParametersInURL(method!) {
if let URLComponents = NSURLComponents(URL: mutableURLRequest.URL!, resolvingAgainstBaseURL: false) {
URLComponents.percentEncodedQuery = (URLComponents.query != nil ? URLComponents.query! + "&" : "") + query(parameters!)
mutableURLRequest.URL = URLComponents.URL
}
} else {
if mutableURLRequest.valueForHTTPHeaderField("Content-Type") == nil {
mutableURLRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
}
mutableURLRequest.HTTPBody = query(parameters!).dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
}
case .JSON:
let options = NSJSONWritingOptions.allZeros
if let data = NSJSONSerialization.dataWithJSONObject(parameters!, options: options, error: &error) {
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.HTTPBody = data
}
case .PropertyList(let (format, options)):
if let data = NSPropertyListSerialization.dataWithPropertyList(parameters!, format: format, options: options, error: &error) {
mutableURLRequest.setValue("application/x-plist", forHTTPHeaderField: "Content-Type")
mutableURLRequest.HTTPBody = data
}
case .Custom(let closure):
return closure(mutableURLRequest, parameters)
}
return (mutableURLRequest, error)
}
func queryComponents(key: String, _ value: AnyObject) -> [(String, String)] {
var components: [(String, String)] = []
if let dictionary = value as? [String: AnyObject] {
for (nestedKey, value) in dictionary {
components += queryComponents("\(key)[\(nestedKey)]", value)
}
} else if let array = value as? [AnyObject] {
for value in array {
components += queryComponents("\(key)[]", value)
}
} else {
components.extend([(escape(key), escape("\(value)"))])
}
return components
}
func escape(string: String) -> String {
let allowedCharacters = NSCharacterSet(charactersInString:" =\"#%/<>?@\\^`{}[]|&+").invertedSet
return string.stringByAddingPercentEncodingWithAllowedCharacters(allowedCharacters) ?? string
}
}
// MARK: - URLStringConvertible
/**
Types adopting the `URLStringConvertible` protocol can be used to construct URL strings, which are then used to construct URL requests.
*/
public protocol URLStringConvertible {
/// The URL string.
var URLString: String { get }
}
extension String: URLStringConvertible {
public var URLString: String {
return self
}
}
extension NSURL: URLStringConvertible {
public var URLString: String {
return absoluteString!
}
}
extension NSURLComponents: URLStringConvertible {
public var URLString: String {
return URL!.URLString
}
}
extension NSURLRequest: URLStringConvertible {
public var URLString: String {
return URL.URLString
}
}
// MARK: - URLRequestConvertible
/**
Types adopting the `URLRequestConvertible` protocol can be used to construct URL requests.
*/
public protocol URLRequestConvertible {
/// The URL request.
var URLRequest: NSURLRequest { get }
}
extension NSURLRequest: URLRequestConvertible {
public var URLRequest: NSURLRequest {
return self
}
}
// MARK: -
/**
Responsible for creating and managing `Request` objects, as well as their underlying `NSURLSession`.
*/
public class Manager {
/**
A shared instance of `Manager`, used by top-level Alamofire request methods, and suitable for use directly for any ad hoc requests.
*/
public class var sharedInstance: Manager {
struct Singleton {
static var configuration: NSURLSessionConfiguration = {
var configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = {
// Accept-Encoding HTTP Header; see http://tools.ietf.org/html/rfc7230#section-4.2.3
let acceptEncoding: String = "gzip;q=1.0,compress;q=0.5"
// Accept-Language HTTP Header; see http://tools.ietf.org/html/rfc7231#section-5.3.5
let acceptLanguage: String = {
var components: [String] = []
for (index, languageCode) in enumerate(NSLocale.preferredLanguages() as [String]) {
let q = 1.0 - (Double(index) * 0.1)
components.append("\(languageCode);q=\(q)")
if q <= 0.5 {
break
}
}
return join(",", components)
}()
// User-Agent Header; see http://tools.ietf.org/html/rfc7231#section-5.5.3
let userAgent: String = {
if let info = NSBundle.mainBundle().infoDictionary {
let executable: AnyObject = info[kCFBundleExecutableKey] ?? "Unknown"
let bundle: AnyObject = info[kCFBundleIdentifierKey] ?? "Unknown"
let version: AnyObject = info[kCFBundleVersionKey] ?? "Unknown"
let os: AnyObject = NSProcessInfo.processInfo().operatingSystemVersionString ?? "Unknown"
var mutableUserAgent = NSMutableString(string: "\(executable)/\(bundle) (\(version); OS \(os))") as CFMutableString
let transform = NSString(string: "Any-Latin; Latin-ASCII; [:^ASCII:] Remove") as CFString
if CFStringTransform(mutableUserAgent, nil, transform, 0) == 1 {
return mutableUserAgent as NSString
}
}
return "Alamofire"
}()
return ["Accept-Encoding": acceptEncoding,
"Accept-Language": acceptLanguage,
"User-Agent": userAgent]
}()
return configuration
}()
static let instance = Manager(configuration: configuration)
}
return Singleton.instance
}
private let delegate: SessionDelegate
private let queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
/// The underlying session.
public let session: NSURLSession
/// Whether to start requests immediately after being constructed. `true` by default.
public var startRequestsImmediately: Bool = true
/**
:param: configuration The configuration used to construct the managed session.
*/
required public init(configuration: NSURLSessionConfiguration? = nil) {
self.delegate = SessionDelegate()
self.session = NSURLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)
}
deinit {
self.session.invalidateAndCancel()
}
// MARK: -
/**
Creates a request for the specified method, URL string, parameters, and parameter encoding.
:param: method The HTTP method.
:param: URLString The URL string.
:param: parameters The parameters. `nil` by default.
:param: encoding The parameter encoding. `.URL` by default.
:returns: The created request.
*/
public func request(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
return request(encoding.encode(URLRequest(method, URLString), parameters: parameters).0)
}
/**
Creates a request for the specified URL request.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
:param: URLRequest The URL request
:returns: The created request.
*/
public func request(URLRequest: URLRequestConvertible) -> Request {
var dataTask: NSURLSessionDataTask?
dispatch_sync(queue) {
dataTask = self.session.dataTaskWithRequest(URLRequest.URLRequest)
}
let request = Request(session: session, task: dataTask!)
delegate[request.delegate.task] = request.delegate
if startRequestsImmediately {
request.resume()
}
return request
}
class SessionDelegate: NSObject, NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate {
private var subdelegates: [Int: Request.TaskDelegate]
private let subdelegateQueue = dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT)
private subscript(task: NSURLSessionTask) -> Request.TaskDelegate? {
get {
var subdelegate: Request.TaskDelegate?
dispatch_sync(subdelegateQueue) {
subdelegate = self.subdelegates[task.taskIdentifier]
}
return subdelegate
}
set {
dispatch_barrier_async(subdelegateQueue) {
self.subdelegates[task.taskIdentifier] = newValue
}
}
}
var sessionDidBecomeInvalidWithError: ((NSURLSession!, NSError!) -> Void)?
var sessionDidFinishEventsForBackgroundURLSession: ((NSURLSession!) -> Void)?
var sessionDidReceiveChallenge: ((NSURLSession!, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential!))?
var taskWillPerformHTTPRedirection: ((NSURLSession!, NSURLSessionTask!, NSHTTPURLResponse!, NSURLRequest!) -> (NSURLRequest!))?
var taskDidReceiveChallenge: ((NSURLSession!, NSURLSessionTask!, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
var taskDidSendBodyData: ((NSURLSession!, NSURLSessionTask!, Int64, Int64, Int64) -> Void)?
var taskNeedNewBodyStream: ((NSURLSession!, NSURLSessionTask!) -> (NSInputStream!))?
var dataTaskDidReceiveResponse: ((NSURLSession!, NSURLSessionDataTask!, NSURLResponse!) -> (NSURLSessionResponseDisposition))?
var dataTaskDidBecomeDownloadTask: ((NSURLSession!, NSURLSessionDataTask!) -> Void)?
var dataTaskDidReceiveData: ((NSURLSession!, NSURLSessionDataTask!, NSData!) -> Void)?
var dataTaskWillCacheResponse: ((NSURLSession!, NSURLSessionDataTask!, NSCachedURLResponse!) -> (NSCachedURLResponse))?
var downloadTaskDidFinishDownloadingToURL: ((NSURLSession!, NSURLSessionDownloadTask!, NSURL) -> (NSURL))?
var downloadTaskDidWriteData: ((NSURLSession!, NSURLSessionDownloadTask!, Int64, Int64, Int64) -> Void)?
var downloadTaskDidResumeAtOffset: ((NSURLSession!, NSURLSessionDownloadTask!, Int64, Int64) -> Void)?
required override init() {
self.subdelegates = Dictionary()
super.init()
}
// MARK: NSURLSessionDelegate
func URLSession(session: NSURLSession!, didBecomeInvalidWithError error: NSError!) {
sessionDidBecomeInvalidWithError?(session, error)
}
func URLSession(session: NSURLSession!, didReceiveChallenge challenge: NSURLAuthenticationChallenge!, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)!) {
if sessionDidReceiveChallenge != nil {
completionHandler(sessionDidReceiveChallenge!(session, challenge))
} else {
completionHandler(.PerformDefaultHandling, nil)
}
}
func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession!) {
sessionDidFinishEventsForBackgroundURLSession?(session)
}
// MARK: NSURLSessionTaskDelegate
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, willPerformHTTPRedirection response: NSHTTPURLResponse!, newRequest request: NSURLRequest!, completionHandler: ((NSURLRequest!) -> Void)!) {
var redirectRequest = request
if taskWillPerformHTTPRedirection != nil {
redirectRequest = taskWillPerformHTTPRedirection!(session, task, response, request)
}
completionHandler(redirectRequest)
}
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didReceiveChallenge challenge: NSURLAuthenticationChallenge!, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)!) {
if let delegate = self[task] {
delegate.URLSession(session, task: task, didReceiveChallenge: challenge, completionHandler: completionHandler)
} else {
URLSession(session, didReceiveChallenge: challenge, completionHandler: completionHandler)
}
}
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, needNewBodyStream completionHandler: ((NSInputStream!) -> Void)!) {
if let delegate = self[task] {
delegate.URLSession(session, task: task, needNewBodyStream: completionHandler)
}
}
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
if let delegate = self[task] as? Request.UploadTaskDelegate {
delegate.URLSession(session, task: task, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend)
}
}
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didCompleteWithError error: NSError!) {
if let delegate = self[task] {
delegate.URLSession(session, task: task, didCompleteWithError: error)
self[task] = nil
}
}
// MARK: NSURLSessionDataDelegate
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, didReceiveResponse response: NSURLResponse!, completionHandler: ((NSURLSessionResponseDisposition) -> Void)!) {
var disposition: NSURLSessionResponseDisposition = .Allow
if dataTaskDidReceiveResponse != nil {
disposition = dataTaskDidReceiveResponse!(session, dataTask, response)
}
completionHandler(disposition)
}
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask!) {
let downloadDelegate = Request.DownloadTaskDelegate(task: downloadTask)
self[downloadTask] = downloadDelegate
}
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, didReceiveData data: NSData!) {
if let delegate = self[dataTask] as? Request.DataTaskDelegate {
delegate.URLSession(session, dataTask: dataTask, didReceiveData: data)
}
dataTaskDidReceiveData?(session, dataTask, data)
}
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, willCacheResponse proposedResponse: NSCachedURLResponse!, completionHandler: ((NSCachedURLResponse!) -> Void)!) {
var cachedResponse = proposedResponse
if dataTaskWillCacheResponse != nil {
cachedResponse = dataTaskWillCacheResponse!(session, dataTask, proposedResponse)
}
completionHandler(cachedResponse)
}
// MARK: NSURLSessionDownloadDelegate
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
delegate.URLSession(session, downloadTask: downloadTask, didFinishDownloadingToURL: location)
}
downloadTaskDidFinishDownloadingToURL?(session, downloadTask, location)
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
delegate.URLSession(session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite)
}
downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
}
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
if let delegate = self[downloadTask] as? Request.DownloadTaskDelegate {
delegate.URLSession(session, downloadTask: downloadTask, didResumeAtOffset: fileOffset, expectedTotalBytes: expectedTotalBytes)
}
downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes)
}
// MARK: NSObject
override func respondsToSelector(selector: Selector) -> Bool {
switch selector {
case "URLSession:didBecomeInvalidWithError:":
return (sessionDidBecomeInvalidWithError != nil)
case "URLSession:didReceiveChallenge:completionHandler:":
return (sessionDidReceiveChallenge != nil)
case "URLSessionDidFinishEventsForBackgroundURLSession:":
return (sessionDidFinishEventsForBackgroundURLSession != nil)
case "URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:":
return (taskWillPerformHTTPRedirection != nil)
case "URLSession:dataTask:didReceiveResponse:completionHandler:":
return (dataTaskDidReceiveResponse != nil)
case "URLSession:dataTask:willCacheResponse:completionHandler:":
return (dataTaskWillCacheResponse != nil)
default:
return self.dynamicType.instancesRespondToSelector(selector)
}
}
}
}
// MARK: -
/**
Responsible for sending a request and receiving the response and associated data from the server, as well as managing its underlying `NSURLSessionTask`.
*/
public class Request {
private let delegate: TaskDelegate
/// The underlying task.
public var task: NSURLSessionTask { return delegate.task }
/// The session belonging to the underlying task.
public let session: NSURLSession
/// The request sent or to be sent to the server.
public var request: NSURLRequest { return task.originalRequest }
/// The response received from the server, if any.
public var response: NSHTTPURLResponse? { return task.response as? NSHTTPURLResponse }
/// The progress of the request lifecycle.
public var progress: NSProgress? { return delegate.progress }
private init(session: NSURLSession, task: NSURLSessionTask) {
self.session = session
switch task {
case is NSURLSessionUploadTask:
self.delegate = UploadTaskDelegate(task: task)
case is NSURLSessionDataTask:
self.delegate = DataTaskDelegate(task: task)
case is NSURLSessionDownloadTask:
self.delegate = DownloadTaskDelegate(task: task)
default:
self.delegate = TaskDelegate(task: task)
}
}
// MARK: Authentication
/**
Associates an HTTP Basic credential with the request.
:param: user The user.
:param: password The password.
:returns: The request.
*/
public func authenticate(#user: String, password: String) -> Self {
let credential = NSURLCredential(user: user, password: password, persistence: .ForSession)
return authenticate(usingCredential: credential)
}
/**
Associates a specified credential with the request.
:param: credential The credential.
:returns: The request.
*/
public func authenticate(usingCredential credential: NSURLCredential) -> Self {
delegate.credential = credential
return self
}
// MARK: Progress
/**
Sets a closure to be called periodically during the lifecycle of the request as data is written to or read from the server.
- For uploads, the progress closure returns the bytes written, total bytes written, and total bytes expected to write.
- For downloads, the progress closure returns the bytes read, total bytes read, and total bytes expected to write.
:param: closure The code to be executed periodically during the lifecycle of the request.
:returns: The request.
*/
public func progress(closure: ((Int64, Int64, Int64) -> Void)? = nil) -> Self {
if let uploadDelegate = delegate as? UploadTaskDelegate {
uploadDelegate.uploadProgress = closure
} else if let dataDelegate = delegate as? DataTaskDelegate {
dataDelegate.dataProgress = closure
} else if let downloadDelegate = delegate as? DownloadTaskDelegate {
downloadDelegate.downloadProgress = closure
}
return self
}
// MARK: Response
/**
A closure used by response handlers that takes a request, response, and data and returns a serialized object and any error that occured in the process.
*/
public typealias Serializer = (NSURLRequest, NSHTTPURLResponse?, NSData?) -> (AnyObject?, NSError?)
/**
Creates a response serializer that returns the associated data as-is.
:returns: A data response serializer.
*/
public class func responseDataSerializer() -> Serializer {
return { (request, response, data) in
return (data, nil)
}
}
/**
Adds a handler to be called once the request has finished.
:param: completionHandler The code to be executed once the request has finished.
:returns: The request.
*/
public func response(completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
return response(Request.responseDataSerializer(), completionHandler: completionHandler)
}
/**
Adds a handler to be called once the request has finished.
:param: queue The queue on which the completion handler is dispatched.
:param: serializer The closure responsible for serializing the request, response, and data.
:param: completionHandler The code to be executed once the request has finished.
:returns: The request.
*/
public func response(queue: dispatch_queue_t? = nil, serializer: Serializer, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
dispatch_async(delegate.queue) {
let (responseObject: AnyObject?, serializationError: NSError?) = serializer(self.request, self.response, self.delegate.data)
dispatch_async(queue ?? dispatch_get_main_queue()) {
completionHandler(self.request, self.response, responseObject, self.delegate.error ?? serializationError)
}
}
return self
}
/**
Suspends the request.
*/
public func suspend() {
task.suspend()
}
/**
Resumes the request.
*/
public func resume() {
task.resume()
}
/**
Cancels the request.
*/
public func cancel() {
if let downloadDelegate = delegate as? DownloadTaskDelegate {
downloadDelegate.downloadTask.cancelByProducingResumeData { (data) in
downloadDelegate.resumeData = data
}
} else {
task.cancel()
}
}
class TaskDelegate: NSObject, NSURLSessionTaskDelegate {
let task: NSURLSessionTask
let queue: dispatch_queue_t
let progress: NSProgress
var data: NSData? { return nil }
private(set) var error: NSError?
var credential: NSURLCredential?
var taskWillPerformHTTPRedirection: ((NSURLSession!, NSURLSessionTask!, NSHTTPURLResponse!, NSURLRequest!) -> (NSURLRequest!))?
var taskDidReceiveChallenge: ((NSURLSession!, NSURLSessionTask!, NSURLAuthenticationChallenge) -> (NSURLSessionAuthChallengeDisposition, NSURLCredential?))?
var taskDidSendBodyData: ((NSURLSession!, NSURLSessionTask!, Int64, Int64, Int64) -> Void)?
var taskNeedNewBodyStream: ((NSURLSession!, NSURLSessionTask!) -> (NSInputStream!))?
init(task: NSURLSessionTask) {
self.task = task
self.progress = NSProgress(totalUnitCount: 0)
self.queue = {
let label: String = "com.alamofire.task-\(task.taskIdentifier)"
let queue = dispatch_queue_create((label as NSString).UTF8String, DISPATCH_QUEUE_SERIAL)
dispatch_suspend(queue)
return queue
}()
}
// MARK: NSURLSessionTaskDelegate
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, willPerformHTTPRedirection response: NSHTTPURLResponse!, newRequest request: NSURLRequest!, completionHandler: ((NSURLRequest!) -> Void)!) {
var redirectRequest = request
if taskWillPerformHTTPRedirection != nil {
redirectRequest = taskWillPerformHTTPRedirection!(session, task, response, request)
}
completionHandler(redirectRequest)
}
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didReceiveChallenge challenge: NSURLAuthenticationChallenge!, completionHandler: ((NSURLSessionAuthChallengeDisposition, NSURLCredential!) -> Void)!) {
var disposition: NSURLSessionAuthChallengeDisposition = .PerformDefaultHandling
var credential: NSURLCredential?
if taskDidReceiveChallenge != nil {
(disposition, credential) = taskDidReceiveChallenge!(session, task, challenge)
} else {
if challenge.previousFailureCount > 0 {
disposition = .CancelAuthenticationChallenge
} else {
// TODO: Incorporate Trust Evaluation & TLS Chain Validation
switch challenge.protectionSpace.authenticationMethod! {
case NSURLAuthenticationMethodServerTrust:
credential = NSURLCredential(forTrust: challenge.protectionSpace.serverTrust)
default:
credential = self.credential ?? session.configuration.URLCredentialStorage?.defaultCredentialForProtectionSpace(challenge.protectionSpace)
}
if credential != nil {
disposition = .UseCredential
}
}
}
completionHandler(disposition, credential)
}
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, needNewBodyStream completionHandler: ((NSInputStream!) -> Void)!) {
var bodyStream: NSInputStream?
if taskNeedNewBodyStream != nil {
bodyStream = taskNeedNewBodyStream!(session, task)
}
completionHandler(bodyStream)
}
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didCompleteWithError error: NSError!) {
if error != nil {
self.error = error
}
dispatch_resume(queue)
}
}
class DataTaskDelegate: TaskDelegate, NSURLSessionDataDelegate {
var dataTask: NSURLSessionDataTask! { return task as NSURLSessionDataTask }
private var mutableData: NSMutableData
override var data: NSData? {
return mutableData
}
private var expectedContentLength: Int64?
var dataTaskDidReceiveResponse: ((NSURLSession!, NSURLSessionDataTask!, NSURLResponse!) -> (NSURLSessionResponseDisposition))?
var dataTaskDidBecomeDownloadTask: ((NSURLSession!, NSURLSessionDataTask!) -> Void)?
var dataTaskDidReceiveData: ((NSURLSession!, NSURLSessionDataTask!, NSData!) -> Void)?
var dataTaskWillCacheResponse: ((NSURLSession!, NSURLSessionDataTask!, NSCachedURLResponse!) -> (NSCachedURLResponse))?
var dataProgress: ((bytesReceived: Int64, totalBytesReceived: Int64, totalBytesExpectedToReceive: Int64) -> Void)?
override init(task: NSURLSessionTask) {
self.mutableData = NSMutableData()
super.init(task: task)
}
// MARK: NSURLSessionDataDelegate
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, didReceiveResponse response: NSURLResponse!, completionHandler: ((NSURLSessionResponseDisposition) -> Void)!) {
var disposition: NSURLSessionResponseDisposition = .Allow
expectedContentLength = response.expectedContentLength
if dataTaskDidReceiveResponse != nil {
disposition = dataTaskDidReceiveResponse!(session, dataTask, response)
}
completionHandler(disposition)
}
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, didBecomeDownloadTask downloadTask: NSURLSessionDownloadTask!) {
dataTaskDidBecomeDownloadTask?(session, dataTask)
}
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, didReceiveData data: NSData!) {
dataTaskDidReceiveData?(session, dataTask, data)
mutableData.appendData(data)
if let expectedContentLength = dataTask?.response?.expectedContentLength {
dataProgress?(bytesReceived: Int64(data.length), totalBytesReceived: Int64(mutableData.length), totalBytesExpectedToReceive: expectedContentLength)
}
}
func URLSession(session: NSURLSession!, dataTask: NSURLSessionDataTask!, willCacheResponse proposedResponse: NSCachedURLResponse!, completionHandler: ((NSCachedURLResponse!) -> Void)!) {
var cachedResponse = proposedResponse
if dataTaskWillCacheResponse != nil {
cachedResponse = dataTaskWillCacheResponse!(session, dataTask, proposedResponse)
}
completionHandler(cachedResponse)
}
}
}
// MARK: - Validation
extension Request {
/**
A closure used to validate a request that takes a URL request and URL response, and returns whether the request was valid.
*/
public typealias Validation = (NSURLRequest, NSHTTPURLResponse) -> (Bool)
/**
Validates the request, using the specified closure.
If validation fails, subsequent calls to response handlers will have an associated error.
:param: validation A closure to validate the request.
:returns: The request.
*/
public func validate(validation: Validation) -> Self {
dispatch_async(delegate.queue) {
if self.response != nil && self.delegate.error == nil {
if !validation(self.request, self.response!) {
self.delegate.error = NSError(domain: AlamofireErrorDomain, code: -1, userInfo: nil)
}
}
}
return self
}
// MARK: Status Code
private class func response(response: NSHTTPURLResponse, hasAcceptableStatusCode statusCodes: [Int]) -> Bool {
return contains(statusCodes, response.statusCode)
}
/**
Validates that the response has a status code in the specified range.
If validation fails, subsequent calls to response handlers will have an associated error.
:param: range The range of acceptable status codes.
:returns: The request.
*/
public func validate(statusCode range: Range<Int>) -> Self {
return validate { (_, response) in
return Request.response(response, hasAcceptableStatusCode: range.map({$0}))
}
}
/**
Validates that the response has a status code in the specified array.
If validation fails, subsequent calls to response handlers will have an associated error.
:param: array The acceptable status codes.
:returns: The request.
*/
public func validate(statusCode array: [Int]) -> Self {
return validate { (_, response) in
return Request.response(response, hasAcceptableStatusCode: array)
}
}
// MARK: Content-Type
private struct MIMEType {
let type: String
let subtype: String
init(_ string: String) {
let components = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).substringToIndex(string.rangeOfString(";")?.endIndex ?? string.endIndex).componentsSeparatedByString("/")
self.type = components.first!
self.subtype = components.last!
}
func matches(MIME: MIMEType) -> Bool {
switch (type, subtype) {
case ("*", "*"), ("*", MIME.subtype), (MIME.type, "*"), (MIME.type, MIME.subtype):
return true
default:
return false
}
}
}
private class func response(response: NSHTTPURLResponse, hasAcceptableContentType contentTypes: [String]) -> Bool {
if response.MIMEType != nil {
let responseMIMEType = MIMEType(response.MIMEType!)
for acceptableMIMEType in contentTypes.map({MIMEType($0)}) {
if acceptableMIMEType.matches(responseMIMEType) {
return true
}
}
}
return false
}
/**
Validates that the response has a content type in the specified array.
If validation fails, subsequent calls to response handlers will have an associated error.
:param: contentType The acceptable content types, which may specify wildcard types and/or subtypes.
:returns: The request.
*/
public func validate(contentType array: [String]) -> Self {
return validate {(_, response) in
return Request.response(response, hasAcceptableContentType: array)
}
}
// MARK: Automatic
/**
Validates that the response has a status code in the default acceptable range of 200...299, and that the content type matches any specified in the Accept HTTP header field.
If validation fails, subsequent calls to response handlers will have an associated error.
:returns: The request.
*/
public func validate() -> Self {
let acceptableStatusCodes: Range<Int> = 200..<300
let acceptableContentTypes: [String] = {
if let accept = self.request.valueForHTTPHeaderField("Accept") {
return accept.componentsSeparatedByString(",")
}
return ["*/*"]
}()
return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes)
}
}
// MARK: - Upload
extension Manager {
private enum Uploadable {
case Data(NSURLRequest, NSData)
case File(NSURLRequest, NSURL)
case Stream(NSURLRequest, NSInputStream)
}
private func upload(uploadable: Uploadable) -> Request {
var uploadTask: NSURLSessionUploadTask!
var stream: NSInputStream?
switch uploadable {
case .Data(let request, let data):
uploadTask = session.uploadTaskWithRequest(request, fromData: data)
case .File(let request, let fileURL):
uploadTask = session.uploadTaskWithRequest(request, fromFile: fileURL)
case .Stream(let request, var stream):
uploadTask = session.uploadTaskWithStreamedRequest(request)
}
let request = Request(session: session, task: uploadTask)
if stream != nil {
request.delegate.taskNeedNewBodyStream = { _, _ in
return stream
}
}
delegate[request.delegate.task] = request.delegate
if startRequestsImmediately {
request.resume()
}
return request
}
// MARK: File
/**
Creates a request for uploading a file to the specified URL request.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
:param: URLRequest The URL request
:param: file The file to upload
:returns: The created upload request.
*/
public func upload(URLRequest: URLRequestConvertible, file: NSURL) -> Request {
return upload(.File(URLRequest.URLRequest, file))
}
// MARK: Data
/**
Creates a request for uploading data to the specified URL request.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
:param: URLRequest The URL request
:param: data The data to upload
:returns: The created upload request.
*/
public func upload(URLRequest: URLRequestConvertible, data: NSData) -> Request {
return upload(.Data(URLRequest.URLRequest, data))
}
// MARK: Stream
/**
Creates a request for uploading a stream to the specified URL request.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
:param: URLRequest The URL request
:param: stream The stream to upload
:returns: The created upload request.
*/
public func upload(URLRequest: URLRequestConvertible, stream: NSInputStream) -> Request {
return upload(.Stream(URLRequest.URLRequest, stream))
}
}
extension Request {
class UploadTaskDelegate: DataTaskDelegate {
var uploadTask: NSURLSessionUploadTask! { return task as NSURLSessionUploadTask }
var uploadProgress: ((Int64, Int64, Int64) -> Void)!
// MARK: NSURLSessionTaskDelegate
func URLSession(session: NSURLSession!, task: NSURLSessionTask!, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
if uploadProgress != nil {
uploadProgress(bytesSent, totalBytesSent, totalBytesExpectedToSend)
}
progress.totalUnitCount = totalBytesExpectedToSend
progress.completedUnitCount = totalBytesSent
}
}
}
// MARK: - Download
extension Manager {
private enum Downloadable {
case Request(NSURLRequest)
case ResumeData(NSData)
}
private func download(downloadable: Downloadable, destination: (NSURL, NSHTTPURLResponse) -> (NSURL)) -> Request {
var downloadTask: NSURLSessionDownloadTask!
switch downloadable {
case .Request(let request):
downloadTask = session.downloadTaskWithRequest(request)
case .ResumeData(let resumeData):
downloadTask = session.downloadTaskWithResumeData(resumeData)
}
let request = Request(session: session, task: downloadTask)
if let downloadDelegate = request.delegate as? Request.DownloadTaskDelegate {
downloadDelegate.downloadTaskDidFinishDownloadingToURL = { (session, downloadTask, URL) in
return destination(URL, downloadTask.response as NSHTTPURLResponse)
}
}
delegate[request.delegate.task] = request.delegate
if startRequestsImmediately {
request.resume()
}
return request
}
// MARK: Request
/**
Creates a request for downloading from the specified URL request.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
:param: URLRequest The URL request
:param: destination The closure used to determine the destination of the downloaded file.
:returns: The created download request.
*/
public func download(URLRequest: URLRequestConvertible, destination: (NSURL, NSHTTPURLResponse) -> (NSURL)) -> Request {
return download(.Request(URLRequest.URLRequest), destination: destination)
}
// MARK: Resume Data
/**
Creates a request for downloading from the resume data produced from a previous request cancellation.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
:param: resumeData The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional information.
:param: destination The closure used to determine the destination of the downloaded file.
:returns: The created download request.
*/
public func download(resumeData: NSData, destination: Request.DownloadFileDestination) -> Request {
return download(.ResumeData(resumeData), destination: destination)
}
}
extension Request {
/**
A closure executed once a request has successfully completed in order to determine where to move the temporary file written to during the download process. The closure takes two arguments: the temporary file URL and the URL response, and returns a single argument: the file URL where the temporary file should be moved.
*/
public typealias DownloadFileDestination = (NSURL, NSHTTPURLResponse) -> (NSURL)
/**
Creates a download file destination closure which uses the default file manager to move the temporary file to a file URL in the first available directory with the specified search path directory and search path domain mask.
:param: directory The search path directory. `.DocumentDirectory` by default.
:param: domain The search path domain mask. `.UserDomainMask` by default.
:returns: A download file destination closure.
*/
public class func suggestedDownloadDestination(directory: NSSearchPathDirectory = .DocumentDirectory, domain: NSSearchPathDomainMask = .UserDomainMask) -> DownloadFileDestination {
return { (temporaryURL, response) -> (NSURL) in
if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
return directoryURL.URLByAppendingPathComponent(response.suggestedFilename!)
}
return temporaryURL
}
}
class DownloadTaskDelegate: TaskDelegate, NSURLSessionDownloadDelegate {
var downloadTask: NSURLSessionDownloadTask! { return task as NSURLSessionDownloadTask }
var downloadProgress: ((Int64, Int64, Int64) -> Void)?
var resumeData: NSData?
override var data: NSData? { return resumeData }
var downloadTaskDidFinishDownloadingToURL: ((NSURLSession!, NSURLSessionDownloadTask!, NSURL) -> (NSURL))?
var downloadTaskDidWriteData: ((NSURLSession!, NSURLSessionDownloadTask!, Int64, Int64, Int64) -> Void)?
var downloadTaskDidResumeAtOffset: ((NSURLSession!, NSURLSessionDownloadTask!, Int64, Int64) -> Void)?
// MARK: NSURLSessionDownloadDelegate
func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
if downloadTaskDidFinishDownloadingToURL != nil {
let destination = downloadTaskDidFinishDownloadingToURL!(session, downloadTask, location)
var fileManagerError: NSError?
NSFileManager.defaultManager().moveItemAtURL(location, toURL: destination, error: &fileManagerError)
if fileManagerError != nil {
error = fileManagerError
}
}
}
func URLSession(session: NSURLSession!, downloadTask: NSURLSessionDownloadTask!, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
downloadTaskDidWriteData?(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
downloadProgress?(bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
progress.totalUnitCount = totalBytesExpectedToWrite
progress.completedUnitCount = totalBytesWritten
}
func URLSession(session: NSURLSession!, downloadTask: NSURLSessionDownloadTask!, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
downloadTaskDidResumeAtOffset?(session, downloadTask, fileOffset, expectedTotalBytes)
progress.totalUnitCount = expectedTotalBytes
progress.completedUnitCount = fileOffset
}
}
}
// MARK: - Printable
extension Request: Printable {
/// The textual representation used when written to an `OutputStreamType`, which includes the HTTP method and URL, as well as the response status code if a response has been received.
public var description: String {
var components: [String] = []
if request.HTTPMethod != nil {
components.append(request.HTTPMethod!)
}
components.append(request.URL.absoluteString!)
if response != nil {
components.append("(\(response!.statusCode))")
}
return join(" ", components)
}
}
extension Request: DebugPrintable {
func cURLRepresentation() -> String {
var components: [String] = ["$ curl -i"]
let URL = request.URL
if request.HTTPMethod != nil && request.HTTPMethod != "GET" {
components.append("-X \(request.HTTPMethod!)")
}
if let credentialStorage = self.session.configuration.URLCredentialStorage {
let protectionSpace = NSURLProtectionSpace(host: URL.host!, port: URL.port?.integerValue ?? 0, `protocol`: URL.scheme!, realm: URL.host!, authenticationMethod: NSURLAuthenticationMethodHTTPBasic)
if let credentials = credentialStorage.credentialsForProtectionSpace(protectionSpace)?.values.array {
for credential: NSURLCredential in (credentials as [NSURLCredential]) {
components.append("-u \(credential.user!):\(credential.password!)")
}
} else {
if let credential = delegate.credential {
components.append("-u \(credential.user!):\(credential.password!)")
}
}
}
if let cookieStorage = session.configuration.HTTPCookieStorage {
if let cookies = cookieStorage.cookiesForURL(URL) as? [NSHTTPCookie] {
if !cookies.isEmpty {
let string = cookies.reduce(""){ $0 + "\($1.name)=\($1.value);" }
components.append("-b \"\(string.substringToIndex(string.endIndex.predecessor()))\"")
}
}
}
for (field, value) in request.allHTTPHeaderFields! {
switch field {
case "Cookie":
continue
default:
components.append("-H \"\(field): \(value)\"")
}
}
for (field, value) in session.configuration.HTTPAdditionalHeaders! {
switch field {
case "Cookie":
continue
default:
components.append("-H \"\(field): \(value)\"")
}
}
if let HTTPBody = request.HTTPBody {
if let escapedBody = NSString(data: HTTPBody, encoding: NSUTF8StringEncoding)?.stringByReplacingOccurrencesOfString("\"", withString: "\\\"") {
components.append("-d \"\(escapedBody)\"")
}
}
components.append("\"\(URL.absoluteString!)\"")
return join(" \\\n\t", components)
}
/// The textual representation used when written to an `OutputStreamType`, in the form of a cURL command.
public var debugDescription: String {
return cURLRepresentation()
}
}
// MARK: - Response Serializers
// MARK: String
extension Request {
/**
Creates a response serializer that returns a string initialized from the response data with the specified string encoding.
:param: encoding The string encoding. `NSUTF8StringEncoding` by default.
:returns: A string response serializer.
*/
public class func stringResponseSerializer(encoding: NSStringEncoding = NSUTF8StringEncoding) -> Serializer {
return { (_, _, data) in
let string = NSString(data: data!, encoding: encoding)
return (string, nil)
}
}
/**
Adds a handler to be called once the request has finished.
:param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the string, if one could be created from the URL response and data, and any error produced while creating the string.
:returns: The request.
*/
public func responseString(completionHandler: (NSURLRequest, NSHTTPURLResponse?, String?, NSError?) -> Void) -> Self {
return responseString(completionHandler: completionHandler)
}
/**
Adds a handler to be called once the request has finished.
:param: encoding The string encoding. `NSUTF8StringEncoding` by default.
:param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the string, if one could be created from the URL response and data, and any error produced while creating the string.
:returns: The request.
*/
public func responseString(encoding: NSStringEncoding = NSUTF8StringEncoding, completionHandler: (NSURLRequest, NSHTTPURLResponse?, String?, NSError?) -> Void) -> Self {
return response(serializer: Request.stringResponseSerializer(encoding: encoding), completionHandler: { request, response, string, error in
completionHandler(request, response, string as? String, error)
})
}
}
// MARK: JSON
extension Request {
/**
Creates a response serializer that returns a JSON object constructed from the response data using `NSJSONSerialization` with the specified reading options.
:param: options The JSON serialization reading options. `.AllowFragments` by default.
:returns: A JSON object response serializer.
*/
public class func JSONResponseSerializer(options: NSJSONReadingOptions = .AllowFragments) -> Serializer {
return { (request, response, data) in
if data == nil {
return (nil, nil)
}
var serializationError: NSError?
let JSON: AnyObject? = NSJSONSerialization.JSONObjectWithData(data!, options: options, error: &serializationError)
return (JSON, serializationError)
}
}
/**
Adds a handler to be called once the request has finished.
:param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the JSON object, if one could be created from the URL response and data, and any error produced while creating the JSON object.
:returns: The request.
*/
public func responseJSON(completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
return responseJSON(completionHandler: completionHandler)
}
/**
Adds a handler to be called once the request has finished.
:param: options The JSON serialization reading options. `.AllowFragments` by default.
:param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the JSON object, if one could be created from the URL response and data, and any error produced while creating the JSON object.
:returns: The request.
*/
public func responseJSON(options: NSJSONReadingOptions = .AllowFragments, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
return response(serializer: Request.JSONResponseSerializer(options: options), completionHandler: { (request, response, JSON, error) in
completionHandler(request, response, JSON, error)
})
}
}
// MARK: Property List
extension Request {
/**
Creates a response serializer that returns an object constructed from the response data using `NSPropertyListSerialization` with the specified reading options.
:param: options The property list reading options. `0` by default.
:returns: A property list object response serializer.
*/
public class func propertyListResponseSerializer(options: NSPropertyListReadOptions = 0) -> Serializer {
return { (request, response, data) in
if data == nil {
return (nil, nil)
}
var propertyListSerializationError: NSError?
let plist: AnyObject? = NSPropertyListSerialization.propertyListWithData(data!, options: options, format: nil, error: &propertyListSerializationError)
return (plist, propertyListSerializationError)
}
}
/**
Adds a handler to be called once the request has finished.
:param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the property list, if one could be created from the URL response and data, and any error produced while creating the property list.
:returns: The request.
*/
public func responsePropertyList(completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
return responsePropertyList(completionHandler: completionHandler)
}
/**
Adds a handler to be called once the request has finished.
:param: options The property list reading options. `0` by default.
:param: completionHandler A closure to be executed once the request has finished. The closure takes 4 arguments: the URL request, the URL response, if one was received, the property list, if one could be created from the URL response and data, and any error produced while creating the property list.
:returns: The request.
*/
public func responsePropertyList(options: NSPropertyListReadOptions = 0, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
return response(serializer: Request.propertyListResponseSerializer(options: options), completionHandler: { (request, response, plist, error) in
completionHandler(request, response, plist, error)
})
}
}
// MARK: - Convenience -
private func URLRequest(method: Method, URL: URLStringConvertible) -> NSURLRequest {
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: URL.URLString)!)
mutableURLRequest.HTTPMethod = method.rawValue
return mutableURLRequest
}
// MARK: - Request
/**
Creates a request using the shared manager instance for the specified method, URL string, parameters, and parameter encoding.
:param: method The HTTP method.
:param: URLString The URL string.
:param: parameters The parameters. `nil` by default.
:param: encoding The parameter encoding. `.URL` by default.
:returns: The created request.
*/
public func request(method: Method, URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
return request(encoding.encode(URLRequest(method, URLString), parameters: parameters).0)
}
/**
Creates a request using the shared manager instance for the specified URL request.
If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
:param: URLRequest The URL request
:returns: The created request.
*/
public func request(URLRequest: URLRequestConvertible) -> Request {
return Manager.sharedInstance.request(URLRequest.URLRequest)
}
// MARK: - Upload
// MARK: File
/**
Creates an upload request using the shared manager instance for the specified method, URL string, and file.
:param: method The HTTP method.
:param: URLString The URL string.
:param: file The file to upload.
:returns: The created upload request.
*/
public func upload(method: Method, URLString: URLStringConvertible, file: NSURL) -> Request {
return Manager.sharedInstance.upload(URLRequest(method, URLString), file: file)
}
/**
Creates an upload request using the shared manager instance for the specified URL request and file.
:param: URLRequest The URL request.
:param: file The file to upload.
:returns: The created upload request.
*/
public func upload(URLRequest: URLRequestConvertible, file: NSURL) -> Request {
return Manager.sharedInstance.upload(URLRequest, file: file)
}
// MARK: Data
/**
Creates an upload request using the shared manager instance for the specified method, URL string, and data.
:param: method The HTTP method.
:param: URLString The URL string.
:param: data The data to upload.
:returns: The created upload request.
*/
public func upload(method: Method, URLString: URLStringConvertible, data: NSData) -> Request {
return Manager.sharedInstance.upload(URLRequest(method, URLString), data: data)
}
/**
Creates an upload request using the shared manager instance for the specified URL request and data.
:param: URLRequest The URL request.
:param: data The data to upload.
:returns: The created upload request.
*/
public func upload(URLRequest: URLRequestConvertible, data: NSData) -> Request {
return Manager.sharedInstance.upload(URLRequest, data: data)
}
// MARK: Stream
/**
Creates an upload request using the shared manager instance for the specified method, URL string, and stream.
:param: method The HTTP method.
:param: URLString The URL string.
:param: stream The stream to upload.
:returns: The created upload request.
*/
public func upload(method: Method, URLString: URLStringConvertible, stream: NSInputStream) -> Request {
return Manager.sharedInstance.upload(URLRequest(method, URLString), stream: stream)
}
/**
Creates an upload request using the shared manager instance for the specified URL request and stream.
:param: URLRequest The URL request.
:param: stream The stream to upload.
:returns: The created upload request.
*/
public func upload(URLRequest: URLRequestConvertible, stream: NSInputStream) -> Request {
return Manager.sharedInstance.upload(URLRequest, stream: stream)
}
// MARK: - Download
// MARK: URL Request
/**
Creates a download request using the shared manager instance for the specified method and URL string.
:param: method The HTTP method.
:param: URLString The URL string.
:param: destination The closure used to determine the destination of the downloaded file.
:returns: The created download request.
*/
public func download(method: Method, URLString: URLStringConvertible, destination: Request.DownloadFileDestination) -> Request {
return Manager.sharedInstance.download(URLRequest(method, URLString), destination: destination)
}
/**
Creates a download request using the shared manager instance for the specified URL request.
:param: URLRequest The URL request.
:param: destination The closure used to determine the destination of the downloaded file.
:returns: The created download request.
*/
public func download(URLRequest: URLRequestConvertible, destination: Request.DownloadFileDestination) -> Request {
return Manager.sharedInstance.download(URLRequest, destination: destination)
}
// MARK: Resume Data
/**
Creates a request using the shared manager instance for downloading from the resume data produced from a previous request cancellation.
:param: resumeData The resume data. This is an opaque data blob produced by `NSURLSessionDownloadTask` when a task is cancelled. See `NSURLSession -downloadTaskWithResumeData:` for additional information.
:param: destination The closure used to determine the destination of the downloaded file.
:returns: The created download request.
*/
public func download(resumeData data: NSData, destination: Request.DownloadFileDestination) -> Request {
return Manager.sharedInstance.download(data, destination: destination)
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>com.alamofire.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
951B3FAD58824E9B86E74AD3 /* Pods_iOS_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EEEDE5D19C6F39708A8F116 /* Pods_iOS_Example.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
F8111E0B19A951050040E7D1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E0A19A951050040E7D1 /* AppDelegate.swift */; };
F8111E0D19A951050040E7D1 /* MasterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E0C19A951050040E7D1 /* MasterViewController.swift */; };
F8111E0F19A951050040E7D1 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8111E0E19A951050040E7D1 /* DetailViewController.swift */; };
F8111E1219A951050040E7D1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8111E1019A951050040E7D1 /* Main.storyboard */; };
F8111E1419A951050040E7D1 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F8111E1319A951050040E7D1 /* Images.xcassets */; };
F8E6024019CB3C0700A3E7F1 /* HTTPBin.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E6023F19CB3C0700A3E7F1 /* HTTPBin.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
4EEEDE5D19C6F39708A8F116 /* Pods_iOS_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_iOS_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E72AAD1560ED38C6203AE071 /* Pods-iOS Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOS Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-iOS Example/Pods-iOS Example.release.xcconfig"; sourceTree = "<group>"; };
F787F2A3625E02D003BE2209 /* Pods-iOS Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iOS Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-iOS Example/Pods-iOS Example.debug.xcconfig"; sourceTree = "<group>"; };
F8111E0519A951050040E7D1 /* iOS Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "iOS Example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
F8111E0919A951050040E7D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
F8111E0A19A951050040E7D1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
F8111E0C19A951050040E7D1 /* MasterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterViewController.swift; sourceTree = "<group>"; };
F8111E0E19A951050040E7D1 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
F8111E1119A951050040E7D1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
F8111E1319A951050040E7D1 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
F8E6023F19CB3C0700A3E7F1 /* HTTPBin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBin.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
F8111E0219A951050040E7D1 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
951B3FAD58824E9B86E74AD3 /* Pods_iOS_Example.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2D42BDE636F2C5E26D98A0B6 /* Pods */ = {
isa = PBXGroup;
children = (
F787F2A3625E02D003BE2209 /* Pods-iOS Example.debug.xcconfig */,
E72AAD1560ED38C6203AE071 /* Pods-iOS Example.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
D8ADE35CA568F7C97750679B /* Frameworks */ = {
isa = PBXGroup;
children = (
4EEEDE5D19C6F39708A8F116 /* Pods_iOS_Example.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
F8111DFC19A951050040E7D1 = {
isa = PBXGroup;
children = (
F8111E0719A951050040E7D1 /* Source */,
F8111E0619A951050040E7D1 /* Products */,
2D42BDE636F2C5E26D98A0B6 /* Pods */,
D8ADE35CA568F7C97750679B /* Frameworks */,
);
sourceTree = "<group>";
};
F8111E0619A951050040E7D1 /* Products */ = {
isa = PBXGroup;
children = (
F8111E0519A951050040E7D1 /* iOS Example.app */,
);
name = Products;
sourceTree = "<group>";
};
F8111E0719A951050040E7D1 /* Source */ = {
isa = PBXGroup;
children = (
F8111E0A19A951050040E7D1 /* AppDelegate.swift */,
F8111E0C19A951050040E7D1 /* MasterViewController.swift */,
F8111E0E19A951050040E7D1 /* DetailViewController.swift */,
F8E6023F19CB3C0700A3E7F1 /* HTTPBin.swift */,
F8111E1019A951050040E7D1 /* Main.storyboard */,
F8111E1319A951050040E7D1 /* Images.xcassets */,
F8111E0819A951050040E7D1 /* Supporting Files */,
);
name = Source;
path = Example;
sourceTree = "<group>";
};
F8111E0819A951050040E7D1 /* Supporting Files */ = {
isa = PBXGroup;
children = (
F8111E0919A951050040E7D1 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
F8111E0419A951050040E7D1 /* iOS Example */ = {
isa = PBXNativeTarget;
buildConfigurationList = F8111E2319A951050040E7D1 /* Build configuration list for PBXNativeTarget "iOS Example" */;
buildPhases = (
6540B3CA5F945114F2BD9C7B /* Check Pods Manifest.lock */,
F8111E0119A951050040E7D1 /* Sources */,
F8111E0219A951050040E7D1 /* Frameworks */,
F8111E0319A951050040E7D1 /* Resources */,
232F8168EB8673C9D2405BA7 /* Embed Pods Frameworks */,
B81850C1CB84CCCCFC8FFC52 /* Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = "iOS Example";
productName = Alamofire;
productReference = F8111E0519A951050040E7D1 /* iOS Example.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
F8111DFD19A951050040E7D1 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0600;
ORGANIZATIONNAME = Alamofire;
TargetAttributes = {
F8111E0419A951050040E7D1 = {
CreatedOnToolsVersion = 6.0;
};
};
};
buildConfigurationList = F8111E0019A951050040E7D1 /* Build configuration list for PBXProject "iOS Example" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = F8111DFC19A951050040E7D1;
productRefGroup = F8111E0619A951050040E7D1 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
F8111E0419A951050040E7D1 /* iOS Example */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
F8111E0319A951050040E7D1 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F8111E1219A951050040E7D1 /* Main.storyboard in Resources */,
F8111E1419A951050040E7D1 /* Images.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
232F8168EB8673C9D2405BA7 /* Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-iOS Example/Pods-iOS Example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
6540B3CA5F945114F2BD9C7B /* Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Check Pods Manifest.lock";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
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";
showEnvVarsInLog = 0;
};
B81850C1CB84CCCCFC8FFC52 /* Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Copy Pods Resources";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-iOS Example/Pods-iOS Example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
F8111E0119A951050040E7D1 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F8111E0F19A951050040E7D1 /* DetailViewController.swift in Sources */,
F8E6024019CB3C0700A3E7F1 /* HTTPBin.swift in Sources */,
F8111E0D19A951050040E7D1 /* MasterViewController.swift in Sources */,
F8111E0B19A951050040E7D1 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
F8111E1019A951050040E7D1 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
F8111E1119A951050040E7D1 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
F8111E2119A951050040E7D1 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
F8111E2219A951050040E7D1 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
F8111E2419A951050040E7D1 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F787F2A3625E02D003BE2209 /* Pods-iOS Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "iOS Example";
};
name = Debug;
};
F8111E2519A951050040E7D1 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = E72AAD1560ED38C6203AE071 /* Pods-iOS Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
INFOPLIST_FILE = "$(SRCROOT)/Example/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_NAME = "iOS Example";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
F8111E0019A951050040E7D1 /* Build configuration list for PBXProject "iOS Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F8111E2119A951050040E7D1 /* Debug */,
F8111E2219A951050040E7D1 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F8111E2319A951050040E7D1 /* Build configuration list for PBXNativeTarget "iOS Example" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F8111E2419A951050040E7D1 /* Debug */,
F8111E2519A951050040E7D1 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = F8111DFD19A951050040E7D1 /* Project object */;
}
...@@ -56,9 +56,13 @@ module Pod ...@@ -56,9 +56,13 @@ module Pod
autoload :BridgeSupport, 'cocoapods/generator/bridge_support' autoload :BridgeSupport, 'cocoapods/generator/bridge_support'
autoload :CopyResourcesScript, 'cocoapods/generator/copy_resources_script' autoload :CopyResourcesScript, 'cocoapods/generator/copy_resources_script'
autoload :DummySource, 'cocoapods/generator/dummy_source' autoload :DummySource, 'cocoapods/generator/dummy_source'
autoload :EmbedFrameworksScript, 'cocoapods/generator/embed_frameworks_script'
autoload :Header, 'cocoapods/generator/header' autoload :Header, 'cocoapods/generator/header'
autoload :InfoPlistFile, 'cocoapods/generator/info_plist_file'
autoload :ModuleMap, 'cocoapods/generator/module_map'
autoload :PrefixHeader, 'cocoapods/generator/prefix_header' autoload :PrefixHeader, 'cocoapods/generator/prefix_header'
autoload :TargetEnvironmentHeader, 'cocoapods/generator/target_environment_header' autoload :TargetEnvironmentHeader, 'cocoapods/generator/target_environment_header'
autoload :UmbrellaHeader, 'cocoapods/generator/umbrella_header'
autoload :XCConfig, 'cocoapods/generator/xcconfig' autoload :XCConfig, 'cocoapods/generator/xcconfig'
end end
......
...@@ -28,7 +28,7 @@ module Pod ...@@ -28,7 +28,7 @@ module Pod
self.abstract_command = true self.abstract_command = true
self.command = 'pod' self.command = 'pod'
self.version = VERSION self.version = VERSION
self.description = 'CocoaPods, the Objective-C library package manager.' self.description = 'CocoaPods, the Cocoa library package manager.'
self.plugin_prefixes = %w(claide cocoapods) self.plugin_prefixes = %w(claide cocoapods)
[Install, Update, Outdated, IPC::Podfile, IPC::Repl].each { |c| c.send(:include, ProjectDirectory) } [Install, Update, Outdated, IPC::Podfile, IPC::Repl].each { |c| c.send(:include, ProjectDirectory) }
......
...@@ -113,6 +113,7 @@ module Pod ...@@ -113,6 +113,7 @@ module Pod
['--subspec=NAME', 'Lint validates only the given subspec'], ['--subspec=NAME', 'Lint validates only the given subspec'],
['--no-subspecs', 'Lint skips validation of subspecs'], ['--no-subspecs', 'Lint skips validation of subspecs'],
['--no-clean', 'Lint leaves the build directory intact for inspection'], ['--no-clean', 'Lint leaves the build directory intact for inspection'],
['--use-frameworks', 'Lint uses frameworks to install the spec'],
['--sources=https://github.com/artsy/Specs', 'The sources from which to pull dependant pods ' \ ['--sources=https://github.com/artsy/Specs', 'The sources from which to pull dependant pods ' \
'(defaults to https://github.com/CocoaPods/Specs.git). '\ '(defaults to https://github.com/CocoaPods/Specs.git). '\
'Multiple sources must be comma-delimited.']].concat(super) 'Multiple sources must be comma-delimited.']].concat(super)
...@@ -124,6 +125,7 @@ module Pod ...@@ -124,6 +125,7 @@ module Pod
@clean = argv.flag?('clean', true) @clean = argv.flag?('clean', true)
@subspecs = argv.flag?('subspecs', true) @subspecs = argv.flag?('subspecs', true)
@only_subspec = argv.option('subspec') @only_subspec = argv.option('subspec')
@use_frameworks = argv.flag?('use-frameworks')
@source_urls = argv.option('sources', 'https://github.com/CocoaPods/Specs.git').split(',') @source_urls = argv.option('sources', 'https://github.com/CocoaPods/Specs.git').split(',')
@podspecs_paths = argv.arguments! @podspecs_paths = argv.arguments!
super super
...@@ -144,6 +146,7 @@ module Pod ...@@ -144,6 +146,7 @@ module Pod
validator.allow_warnings = @allow_warnings validator.allow_warnings = @allow_warnings
validator.no_subspecs = !@subspecs || @only_subspec validator.no_subspecs = !@subspecs || @only_subspec
validator.only_subspec = @only_subspec validator.only_subspec = @only_subspec
validator.use_frameworks = @use_frameworks
validator.validate validator.validate
unless @clean unless @clean
......
...@@ -68,6 +68,7 @@ module Pod ...@@ -68,6 +68,7 @@ module Pod
['--subspec=NAME', 'Lint validates only the given subspec'], ['--subspec=NAME', 'Lint validates only the given subspec'],
['--no-subspecs', 'Lint skips validation of subspecs'], ['--no-subspecs', 'Lint skips validation of subspecs'],
['--no-clean', 'Lint leaves the build directory intact for inspection'], ['--no-clean', 'Lint leaves the build directory intact for inspection'],
['--use-frameworks', 'Lint uses frameworks to install the spec'],
['--sources=https://github.com/artsy/Specs', 'The sources from which to pull dependant pods ' \ ['--sources=https://github.com/artsy/Specs', 'The sources from which to pull dependant pods ' \
'(defaults to https://github.com/CocoaPods/Specs.git). '\ '(defaults to https://github.com/CocoaPods/Specs.git). '\
'Multiple sources must be comma-delimited.']].concat(super) 'Multiple sources must be comma-delimited.']].concat(super)
...@@ -79,6 +80,7 @@ module Pod ...@@ -79,6 +80,7 @@ module Pod
@clean = argv.flag?('clean', true) @clean = argv.flag?('clean', true)
@subspecs = argv.flag?('subspecs', true) @subspecs = argv.flag?('subspecs', true)
@only_subspec = argv.option('subspec') @only_subspec = argv.option('subspec')
@use_frameworks = argv.flag?('use-frameworks')
@source_urls = argv.option('sources', 'https://github.com/CocoaPods/Specs.git').split(',') @source_urls = argv.option('sources', 'https://github.com/CocoaPods/Specs.git').split(',')
@podspecs_paths = argv.arguments! @podspecs_paths = argv.arguments!
super super
...@@ -94,6 +96,7 @@ module Pod ...@@ -94,6 +96,7 @@ module Pod
validator.allow_warnings = @allow_warnings validator.allow_warnings = @allow_warnings
validator.no_subspecs = !@subspecs || @only_subspec validator.no_subspecs = !@subspecs || @only_subspec
validator.only_subspec = @only_subspec validator.only_subspec = @only_subspec
validator.use_frameworks = @use_frameworks
validator.validate validator.validate
invalid_count += 1 unless validator.validated? invalid_count += 1 unless validator.validated?
...@@ -580,8 +583,8 @@ Pod::Spec.new do |s| ...@@ -580,8 +583,8 @@ Pod::Spec.new do |s|
# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
# #
# CocoaPods is smart about how it includes source code. For source files # CocoaPods is smart about how it includes source code. For source files
# giving a folder will include any h, m, mm, c & cpp files. For header # giving a folder will include any swift, h, m, mm, c & cpp files.
# files it will include any header in the folder. # For header files it will include any header in the folder.
# Not including the public_header_files will make all headers public. # Not including the public_header_files will make all headers public.
# #
......
...@@ -264,7 +264,7 @@ module Pod ...@@ -264,7 +264,7 @@ module Pod
'CocoaPods.podfile.yaml', 'CocoaPods.podfile.yaml',
'CocoaPods.podfile', 'CocoaPods.podfile',
'Podfile', 'Podfile',
] ].freeze
public public
......
module Pod
module Generator
class EmbedFrameworksScript
# @return [TargetDefinition] The target definition, whose label will be
# used to locate the target-specific build products.
#
attr_reader :target_definition
# @return [Hash{String, Array{String}] Multiple lists of frameworks per
# configuration.
#
attr_reader :frameworks_by_config
# @param [TargetDefinition] target_definition
# @see #target_definition
#
# @param [Hash{String, Array{String}] frameworks_by_config
# @see #frameworks_by_config
#
def initialize(target_definition, frameworks_by_config)
@target_definition = target_definition
@frameworks_by_config = frameworks_by_config
end
# Saves the resource script to the given pathname.
#
# @param [Pathname] pathname
# The path where the embed frameworks script should be saved.
#
# @return [void]
#
def save_as(pathname)
pathname.open('w') do |file|
file.puts(script)
end
File.chmod(0755, pathname.to_s)
end
private
# @!group Private Helpers
# @return [String] The contents of the embed frameworks script.
#
def script
script = <<-eos.strip_heredoc
#!/bin/sh
set -e
echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}"
install_framework()
{
local source="${BUILT_PRODUCTS_DIR}/#{target_definition.label}/$1"
local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}"
if [ -L ${source} ]; then
echo "Symlinked..."
source=$(readlink "${source}")
fi
# use filter instead of exclude so missing patterns dont' throw errors
echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers/\" --filter \"- PrivateHeaders/\" ${source} ${destination}"
rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers/" --filter "- PrivateHeaders/" "${source}" "${destination}"
# Resign the code if required by the build settings to avoid unstable apps
if [ "${CODE_SIGNING_REQUIRED}" == "YES" ]; then
code_sign "${destination}/$1"
fi
# Embed linked Swift runtime libraries
local basename
basename=$(echo $1 | sed -E s/\\\\..+// && exit ${PIPESTATUS[0]})
local swift_runtime_libs
swift_runtime_libs=$(xcrun otool -LX "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/$1/${basename}" | grep @rpath/libswift | sed -E s/@rpath\\\\/\\(.+dylib\\).*/\\\\1/g | uniq -u && exit ${PIPESTATUS[0]})
for lib in $swift_runtime_libs; do
echo "rsync -av \\"${SWIFT_STDLIB_PATH}/${lib}\\" \\"${destination}\\""
rsync -av "${SWIFT_STDLIB_PATH}/${lib}" "${destination}"
done
}
# Signs a framework with the provided identity
code_sign() {
# Use the current code_sign_identitiy
echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}"
echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements $1"
/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1"
}
eos
script += "\n" unless frameworks_by_config.values.all?(&:empty?)
frameworks_by_config.each do |config, frameworks|
unless frameworks.empty?
script += %(if [[ "$CONFIGURATION" == "#{config}" ]]; then\n)
frameworks.each do |framework|
script += " install_framework '#{framework}'\n"
end
script += "fi\n"
end
end
script
end
end
end
end
...@@ -13,7 +13,11 @@ module Pod ...@@ -13,7 +13,11 @@ module Pod
# @return [Array<String>] The list of the headers to import. # @return [Array<String>] The list of the headers to import.
# #
attr_reader :imports attr_accessor :imports
# @return [Array<String>] The list of the modules to import.
#
attr_accessor :module_imports
# @param [Symbol] platform # @param [Symbol] platform
# @see platform # @see platform
...@@ -21,6 +25,7 @@ module Pod ...@@ -21,6 +25,7 @@ module Pod
def initialize(platform) def initialize(platform)
@platform = platform @platform = platform
@imports = [] @imports = []
@module_imports = []
end end
# Generates the contents of the header according to the platform. # Generates the contents of the header according to the platform.
...@@ -41,6 +46,13 @@ module Pod ...@@ -41,6 +46,13 @@ module Pod
result << %|#import "#{import}"\n| result << %|#import "#{import}"\n|
end end
unless module_imports.empty?
module_imports.each do |import|
result << %|\n@import #{import}|
end
result << "\n"
end
result result
end end
......
module Pod
module Generator
# Generates Info.plist files. A Info.plist file is generated for each
# Pod and for each Pod target definition, that requires to be built as
# framework. It states public attributes.
#
class InfoPlistFile
# @return [Target] the target represented by this Info.plist.
#
attr_reader :target
# @param [Target] target @see target
#
def initialize(target)
@target = target
end
# Generates and saves the Info.plist to the given path.
#
# @param [Pathname] path
# the path where the prefix header should be stored.
#
# @return [void]
#
def save_as(path)
contents = generate
path.open('w') do |f|
f.write(contents)
end
end
# The version associated with the current target
#
# @note Will return 1.0.0 for the AggregateTarget
#
# @return [String]
#
def target_version
if target && target.respond_to?(:root_spec)
target.root_spec.version.to_s
else
'1.0.0'
end
end
# Generates the contents of the Info.plist
#
# @return [String]
#
def generate
FILE_CONTENTS.sub('${CURRENT_PROJECT_VERSION_STRING}', target_version)
end
FILE_CONTENTS = <<-EOS
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>org.cocoapods.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>${CURRENT_PROJECT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
EOS
end
end
end
module Pod
module Generator
# Generates LLVM module map files. A module map file is generated for each
# Pod and for each Pod target definition that is built as a framework. It
# specifies a different umbrella header than usual to avoid name conflicts
# with existing headers of the podspec.
#
class ModuleMap
# @return [Target] the target represented by this Info.plist.
#
attr_reader :target
# @param [Target] target @see target
#
def initialize(target)
@target = target
end
# Generates and saves the Info.plist to the given path.
#
# @param [Pathname] path
# the path where the prefix header should be stored.
#
# @return [void]
#
def save_as(path)
contents = generate
path.open('w') do |f|
f.write(contents)
end
end
# Generates the contents of the module.modulemap file.
#
# @return [String]
#
def generate
<<-eos.strip_heredoc
framework module #{target.product_module_name} {
umbrella header "#{target.umbrella_header_path.basename}"
export *
module * { export * }
}
eos
end
end
end
end
module Pod
module Generator
# Generates an umbrella header file for clang modules, which are used by
# dynamic frameworks on iOS 8 and OSX 10.10 under the hood.
#
# If the target is a +PodTarget+, then the umbrella header is required
# to make all public headers in a convenient manner available without the
# need to write out header declarations for every library header.
#
class UmbrellaHeader < Header
# @return [Target]
# the target, which provides the product name
attr_reader :target
# @param [Target] target
# @see target
#
def initialize(target)
super(target.target_definition.platform)
@target = target
end
# Generates the contents of the umbrella header according to the included
# pods.
#
# @return [String]
#
def generate
result = super
result << "\n"
result << <<-eos.strip_heredoc
FOUNDATION_EXPORT double #{target.product_module_name}VersionNumber;
FOUNDATION_EXPORT const unsigned char #{target.product_module_name}VersionString[];
eos
result << "\n"
result
end
end
end
end
...@@ -47,34 +47,58 @@ module Pod ...@@ -47,34 +47,58 @@ module Pod
# @return [Xcodeproj::Config] # @return [Xcodeproj::Config]
# #
def generate def generate
header_search_path_flags = target.sandbox.public_headers.search_paths(target.platform) pod_targets = target.pod_targets_for_build_configuration(@configuration_name)
@xcconfig = Xcodeproj::Config.new( config = {
'OTHER_LDFLAGS' => XCConfigHelper.default_ld_flags(target), 'OTHER_LDFLAGS' => XCConfigHelper.default_ld_flags(target),
'OTHER_LIBTOOLFLAGS' => '$(OTHER_LDFLAGS)', 'OTHER_LIBTOOLFLAGS' => '$(OTHER_LDFLAGS)',
'HEADER_SEARCH_PATHS' => XCConfigHelper.quote(header_search_path_flags), 'PODS_ROOT' => target.relative_pods_root,
'PODS_ROOT' => target.relative_pods_root, 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1',
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1', }
'OTHER_CFLAGS' => '$(inherited) ' + XCConfigHelper.quote(header_search_path_flags, '-isystem')
)
target.pod_targets.each do |pod_target| if target.requires_frameworks?
next unless pod_target.include_in_build_config?(@configuration_name) # Framework headers are automatically discoverable by `#import <…>`.
header_search_paths = pod_targets.map { |target| "$PODS_FRAMEWORK_BUILD_PATH/#{target.product_name}/Headers" }
build_settings = {
'PODS_FRAMEWORK_BUILD_PATH' => target.configuration_build_dir,
'FRAMEWORK_SEARCH_PATHS' => '"$PODS_FRAMEWORK_BUILD_PATH"',
# Make headers discoverable by `import "…"`
'OTHER_CFLAGS' => '$(inherited) ' + XCConfigHelper.quote(header_search_paths, '-iquote')
}
config.merge!(build_settings)
else
# Make headers discoverable from $PODS_ROOT/Headers directory
header_search_paths = target.sandbox.public_headers.search_paths(target.platform)
build_settings = {
# by `#import "…"`
'HEADER_SEARCH_PATHS' => XCConfigHelper.quote(header_search_paths),
# by `#import <…>`
'OTHER_CFLAGS' => '$(inherited) ' + XCConfigHelper.quote(header_search_paths, '-isystem')
}
config.merge!(build_settings)
end
pod_target.file_accessors.each do |file_accessor| @xcconfig = Xcodeproj::Config.new(config)
XCConfigHelper.add_spec_build_settings_to_xcconfig(file_accessor.spec_consumer, @xcconfig)
file_accessor.vendored_frameworks.each do |vendored_framework|
XCConfigHelper.add_framework_build_settings(vendored_framework, @xcconfig, target.sandbox.root)
end
file_accessor.vendored_libraries.each do |vendored_library|
XCConfigHelper.add_library_build_settings(vendored_library, @xcconfig, target.sandbox.root)
end
end
# Add pod static lib to list of libraries that are to be linked with XCConfigHelper.add_target_specific_settings(target, @xcconfig)
# the user’s project.
next unless pod_target.should_build? pod_targets.each do |pod_target|
@xcconfig.merge!('OTHER_LDFLAGS' => %(-l "#{pod_target.name}")) unless pod_target.should_build? && pod_target.requires_frameworks?
# In case of generated pod targets, which require frameworks, the
# vendored frameworks and libraries are already linked statically
# into the framework binary and must not be linked again to the
# user target.
XCConfigHelper.add_settings_for_file_accessors_of_target(pod_target, @xcconfig)
end
# Add pod target to list of frameworks / libraries that are
# linked with the user’s project.
if pod_target.should_build?
if pod_target.requires_frameworks?
@xcconfig.merge!('OTHER_LDFLAGS' => %(-framework "#{pod_target.product_basename}"))
else
@xcconfig.merge!('OTHER_LDFLAGS' => %(-l "#{pod_target.product_basename}"))
end
end
end end
# TODO Need to decide how we are going to ensure settings like these # TODO Need to decide how we are going to ensure settings like these
...@@ -83,9 +107,30 @@ module Pod ...@@ -83,9 +107,30 @@ module Pod
# See https://github.com/CocoaPods/CocoaPods/issues/1216 # See https://github.com/CocoaPods/CocoaPods/issues/1216
@xcconfig.attributes.delete('USE_HEADERMAP') @xcconfig.attributes.delete('USE_HEADERMAP')
generate_ld_runpath_search_paths if target.requires_frameworks?
@xcconfig @xcconfig
end end
def generate_ld_runpath_search_paths
ld_runpath_search_paths = ['$(inherited)']
if target.platform.symbolic_name == :osx
ld_runpath_search_paths << "'@executable_path/../Frameworks'"
ld_runpath_search_paths << \
if target.native_target.symbol_type == :unit_test_bundle
"'@loader_path/../Frameworks'"
else
"'@loader_path/Frameworks'"
end
else
ld_runpath_search_paths << [
"'@executable_path/Frameworks'",
"'@loader_path/Frameworks'"
]
end
@xcconfig.merge!('LD_RUNPATH_SEARCH_PATHS' => ld_runpath_search_paths.join(' '))
end
#---------------------------------------------------------------------# #---------------------------------------------------------------------#
end end
end end
......
...@@ -57,8 +57,20 @@ module Pod ...@@ -57,8 +57,20 @@ module Pod
# 'USE_HEADERMAP' => 'NO' # 'USE_HEADERMAP' => 'NO'
} }
if target.requires_frameworks?
# Only quote the FRAMEWORK_SEARCH_PATHS entry, because it’s a setting that takes multiple values.
# In addition, quoting CONFIGURATION_BUILD_DIR would make it be interpreted as a relative path.
build_settings = {
'PODS_FRAMEWORK_BUILD_PATH' => target.configuration_build_dir,
'CONFIGURATION_BUILD_DIR' => '$PODS_FRAMEWORK_BUILD_PATH',
'FRAMEWORK_SEARCH_PATHS' => '"$PODS_FRAMEWORK_BUILD_PATH"',
}
config.merge!(build_settings)
end
xcconfig_hash = add_xcconfig_namespaced_keys(public_xcconfig.to_hash, config, target.xcconfig_prefix) xcconfig_hash = add_xcconfig_namespaced_keys(public_xcconfig.to_hash, config, target.xcconfig_prefix)
@xcconfig = Xcodeproj::Config.new(xcconfig_hash) @xcconfig = Xcodeproj::Config.new(xcconfig_hash)
XCConfigHelper.add_target_specific_settings(target, @xcconfig)
@xcconfig.includes = [target.name] @xcconfig.includes = [target.name]
@xcconfig @xcconfig
end end
......
...@@ -40,15 +40,7 @@ module Pod ...@@ -40,15 +40,7 @@ module Pod
# #
def generate def generate
@xcconfig = Xcodeproj::Config.new @xcconfig = Xcodeproj::Config.new
target.file_accessors.each do |file_accessor| XCConfigHelper.add_settings_for_file_accessors_of_target(target, @xcconfig)
XCConfigHelper.add_spec_build_settings_to_xcconfig(file_accessor.spec_consumer, @xcconfig)
file_accessor.vendored_frameworks.each do |vendored_framework|
XCConfigHelper.add_framework_build_settings(vendored_framework, @xcconfig, target.sandbox.root)
end
file_accessor.vendored_libraries.each do |vendored_library|
XCConfigHelper.add_library_build_settings(vendored_library, @xcconfig, target.sandbox.root)
end
end
@xcconfig @xcconfig
end end
......
...@@ -33,6 +33,26 @@ module Pod ...@@ -33,6 +33,26 @@ module Pod
ld_flags ld_flags
end end
# Configures the given Xcconfig
#
# @param [PodTarget] pod_target
# The pod target, which holds the list of +Spec::FileAccessor+.
#
# @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit.
#
def self.add_settings_for_file_accessors_of_target(target, xcconfig)
target.file_accessors.each do |file_accessor|
XCConfigHelper.add_spec_build_settings_to_xcconfig(file_accessor.spec_consumer, xcconfig)
file_accessor.vendored_frameworks.each do |vendored_framework|
XCConfigHelper.add_framework_build_settings(vendored_framework, xcconfig, target.sandbox.root)
end
file_accessor.vendored_libraries.each do |vendored_library|
XCConfigHelper.add_library_build_settings(vendored_library, xcconfig, target.sandbox.root)
end
end
end
# Configures the given Xcconfig according to the build settings of the # Configures the given Xcconfig according to the build settings of the
# given Specification. # given Specification.
# #
...@@ -59,6 +79,9 @@ module Pod ...@@ -59,6 +79,9 @@ module Pod
# @param [Xcodeproj::Config] xcconfig # @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit. # The xcconfig to edit.
# #
# @param [Pathname] sandbox_root
# The path retrieved from Sandbox#root.
#
def self.add_framework_build_settings(framework_path, xcconfig, sandbox_root) def self.add_framework_build_settings(framework_path, xcconfig, sandbox_root)
name = File.basename(framework_path, '.framework') name = File.basename(framework_path, '.framework')
dirname = '$(PODS_ROOT)/' + framework_path.dirname.relative_path_from(sandbox_root).to_s dirname = '$(PODS_ROOT)/' + framework_path.dirname.relative_path_from(sandbox_root).to_s
...@@ -70,7 +93,7 @@ module Pod ...@@ -70,7 +93,7 @@ module Pod
end end
# Configures the given Xcconfig with the the build settings for the given # Configures the given Xcconfig with the the build settings for the given
# framework path. # library path.
# #
# @param [Pathanme] framework_path # @param [Pathanme] framework_path
# The path of the framework. # The path of the framework.
...@@ -78,6 +101,9 @@ module Pod ...@@ -78,6 +101,9 @@ module Pod
# @param [Xcodeproj::Config] xcconfig # @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit. # The xcconfig to edit.
# #
# @param [Pathname] sandbox_root
# The path retrieved from Sandbox#root.
#
def self.add_library_build_settings(library_path, xcconfig, sandbox_root) def self.add_library_build_settings(library_path, xcconfig, sandbox_root)
name = File.basename(library_path, '.a').sub(/\Alib/, '') name = File.basename(library_path, '.a').sub(/\Alib/, '')
dirname = '$(PODS_ROOT)/' + library_path.dirname.relative_path_from(sandbox_root).to_s dirname = '$(PODS_ROOT)/' + library_path.dirname.relative_path_from(sandbox_root).to_s
...@@ -88,6 +114,58 @@ module Pod ...@@ -88,6 +114,58 @@ module Pod
xcconfig.merge!(build_settings) xcconfig.merge!(build_settings)
end end
# Add the code signing settings for generated targets to ensure that
# frameworks are correctly signed to be integrated and re-signed when
# building the application and embedding the framework
#
# @param [Target] target
# The target.
#
# @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit.
#
def self.add_code_signing_settings(target, xcconfig)
build_settings = {}
if target.platform.to_sym == :osx
build_settings['CODE_SIGN_IDENTITY'] = ''
end
xcconfig.merge!(build_settings)
end
# Checks if the given target requires specific settings and configures
# the given Xcconfig.
#
# @param [Target] target
# The target.
#
# @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit.
#
def self.add_target_specific_settings(target, xcconfig)
if target.requires_frameworks?
add_code_signing_settings(target, xcconfig)
end
add_language_specific_settings(target, xcconfig)
end
# Checks if the given target requires language specific settings and
# configures the given Xcconfig.
#
# @param [Target] target
# The target.
#
# @param [Xcodeproj::Config] xcconfig
# The xcconfig to edit.
#
def self.add_language_specific_settings(target, xcconfig)
if target.uses_swift?
build_settings = {
'OTHER_SWIFT_FLAGS' => quote(['-D COCOAPODS']),
}
xcconfig.merge!(build_settings)
end
end
# Adds the search paths of the developer frameworks to the specification # Adds the search paths of the developer frameworks to the specification
# if needed. This is done because the `SenTestingKit` requires them and # if needed. This is done because the `SenTestingKit` requires them and
# adding them to each specification which requires it is repetitive and # adding them to each specification which requires it is repetitive and
......
...@@ -89,6 +89,7 @@ module Pod ...@@ -89,6 +89,7 @@ module Pod
prepare prepare
resolve_dependencies resolve_dependencies
download_dependencies download_dependencies
determine_dependency_product_types
generate_pods_project generate_pods_project
integrate_user_project if config.integrate_targets? integrate_user_project if config.integrate_targets?
perform_post_install_actions perform_post_install_actions
...@@ -311,6 +312,21 @@ module Pod ...@@ -311,6 +312,21 @@ module Pod
@pod_installers.each(&:clean!) @pod_installers.each(&:clean!)
end end
# Determines if the dependencies need to be built as dynamic frameworks or
# if they can be built as static libraries by checking for the Swift source
# presence. Therefore it is important that the file accessors of the
# #pod_targets are created.
#
# @return [void]
#
def determine_dependency_product_types
aggregate_targets.each do |aggregate_target|
aggregate_target.pod_targets.each do |pod_target|
pod_target.host_requires_frameworks = aggregate_target.requires_frameworks?
end
end
end
# Performs any post-installation actions # Performs any post-installation actions
# #
# @return [void] # @return [void]
...@@ -444,7 +460,13 @@ module Pod ...@@ -444,7 +460,13 @@ module Pod
end end
end end
# Adds a target dependency for each pod spec to each aggregate target and
# links the pod targets among each other.
#
# @return [void]
#
def set_target_dependencies def set_target_dependencies
frameworks_group = pods_project.frameworks_group
aggregate_targets.each do |aggregate_target| aggregate_targets.each do |aggregate_target|
aggregate_target.pod_targets.each do |pod_target| aggregate_target.pod_targets.each do |pod_target|
unless pod_target.should_build? unless pod_target.should_build?
...@@ -464,7 +486,15 @@ module Pod ...@@ -464,7 +486,15 @@ module Pod
unless pod_dependency_target unless pod_dependency_target
puts "[BUG] DEP: #{dep}" puts "[BUG] DEP: #{dep}"
end end
next unless pod_dependency_target.should_build?
pod_target.native_target.add_dependency(pod_dependency_target.native_target) pod_target.native_target.add_dependency(pod_dependency_target.native_target)
if pod_target.requires_frameworks?
product_ref = frameworks_group.files.find { |f| f.path == pod_dependency_target.product_name } ||
frameworks_group.new_product_ref_for_target(pod_dependency_target.product_basename, pod_dependency_target.product_type)
pod_target.native_target.frameworks_build_phase.add_file_reference(product_ref)
end
end end
end end
end end
......
...@@ -180,47 +180,92 @@ module Pod ...@@ -180,47 +180,92 @@ module Pod
def generate_targets def generate_targets
targets = [] targets = []
result.specs_by_target.each do |target_definition, specs| result.specs_by_target.each do |target_definition, specs|
target = AggregateTarget.new(target_definition, sandbox) targets << generate_target(target_definition, specs)
targets << target end
targets
if config.integrate_targets? end
project_path = compute_user_project_path(target_definition)
user_project = Xcodeproj::Project.open(project_path) # Setup the aggregate target for a single user target
native_targets = compute_user_project_targets(target_definition, user_project) #
# @param [TargetDefinition] target_definition
target.user_project_path = project_path # the target definition for the user target.
target.client_root = project_path.dirname #
target.user_target_uuids = native_targets.map(&:uuid) # @param [Array<Specification>] specs
target.user_build_configurations = compute_user_build_configurations(target_definition, native_targets) # the specifications that need to be installed grouped by the
target.archs = @archs_by_target_def[target_definition] # given target definition.
else #
target.client_root = config.installation_root # @return [AggregateTarget]
target.user_target_uuids = [] #
target.user_build_configurations = target_definition.build_configurations || { 'Release' => :release, 'Debug' => :debug } def generate_target(target_definition, specs)
if target_definition.platform.name == :osx target = AggregateTarget.new(target_definition, sandbox)
target.archs = '$(ARCHS_STANDARD_64_BIT)' target.host_requires_frameworks |= target_definition.uses_frameworks?
end
if config.integrate_targets?
project_path = compute_user_project_path(target_definition)
user_project = Xcodeproj::Project.open(project_path)
native_targets = compute_user_project_targets(target_definition, user_project)
target.user_project_path = project_path
target.client_root = project_path.dirname
target.user_target_uuids = native_targets.map(&:uuid)
target.user_build_configurations = compute_user_build_configurations(target_definition, native_targets)
target.archs = @archs_by_target_def[target_definition]
else
target.client_root = config.installation_root
target.user_target_uuids = []
target.user_build_configurations = target_definition.build_configurations || { 'Release' => :release, 'Debug' => :debug }
if target_definition.platform.name == :osx
target.archs = '$(ARCHS_STANDARD_64_BIT)'
end end
end
grouped_specs = specs.map do |spec| target.pod_targets = generate_pod_targets(target, specs)
specs.select { |s| s.root == spec.root }
end.uniq
grouped_specs.each do |pod_specs| target
pod_target = PodTarget.new(pod_specs, target_definition, sandbox) end
if config.integrate_targets?
pod_target.user_build_configurations = target.user_build_configurations # Setup the pod targets for an aggregate target. Group specs and subspecs
pod_target.archs = @archs_by_target_def[target_definition] # by their root to create a {PodTarget} for each spec.
else #
pod_target.user_build_configurations = {} # @param [AggregateTarget] target
if target_definition.platform.name == :osx # the aggregate target
pod_target.archs = '$(ARCHS_STANDARD_64_BIT)' #
end # @param [Array<Specification>] specs
end # the specifications that need to be installed.
target.pod_targets << pod_target #
# @return [Array<PodTarget>]
#
def generate_pod_targets(target, specs)
grouped_specs = specs.group_by(&:root).values.uniq
grouped_specs.map do |pod_specs|
generate_pod_target(target, pod_specs)
end
end
# Create a target for each spec group and add it to the aggregate target
#
# @param [AggregateTarget] target
# the aggregate target
#
# @param [Array<Specification>] specs
# the specifications of an equal root.
#
# @return [PodTarget]
#
def generate_pod_target(target, pod_specs)
pod_target = PodTarget.new(pod_specs, target.target_definition, sandbox)
if config.integrate_targets?
pod_target.user_build_configurations = target.user_build_configurations
pod_target.archs = @archs_by_target_def[target.target_definition]
else
pod_target.user_build_configurations = {}
if target.platform.name == :osx
pod_target.archs = '$(ARCHS_STANDARD_64_BIT)'
end end
end end
targets
pod_target
end end
# Generates dependencies that require the specific version of the Pods # Generates dependencies that require the specific version of the Pods
...@@ -515,6 +560,37 @@ module Pod ...@@ -515,6 +560,37 @@ module Pod
end end
end end
# Checks if any of the targets for the {TargetDefinition} computed before
# by #compute_user_project_targets require to be build as a framework due
# the presence of Swift source code in any of the source build phases.
#
# @param [TargetDefinition] target_definition
# the target definition
#
# @param [Array<PBXNativeTarget>] native_targets
# the targets which are checked for presence of Swift source code
#
# @return [Boolean] Whether the user project targets to integrate into
# uses Swift
#
def compute_user_project_targets_require_framework(target_definition, native_targets)
file_predicate = nil
file_predicate = proc do |file_ref|
if file_ref.respond_to?(:last_known_file_type)
file_ref.last_known_file_type == 'sourcecode.swift'
elsif file_ref.respond_to?(:files)
file_ref.files.any?(&file_predicate)
else
false
end
end
target_definition.platform.supports_dynamic_frameworks? || native_targets.any? do |target|
target.source_build_phase.files.any? do |build_file|
file_predicate.call(build_file.file_ref)
end
end
end
# @return [Hash{String=>Symbol}] A hash representing the user build # @return [Hash{String=>Symbol}] A hash representing the user build
# configurations where each key corresponds to the name of a # configurations where each key corresponds to the name of a
# configuration and its value to its type (`:debug` or `:release`). # configuration and its value to its type (`:debug` or `:release`).
......
...@@ -36,25 +36,47 @@ module Pod ...@@ -36,25 +36,47 @@ module Pod
# @return [void] # @return [void]
# #
def add_target def add_target
product_type = target.product_type
name = target.label name = target.label
platform = target.platform.name platform = target.platform.name
deployment_target = target.platform.deployment_target.to_s deployment_target = target.platform.deployment_target.to_s
@native_target = project.new_target(:static_library, name, platform, deployment_target) @native_target = project.new_target(product_type, name, platform, deployment_target)
product_name = target.product_name
product = @native_target.product_reference
product.name = product_name
product.path = product_name
target.user_build_configurations.each do |bc_name, type| target.user_build_configurations.each do |bc_name, type|
configuration = @native_target.add_build_configuration(bc_name, type) configuration = @native_target.add_build_configuration(bc_name, type)
end end
settings = { 'OTHER_LDFLAGS' => '', 'OTHER_LIBTOOLFLAGS' => '' } @native_target.build_configurations.each do |configuration|
configuration.build_settings.merge!(custom_build_settings)
end
target.native_target = @native_target
end
# Returns the customized build settings which are overridden in the build
# settings of the user target.
#
# @return [Hash{String => String}]
#
def custom_build_settings
settings = {}
if target.archs if target.archs
settings['ARCHS'] = target.archs settings['ARCHS'] = target.archs
end end
@native_target.build_configurations.each do |configuration| if target.requires_frameworks?
configuration.build_settings.merge!(settings) settings['PRODUCT_NAME'] = target.product_module_name
else
settings.merge!('OTHER_LDFLAGS' => '', 'OTHER_LIBTOOLFLAGS' => '')
end end
target.native_target = @native_target settings
end end
# Creates the directory where to store the support files of the target. # Creates the directory where to store the support files of the target.
...@@ -63,6 +85,68 @@ module Pod ...@@ -63,6 +85,68 @@ module Pod
target.support_files_dir.mkdir target.support_files_dir.mkdir
end end
# Creates the Info.plist file which sets public framework attributes
#
# @return [void]
#
def create_info_plist_file
path = target.info_plist_path
UI.message "- Generating Info.plist file at #{UI.path(path)}" do
generator = Generator::InfoPlistFile.new(target)
generator.save_as(path)
add_file_to_support_group(path)
native_target.build_configurations.each do |c|
relative_path = path.relative_path_from(sandbox.root)
c.build_settings['INFOPLIST_FILE'] = relative_path.to_s
end
end
end
# Creates the module map file which ensures that the umbrella header is
# recognized with a customized path
#
# @return [void]
#
def create_module_map
path = target.module_map_path
UI.message "- Generating module map file at #{UI.path(path)}" do
generator = Generator::ModuleMap.new(target)
generator.save_as(path)
add_file_to_support_group(path)
native_target.build_configurations.each do |c|
relative_path = path.relative_path_from(sandbox.root)
c.build_settings['MODULEMAP_FILE'] = relative_path.to_s
end
end
end
# Generates a header which ensures that all header files are exported
# in the module map
#
# @yield_param [Generator::UmbrellaHeader]
# yielded once to configure the imports
#
def create_umbrella_header
path = target.umbrella_header_path
UI.message "- Generating umbrella header at #{UI.path(path)}" do
generator = Generator::UmbrellaHeader.new(target)
yield generator if block_given?
generator.save_as(path)
# Add the file to the support group and the native target,
# so it will been added to the header build phase
file_ref = add_file_to_support_group(path)
native_target.add_file_references([file_ref])
# Make the umbrella header public
build_file = native_target.headers_build_phase.build_file(file_ref)
build_file.settings ||= {}
build_file.settings['ATTRIBUTES'] = ['Public']
end
end
# Generates a dummy source file for each target so libraries that contain # Generates a dummy source file for each target so libraries that contain
# only categories build. # only categories build.
# #
......
...@@ -12,8 +12,14 @@ module Pod ...@@ -12,8 +12,14 @@ module Pod
UI.message "- Installing target `#{target.name}` #{target.platform}" do UI.message "- Installing target `#{target.name}` #{target.platform}" do
add_target add_target
create_support_files_dir create_support_files_dir
create_suport_files_group create_support_files_group
create_xcconfig_file create_xcconfig_file
if target.requires_frameworks?
create_info_plist_file
create_module_map
create_umbrella_header
create_embed_frameworks_script
end
create_target_environment_header create_target_environment_header
create_bridge_support_file create_bridge_support_file
create_copy_resources_script create_copy_resources_script
...@@ -26,12 +32,25 @@ module Pod ...@@ -26,12 +32,25 @@ module Pod
private private
# Ensure that vendored static frameworks and libraries are not linked
# twice to the aggregate target, which shares the xcconfig of the user
# target.
#
def custom_build_settings
settings = {
'OTHER_LDFLAGS' => '',
'OTHER_LIBTOOLFLAGS' => '',
'PODS_ROOT' => '$(SRCROOT)',
}
super.merge(settings)
end
# Creates the group that holds the references to the support files # Creates the group that holds the references to the support files
# generated by this installer. # generated by this installer.
# #
# @return [void] # @return [void]
# #
def create_suport_files_group def create_support_files_group
parent = project.support_files_group parent = project.support_files_group
name = target.name name = target.name
dir = target.support_files_dir dir = target.support_files_dir
...@@ -92,9 +111,12 @@ module Pod ...@@ -92,9 +111,12 @@ module Pod
# #
def create_copy_resources_script def create_copy_resources_script
path = target.copy_resources_script_path path = target.copy_resources_script_path
file_accessors = target.pod_targets.map(&:file_accessors).flatten library_targets = target.pod_targets.reject do |pod_target|
resource_paths = file_accessors.map { |accessor| accessor.resources.flatten.map { |res| res.relative_path_from(project.path.dirname) } }.flatten pod_target.should_build? && pod_target.requires_frameworks?
resource_bundles = file_accessors.map { |accessor| accessor.resource_bundles.keys.map { |name| "${BUILT_PRODUCTS_DIR}/#{name}.bundle" } }.flatten end
file_accessors = library_targets.flat_map(&:file_accessors)
resource_paths = file_accessors.flat_map { |accessor| accessor.resources.flat_map { |res| res.relative_path_from(project.path.dirname) } }
resource_bundles = file_accessors.flat_map { |accessor| accessor.resource_bundles.keys.map { |name| "${BUILT_PRODUCTS_DIR}/#{name}.bundle" } }
resources = [] resources = []
resources.concat(resource_paths) resources.concat(resource_paths)
resources.concat(resource_bundles) resources.concat(resource_bundles)
...@@ -104,6 +126,28 @@ module Pod ...@@ -104,6 +126,28 @@ module Pod
add_file_to_support_group(path) add_file_to_support_group(path)
end end
# Creates a script that embeds the frameworks to the bundle of the client
# target.
#
# @note We can't use Xcode default copy bundle resource phase, because
# we need to ensure that we only copy the resources, which are
# relevant for the current build configuration.
#
# @return [void]
#
def create_embed_frameworks_script
path = target.embed_frameworks_script_path
frameworks_by_config = {}
target.user_build_configurations.keys.each do |config|
frameworks_by_config[config] = target.pod_targets.select do |pod_target|
pod_target.include_in_build_config?(config) && pod_target.should_build?
end.map(&:product_name)
end
generator = Generator::EmbedFrameworksScript.new(target_definition, frameworks_by_config)
generator.save_as(path)
add_file_to_support_group(path)
end
# Generates the acknowledgement files (markdown and plist) for the target. # Generates the acknowledgement files (markdown and plist) for the target.
# #
# @return [void] # @return [void]
......
...@@ -17,9 +17,16 @@ module Pod ...@@ -17,9 +17,16 @@ module Pod
UI.message "- Installing target `#{target.name}` #{target.platform}" do UI.message "- Installing target `#{target.name}` #{target.platform}" do
add_target add_target
create_support_files_dir create_support_files_dir
add_files_to_build_phases
add_resources_bundle_targets add_resources_bundle_targets
add_files_to_build_phases
create_xcconfig_file create_xcconfig_file
if target.requires_frameworks?
create_info_plist_file
create_module_map
create_umbrella_header do |generator|
generator.imports += target.file_accessors.flat_map(&:public_headers).map(&:basename)
end
end
create_prefix_header create_prefix_header
create_dummy_source create_dummy_source
end end
...@@ -41,20 +48,41 @@ module Pod ...@@ -41,20 +48,41 @@ module Pod
target.file_accessors.each do |file_accessor| target.file_accessors.each do |file_accessor|
consumer = file_accessor.spec_consumer consumer = file_accessor.spec_consumer
headers = file_accessor.headers
public_headers = file_accessor.public_headers
other_source_files = file_accessor.source_files.select { |sf| sf.extname == '.d' } other_source_files = file_accessor.source_files.select { |sf| sf.extname == '.d' }
{ {
true => file_accessor.arc_source_files, true => file_accessor.arc_source_files,
false => file_accessor.non_arc_source_files, false => file_accessor.non_arc_source_files,
}.each do |arc, files| }.each do |arc, files|
files = files - other_source_files files = files - headers - other_source_files
flags = compiler_flags_for_consumer(consumer, arc) flags = compiler_flags_for_consumer(consumer, arc)
regular_file_refs = files.map { |sf| project.reference_for_path(sf) } regular_file_refs = files.map { |sf| project.reference_for_path(sf) }
native_target.add_file_references(regular_file_refs, flags) native_target.add_file_references(regular_file_refs, flags)
end end
header_file_refs = headers.map { |sf| project.reference_for_path(sf) }
native_target.add_file_references(header_file_refs) do |build_file|
# Set added headers as public if needed
if target.requires_frameworks?
if public_headers.include?(build_file.file_ref.real_path)
build_file.settings ||= {}
build_file.settings['ATTRIBUTES'] = ['Public']
end
end
end
other_file_refs = other_source_files.map { |sf| project.reference_for_path(sf) } other_file_refs = other_source_files.map { |sf| project.reference_for_path(sf) }
native_target.add_file_references(other_file_refs, nil) native_target.add_file_references(other_file_refs, nil)
next unless target.requires_frameworks?
resource_refs = file_accessor.resources.flatten.map do |res|
project.reference_for_path(res)
end
native_target.add_resources(resource_refs)
end end
end end
...@@ -68,23 +96,34 @@ module Pod ...@@ -68,23 +96,34 @@ module Pod
def add_resources_bundle_targets def add_resources_bundle_targets
target.file_accessors.each do |file_accessor| target.file_accessors.each do |file_accessor|
file_accessor.resource_bundles.each do |bundle_name, paths| file_accessor.resource_bundles.each do |bundle_name, paths|
# Add a dependency on an existing Resource Bundle target if possible file_references = paths.map { |sf| project.reference_for_path(sf) }
bundle_target = project.targets.find { |target| target.name == bundle_name } label = target.resources_bundle_target_label(bundle_name)
bundle_target = project.new_resources_bundle(label, file_accessor.spec_consumer.platform_name)
unless bundle_target bundle_target.product_reference.tap do |bundle_product|
file_references = paths.map { |sf| project.reference_for_path(sf) } bundle_file_name = "#{bundle_name}.bundle"
bundle_target = project.new_resources_bundle(bundle_name, file_accessor.spec_consumer.platform_name) bundle_product.name = bundle_file_name
bundle_target.add_resources(file_references) bundle_product.path = bundle_file_name
end
bundle_target.add_resources(file_references)
target.user_build_configurations.each do |bc_name, type| target.user_build_configurations.each do |bc_name, type|
bundle_target.add_build_configuration(bc_name, type) bundle_target.add_build_configuration(bc_name, type)
end
end end
target.resource_bundle_targets << bundle_target target.resource_bundle_targets << bundle_target
if target.should_build? if target.should_build?
native_target.add_dependency(bundle_target) native_target.add_dependency(bundle_target)
if target.requires_frameworks?
native_target.add_resources([bundle_target.product_reference])
end
end
bundle_target.build_configurations.each do |c|
c.build_settings['PRODUCT_NAME'] = bundle_name
if target.requires_frameworks?
c.build_settings['CONFIGURATION_BUILD_DIR'] = target.configuration_build_dir
end
end end
end end
end end
......
...@@ -132,7 +132,8 @@ module Pod ...@@ -132,7 +132,8 @@ module Pod
end end
end end
INHERITED_FLAGS = ['$(inherited)', '${inherited}'] IGNORED_KEYS = %w(CODE_SIGN_IDENTITY).freeze
INHERITED_FLAGS = %w($(inherited) ${inherited}).freeze
# Checks whether the settings of the CocoaPods generated xcconfig are # Checks whether the settings of the CocoaPods generated xcconfig are
# overridden by the build configuration of a target and prints a # overridden by the build configuration of a target and prints a
...@@ -144,7 +145,7 @@ module Pod ...@@ -144,7 +145,7 @@ module Pod
user_target.build_configurations.each do |config| user_target.build_configurations.each do |config|
xcconfig = aggregate_target.xcconfigs[config.name] xcconfig = aggregate_target.xcconfigs[config.name]
if xcconfig if xcconfig
xcconfig.to_hash.keys.each do |key| (xcconfig.to_hash.keys - IGNORED_KEYS).each do |key|
target_values = config.build_settings[key] target_values = config.build_settings[key]
if target_values && if target_values &&
!INHERITED_FLAGS.any? { |flag| target_values.include?(flag) } !INHERITED_FLAGS.any? { |flag| target_values.include?(flag) }
......
...@@ -31,8 +31,10 @@ module Pod ...@@ -31,8 +31,10 @@ module Pod
project_is_dirty = [ project_is_dirty = [
XCConfigIntegrator.integrate(target, native_targets), XCConfigIntegrator.integrate(target, native_targets),
update_to_cocoapods_0_34, update_to_cocoapods_0_34,
remove_embed_frameworks_script_phases,
unless native_targets_to_integrate.empty? unless native_targets_to_integrate.empty?
add_pods_library add_pods_library
add_embed_frameworks_script_phase if target.requires_frameworks?
add_copy_resources_script_phase add_copy_resources_script_phase
add_check_manifest_lock_script_phase add_check_manifest_lock_script_phase
true true
...@@ -91,9 +93,9 @@ module Pod ...@@ -91,9 +93,9 @@ module Pod
changes changes
end end
# Adds spec libraries to the frameworks build phase of the # Adds spec product reference to the frameworks build phase of the
# {TargetDefinition} integration libraries. Adds a file reference to # {TargetDefinition} integration libraries. Adds a file reference to
# the library of the {TargetDefinition} and adds it to the frameworks # the frameworks group of the project and adds it to the frameworks
# build phase of the targets. # build phase of the targets.
# #
# @return [void] # @return [void]
...@@ -101,12 +103,71 @@ module Pod ...@@ -101,12 +103,71 @@ module Pod
def add_pods_library def add_pods_library
frameworks = user_project.frameworks_group frameworks = user_project.frameworks_group
native_targets_to_integrate.each do |native_target| native_targets_to_integrate.each do |native_target|
library = frameworks.files.select { |f| f.path == target.product_name }.first || build_phase = native_target.frameworks_build_phase
frameworks.new_product_ref_for_target(target.name, :static_library)
unless native_target.frameworks_build_phase.files_references.include?(library) # Find and delete possible reference for the other product type
native_target.frameworks_build_phase.add_file_reference(library) old_product_name = target.requires_frameworks? ? target.static_library_name : target.framework_name
old_product_ref = frameworks.files.find { |f| f.path == old_product_name }
if old_product_ref.present?
UI.message("Removing old Pod product reference #{old_product_name} from project.")
build_phase.remove_file_reference(old_product_ref)
frameworks.remove_reference(old_product_ref)
end
# Find or create and add a reference for the current product type
target_basename = target.product_basename
new_product_ref = frameworks.files.find { |f| f.path == target.product_name } ||
frameworks.new_product_ref_for_target(target_basename, target.product_type)
build_file = build_phase.build_file(new_product_ref) ||
build_phase.add_file_reference(new_product_ref)
if target.requires_frameworks?
# Weak link the aggregate target's product, because as it contains
# no symbols, it isn't copied into the app bundle. dyld will so
# never try to find the missing executable at runtime.
build_file.settings ||= {}
build_file.settings['ATTRIBUTES'] = ['Weak']
end
end
end
# Find or create a 'Embed Pods Frameworks' Copy Files Build Phase
#
# @return [void]
#
def add_embed_frameworks_script_phase
phase_name = 'Embed Pods Frameworks'
native_targets_to_integrate.each do |native_target|
embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name == phase_name }
unless embed_build_phase.present?
UI.message("Adding Build Phase '#{phase_name}' to project.")
embed_build_phase = native_target.new_shell_script_build_phase(phase_name)
end end
script_path = target.embed_frameworks_script_relative_path
embed_build_phase.shell_script = %("#{script_path}"\n)
embed_build_phase.show_env_vars_in_log = '0'
end
end
# Delete 'Embed Pods Frameworks' Build Phases, if they exist
# and are not needed anymore due to not integrating the
# dependencies by frameworks.
#
# @return [Bool] whether any changes to the project were made.
#
def remove_embed_frameworks_script_phases
return false if target.requires_frameworks?
phase_name = 'Embed Pods Frameworks'
result = false
native_targets.each do |native_target|
embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name == phase_name }
next unless embed_build_phase.present?
native_target.build_phases.delete(embed_build_phase)
result = true
end end
result
end end
# Adds a shell script build phase responsible to copy the resources # Adds a shell script build phase responsible to copy the resources
......
...@@ -12,7 +12,7 @@ module Pod ...@@ -12,7 +12,7 @@ module Pod
GLOB_PATTERNS = { GLOB_PATTERNS = {
:readme => 'readme{*,.*}'.freeze, :readme => 'readme{*,.*}'.freeze,
:license => 'licen{c,s}e{*,.*}'.freeze, :license => 'licen{c,s}e{*,.*}'.freeze,
:source_files => '*.{h,hpp,hh,m,mm,c,cpp}'.freeze, :source_files => '*.{h,hpp,hh,m,mm,c,cpp,swift}'.freeze,
:public_header_files => "*{#{HEADER_EXTENSIONS.join(',')}}".freeze, :public_header_files => "*{#{HEADER_EXTENSIONS.join(',')}}".freeze,
}.freeze }.freeze
......
...@@ -26,6 +26,10 @@ module Pod ...@@ -26,6 +26,10 @@ module Pod
@search_paths = [] @search_paths = []
end end
# @param [Platform] platform
# the platform for which the header search paths should be
# returned
#
# @return [Array<String>] All the search paths of the header directory in # @return [Array<String>] All the search paths of the header directory in
# xcconfig format. The paths are specified relative to the pods # xcconfig format. The paths are specified relative to the pods
# root with the `${PODS_ROOT}` variable. # root with the `${PODS_ROOT}` variable.
......
...@@ -15,18 +15,65 @@ module Pod ...@@ -15,18 +15,65 @@ module Pod
# #
attr_reader :sandbox attr_reader :sandbox
# @return [Boolean] Whether the target needs to be implemented as a framework.
# Computed by analyzer.
#
attr_accessor :host_requires_frameworks
alias_method :host_requires_frameworks?, :host_requires_frameworks
# @return [String] the name of the library. # @return [String] the name of the library.
# #
def name def name
label label
end end
# @return [String] the name of the library. # @return [String] the name of the product.
# #
def product_name def product_name
if requires_frameworks?
framework_name
else
static_library_name
end
end
# @return [String] the name of the product excluding the file extension or
# a product type specific prefix, depends on #requires_frameworks?
# and #product_module_name or #label.
#
def product_basename
if requires_frameworks?
product_module_name
else
label
end
end
# @return [String] the name of the framework, depends on #label.
#
# @note This may not depend on #requires_frameworks? indirectly as it is
# used for migration.
#
def framework_name
"#{product_module_name}.framework"
end
# @return [String] the name of the library, depends on #label.
#
# @note This may not depend on #requires_frameworks? indirectly as it is
# used for migration.
#
def static_library_name
"lib#{label}.a" "lib#{label}.a"
end end
# @return [Symbol] either :framework or :static_library, depends on
# #requires_frameworks?.
#
def product_type
requires_frameworks? ? :framework : :static_library
end
# @return [String] the XCConfig namespaced prefix. # @return [String] the XCConfig namespaced prefix.
# #
def xcconfig_prefix def xcconfig_prefix
...@@ -41,6 +88,20 @@ module Pod ...@@ -41,6 +88,20 @@ module Pod
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
# @return [Boolean] whether the generated target needs to be implemented
# as a framework
#
# @note This applies either if Swift was used by the host, which was checked
# eagerly by the analyzer before, or in the given target or its
# dependents, which can only be checked after the specs were been
# fetched.
#
def requires_frameworks?
host_requires_frameworks? || uses_swift?
end
#-------------------------------------------------------------------------#
# @!group Information storage # @!group Information storage
# @return [Hash{String=>Symbol}] A hash representing the user build # @return [Hash{String=>Symbol}] A hash representing the user build
...@@ -95,6 +156,22 @@ module Pod ...@@ -95,6 +156,22 @@ module Pod
support_files_dir + "#{label}-Private.xcconfig" support_files_dir + "#{label}-Private.xcconfig"
end end
# @return [Pathname] the absolute path of the header file which contains
# the exported foundation constants with framework version
# information and all headers, which should been exported in the
# module map.
#
def umbrella_header_path
support_files_dir + "#{label}-umbrella.h"
end
# @return [Pathname] the absolute path of the LLVM module map file that
# defines the module structure for the compiler.
#
def module_map_path
support_files_dir + "#{label}.modulemap"
end
# @return [Pathname] the absolute path of the header file which contains # @return [Pathname] the absolute path of the header file which contains
# the information about the installed pods. # the information about the installed pods.
# #
...@@ -115,12 +192,51 @@ module Pod ...@@ -115,12 +192,51 @@ module Pod
support_files_dir + "#{label}.bridgesupport" support_files_dir + "#{label}.bridgesupport"
end end
# @return [Pathname] the absolute path of the Info.plist file.
#
def info_plist_path
support_files_dir + "Info.plist"
end
# @return [Pathname] the path of the dummy source generated by CocoaPods # @return [Pathname] the path of the dummy source generated by CocoaPods
# #
def dummy_source_path def dummy_source_path
support_files_dir + "#{label}-dummy.m" support_files_dir + "#{label}-dummy.m"
end end
# @return [String] The configuration build dir, if the target is integrated
# as framework.
#
# @note Namespace the pod target product with its target definition name.
# Pod target products are named after their specs. The namespacing
# cannot directly happen in the product name itself, because this
# must be equal to the module name and this will be used in source
# code, which should stay agnostic over the dependency manager.
# We need namespacing at all because multiple targets can exist for
# the same podspec and their products should not collide. This
# happens when multiple user targets require the same pod, because
# they could require different sets of subspecs.
#
def configuration_build_dir
"$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/#{target_definition.label}"
end
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
private
# Transforms the given string into a valid +identifier+ after C99ext
# standard, so that it can be used in source code where escaping of
# ambiguous characters is not applicable.
#
# @param [String] name
# any name, which may contain leading numbers, spaces or invalid
# characters.
#
# @return [String]
#
def c99ext_identifier(name)
name.gsub(/^([0-9])/, '_\1').gsub(/[^a-zA-Z0-9_]/, '_')
end
end end
end end
...@@ -20,6 +20,14 @@ module Pod ...@@ -20,6 +20,14 @@ module Pod
target_definition.label.to_s target_definition.label.to_s
end end
# @return [String] the name to use for the source code module constructed
# for this target, and which will be used to import the module in
# implementation source files.
#
def product_module_name
c99ext_identifier(label)
end
# @return [Pathname] the folder where the client is stored used for # @return [Pathname] the folder where the client is stored used for
# computing the relative paths. If integrating it should be the # computing the relative paths. If integrating it should be the
# folder where the user project is stored, otherwise it should # folder where the user project is stored, otherwise it should
...@@ -71,21 +79,32 @@ module Pod ...@@ -71,21 +79,32 @@ module Pod
# #
attr_accessor :pod_targets attr_accessor :pod_targets
# @param [String] build_configuration The build configuration for which the
# the pod targets should be returned.
#
# @return [Array<PodTarget>] the pod targets for the given build
# configuration.
#
def pod_targets_for_build_configuration(build_configuration)
pod_targets.select do |pod_target|
pod_target.include_in_build_config?(build_configuration)
end
end
# @return [Array<Specification>] The specifications used by this aggregate target. # @return [Array<Specification>] The specifications used by this aggregate target.
# #
def specs def specs
pod_targets.map(&:specs).flatten pod_targets.map(&:specs).flatten
end end
# @return [Hash{Symbol => Array<PodTarget>}] The pod targets for each # @return [Hash{Symbol => Array<Specification>}] The pod targets for each
# build configuration. # build configuration.
# #
def specs_by_build_configuration def specs_by_build_configuration
result = {} result = {}
user_build_configurations.keys.each do |build_configuration| user_build_configurations.keys.each do |build_configuration|
result[build_configuration] = pod_targets.select do |pod_target| result[build_configuration] = pod_targets_for_build_configuration(build_configuration).
pod_target.include_in_build_config?(build_configuration) flat_map(&:specs)
end.map(&:specs).flatten
end end
result result
end end
...@@ -96,6 +115,16 @@ module Pod ...@@ -96,6 +115,16 @@ module Pod
specs.map { |spec| spec.consumer(platform) } specs.map { |spec| spec.consumer(platform) }
end end
# @return [Boolean] Whether the target uses Swift code
#
def uses_swift?
pod_targets.any?(&:uses_swift?)
end
#-------------------------------------------------------------------------#
# @!group Support files
# @return [Pathname] The absolute path of acknowledgements file. # @return [Pathname] The absolute path of acknowledgements file.
# #
# @note The acknowledgements generators add the extension according to # @note The acknowledgements generators add the extension according to
...@@ -111,6 +140,12 @@ module Pod ...@@ -111,6 +140,12 @@ module Pod
support_files_dir + "#{label}-resources.sh" support_files_dir + "#{label}-resources.sh"
end end
# @return [Pathname] The absolute path of the embed frameworks script.
#
def embed_frameworks_script_path
support_files_dir + "#{label}-frameworks.sh"
end
# @return [String] The xcconfig path of the root from the `$(SRCROOT)` # @return [String] The xcconfig path of the root from the `$(SRCROOT)`
# variable of the user's project. # variable of the user's project.
# #
...@@ -133,6 +168,13 @@ module Pod ...@@ -133,6 +168,13 @@ module Pod
"${SRCROOT}/#{relative_to_srcroot(copy_resources_script_path)}" "${SRCROOT}/#{relative_to_srcroot(copy_resources_script_path)}"
end end
# @return [String] The path of the embed frameworks relative to the
# root of the user project.
#
def embed_frameworks_script_relative_path
"${SRCROOT}/#{relative_to_srcroot(embed_frameworks_script_path)}"
end
private private
# @!group Private Helpers # @!group Private Helpers
......
...@@ -30,6 +30,14 @@ module Pod ...@@ -30,6 +30,14 @@ module Pod
"#{target_definition.label}-#{root_spec.name}" "#{target_definition.label}-#{root_spec.name}"
end end
# @return [String] The name to use for the source code module constructed
# for this target, and which will be used to import the module in
# implementation source files.
#
def product_module_name
root_spec.module_name
end
# @return [Array<Sandbox::FileAccessor>] the file accessors for the # @return [Array<Sandbox::FileAccessor>] the file accessors for the
# specifications of this target. # specifications of this target.
# #
...@@ -54,6 +62,14 @@ module Pod ...@@ -54,6 +62,14 @@ module Pod
specs.map { |spec| spec.consumer(platform) } specs.map { |spec| spec.consumer(platform) }
end end
# @return [Boolean] Whether the target uses Swift code
#
def uses_swift?
file_accessors.any? do |file_accessor|
file_accessor.source_files.any? { |sf| sf.extname == ".swift" }
end
end
# @return [Specification] The root specification for the target. # @return [Specification] The root specification for the target.
# #
def root_spec def root_spec
...@@ -66,6 +82,15 @@ module Pod ...@@ -66,6 +82,15 @@ module Pod
root_spec.name root_spec.name
end end
# @param [String] bundle_name
# The name of the bundle product, which is given by the +spec+.
#
# @return [String] The derived name of the resource bundle target.
#
def resources_bundle_target_label(bundle_name)
"#{label}-#{bundle_name}"
end
# @return [Array<String>] The names of the Pods on which this target # @return [Array<String>] The names of the Pods on which this target
# depends. # depends.
# #
......
...@@ -143,6 +143,10 @@ module Pod ...@@ -143,6 +143,10 @@ module Pod
# #
attr_accessor :no_subspecs attr_accessor :no_subspecs
# @return [Bool] Whether frameworks should be used for the installation.
#
attr_accessor :use_frameworks
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
# !@group Lint results # !@group Lint results
...@@ -299,7 +303,7 @@ module Pod ...@@ -299,7 +303,7 @@ module Pod
# #
def install_pod def install_pod
deployment_target = spec.subspec_by_name(subspec_name).deployment_target(consumer.platform_name) deployment_target = spec.subspec_by_name(subspec_name).deployment_target(consumer.platform_name)
podfile = podfile_from_spec(consumer.platform_name, deployment_target) podfile = podfile_from_spec(consumer.platform_name, deployment_target, use_frameworks)
sandbox = Sandbox.new(config.sandbox_root) sandbox = Sandbox.new(config.sandbox_root)
installer = Installer.new(sandbox, podfile) installer = Installer.new(sandbox, podfile)
installer.install! installer.install!
...@@ -449,19 +453,31 @@ module Pod ...@@ -449,19 +453,31 @@ module Pod
# #
attr_reader :source_urls attr_reader :source_urls
# @param [String] platform_name
# the name of the platform, which should be declared
# in the Podfile.
#
# @param [String] deployment_target
# the deployment target, which should be declared in
# the Podfile.
#
# @param [Bool] use_frameworks
# whether frameworks should be used for the installation
#
# @return [Podfile] a podfile that requires the specification on the # @return [Podfile] a podfile that requires the specification on the
# current platform. # current platform.
# #
# @note The generated podfile takes into account whether the linter is # @note The generated podfile takes into account whether the linter is
# in local mode. # in local mode.
# #
def podfile_from_spec(platform_name, deployment_target) def podfile_from_spec(platform_name, deployment_target, use_frameworks = nil)
name = subspec_name ? subspec_name : spec.name name = subspec_name ? subspec_name : spec.name
podspec = file.realpath podspec = file.realpath
local = local? local = local?
urls = source_urls urls = source_urls
podfile = Pod::Podfile.new do podfile = Pod::Podfile.new do
urls.each { |u| source(u) } urls.each { |u| source(u) }
use_frameworks!(use_frameworks) unless use_frameworks.nil?
platform(platform_name, deployment_target) platform(platform_name, deployment_target)
if local if local
pod name, :path => podspec.dirname.to_s pod name, :path => podspec.dirname.to_s
......
Subproject commit 3d83baf65d54e6abae10d480bdeb4f00a11e3682 Subproject commit 5ad1e8ba91444988fcc96379fd986f5b8641ded2
Pod::Spec.new do |s|
s.name = "monkey"
s.version = "1.0.2"
s.author = { "Funky Monkey" => "funky@monkey.local" }
s.summary = "🙈🙉🙊"
s.description = "See no evil! Hear no evil! Speak no evil!"
s.homepage = "http://httpbin.org/html"
s.source = { :git => "http://monkey.local/monkey.git", :tag => s.version.to_s }
s.license = 'MIT'
s.vendored_library = 'monkey.a'
end
# OS X
.DS_Store
# Xcode
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
profile
*.moved-aside
DerivedData
*.hmap
*.ipa
# Bundler
.bundle
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Note: if you ignore the Pods directory, make sure to uncomment
# `pod install` in .travis.yml
#
# Pods/
Copyright (c) 2014 Marius Rackwitz <git@mariusrackwitz.de>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Pod::Spec.new do |s|
s.name = "OrangeFramework"
s.version = "0.1.0"
s.author = { "Swiftest Orang-Utan" => "swiftest@orang.utan.local" }
s.summary = "Fresh juice!"
s.description = "Blends fresh orange juice."
s.homepage = "http://httpbin.org/html"
s.source = { :git => "http://utan.local/orange-framework.git", :tag => s.version.to_s }
s.license = 'MIT'
s.platform = :ios, '8.0'
s.source_files = 'Source/Juicer.swift'
s.frameworks = 'UIKit'
end
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
import Foundation
public struct Orange {
public var weight: Float
public init(weight: Float) {
self.weight = weight
}
}
public struct Glass : Equatable {
public var volume: Float
public init(volume: Float) {
self.volume = volume
}
public static func empty() -> Glass {
return Glass(volume: 0)
}
public func pour(additionalVolume: Float) -> Glass {
return Glass(volume: self.volume + additionalVolume)
}
}
public class Juicer {
public required init() {}
public func pressOut(fruits: [Orange]) -> Glass {
return reduce(fruits, Glass.empty()) { (glass, fruit) in
glass.pour(self.juiceOf(fruit))
}
}
public func juiceOf(fruit: Orange) -> Float {
return fruit.weight * 0.5;
}
}
public func ==(lhs: Glass, rhs: Glass) -> Bool {
return lhs.volume == rhs.volume
}
#import <UIKit/UIKit.h>
//! Project version number for orange-framework.
FOUNDATION_EXPORT double orangeVersionNumber;
//! Project version string for orange-framework.
FOUNDATION_EXPORT const unsigned char orangeVersionString[];
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
import UIKit
import XCTest
import orange
class orange_frameworkTests: XCTestCase {
func testExample() {
let juicer = Juicer()
XCTAssertEqual(juicer.pressOut([Orange(weight: 1.5), Orange(weight: 0.5)]), Glass(volume: 1.0))
}
}
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
71D6BB9B1A11885200535476 /* orange.h in Headers */ = {isa = PBXBuildFile; fileRef = 71D6BB9A1A11885200535476 /* orange.h */; settings = {ATTRIBUTES = (Public, ); }; };
71D6BBA11A11885200535476 /* orange.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 71D6BB961A11885100535476 /* orange.framework */; };
71D6BBA81A11885200535476 /* orange_frameworkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D6BBA71A11885200535476 /* orange_frameworkTests.swift */; };
71D6BBB01A11887700535476 /* Juicer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71D6BBAF1A11887700535476 /* Juicer.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
71D6BBA21A11885200535476 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6003F582195388D10070C39A /* Project object */;
proxyType = 1;
remoteGlobalIDString = 71D6BB951A11885100535476;
remoteInfo = "oranage-framework";
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6003F58D195388D20070C39A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
6003F58F195388D20070C39A /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
6003F591195388D20070C39A /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
71D6BB961A11885100535476 /* orange.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = orange.framework; sourceTree = BUILT_PRODUCTS_DIR; };
71D6BB991A11885100535476 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
71D6BB9A1A11885200535476 /* orange.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = orange.h; sourceTree = "<group>"; };
71D6BBA01A11885200535476 /* oranage-frameworkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "oranage-frameworkTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
71D6BBA61A11885200535476 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
71D6BBA71A11885200535476 /* orange_frameworkTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = orange_frameworkTests.swift; sourceTree = "<group>"; };
71D6BBAF1A11887700535476 /* Juicer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Juicer.swift; sourceTree = "<group>"; };
9A2532323C66339977720A89 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
9F0ED7D852E0FED4837FEF06 /* OrangeFramework.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = OrangeFramework.podspec; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
71D6BB921A11885100535476 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
71D6BB9D1A11885200535476 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
71D6BBA11A11885200535476 /* orange.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
6003F581195388D10070C39A = {
isa = PBXGroup;
children = (
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */,
71D6BB971A11885100535476 /* Source */,
71D6BBA41A11885200535476 /* Tests */,
6003F58C195388D20070C39A /* Frameworks */,
6003F58B195388D20070C39A /* Products */,
);
sourceTree = "<group>";
};
6003F58B195388D20070C39A /* Products */ = {
isa = PBXGroup;
children = (
71D6BB961A11885100535476 /* orange.framework */,
71D6BBA01A11885200535476 /* oranage-frameworkTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
6003F58C195388D20070C39A /* Frameworks */ = {
isa = PBXGroup;
children = (
6003F58D195388D20070C39A /* Foundation.framework */,
6003F58F195388D20070C39A /* CoreGraphics.framework */,
6003F591195388D20070C39A /* UIKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
60FF7A9C1954A5C5007DD14C /* Podspec Metadata */ = {
isa = PBXGroup;
children = (
9F0ED7D852E0FED4837FEF06 /* OrangeFramework.podspec */,
9A2532323C66339977720A89 /* LICENSE */,
);
name = "Podspec Metadata";
sourceTree = "<group>";
};
71D6BB971A11885100535476 /* Source */ = {
isa = PBXGroup;
children = (
71D6BB9A1A11885200535476 /* orange.h */,
71D6BBAF1A11887700535476 /* Juicer.swift */,
71D6BB981A11885100535476 /* Supporting Files */,
);
path = Source;
sourceTree = "<group>";
};
71D6BB981A11885100535476 /* Supporting Files */ = {
isa = PBXGroup;
children = (
71D6BB991A11885100535476 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
71D6BBA41A11885200535476 /* Tests */ = {
isa = PBXGroup;
children = (
71D6BBA71A11885200535476 /* orange_frameworkTests.swift */,
71D6BBA51A11885200535476 /* Supporting Files */,
);
name = Tests;
path = Test;
sourceTree = "<group>";
};
71D6BBA51A11885200535476 /* Supporting Files */ = {
isa = PBXGroup;
children = (
71D6BBA61A11885200535476 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
71D6BB931A11885100535476 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
71D6BB9B1A11885200535476 /* orange.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
71D6BB951A11885100535476 /* oranage-framework */ = {
isa = PBXNativeTarget;
buildConfigurationList = 71D6BBA91A11885200535476 /* Build configuration list for PBXNativeTarget "oranage-framework" */;
buildPhases = (
71D6BB911A11885100535476 /* Sources */,
71D6BB921A11885100535476 /* Frameworks */,
71D6BB931A11885100535476 /* Headers */,
71D6BB941A11885100535476 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "oranage-framework";
productName = "oranage-framework";
productReference = 71D6BB961A11885100535476 /* orange.framework */;
productType = "com.apple.product-type.framework";
};
71D6BB9F1A11885200535476 /* oranage-frameworkTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 71D6BBAC1A11885200535476 /* Build configuration list for PBXNativeTarget "oranage-frameworkTests" */;
buildPhases = (
71D6BB9C1A11885200535476 /* Sources */,
71D6BB9D1A11885200535476 /* Frameworks */,
71D6BB9E1A11885200535476 /* Resources */,
);
buildRules = (
);
dependencies = (
71D6BBA31A11885200535476 /* PBXTargetDependency */,
);
name = "oranage-frameworkTests";
productName = "oranage-frameworkTests";
productReference = 71D6BBA01A11885200535476 /* oranage-frameworkTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
6003F582195388D10070C39A /* Project object */ = {
isa = PBXProject;
attributes = {
CLASSPREFIX = CP;
LastUpgradeCheck = 0510;
ORGANIZATIONNAME = "Marius Rackwitz";
TargetAttributes = {
71D6BB951A11885100535476 = {
CreatedOnToolsVersion = 6.1;
};
71D6BB9F1A11885200535476 = {
CreatedOnToolsVersion = 6.1;
};
};
};
buildConfigurationList = 6003F585195388D10070C39A /* Build configuration list for PBXProject "orange-framework" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 6003F581195388D10070C39A;
productRefGroup = 6003F58B195388D20070C39A /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
71D6BB951A11885100535476 /* oranage-framework */,
71D6BB9F1A11885200535476 /* oranage-frameworkTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
71D6BB941A11885100535476 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
71D6BB9E1A11885200535476 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
71D6BB911A11885100535476 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
71D6BBB01A11887700535476 /* Juicer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
71D6BB9C1A11885200535476 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
71D6BBA81A11885200535476 /* orange_frameworkTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
71D6BBA31A11885200535476 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 71D6BB951A11885100535476 /* oranage-framework */;
targetProxy = 71D6BBA21A11885200535476 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
6003F5BD195388D20070C39A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
6003F5BE195388D20070C39A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 7.1;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
71D6BBAA1A11885200535476 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = Source/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_NAME = orange;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
71D6BBAB1A11885200535476 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CURRENT_PROJECT_VERSION = 1;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_STRICT_OBJC_MSGSEND = YES;
INFOPLIST_FILE = Source/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = orange;
SKIP_INSTALL = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
71D6BBAD1A11885200535476 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = Test/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
71D6BBAE1A11885200535476 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_WARN_UNREACHABLE_CODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = Test/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6003F585195388D10070C39A /* Build configuration list for PBXProject "orange-framework" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6003F5BD195388D20070C39A /* Debug */,
6003F5BE195388D20070C39A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
71D6BBA91A11885200535476 /* Build configuration list for PBXNativeTarget "oranage-framework" */ = {
isa = XCConfigurationList;
buildConfigurations = (
71D6BBAA1A11885200535476 /* Debug */,
71D6BBAB1A11885200535476 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
71D6BBAC1A11885200535476 /* Build configuration list for PBXNativeTarget "oranage-frameworkTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
71D6BBAD1A11885200535476 /* Debug */,
71D6BBAE1A11885200535476 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 6003F582195388D10070C39A /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "71D6BB951A11885100535476"
BuildableName = "orange.framework"
BlueprintName = "oranage-framework"
ReferencedContainer = "container:orange-framework.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "71D6BB9F1A11885200535476"
BuildableName = "oranage-frameworkTests.xctest"
BlueprintName = "oranage-frameworkTests"
ReferencedContainer = "container:orange-framework.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "71D6BB9F1A11885200535476"
BuildableName = "oranage-frameworkTests.xctest"
BlueprintName = "oranage-frameworkTests"
ReferencedContainer = "container:orange-framework.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "71D6BB951A11885100535476"
BuildableName = "orange.framework"
BlueprintName = "oranage-framework"
ReferencedContainer = "container:orange-framework.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "71D6BB951A11885100535476"
BuildableName = "orange.framework"
BlueprintName = "oranage-framework"
ReferencedContainer = "container:orange-framework.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "71D6BB951A11885100535476"
BuildableName = "orange.framework"
BlueprintName = "oranage-framework"
ReferencedContainer = "container:orange-framework.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
Pod::Spec.new do |s|
s.name = "OrangeFramework"
s.version = "0.1.0"
s.author = { "Swiftest Orang-Utan" => "swiftest@orang.utan.local" }
s.summary = "Fresh juice!"
s.description = "Blends fresh orange juice."
s.homepage = "http://httpbin.org/html"
s.source = { :git => "http://utan.local/orange-framework.git", :tag => s.version.to_s }
s.license = 'MIT'
s.platform = :ios, '8.0'
s.source_files = 'Source/Juicer.swift'
s.frameworks = 'UIKit'
end
Pod::Spec.new do |s|
s.name = "monkey"
s.version = "1.0.2"
s.author = { "Funky Monkey" => "funky@monkey.local" }
s.summary = "🙈🙉🙊"
s.description = "See no evil! Hear no evil! Speak no evil!"
s.homepage = "http://httpbin.org/html"
s.source = { :git => "http://monkey.local/monkey.git", :tag => s.version.to_s }
s.license = 'MIT'
s.vendored_library = 'monkey.a'
end
...@@ -194,6 +194,11 @@ describe_cli 'pod' do ...@@ -194,6 +194,11 @@ describe_cli 'pod' do
'install --no-repo-update' 'install --no-repo-update'
end end
describe 'Integrates a Pod using frameworks with resources' do
behaves_like cli_spec 'install_framework_resources',
'install --no-repo-update'
end
# @todo add tests for all the hooks API # @todo add tests for all the hooks API
# #
describe 'Runs the Podfile callbacks' do describe 'Runs the Podfile callbacks' do
......
...@@ -118,6 +118,34 @@ def fixture_file_accessor(name, platform = :ios) ...@@ -118,6 +118,34 @@ def fixture_file_accessor(name, platform = :ios)
Pod::Sandbox::FileAccessor.new(path_list, spec.consumer(platform)) Pod::Sandbox::FileAccessor.new(path_list, spec.consumer(platform))
end end
def fixture_target_definition(podfile = nil, &block)
podfile ||= Pod::Podfile.new(&block)
Pod::Podfile::TargetDefinition.new('Pods', podfile)
end
def fixture_pod_target(spec_or_name, platform = :ios, target_definition = nil)
spec = spec_or_name.is_a?(Pod::Specification) ? spec_or_name : fixture_spec(spec_or_name)
target_definition ||= fixture_target_definition
target_definition.store_pod(spec.name)
Pod::PodTarget.new([spec], target_definition, config.sandbox).tap do |pod_target|
pod_target.stubs(:platform).returns(platform)
pod_target.file_accessors << fixture_file_accessor(spec.defined_in_file, platform)
consumer = spec.consumer(platform)
pod_target.spec_consumers << consumer
end
end
def fixture_aggregate_target(pod_targets = [], platform = :ios, target_definition = nil)
target_definition ||= pod_targets.map(&:target_definition).first || fixture_target_definition
target = Pod::AggregateTarget.new(target_definition, config.sandbox)
target.client_root = config.sandbox.root.dirname
version ||= (platform == :ios ? '4.3' : '10.6')
target.stubs(:platform).returns(Pod::Platform.new(platform, version))
target.pod_targets = pod_targets
target
end
#-----------------------------------------------------------------------------# #-----------------------------------------------------------------------------#
SpecHelper::Fixture.fixture('banana-lib') # ensure it exists SpecHelper::Fixture.fixture('banana-lib') # ensure it exists
SpecHelper::Fixture.fixture('orange-framework')
...@@ -30,7 +30,7 @@ module SpecHelper ...@@ -30,7 +30,7 @@ module SpecHelper
# Clones a repo to the given name. # Clones a repo to the given name.
# #
def repo_clone(from_name, to_name) def repo_clone(from_name, to_name)
Dir.chdir(tmp_repos_path) { `git clone #{from_name} #{to_name}` } Dir.chdir(tmp_repos_path) { `git clone #{from_name} #{to_name} 2>&1 > /dev/null` }
repo_path(to_name) repo_path(to_name)
end end
......
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Generator::EmbedFrameworksScript do
it 'returns the embed frameworks script' do
target_definition = Podfile::TargetDefinition.new(:default, nil)
frameworks = {
'Debug' => %w(Loopback.framework Reveal.framework),
'Release' => %w(CrashlyticsFramework.framework)
}
generator = Pod::Generator::EmbedFrameworksScript.new(target_definition, frameworks)
generator.send(:script).should.include <<-eos.strip_heredoc
if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework 'Loopback.framework'
install_framework 'Reveal.framework'
fi
eos
generator.send(:script).should.include <<-eos.strip_heredoc
if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework 'CrashlyticsFramework.framework'
fi
eos
end
end
end
...@@ -16,6 +16,16 @@ module Pod ...@@ -16,6 +16,16 @@ module Pod
EOS EOS
end end
it 'includes the module imports' do
@gen.module_imports << 'Module'
@gen.generate.should == <<-EOS.strip_heredoc
#import <UIKit/UIKit.h>
@import Module
EOS
end
it 'imports UIKit in iOS platforms' do it 'imports UIKit in iOS platforms' do
@gen.stubs(:platform).returns(Pod::Platform.ios) @gen.stubs(:platform).returns(Pod::Platform.ios)
@gen.generate.should.include?('#import <UIKit/UIKit.h>') @gen.generate.should.include?('#import <UIKit/UIKit.h>')
......
require File.expand_path('../../../spec_helper', __FILE__)
describe Pod::Generator::InfoPlistFile do
describe '#target_version' do
it 'returns 1.0.0 for the aggregate target' do
generator = Pod::Generator::InfoPlistFile.new(fixture_aggregate_target)
generator.target_version.should == '1.0.0'
end
it 'returns the specification\'s version for the pod target' do
generator = Pod::Generator::InfoPlistFile.new(fixture_pod_target('orange-framework/OrangeFramework.podspec'))
generator.target_version.should == '0.1.0'
end
end
it 'replaces the version in the generated plist' do
generator = Pod::Generator::InfoPlistFile.new(fixture_pod_target('orange-framework/OrangeFramework.podspec'))
generator.generate.should.include "<key>CFBundleShortVersionString</key>\n <string>0.1.0</string>"
end
it 'generates a valid Info.plist file' do
generator = Pod::Generator::InfoPlistFile.new(mock('Target'))
file = temporary_directory + 'Info.plist'
generator.save_as(file)
`plutil -lint #{file}`
$?.should.be.success
end
end
require File.expand_path('../../../spec_helper', __FILE__)
module Pod
describe Generator::ModuleMap do
before do
spec = fixture_spec('banana-lib/BananaLib.podspec')
target_definition = Podfile::TargetDefinition.new(:default, nil)
@pod_target = PodTarget.new([spec], target_definition, config.sandbox)
@gen = Generator::ModuleMap.new(@pod_target)
end
it 'writes the module map to the disk' do
path = temporary_directory + 'BananaLib.modulemap'
@gen.save_as(path)
path.read.should == <<-EOS.strip_heredoc
framework module BananaLib {
umbrella header "Pods-default-BananaLib-umbrella.h"
export *
module * { export * }
}
EOS
end
end
end
...@@ -5,90 +5,69 @@ module Pod ...@@ -5,90 +5,69 @@ module Pod
module XCConfig module XCConfig
describe AggregateXCConfig do describe AggregateXCConfig do
before do def spec
@spec = fixture_spec('banana-lib/BananaLib.podspec') fixture_spec('banana-lib/BananaLib.podspec')
@consumer = @spec.consumer(:ios)
target_definition = Podfile::TargetDefinition.new('Pods', nil)
target_definition.store_pod('BananaLib')
target_definition.whitelist_pod_for_configuration('BananaLib', 'Release')
@target = AggregateTarget.new(target_definition, config.sandbox)
@target.client_root = config.sandbox.root.dirname
@target.stubs(:platform).returns(:ios)
@pod_target = PodTarget.new([@spec], target_definition, config.sandbox)
@pod_target.stubs(:platform).returns(:ios)
@pod_target.stubs(:spec_consumers).returns([@consumer])
file_accessor = fixture_file_accessor('banana-lib/BananaLib.podspec')
@pod_target.stubs(:file_accessors).returns([file_accessor])
@target.pod_targets = [@pod_target]
@generator = AggregateXCConfig.new(@target, 'Release')
end end
it 'returns the path of the pods root relative to the user project' do
@generator.target.relative_pods_root.should == '${SRCROOT}/Pods'
end
#-----------------------------------------------------------------------#
before do before do
@podfile = Podfile.new @spec = spec
@target.target_definition.stubs(:podfile).returns(@podfile) @pod_target = fixture_pod_target(@spec)
@xcconfig = @generator.generate @consumer = @pod_target.spec_consumers.last
@target = fixture_aggregate_target([@pod_target])
@target.target_definition.should == @pod_target.target_definition
@target.target_definition.whitelist_pod_for_configuration(@spec.name, 'Release')
@podfile = @target.target_definition.podfile
@generator = AggregateXCConfig.new(@target, 'Release')
end end
it 'generates the xcconfig' do shared 'AggregateXCConfig' do
@xcconfig.class.should == Xcodeproj::Config
end
it 'configures the project to load all members that implement Objective-c classes or categories from the static library' do it 'returns the path of the pods root relative to the user project' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-ObjC' @generator.target.relative_pods_root.should == '${SRCROOT}/Pods'
end end
it 'does not add the -fobjc-arc to OTHER_LDFLAGS by default as Xcode 4.3.2 does not support it' do #--------------------------------------------------------------------#
@consumer.stubs(:requires_arc?).returns(true)
@xcconfig.to_hash['OTHER_LDFLAGS'].should.not.include('-fobjc-arc')
end
it 'adds the -fobjc-arc to OTHER_LDFLAGS if any pods require arc and the podfile explicitly requires it' do before do
@podfile.stubs(:set_arc_compatibility_flag?).returns(true) @xcconfig = @generator.generate
@consumer.stubs(:requires_arc?).returns(true) end
@xcconfig = @generator.generate
@xcconfig.to_hash['OTHER_LDFLAGS'].split(' ').should.include('-fobjc-arc')
end
it 'sets the PODS_ROOT build variable' do it 'generates the xcconfig' do
@xcconfig.to_hash['PODS_ROOT'].should == '${SRCROOT}/Pods' @xcconfig.class.should == Xcodeproj::Config
end end
it 'adds the sandbox public headers search paths to the xcconfig, with quotes, as header search paths' do it 'configures the project to load all members that implement Objective-c classes or categories' do
expected = "\"#{config.sandbox.public_headers.search_paths(:ios).join('" "')}\"" @xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-ObjC'
@xcconfig.to_hash['HEADER_SEARCH_PATHS'].should == expected end
end
it 'adds the sandbox public headers search paths to the xcconfig, with quotes, as system headers' do it 'does not add the -fobjc-arc to OTHER_LDFLAGS by default as Xcode 4.3.2 does not support it' do
expected = "$(inherited) -isystem \"#{config.sandbox.public_headers.search_paths(:ios).join('" -isystem "')}\"" @consumer.stubs(:requires_arc?).returns(true)
@xcconfig.to_hash['OTHER_CFLAGS'].should == expected @xcconfig.to_hash['OTHER_LDFLAGS'].should.not.include('-fobjc-arc')
end end
it 'adds the COCOAPODS macro definition' do it 'adds the -fobjc-arc to OTHER_LDFLAGS if any pods require arc and the podfile explicitly requires it' do
@xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include 'COCOAPODS=1' @podfile.stubs(:set_arc_compatibility_flag?).returns(true)
end @consumer.stubs(:requires_arc?).returns(true)
@xcconfig = @generator.generate
@xcconfig.to_hash['OTHER_LDFLAGS'].split(' ').should.include('-fobjc-arc')
end
it 'inherits the parent GCC_PREPROCESSOR_DEFINITIONS value' do it 'sets the PODS_ROOT build variable' do
@xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include '$(inherited)' @xcconfig.to_hash['PODS_ROOT'].should == '${SRCROOT}/Pods'
end end
it 'links the pod targets with the aggregate integration library target' do it 'adds the COCOAPODS macro definition' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"Pods-BananaLib"' @xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include 'COCOAPODS=1'
end end
it 'does not links the pod targets with the aggregate integration library target for non-whitelisted configuration' do it 'inherits the parent GCC_PREPROCESSOR_DEFINITIONS value' do
@generator = AggregateXCConfig.new(@target, 'Debug') @xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include '$(inherited)'
@xcconfig = @generator.generate end
@xcconfig.to_hash['OTHER_LDFLAGS'].should.not.include '-l"Pods-BananaLib"'
end it 'should configure OTHER_LIBTOOLFLAGS flags to include OTHER_LDFLAGS' do
@xcconfig.to_hash['OTHER_LIBTOOLFLAGS'].should == '$(OTHER_LDFLAGS)'
end
it 'should configure OTHER_LIBTOOLFLAGS flags to include OTHER_LDFLAGS' do
@xcconfig.to_hash['OTHER_LIBTOOLFLAGS'].should == '$(OTHER_LDFLAGS)'
end end
#-----------------------------------------------------------------------# #-----------------------------------------------------------------------#
...@@ -116,14 +95,77 @@ module Pod ...@@ -116,14 +95,77 @@ module Pod
#-----------------------------------------------------------------------# #-----------------------------------------------------------------------#
before do describe 'with library' do
@path = temporary_directory + 'sample.xcconfig' def spec
@generator.save_as(@path) fixture_spec('banana-lib/BananaLib.podspec')
end
behaves_like 'AggregateXCConfig'
it 'adds the sandbox public headers search paths to the xcconfig, with quotes, as header search paths' do
expected = "\"#{config.sandbox.public_headers.search_paths(:ios).join('" "')}\""
@xcconfig.to_hash['HEADER_SEARCH_PATHS'].should == expected
end
it 'adds the sandbox public headers search paths to the xcconfig, with quotes, as system headers' do
expected = "$(inherited) -isystem \"#{config.sandbox.public_headers.search_paths(:ios).join('" -isystem "')}\""
@xcconfig.to_hash['OTHER_CFLAGS'].should == expected
end
it 'links the pod targets with the aggregate target' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"Pods-BananaLib"'
end
it 'does not links the pod targets with the aggregate target for non-whitelisted configuration' do
@generator = AggregateXCConfig.new(@target, 'Debug')
@xcconfig = @generator.generate
@xcconfig.to_hash['OTHER_LDFLAGS'].should.not.include '-l"Pods-BananaLib"'
end
end end
it 'saves the xcconfig' do describe 'with framework' do
generated = Xcodeproj::Config.new(@path) def spec
generated.class.should == Xcodeproj::Config fixture_spec('orange-framework/OrangeFramework.podspec')
end
behaves_like 'AggregateXCConfig'
it 'sets the PODS_FRAMEWORK_BUILD_PATH build variable' do
@xcconfig.to_hash['PODS_FRAMEWORK_BUILD_PATH'].should == '$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods'
end
it 'adds the framework build path to the xcconfig, with quotes, as framework search paths' do
@xcconfig.to_hash['FRAMEWORK_SEARCH_PATHS'].should == '"$PODS_FRAMEWORK_BUILD_PATH"'
end
it 'adds the framework header paths to the xcconfig, with quotes, as local headers' do
expected = '$(inherited) -iquote "$PODS_FRAMEWORK_BUILD_PATH/OrangeFramework.framework/Headers"'
@xcconfig.to_hash['OTHER_CFLAGS'].should == expected
end
it 'links the pod targets with the aggregate target' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-framework "OrangeFramework"'
end
it 'adds the COCOAPODS macro definition' do
@xcconfig.to_hash['OTHER_SWIFT_FLAGS'].should.include '-D COCOAPODS'
end
end
#-----------------------------------------------------------------------#
describe "serializing and deserializing" do
before do
@path = temporary_directory + 'sample.xcconfig'
@generator.save_as(@path)
end
it 'saves the xcconfig' do
generated = Xcodeproj::Config.new(@path)
generated.class.should == Xcodeproj::Config
end
end end
end end
......
...@@ -140,7 +140,7 @@ module Pod ...@@ -140,7 +140,7 @@ module Pod
hash_config['FRAMEWORK_SEARCH_PATHS'].should == '"$(PODS_ROOT)/Parse"' hash_config['FRAMEWORK_SEARCH_PATHS'].should == '"$(PODS_ROOT)/Parse"'
end end
it "doesn't ovverides exiting linker flags" do it "doesn't override existing linker flags" do
framework_path = config.sandbox.root + 'Parse/Parse.framework' framework_path = config.sandbox.root + 'Parse/Parse.framework'
xcconfig = Xcodeproj::Config.new('OTHER_LDFLAGS' => '-framework CoreAnimation') xcconfig = Xcodeproj::Config.new('OTHER_LDFLAGS' => '-framework CoreAnimation')
@sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root) @sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root)
...@@ -148,7 +148,7 @@ module Pod ...@@ -148,7 +148,7 @@ module Pod
hash_config['OTHER_LDFLAGS'].should == '-framework "CoreAnimation" -framework "Parse"' hash_config['OTHER_LDFLAGS'].should == '-framework "CoreAnimation" -framework "Parse"'
end end
it "doesn't ovverides exiting frameworks search paths" do it "doesn't override existing frameworks search paths" do
framework_path = config.sandbox.root + 'Parse/Parse.framework' framework_path = config.sandbox.root + 'Parse/Parse.framework'
xcconfig = Xcodeproj::Config.new('FRAMEWORK_SEARCH_PATHS' => '"path/to/frameworks"') xcconfig = Xcodeproj::Config.new('FRAMEWORK_SEARCH_PATHS' => '"path/to/frameworks"')
@sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root) @sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root)
...@@ -172,7 +172,7 @@ module Pod ...@@ -172,7 +172,7 @@ module Pod
#---------------------------------------------------------------------# #---------------------------------------------------------------------#
describe '::add_framework_build_settings' do describe '::add_developers_frameworks_if_needed' do
it 'adds the developer frameworks search paths to the xcconfig if SenTestingKit has been detected' do it 'adds the developer frameworks search paths to the xcconfig if SenTestingKit has been detected' do
xcconfig = Xcodeproj::Config.new('OTHER_LDFLAGS' => '-framework SenTestingKit') xcconfig = Xcodeproj::Config.new('OTHER_LDFLAGS' => '-framework SenTestingKit')
@sut.add_developers_frameworks_if_needed(xcconfig, :ios) @sut.add_developers_frameworks_if_needed(xcconfig, :ios)
......
...@@ -64,19 +64,6 @@ module Pod ...@@ -64,19 +64,6 @@ module Pod
@project.targets.first.name.should == @target_definition.label @project.targets.first.name.should == @target_definition.label
end end
it 'adds the user build configurations to the target' do
@installer.install!
target = @project.targets.first
target.build_settings('Test')['VALIDATE_PRODUCT'].should.nil?
target.build_settings('AppStore')['VALIDATE_PRODUCT'].should == 'YES'
end
it 'sets VALIDATE_PRODUCT to YES for the Release configuration for iOS targets' do
@installer.install!
target = @project.targets.first
target.build_settings('Release')['VALIDATE_PRODUCT'].should == 'YES'
end
it 'sets the platform and the deployment target for iOS targets' do it 'sets the platform and the deployment target for iOS targets' do
@installer.install! @installer.install!
target = @project.targets.first target = @project.targets.first
...@@ -147,6 +134,14 @@ module Pod ...@@ -147,6 +134,14 @@ module Pod
script.read.should.include?('logo-sidebar.png') script.read.should.include?('logo-sidebar.png')
end end
it 'does not add framework resources to copy resources script' do
@pod_target.stubs(:requires_frameworks? => true)
@installer.install!
support_files_dir = config.sandbox.target_support_files_dir('Pods')
script = support_files_dir + 'Pods-resources.sh'
script.read.should.not.include?('logo-sidebar.png')
end
xit 'adds the resources bundles to the copy resources script' do xit 'adds the resources bundles to the copy resources script' do
end end
...@@ -155,6 +150,25 @@ module Pod ...@@ -155,6 +150,25 @@ module Pod
end end
it 'does add pods to the embed frameworks script' do
@pod_target.stubs(:requires_frameworks? => true)
@target.stubs(:requires_frameworks? => true)
@installer.install!
support_files_dir = config.sandbox.target_support_files_dir('Pods')
script = support_files_dir + 'Pods-frameworks.sh'
script.read.should.include?('BananaLib.framework')
end
it 'does not add pods to the embed frameworks script if they are not to be built' do
@pod_target.stubs(:should_build? => false)
@pod_target.stubs(:requires_frameworks? => true)
@target.stubs(:requires_frameworks? => true)
@installer.install!
support_files_dir = config.sandbox.target_support_files_dir('Pods')
script = support_files_dir + 'Pods-frameworks.sh'
script.read.should.not.include?('BananaLib.framework')
end
it 'creates the acknowledgements files ' do it 'creates the acknowledgements files ' do
@installer.install! @installer.install!
support_files_dir = config.sandbox.target_support_files_dir('Pods') support_files_dir = config.sandbox.target_support_files_dir('Pods')
......
...@@ -21,6 +21,9 @@ module Pod ...@@ -21,6 +21,9 @@ module Pod
file_accessor.source_files.each do |file| file_accessor.source_files.each do |file|
@project.add_file_reference(file, group) @project.add_file_reference(file, group)
end end
file_accessor.resources.each do |resource|
@project.add_file_reference(resource, group)
end
@pod_target = PodTarget.new([@spec], @target_definition, config.sandbox) @pod_target = PodTarget.new([@spec], @target_definition, config.sandbox)
@pod_target.stubs(:platform).returns(Platform.new(:ios, '6.0')) @pod_target.stubs(:platform).returns(Platform.new(:ios, '6.0'))
...@@ -51,12 +54,6 @@ module Pod ...@@ -51,12 +54,6 @@ module Pod
@project.targets.first.name.should == 'Pods-BananaLib' @project.targets.first.name.should == 'Pods-BananaLib'
end end
it 'sets VALIDATE_PRODUCT to YES for the Release configuration for iOS targets' do
@installer.install!
target = @project.targets.first
target.build_settings('Release')['VALIDATE_PRODUCT'].should == 'YES'
end
it 'sets the platform and the deployment target for iOS targets' do it 'sets the platform and the deployment target for iOS targets' do
@installer.install! @installer.install!
target = @project.targets.first target = @project.targets.first
...@@ -106,7 +103,19 @@ module Pod ...@@ -106,7 +103,19 @@ module Pod
it 'adds the resource bundle targets' do it 'adds the resource bundle targets' do
@pod_target.file_accessors.first.stubs(:resource_bundles).returns('banana_bundle' => []) @pod_target.file_accessors.first.stubs(:resource_bundles).returns('banana_bundle' => [])
@installer.install! @installer.install!
@project.targets.map(&:name).should == ['Pods-BananaLib', 'banana_bundle'] bundle_target = @project.targets.find { |t| t.name == 'Pods-BananaLib-banana_bundle' }
bundle_target.should.be.an.instance_of Xcodeproj::Project::Object::PBXNativeTarget
bundle_target.product_reference.name.should == 'banana_bundle.bundle'
bundle_target.product_reference.path.should == 'banana_bundle.bundle'
end
it 'adds framework resources to the framework target' do
@pod_target.stubs(:requires_frameworks? => true)
@installer.install!
resources = @project.targets.first.resources_build_phase.files
resources.count.should > 0
resource = resources.find { |res| res.file_ref.path.include?('logo-sidebar.png') }
resource.should.be.not.nil
end end
xit 'adds the build configurations to the resources bundle targets' do xit 'adds the build configurations to the resources bundle targets' do
......
...@@ -23,9 +23,7 @@ module Pod ...@@ -23,9 +23,7 @@ module Pod
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1' 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1'
) )
@pod_bundle.xcconfigs['Debug'] = configuration @pod_bundle.xcconfigs['Debug'] = configuration
@pod_bundle.xcconfigs['Test'] = configuration
@pod_bundle.xcconfigs['Release'] = configuration @pod_bundle.xcconfigs['Release'] = configuration
@pod_bundle.xcconfigs['App Store'] = configuration
@target_integrator = TargetIntegrator.new(@pod_bundle) @target_integrator = TargetIntegrator.new(@pod_bundle)
end end
...@@ -102,6 +100,31 @@ module Pod ...@@ -102,6 +100,31 @@ module Pod
@target_integrator.send(:user_project).expects(:save).never @target_integrator.send(:user_project).expects(:save).never
@target_integrator.integrate! @target_integrator.integrate!
end end
it 'adds an embed frameworks build phase if frameworks are used' do
@pod_bundle.stubs(:requires_frameworks? => true)
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == 'Embed Pods Frameworks' }
phase.nil?.should == false
end
it 'does not add an embed frameworks build phase by default' do
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == 'Embed Pods Frameworks' }
phase.nil?.should == true
end
it 'removes existing embed frameworks build phases if frameworks are not used anymore' do
@pod_bundle.stubs(:requires_frameworks? => true)
@target_integrator.integrate!
@pod_bundle.stubs(:requires_frameworks? => false)
@target_integrator.integrate!
target = @target_integrator.send(:native_targets).first
phase = target.shell_script_build_phases.find { |bp| bp.name == 'Embed Pods Frameworks' }
phase.nil?.should == true
end
end end
describe 'Private helpers' do describe 'Private helpers' do
......
...@@ -55,47 +55,43 @@ module Pod ...@@ -55,47 +55,43 @@ module Pod
UI.warnings.should.include?('The Podfile does not contain any dependencies') UI.warnings.should.include?('The Podfile does not contain any dependencies')
end end
it 'check that the integrated target does not override the CocoaPods build settings' do describe '#warn_about_xcconfig_overrides' do
UI.warnings = '' shared 'warn_about_xcconfig_overrides' do
target_config = stub(:name => 'Release', :build_settings => { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1'] }) target_config = stub(:name => 'Release', :build_settings => @user_target_build_settings)
user_target = stub(:name => 'SampleProject', :build_configurations => [target_config]) user_target = stub(:name => 'SampleProject', :build_configurations => [target_config])
@target.stubs(:user_targets).returns([user_target]) @target.stubs(:user_targets).returns([user_target])
@target.xcconfigs['Release'] = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1' } @target.xcconfigs['Release'] = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1' }
@integrator = UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@target]) @integrator = UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@target])
@integrator.unstub(:warn_about_xcconfig_overrides) @integrator.unstub(:warn_about_xcconfig_overrides)
@integrator.send(:warn_about_xcconfig_overrides) @integrator.send(:warn_about_xcconfig_overrides)
UI.warnings.should.include 'The `SampleProject [Release]` target ' \ end
'overrides the `GCC_PREPROCESSOR_DEFINITIONS` build setting'
end
it 'allows the use of the alternate form of the inherited flag' do
UI.warnings = ''
target_config = stub(:name => 'Release', :build_settings => { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1', '${inherited}'] })
user_target = stub(:name => 'SampleProject', :build_configurations => [target_config])
@target.stubs(:user_targets).returns([user_target])
@target.xcconfigs['Release'] = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1' }
@integrator = UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@target])
@integrator.unstub(:warn_about_xcconfig_overrides) it 'check that the integrated target does not override the CocoaPods build settings' do
@integrator.send(:warn_about_xcconfig_overrides) @user_target_build_settings = { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1'] }
UI.warnings.should.not.include 'GCC_PREPROCESSOR_DEFINITIONS' behaves_like 'warn_about_xcconfig_overrides'
end UI.warnings.should.include 'The `SampleProject [Release]` target ' \
'overrides the `GCC_PREPROCESSOR_DEFINITIONS` build setting'
end
it 'allows build settings which inherit the settings form the CocoaPods xcconfig' do it 'allows the use of the alternate form of the inherited flag' do
UI.warnings = '' @user_target_build_settings = { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1', '${inherited}'] }
target_config = stub(:name => 'Release', :build_settings => { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1', '$(inherited)'] }) behaves_like 'warn_about_xcconfig_overrides'
user_target = stub(:name => 'SampleProject', :build_configurations => [target_config]) UI.warnings.should.not.include 'GCC_PREPROCESSOR_DEFINITIONS'
@target.stubs(:user_targets).returns([user_target]) end
@target.xcconfigs['Release'] = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'COCOAPODS=1' } it 'allows build settings which inherit the settings form the CocoaPods xcconfig' do
@integrator = UserProjectIntegrator.new(@podfile, config.sandbox, temporary_directory, [@target]) @user_target_build_settings = { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1', '$(inherited)'] }
behaves_like 'warn_about_xcconfig_overrides'
UI.warnings.should.not.include 'GCC_PREPROCESSOR_DEFINITIONS'
end
@integrator.unstub(:warn_about_xcconfig_overrides) it "ignores certain build settings which don't inherit the settings form the CocoaPods xcconfig" do
@integrator.send(:warn_about_xcconfig_overrides) @user_target_build_settings = { 'CODE_SIGN_IDENTITY' => "Mac Developer" }
UI.warnings.should.not.include 'GCC_PREPROCESSOR_DEFINITIONS' behaves_like 'warn_about_xcconfig_overrides'
UI.warnings.should.not.include 'CODE_SIGN_IDENTITY'
end
end end
end end
......
...@@ -50,6 +50,7 @@ module Pod ...@@ -50,6 +50,7 @@ module Pod
before do before do
@installer.stubs(:resolve_dependencies) @installer.stubs(:resolve_dependencies)
@installer.stubs(:download_dependencies) @installer.stubs(:download_dependencies)
@installer.stubs(:determine_dependency_product_types)
@installer.stubs(:generate_pods_project) @installer.stubs(:generate_pods_project)
@installer.stubs(:integrate_user_project) @installer.stubs(:integrate_user_project)
@installer.stubs(:run_plugins_post_install_hooks) @installer.stubs(:run_plugins_post_install_hooks)
...@@ -75,6 +76,7 @@ module Pod ...@@ -75,6 +76,7 @@ module Pod
@installer.stubs(:run_pre_install_hooks) @installer.stubs(:run_pre_install_hooks)
@installer.stubs(:install_file_references) @installer.stubs(:install_file_references)
@installer.stubs(:install_libraries) @installer.stubs(:install_libraries)
@installer.stubs(:set_target_dependencies)
@installer.stubs(:write_lockfiles) @installer.stubs(:write_lockfiles)
@installer.stubs(:aggregate_targets).returns([]) @installer.stubs(:aggregate_targets).returns([])
@installer.unstub(:generate_pods_project) @installer.unstub(:generate_pods_project)
...@@ -113,6 +115,37 @@ module Pod ...@@ -113,6 +115,37 @@ module Pod
end end
end end
#-------------------------------------------------------------------------#
describe '#determine_dependency_product_type' do
it 'does propagate that frameworks are required to all pod targets' do
fixture_path = ROOT + 'spec/fixtures'
config.repos_dir = fixture_path + 'spec-repos'
podfile = Pod::Podfile.new do
platform :ios, '8.0'
xcodeproj 'SampleProject/SampleProject'
pod 'BananaLib', :path => (fixture_path + 'banana-lib').to_s
pod 'OrangeFramework', :path => (fixture_path + 'orange-framework').to_s
pod 'monkey', :path => (fixture_path + 'monkey').to_s
end
lockfile = generate_lockfile
config.integrate_targets = false
@installer = Installer.new(config.sandbox, podfile, lockfile)
@installer.install!
target = @installer.aggregate_targets.first
target.requires_frameworks?.should == true
target.pod_targets.select(&:requires_frameworks?).map(&:name).sort.should == [
'Pods-BananaLib',
'Pods-OrangeFramework',
'Pods-monkey',
]
end
end
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
...@@ -417,9 +450,12 @@ module Pod ...@@ -417,9 +450,12 @@ module Pod
pod_target.stubs(:should_build? => false) pod_target.stubs(:should_build? => false)
target = AggregateTarget.new(target_definition, config.sandbox) target = AggregateTarget.new(target_definition, config.sandbox)
mock_target = mock mock_target = mock('PodNativeTarget')
mock_target.expects(:add_dependency).with('dummy') mock_target.expects(:add_dependency).with('dummy')
mock_project = mock('PodsProject', :frameworks_group => mock('FrameworksGroup'))
@installer.stubs(:pods_project).returns(mock_project)
target.stubs(:native_target).returns(mock_target) target.stubs(:native_target).returns(mock_target)
target.stubs(:pod_targets).returns([pod_target]) target.stubs(:pod_targets).returns([pod_target])
@installer.stubs(:aggregate_targets).returns([target]) @installer.stubs(:aggregate_targets).returns([target])
......
...@@ -215,7 +215,7 @@ module Pod ...@@ -215,7 +215,7 @@ module Pod
file_patterns = ['Classes/*.{h,m,d}', 'Vendor'] file_patterns = ['Classes/*.{h,m,d}', 'Vendor']
options = { options = {
:exclude_patterns => ['Classes/**/osx/**/*', 'Resources/**/osx/**/*'], :exclude_patterns => ['Classes/**/osx/**/*', 'Resources/**/osx/**/*'],
:dir_pattern => '*.{h,hpp,hh,m,mm,c,cpp}', :dir_pattern => '*.{h,hpp,hh,m,mm,c,cpp,swift}',
:include_dirs => false, :include_dirs => false,
} }
@spec.exclude_files = options[:exclude_patterns] @spec.exclude_files = options[:exclude_patterns]
......
...@@ -51,6 +51,10 @@ module Pod ...@@ -51,6 +51,10 @@ module Pod
@target.copy_resources_script_path.to_s.should.include?('Pods/Target Support Files/Pods/Pods-resources.sh') @target.copy_resources_script_path.to_s.should.include?('Pods/Target Support Files/Pods/Pods-resources.sh')
end end
it 'returns the absolute path of the frameworks script' do
@target.embed_frameworks_script_path.to_s.should.include?('Pods/Target Support Files/Pods/Pods-frameworks.sh')
end
it 'returns the absolute path of the target header file' do it 'returns the absolute path of the target header file' do
@target.target_environment_header_path.to_s.should.include?('Pods/Target Support Files/Pods/Pods-environment.h') @target.target_environment_header_path.to_s.should.include?('Pods/Target Support Files/Pods/Pods-environment.h')
end end
...@@ -71,9 +75,17 @@ module Pod ...@@ -71,9 +75,17 @@ module Pod
@target.copy_resources_script_relative_path.should == '${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh' @target.copy_resources_script_relative_path.should == '${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh'
end end
it 'returns the path of the frameworks script relative to the user project' do
@target.embed_frameworks_script_relative_path.should == '${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh'
end
it 'returns the path of the xcconfig file relative to the user project' do it 'returns the path of the xcconfig file relative to the user project' do
@target.xcconfig_relative_path('Release').should == 'Pods/Target Support Files/Pods/Pods.release.xcconfig' @target.xcconfig_relative_path('Release').should == 'Pods/Target Support Files/Pods/Pods.release.xcconfig'
end end
it 'returns the path for the CONFIGURATION_BUILD_DIR build setting' do
@target.configuration_build_dir.should == '$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods'
end
end end
describe 'Pod targets' do describe 'Pod targets' do
...@@ -86,20 +98,29 @@ module Pod ...@@ -86,20 +98,29 @@ module Pod
@target.pod_targets = [@pod_target] @target.pod_targets = [@pod_target]
end end
it 'returns pod targets by build configuration' do describe 'with configuration dependent pod targets' do
pod_target_release = PodTarget.new([@spec], @target_definition, config.sandbox) before do
pod_target_release.expects(:include_in_build_config?).with('Debug').returns(false) @pod_target_release = PodTarget.new([@spec], @target_definition, config.sandbox)
pod_target_release.expects(:include_in_build_config?).with('Release').returns(true) @pod_target_release.expects(:include_in_build_config?).with('Debug').returns(false)
@target.pod_targets = [@pod_target, pod_target_release] @pod_target_release.expects(:include_in_build_config?).with('Release').returns(true)
@target.user_build_configurations = { @target.pod_targets = [@pod_target, @pod_target_release]
'Debug' => :debug, @target.user_build_configurations = {
'Release' => :release, 'Debug' => :debug,
} 'Release' => :release,
expected = { }
'Debug' => @pod_target.specs, end
'Release' => (@pod_target.specs + pod_target_release.specs),
} it 'returns pod targets for given build configuration' do
@target.specs_by_build_configuration.should == expected @target.pod_targets_for_build_configuration('Debug').should == [@pod_target]
@target.pod_targets_for_build_configuration('Release').should == [@pod_target, @pod_target_release]
end
it 'returns pod target specs by build configuration' do
@target.specs_by_build_configuration.should == {
'Debug' => @pod_target.specs,
'Release' => (@pod_target.specs + @pod_target_release.specs),
}
end
end end
it 'returns the specs of the Pods used by this aggregate target' do it 'returns the specs of the Pods used by this aggregate target' do
...@@ -115,5 +136,103 @@ module Pod ...@@ -115,5 +136,103 @@ module Pod
consumer_reps.should == [['BananaLib', :ios]] consumer_reps.should == [['BananaLib', :ios]]
end end
end end
describe 'Product type dependent helpers' do
describe 'With libraries' do
before do
@pod_target = fixture_pod_target('banana-lib/BananaLib.podspec')
@target = AggregateTarget.new(@pod_target.target_definition, config.sandbox)
@target.pod_targets = [@pod_target]
end
it 'returns that it does not use swift' do
@target.uses_swift?.should == false
end
describe 'Host requires frameworks' do
before do
@target.host_requires_frameworks = true
end
it 'returns the product name' do
@target.product_name.should == 'Pods.framework'
end
it 'returns the framework name' do
@target.framework_name.should == 'Pods.framework'
end
it 'returns the library name' do
@target.static_library_name.should == 'libPods.a'
end
it 'returns :framework as product type' do
@target.product_type.should == :framework
end
it 'returns that it requires being built as framework' do
@target.requires_frameworks?.should == true
end
end
describe 'Host does not requires frameworks' do
it 'returns the product name' do
@target.product_name.should == 'libPods.a'
end
it 'returns the framework name' do
@target.framework_name.should == 'Pods.framework'
end
it 'returns the library name' do
@target.static_library_name.should == 'libPods.a'
end
it 'returns :static_library as product type' do
@target.product_type.should == :static_library
end
it 'returns that it does not require being built as framework' do
@target.requires_frameworks?.should == false
end
end
end
describe 'With frameworks' do
before do
@pod_target = fixture_pod_target('orange-framework/OrangeFramework.podspec', :ios, Podfile::TargetDefinition.new('iOS Example', nil))
@target = AggregateTarget.new(@pod_target.target_definition, config.sandbox)
@target.pod_targets = [@pod_target]
end
it 'returns that it uses swift' do
@target.uses_swift?.should == true
end
it 'returns the product module name' do
@target.product_module_name.should == 'Pods_iOS_Example'
end
it 'returns the product name' do
@target.product_name.should == 'Pods_iOS_Example.framework'
end
it 'returns the framework name' do
@target.framework_name.should == 'Pods_iOS_Example.framework'
end
it 'returns the library name' do
@target.static_library_name.should == 'libPods-iOS Example.a'
end
it 'returns :framework as product type' do
@target.product_type.should == :framework
end
it 'returns that it requires being built as framework' do
@target.requires_frameworks?.should == true
end
end
end
end end
end end
...@@ -34,6 +34,10 @@ module Pod ...@@ -34,6 +34,10 @@ module Pod
@pod_target.pod_name.should == 'BananaLib' @pod_target.pod_name.should == 'BananaLib'
end end
it 'returns the name of the resources bundle target' do
@pod_target.resources_bundle_target_label('Fruits').should == 'Pods-BananaLib-Fruits'
end
it 'returns the name of the Pods on which this target depends' do it 'returns the name of the Pods on which this target depends' do
@pod_target.dependencies.should == ['monkey'] @pod_target.dependencies.should == ['monkey']
end end
...@@ -92,6 +96,12 @@ module Pod ...@@ -92,6 +96,12 @@ module Pod
) )
end end
it 'returns the absolute path of the info plist file' do
@pod_target.info_plist_path.to_s.should.include?(
'Pods/Target Support Files/Pods-BananaLib/Info.plist'
)
end
it 'returns the absolute path of the public and private xcconfig files' do it 'returns the absolute path of the public and private xcconfig files' do
@pod_target.xcconfig_path.to_s.should.include?( @pod_target.xcconfig_path.to_s.should.include?(
'Pods/Target Support Files/Pods-BananaLib/Pods-BananaLib.xcconfig' 'Pods/Target Support Files/Pods-BananaLib/Pods-BananaLib.xcconfig'
...@@ -100,6 +110,104 @@ module Pod ...@@ -100,6 +110,104 @@ module Pod
'Pods/Target Support Files/Pods-BananaLib/Pods-BananaLib-Private.xcconfig' 'Pods/Target Support Files/Pods-BananaLib/Pods-BananaLib-Private.xcconfig'
) )
end end
it 'returns the path for the CONFIGURATION_BUILD_DIR build setting' do
@pod_target.configuration_build_dir.should == '$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods'
end
end
describe 'Product type dependent helpers' do
describe 'With libraries' do
before do
@pod_target = fixture_pod_target('banana-lib/BananaLib.podspec')
end
it 'returns that it does not use swift' do
@pod_target.uses_swift?.should == false
end
describe 'Host requires frameworks' do
before do
@pod_target.host_requires_frameworks = true
end
it 'returns the product name' do
@pod_target.product_name.should == 'BananaLib.framework'
end
it 'returns the framework name' do
@pod_target.framework_name.should == 'BananaLib.framework'
end
it 'returns the library name' do
@pod_target.static_library_name.should == 'libPods-BananaLib.a'
end
it 'returns :framework as product type' do
@pod_target.product_type.should == :framework
end
it 'returns that it requires being built as framework' do
@pod_target.requires_frameworks?.should == true
end
end
describe 'Host does not requires frameworks' do
it 'returns the product name' do
@pod_target.product_name.should == 'libPods-BananaLib.a'
end
it 'returns the framework name' do
@pod_target.framework_name.should == 'BananaLib.framework'
end
it 'returns the library name' do
@pod_target.static_library_name.should == 'libPods-BananaLib.a'
end
it 'returns :static_library as product type' do
@pod_target.product_type.should == :static_library
end
it 'returns that it does not require being built as framework' do
@pod_target.requires_frameworks?.should == false
end
end
end
describe 'With frameworks' do
before do
@pod_target = fixture_pod_target('orange-framework/OrangeFramework.podspec')
end
it 'returns that it uses swift' do
@pod_target.uses_swift?.should == true
end
it 'returns the product module name' do
@pod_target.product_module_name.should == 'OrangeFramework'
end
it 'returns the product name' do
@pod_target.product_name.should == 'OrangeFramework.framework'
end
it 'returns the framework name' do
@pod_target.framework_name.should == 'OrangeFramework.framework'
end
it 'returns the library name' do
@pod_target.static_library_name.should == 'libPods-OrangeFramework.a'
end
it 'returns :framework as product type' do
@pod_target.product_type.should == :framework
end
it 'returns that it requires being built as framework' do
@pod_target.requires_frameworks?.should == true
end
end
end end
end end
......
...@@ -3,5 +3,20 @@ require File.expand_path('../../spec_helper', __FILE__) ...@@ -3,5 +3,20 @@ require File.expand_path('../../spec_helper', __FILE__)
module Pod module Pod
describe Target do describe Target do
describe '#c99ext_identifier' do
before do
@target = Target.new
end
it 'should mask, but keep leading numbers' do
@target.send(:c99ext_identifier, '123BananaLib').should == '_123BananaLib'
end
it 'should mask invalid chars' do
@target.send(:c99ext_identifier, 'iOS-App BânánàLïb').should == 'iOS_App_B_n_n_L_b'
end
end
end end
end end
...@@ -295,18 +295,37 @@ module Pod ...@@ -295,18 +295,37 @@ module Pod
s.ios.deployment_target = '7.0' s.ios.deployment_target = '7.0'
end end
validator.spec.stubs(:subspecs).returns([subspec]) validator.spec.stubs(:subspecs).returns([subspec])
validator.expects(:podfile_from_spec).with(:osx, nil).once validator.expects(:podfile_from_spec).with(:osx, nil, nil).once
validator.expects(:podfile_from_spec).with(:ios, nil).once validator.expects(:podfile_from_spec).with(:ios, nil, nil).once
validator.expects(:podfile_from_spec).with(:ios, '7.0').once validator.expects(:podfile_from_spec).with(:ios, '7.0', nil).once
podfile = validator.send(:perform_extensive_analysis, validator.spec) podfile = validator.send(:perform_extensive_analysis, validator.spec)
end end
it 'respects the local option' do describe '#podfile_from_spec' do
sut = Validator.new(podspec_path, SourcesManager.master.map(&:url)) before do
sut.stubs(:validate_url) @sut = Validator.new(podspec_path, SourcesManager.master.map(&:url))
podfile = sut.send(:podfile_from_spec, :ios, '5.0') @sut.stubs(:validate_url)
deployment_target = podfile.target_definitions['Pods'].platform.deployment_target end
deployment_target.to_s.should == '5.0'
it 'configures the deployment target' do
podfile = @sut.send(:podfile_from_spec, :ios, '5.0')
target_definition = podfile.target_definitions['Pods']
platform = target_definition.platform
platform.symbolic_name.should == :ios
platform.deployment_target.to_s.should == '5.0'
end
it 'includes the use_frameworks! directive' do
podfile = @sut.send(:podfile_from_spec, :ios, '5.0', true)
target_definition = podfile.target_definitions['Pods']
target_definition.uses_frameworks?.should == true
end
it 'includes the use_frameworks!(false) directive' do
podfile = @sut.send(:podfile_from_spec, :ios, '5.0', false)
target_definition = podfile.target_definitions['Pods']
(!!target_definition.uses_frameworks?).should == false
end
end end
it 'repects the source_urls parameter' do it 'repects the source_urls parameter' do
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment