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`
## 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
* 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`
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
......
......@@ -7,7 +7,7 @@ GIT
GIT
remote: https://github.com/CocoaPods/Core.git
revision: c9c91c5296067ebf5af122ed87580e9f0479b47c
revision: a01ba8e384324922bd173a415330b0a95fb13c5d
branch: master
specs:
cocoapods-core (0.35.0)
......
![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)
[![Gem Version](http://img.shields.io/gem/v/cocoapods.svg?style=flat)](http://badge.fury.io/rb/cocoapods)
......
......@@ -225,8 +225,12 @@ begin
desc "Build all examples"
task :build do
Dir.chdir("examples/AFNetworking Example") do
puts "Installing Pods"
Bundler.require 'xcodeproj', :development
Dir['examples/*'].each do |dir|
Dir.chdir(dir) do
puts "Example: #{dir}"
puts " Installing Pods"
# pod_command = ENV['FROM_GEM'] ? 'sandbox-pod' : 'bundle exec ../../bin/sandbox-pod'
# TODO: The sandbox is blocking local git repos making bundler crash
pod_command = ENV['FROM_GEM'] ? 'sandbox-pod' : 'bundle exec ../../bin/pod'
......@@ -234,17 +238,26 @@ begin
execute_command "rm -rf Pods"
execute_command "#{pod_command} install --verbose --no-repo-update"
puts "Building example: AFNetworking Mac Example"
execute_command "xcodebuild -workspace 'AFNetworking Examples.xcworkspace' -scheme 'AFNetworking Example' clean build"
workspace_path = 'Examples.xcworkspace'
workspace = Xcodeproj::Workspace.new_from_xcworkspace(workspace_path)
workspace.schemes.each do |scheme_name, project_path|
next if scheme_name == 'Pods'
puts " Building scheme: #{scheme_name}"
project = Xcodeproj::Project.open(project_path)
target = project.targets.first
puts "Building example: AFNetworking iOS Example"
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.
if major_version > 5
execute_command "xcodebuild -workspace 'AFNetworking Examples.xcworkspace' -scheme 'AFNetworking iOS Example' clean build ONLY_ACTIVE_ARCH=NO -destination 'platform=iOS Simulator,name=iPhone 6'"
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)'"
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
......
......@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
s.homepage = "https://github.com/CocoaPods/CocoaPods"
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" \
"You specify the dependencies for your project in one easy text file. " \
"CocoaPods resolves dependencies between libraries, fetches source " \
......
......@@ -7,7 +7,7 @@
objects = {
/* 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 */; };
F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8129BFF1591061B009BFE23 /* Cocoa.framework */; };
F8129C321591073C009BFE23 /* AFAppDotNetAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F8129C251591073C009BFE23 /* AFAppDotNetAPIClient.m */; };
......@@ -20,7 +20,7 @@
/* 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>"; };
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>"; };
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; };
......@@ -47,7 +47,7 @@
buildActionMask = 2147483647;
files = (
F8129C001591061B009BFE23 /* Cocoa.framework in Frameworks */,
26E82B9975F54DA9BAC1E0C9 /* libPods-AFNetworking Example.a in Frameworks */,
1A2265312C5CBC89CD477BF8 /* libPods-AFNetworking Example.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -90,7 +90,7 @@
children = (
F8129BFF1591061B009BFE23 /* Cocoa.framework */,
F8129C011591061B009BFE23 /* Other Frameworks */,
6935B1E417A24F0E958977ED /* libPods-AFNetworking Example.a */,
53BDE93051BED183E9F4D921 /* libPods-AFNetworking Example.a */,
);
name = Frameworks;
sourceTree = "<group>";
......@@ -169,7 +169,7 @@
F8129BF21591061B009BFE23 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0460;
LastUpgradeCheck = 0610;
};
buildConfigurationList = F8129BF51591061B009BFE23 /* Build configuration list for PBXProject "AFNetworking Mac Example" */;
compatibilityVersion = "Xcode 3.2";
......@@ -253,13 +253,16 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
......@@ -272,7 +275,9 @@
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
......@@ -284,20 +289,25 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
......@@ -313,6 +323,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Prefix.pch;
INFOPLIST_FILE = "Mac-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
......@@ -327,6 +338,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Prefix.pch;
INFOPLIST_FILE = "Mac-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0500"
LastUpgradeVersion = "0610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......
......@@ -8,7 +8,7 @@
/* Begin PBXBuildFile section */
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 */; };
F818101615E6A0C600EF93C2 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50ABD6EC159FC2CE001BE42C /* MobileCoreServices.framework */; };
F88812F016C533D6003C8B8C /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8E469E013957DF100DB05C8 /* Security.framework */; };
......@@ -35,11 +35,9 @@
/* Begin PBXFileReference section */
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; };
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>"; };
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>"; };
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; };
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; };
......@@ -83,7 +81,7 @@
F8E469DF13957DD500DB05C8 /* CoreLocation.framework in Frameworks */,
F8D0701B14310F4A00653FD3 /* SystemConfiguration.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;
};
......@@ -93,8 +91,6 @@
9CE17E7C344B0E75BC0723EA /* Pods */ = {
isa = PBXGroup;
children = (
C3B414D5F7FCC5379A284B4E /* Pods-AFNetworking iOS Example.debug.xcconfig */,
5628C4E1DE6DDC806B14CF8D /* Pods-AFNetworking iOS Example.release.xcconfig */,
7669B6F3267891B0317A0167 /* Pods-AFNetworking iOS Example.debug.xcconfig */,
BEA3D3BDD12996573240F05F /* Pods-AFNetworking iOS Example.release.xcconfig */,
);
......@@ -169,7 +165,7 @@
F8E469641395739D00DB05C8 /* UIKit.framework */,
F8E469661395739D00DB05C8 /* Foundation.framework */,
F8E469681395739D00DB05C8 /* CoreGraphics.framework */,
9D87F8FEDE4A4313B0D579A3 /* libPods-AFNetworking iOS Example.a */,
DB1F7D464FF374F6931E274F /* libPods-AFNetworking iOS Example.a */,
);
name = Frameworks;
sourceTree = "<group>";
......@@ -383,7 +379,7 @@
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNUSED_PARAMETER = NO;
INFOPLIST_FILE = "iOS-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
PRODUCT_NAME = "$(TARGET_NAME)";
WARNING_CFLAGS = "";
WRAPPER_EXTENSION = app;
......@@ -404,7 +400,7 @@
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNUSED_PARAMETER = NO;
INFOPLIST_FILE = "iOS-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 5.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
PRODUCT_NAME = "$(TARGET_NAME)";
VALIDATE_PRODUCT = YES;
WARNING_CFLAGS = "";
......
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0500"
LastUpgradeVersion = "0610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......
......@@ -25,6 +25,7 @@
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import "GlobalTimelineViewController.h"
#import <AFNetworking/AFNetworkActivityIndicatorManager.h>
#import "AFNetworkActivityIndicatorManager.h"
@implementation AppDelegate
......
......@@ -2,7 +2,7 @@
xcodeproj "AFNetworking Mac Example.xcodeproj"
####
workspace 'AFNetworking Examples.xcworkspace'
workspace 'Examples.xcworkspace'
target "AFNetworking Example" do
platform :osx, '10.8'
......@@ -11,7 +11,7 @@ target "AFNetworking Example" do
end
target "AFNetworking iOS Example" do
platform :ios, '7.0'
platform :ios, '8.0'
xcodeproj "AFNetworking iOS Example.xcodeproj"
pod "AFNetworking", "1.3.3"
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
autoload :BridgeSupport, 'cocoapods/generator/bridge_support'
autoload :CopyResourcesScript, 'cocoapods/generator/copy_resources_script'
autoload :DummySource, 'cocoapods/generator/dummy_source'
autoload :EmbedFrameworksScript, 'cocoapods/generator/embed_frameworks_script'
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 :TargetEnvironmentHeader, 'cocoapods/generator/target_environment_header'
autoload :UmbrellaHeader, 'cocoapods/generator/umbrella_header'
autoload :XCConfig, 'cocoapods/generator/xcconfig'
end
......
......@@ -28,7 +28,7 @@ module Pod
self.abstract_command = true
self.command = 'pod'
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)
[Install, Update, Outdated, IPC::Podfile, IPC::Repl].each { |c| c.send(:include, ProjectDirectory) }
......
......@@ -113,6 +113,7 @@ module Pod
['--subspec=NAME', 'Lint validates only the given subspec'],
['--no-subspecs', 'Lint skips validation of subspecs'],
['--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 ' \
'(defaults to https://github.com/CocoaPods/Specs.git). '\
'Multiple sources must be comma-delimited.']].concat(super)
......@@ -124,6 +125,7 @@ module Pod
@clean = argv.flag?('clean', true)
@subspecs = argv.flag?('subspecs', true)
@only_subspec = argv.option('subspec')
@use_frameworks = argv.flag?('use-frameworks')
@source_urls = argv.option('sources', 'https://github.com/CocoaPods/Specs.git').split(',')
@podspecs_paths = argv.arguments!
super
......@@ -144,6 +146,7 @@ module Pod
validator.allow_warnings = @allow_warnings
validator.no_subspecs = !@subspecs || @only_subspec
validator.only_subspec = @only_subspec
validator.use_frameworks = @use_frameworks
validator.validate
unless @clean
......
......@@ -68,6 +68,7 @@ module Pod
['--subspec=NAME', 'Lint validates only the given subspec'],
['--no-subspecs', 'Lint skips validation of subspecs'],
['--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 ' \
'(defaults to https://github.com/CocoaPods/Specs.git). '\
'Multiple sources must be comma-delimited.']].concat(super)
......@@ -79,6 +80,7 @@ module Pod
@clean = argv.flag?('clean', true)
@subspecs = argv.flag?('subspecs', true)
@only_subspec = argv.option('subspec')
@use_frameworks = argv.flag?('use-frameworks')
@source_urls = argv.option('sources', 'https://github.com/CocoaPods/Specs.git').split(',')
@podspecs_paths = argv.arguments!
super
......@@ -94,6 +96,7 @@ module Pod
validator.allow_warnings = @allow_warnings
validator.no_subspecs = !@subspecs || @only_subspec
validator.only_subspec = @only_subspec
validator.use_frameworks = @use_frameworks
validator.validate
invalid_count += 1 unless validator.validated?
......@@ -580,8 +583,8 @@ Pod::Spec.new do |s|
# ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― #
#
# 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
# files it will include any header in the folder.
# giving a folder will include any swift, h, m, mm, c & cpp files.
# For header files it will include any header in the folder.
# Not including the public_header_files will make all headers public.
#
......
......@@ -264,7 +264,7 @@ module Pod
'CocoaPods.podfile.yaml',
'CocoaPods.podfile',
'Podfile',
]
].freeze
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
# @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
# @see platform
......@@ -21,6 +25,7 @@ module Pod
def initialize(platform)
@platform = platform
@imports = []
@module_imports = []
end
# Generates the contents of the header according to the platform.
......@@ -41,6 +46,13 @@ module Pod
result << %|#import "#{import}"\n|
end
unless module_imports.empty?
module_imports.each do |import|
result << %|\n@import #{import}|
end
result << "\n"
end
result
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
# @return [Xcodeproj::Config]
#
def generate
header_search_path_flags = target.sandbox.public_headers.search_paths(target.platform)
@xcconfig = Xcodeproj::Config.new(
pod_targets = target.pod_targets_for_build_configuration(@configuration_name)
config = {
'OTHER_LDFLAGS' => XCConfigHelper.default_ld_flags(target),
'OTHER_LIBTOOLFLAGS' => '$(OTHER_LDFLAGS)',
'HEADER_SEARCH_PATHS' => XCConfigHelper.quote(header_search_path_flags),
'PODS_ROOT' => target.relative_pods_root,
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1',
'OTHER_CFLAGS' => '$(inherited) ' + XCConfigHelper.quote(header_search_path_flags, '-isystem')
)
}
target.pod_targets.each do |pod_target|
next unless pod_target.include_in_build_config?(@configuration_name)
if target.requires_frameworks?
# 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
@xcconfig = Xcodeproj::Config.new(config)
XCConfigHelper.add_target_specific_settings(target, @xcconfig)
pod_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)
pod_targets.each do |pod_target|
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
file_accessor.vendored_libraries.each do |vendored_library|
XCConfigHelper.add_library_build_settings(vendored_library, @xcconfig, target.sandbox.root)
# 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
# Add pod static lib to list of libraries that are to be linked with
# the user’s project.
next unless pod_target.should_build?
@xcconfig.merge!('OTHER_LDFLAGS' => %(-l "#{pod_target.name}"))
end
# TODO Need to decide how we are going to ensure settings like these
......@@ -83,9 +107,30 @@ module Pod
# See https://github.com/CocoaPods/CocoaPods/issues/1216
@xcconfig.attributes.delete('USE_HEADERMAP')
generate_ld_runpath_search_paths if target.requires_frameworks?
@xcconfig
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
......
......@@ -57,8 +57,20 @@ module Pod
# '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 = Xcodeproj::Config.new(xcconfig_hash)
XCConfigHelper.add_target_specific_settings(target, @xcconfig)
@xcconfig.includes = [target.name]
@xcconfig
end
......
......@@ -40,15 +40,7 @@ module Pod
#
def generate
@xcconfig = Xcodeproj::Config.new
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
XCConfigHelper.add_settings_for_file_accessors_of_target(target, @xcconfig)
@xcconfig
end
......
......@@ -33,6 +33,26 @@ module Pod
ld_flags
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
# given Specification.
#
......@@ -59,6 +79,9 @@ module Pod
# @param [Xcodeproj::Config] xcconfig
# 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)
name = File.basename(framework_path, '.framework')
dirname = '$(PODS_ROOT)/' + framework_path.dirname.relative_path_from(sandbox_root).to_s
......@@ -70,7 +93,7 @@ module Pod
end
# Configures the given Xcconfig with the the build settings for the given
# framework path.
# library path.
#
# @param [Pathanme] framework_path
# The path of the framework.
......@@ -78,6 +101,9 @@ module Pod
# @param [Xcodeproj::Config] xcconfig
# 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)
name = File.basename(library_path, '.a').sub(/\Alib/, '')
dirname = '$(PODS_ROOT)/' + library_path.dirname.relative_path_from(sandbox_root).to_s
......@@ -88,6 +114,58 @@ module Pod
xcconfig.merge!(build_settings)
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
# if needed. This is done because the `SenTestingKit` requires them and
# adding them to each specification which requires it is repetitive and
......
......@@ -89,6 +89,7 @@ module Pod
prepare
resolve_dependencies
download_dependencies
determine_dependency_product_types
generate_pods_project
integrate_user_project if config.integrate_targets?
perform_post_install_actions
......@@ -311,6 +312,21 @@ module Pod
@pod_installers.each(&:clean!)
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
#
# @return [void]
......@@ -444,7 +460,13 @@ module Pod
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
frameworks_group = pods_project.frameworks_group
aggregate_targets.each do |aggregate_target|
aggregate_target.pod_targets.each do |pod_target|
unless pod_target.should_build?
......@@ -464,7 +486,15 @@ module Pod
unless pod_dependency_target
puts "[BUG] DEP: #{dep}"
end
next unless pod_dependency_target.should_build?
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
......
......@@ -180,8 +180,25 @@ module Pod
def generate_targets
targets = []
result.specs_by_target.each do |target_definition, specs|
targets << generate_target(target_definition, specs)
end
targets
end
# Setup the aggregate target for a single user target
#
# @param [TargetDefinition] target_definition
# the target definition for the user target.
#
# @param [Array<Specification>] specs
# the specifications that need to be installed grouped by the
# given target definition.
#
# @return [AggregateTarget]
#
def generate_target(target_definition, specs)
target = AggregateTarget.new(target_definition, sandbox)
targets << target
target.host_requires_frameworks |= target_definition.uses_frameworks?
if config.integrate_targets?
project_path = compute_user_project_path(target_definition)
......@@ -202,25 +219,53 @@ module Pod
end
end
grouped_specs = specs.map do |spec|
specs.select { |s| s.root == spec.root }
end.uniq
target.pod_targets = generate_pod_targets(target, specs)
target
end
# Setup the pod targets for an aggregate target. Group specs and subspecs
# by their root to create a {PodTarget} for each spec.
#
# @param [AggregateTarget] target
# the aggregate target
#
# @param [Array<Specification>] specs
# the specifications that need to be installed.
#
# @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)
grouped_specs.each do |pod_specs|
pod_target = PodTarget.new(pod_specs, target_definition, sandbox)
if config.integrate_targets?
pod_target.user_build_configurations = target.user_build_configurations
pod_target.archs = @archs_by_target_def[target_definition]
pod_target.archs = @archs_by_target_def[target.target_definition]
else
pod_target.user_build_configurations = {}
if target_definition.platform.name == :osx
if target.platform.name == :osx
pod_target.archs = '$(ARCHS_STANDARD_64_BIT)'
end
end
target.pod_targets << pod_target
end
end
targets
pod_target
end
# Generates dependencies that require the specific version of the Pods
......@@ -515,6 +560,37 @@ module Pod
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
# configurations where each key corresponds to the name of a
# configuration and its value to its type (`:debug` or `:release`).
......
......@@ -36,25 +36,47 @@ module Pod
# @return [void]
#
def add_target
product_type = target.product_type
name = target.label
platform = target.platform.name
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|
configuration = @native_target.add_build_configuration(bc_name, type)
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
settings['ARCHS'] = target.archs
end
@native_target.build_configurations.each do |configuration|
configuration.build_settings.merge!(settings)
if target.requires_frameworks?
settings['PRODUCT_NAME'] = target.product_module_name
else
settings.merge!('OTHER_LDFLAGS' => '', 'OTHER_LIBTOOLFLAGS' => '')
end
target.native_target = @native_target
settings
end
# Creates the directory where to store the support files of the target.
......@@ -63,6 +85,68 @@ module Pod
target.support_files_dir.mkdir
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
# only categories build.
#
......
......@@ -12,8 +12,14 @@ module Pod
UI.message "- Installing target `#{target.name}` #{target.platform}" do
add_target
create_support_files_dir
create_suport_files_group
create_support_files_group
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_bridge_support_file
create_copy_resources_script
......@@ -26,12 +32,25 @@ module Pod
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
# generated by this installer.
#
# @return [void]
#
def create_suport_files_group
def create_support_files_group
parent = project.support_files_group
name = target.name
dir = target.support_files_dir
......@@ -92,9 +111,12 @@ module Pod
#
def create_copy_resources_script
path = target.copy_resources_script_path
file_accessors = target.pod_targets.map(&:file_accessors).flatten
resource_paths = file_accessors.map { |accessor| accessor.resources.flatten.map { |res| res.relative_path_from(project.path.dirname) } }.flatten
resource_bundles = file_accessors.map { |accessor| accessor.resource_bundles.keys.map { |name| "${BUILT_PRODUCTS_DIR}/#{name}.bundle" } }.flatten
library_targets = target.pod_targets.reject do |pod_target|
pod_target.should_build? && pod_target.requires_frameworks?
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.concat(resource_paths)
resources.concat(resource_bundles)
......@@ -104,6 +126,28 @@ module Pod
add_file_to_support_group(path)
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.
#
# @return [void]
......
......@@ -17,9 +17,16 @@ module Pod
UI.message "- Installing target `#{target.name}` #{target.platform}" do
add_target
create_support_files_dir
add_files_to_build_phases
add_resources_bundle_targets
add_files_to_build_phases
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_dummy_source
end
......@@ -41,20 +48,41 @@ module Pod
target.file_accessors.each do |file_accessor|
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' }
{
true => file_accessor.arc_source_files,
false => file_accessor.non_arc_source_files,
}.each do |arc, files|
files = files - other_source_files
files = files - headers - other_source_files
flags = compiler_flags_for_consumer(consumer, arc)
regular_file_refs = files.map { |sf| project.reference_for_path(sf) }
native_target.add_file_references(regular_file_refs, flags)
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) }
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
......@@ -68,23 +96,34 @@ module Pod
def add_resources_bundle_targets
target.file_accessors.each do |file_accessor|
file_accessor.resource_bundles.each do |bundle_name, paths|
# Add a dependency on an existing Resource Bundle target if possible
bundle_target = project.targets.find { |target| target.name == bundle_name }
unless bundle_target
file_references = paths.map { |sf| project.reference_for_path(sf) }
bundle_target = project.new_resources_bundle(bundle_name, file_accessor.spec_consumer.platform_name)
label = target.resources_bundle_target_label(bundle_name)
bundle_target = project.new_resources_bundle(label, file_accessor.spec_consumer.platform_name)
bundle_target.product_reference.tap do |bundle_product|
bundle_file_name = "#{bundle_name}.bundle"
bundle_product.name = bundle_file_name
bundle_product.path = bundle_file_name
end
bundle_target.add_resources(file_references)
target.user_build_configurations.each do |bc_name, type|
bundle_target.add_build_configuration(bc_name, type)
end
end
target.resource_bundle_targets << bundle_target
if target.should_build?
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
......
......@@ -132,7 +132,8 @@ module Pod
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
# overridden by the build configuration of a target and prints a
......@@ -144,7 +145,7 @@ module Pod
user_target.build_configurations.each do |config|
xcconfig = aggregate_target.xcconfigs[config.name]
if xcconfig
xcconfig.to_hash.keys.each do |key|
(xcconfig.to_hash.keys - IGNORED_KEYS).each do |key|
target_values = config.build_settings[key]
if target_values &&
!INHERITED_FLAGS.any? { |flag| target_values.include?(flag) }
......
......@@ -31,8 +31,10 @@ module Pod
project_is_dirty = [
XCConfigIntegrator.integrate(target, native_targets),
update_to_cocoapods_0_34,
remove_embed_frameworks_script_phases,
unless native_targets_to_integrate.empty?
add_pods_library
add_embed_frameworks_script_phase if target.requires_frameworks?
add_copy_resources_script_phase
add_check_manifest_lock_script_phase
true
......@@ -91,9 +93,9 @@ module Pod
changes
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
# 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.
#
# @return [void]
......@@ -101,12 +103,71 @@ module Pod
def add_pods_library
frameworks = user_project.frameworks_group
native_targets_to_integrate.each do |native_target|
library = frameworks.files.select { |f| f.path == target.product_name }.first ||
frameworks.new_product_ref_for_target(target.name, :static_library)
unless native_target.frameworks_build_phase.files_references.include?(library)
native_target.frameworks_build_phase.add_file_reference(library)
build_phase = native_target.frameworks_build_phase
# Find and delete possible reference for the other product type
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
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
result
end
# Adds a shell script build phase responsible to copy the resources
......
......@@ -12,7 +12,7 @@ module Pod
GLOB_PATTERNS = {
:readme => 'readme{*,.*}'.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,
}.freeze
......
......@@ -26,6 +26,10 @@ module Pod
@search_paths = []
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
# xcconfig format. The paths are specified relative to the pods
# root with the `${PODS_ROOT}` variable.
......
......@@ -15,18 +15,65 @@ module Pod
#
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.
#
def name
label
end
# @return [String] the name of the library.
# @return [String] the name of the product.
#
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"
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.
#
def xcconfig_prefix
......@@ -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
# @return [Hash{String=>Symbol}] A hash representing the user build
......@@ -95,6 +156,22 @@ module Pod
support_files_dir + "#{label}-Private.xcconfig"
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
# the information about the installed pods.
#
......@@ -115,12 +192,51 @@ module Pod
support_files_dir + "#{label}.bridgesupport"
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
#
def dummy_source_path
support_files_dir + "#{label}-dummy.m"
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
......@@ -20,6 +20,14 @@ module Pod
target_definition.label.to_s
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
# computing the relative paths. If integrating it should be the
# folder where the user project is stored, otherwise it should
......@@ -71,21 +79,32 @@ module Pod
#
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.
#
def specs
pod_targets.map(&:specs).flatten
end
# @return [Hash{Symbol => Array<PodTarget>}] The pod targets for each
# @return [Hash{Symbol => Array<Specification>}] The pod targets for each
# build configuration.
#
def specs_by_build_configuration
result = {}
user_build_configurations.keys.each do |build_configuration|
result[build_configuration] = pod_targets.select do |pod_target|
pod_target.include_in_build_config?(build_configuration)
end.map(&:specs).flatten
result[build_configuration] = pod_targets_for_build_configuration(build_configuration).
flat_map(&:specs)
end
result
end
......@@ -96,6 +115,16 @@ module Pod
specs.map { |spec| spec.consumer(platform) }
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.
#
# @note The acknowledgements generators add the extension according to
......@@ -111,6 +140,12 @@ module Pod
support_files_dir + "#{label}-resources.sh"
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)`
# variable of the user's project.
#
......@@ -133,6 +168,13 @@ module Pod
"${SRCROOT}/#{relative_to_srcroot(copy_resources_script_path)}"
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
# @!group Private Helpers
......
......@@ -30,6 +30,14 @@ module Pod
"#{target_definition.label}-#{root_spec.name}"
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
# specifications of this target.
#
......@@ -54,6 +62,14 @@ module Pod
specs.map { |spec| spec.consumer(platform) }
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.
#
def root_spec
......@@ -66,6 +82,15 @@ module Pod
root_spec.name
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
# depends.
#
......
......@@ -143,6 +143,10 @@ module Pod
#
attr_accessor :no_subspecs
# @return [Bool] Whether frameworks should be used for the installation.
#
attr_accessor :use_frameworks
#-------------------------------------------------------------------------#
# !@group Lint results
......@@ -299,7 +303,7 @@ module Pod
#
def install_pod
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)
installer = Installer.new(sandbox, podfile)
installer.install!
......@@ -449,19 +453,31 @@ module Pod
#
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
# current platform.
#
# @note The generated podfile takes into account whether the linter is
# 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
podspec = file.realpath
local = local?
urls = source_urls
podfile = Pod::Podfile.new do
urls.each { |u| source(u) }
use_frameworks!(use_frameworks) unless use_frameworks.nil?
platform(platform_name, deployment_target)
if local
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
'install --no-repo-update'
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
#
describe 'Runs the Podfile callbacks' do
......
......@@ -118,6 +118,34 @@ def fixture_file_accessor(name, platform = :ios)
Pod::Sandbox::FileAccessor.new(path_list, spec.consumer(platform))
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('orange-framework')
......@@ -30,7 +30,7 @@ module SpecHelper
# Clones a repo to the given 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)
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
EOS
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
@gen.stubs(:platform).returns(Pod::Platform.ios)
@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,33 +5,30 @@ module Pod
module XCConfig
describe AggregateXCConfig do
def spec
fixture_spec('banana-lib/BananaLib.podspec')
end
before do
@spec = 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]
@spec = spec
@pod_target = fixture_pod_target(@spec)
@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
shared 'AggregateXCConfig' do
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
@podfile = Podfile.new
@target.target_definition.stubs(:podfile).returns(@podfile)
@xcconfig = @generator.generate
end
......@@ -39,7 +36,7 @@ module Pod
@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 'configures the project to load all members that implement Objective-c classes or categories' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-ObjC'
end
......@@ -59,16 +56,6 @@ module Pod
@xcconfig.to_hash['PODS_ROOT'].should == '${SRCROOT}/Pods'
end
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 'adds the COCOAPODS macro definition' do
@xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include 'COCOAPODS=1'
end
......@@ -77,20 +64,12 @@ module Pod
@xcconfig.to_hash['GCC_PREPROCESSOR_DEFINITIONS'].should.include '$(inherited)'
end
it 'links the pod targets with the aggregate integration library target' do
@xcconfig.to_hash['OTHER_LDFLAGS'].should.include '-l"Pods-BananaLib"'
end
it 'does not links the pod targets with the aggregate integration library 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
it 'should configure OTHER_LIBTOOLFLAGS flags to include OTHER_LDFLAGS' do
@xcconfig.to_hash['OTHER_LIBTOOLFLAGS'].should == '$(OTHER_LDFLAGS)'
end
end
#-----------------------------------------------------------------------#
describe "if a pod target does not contain source files" do
......@@ -116,6 +95,67 @@ module Pod
#-----------------------------------------------------------------------#
describe 'with library' do
def spec
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
describe 'with framework' do
def spec
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)
......@@ -127,6 +167,8 @@ module Pod
end
end
end
end
end
end
......@@ -140,7 +140,7 @@ module Pod
hash_config['FRAMEWORK_SEARCH_PATHS'].should == '"$(PODS_ROOT)/Parse"'
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'
xcconfig = Xcodeproj::Config.new('OTHER_LDFLAGS' => '-framework CoreAnimation')
@sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root)
......@@ -148,7 +148,7 @@ module Pod
hash_config['OTHER_LDFLAGS'].should == '-framework "CoreAnimation" -framework "Parse"'
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'
xcconfig = Xcodeproj::Config.new('FRAMEWORK_SEARCH_PATHS' => '"path/to/frameworks"')
@sut.add_framework_build_settings(framework_path, xcconfig, config.sandbox.root)
......@@ -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
xcconfig = Xcodeproj::Config.new('OTHER_LDFLAGS' => '-framework SenTestingKit')
@sut.add_developers_frameworks_if_needed(xcconfig, :ios)
......
......@@ -64,19 +64,6 @@ module Pod
@project.targets.first.name.should == @target_definition.label
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
@installer.install!
target = @project.targets.first
......@@ -147,6 +134,14 @@ module Pod
script.read.should.include?('logo-sidebar.png')
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
end
......@@ -155,6 +150,25 @@ module Pod
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
@installer.install!
support_files_dir = config.sandbox.target_support_files_dir('Pods')
......
......@@ -21,6 +21,9 @@ module Pod
file_accessor.source_files.each do |file|
@project.add_file_reference(file, group)
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.stubs(:platform).returns(Platform.new(:ios, '6.0'))
......@@ -51,12 +54,6 @@ module Pod
@project.targets.first.name.should == 'Pods-BananaLib'
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
@installer.install!
target = @project.targets.first
......@@ -106,7 +103,19 @@ module Pod
it 'adds the resource bundle targets' do
@pod_target.file_accessors.first.stubs(:resource_bundles).returns('banana_bundle' => [])
@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
xit 'adds the build configurations to the resources bundle targets' do
......
......@@ -23,9 +23,7 @@ module Pod
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) COCOAPODS=1'
)
@pod_bundle.xcconfigs['Debug'] = configuration
@pod_bundle.xcconfigs['Test'] = configuration
@pod_bundle.xcconfigs['Release'] = configuration
@pod_bundle.xcconfigs['App Store'] = configuration
@target_integrator = TargetIntegrator.new(@pod_bundle)
end
......@@ -102,6 +100,31 @@ module Pod
@target_integrator.send(:user_project).expects(:save).never
@target_integrator.integrate!
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
describe 'Private helpers' do
......
......@@ -55,9 +55,9 @@ module Pod
UI.warnings.should.include?('The Podfile does not contain any dependencies')
end
it 'check that the integrated target does not override the CocoaPods build settings' do
UI.warnings = ''
target_config = stub(:name => 'Release', :build_settings => { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1'] })
describe '#warn_about_xcconfig_overrides' do
shared 'warn_about_xcconfig_overrides' do
target_config = stub(:name => 'Release', :build_settings => @user_target_build_settings)
user_target = stub(:name => 'SampleProject', :build_configurations => [target_config])
@target.stubs(:user_targets).returns([user_target])
......@@ -66,38 +66,34 @@ module Pod
@integrator.unstub(:warn_about_xcconfig_overrides)
@integrator.send(:warn_about_xcconfig_overrides)
end
it 'check that the integrated target does not override the CocoaPods build settings' do
@user_target_build_settings = { 'GCC_PREPROCESSOR_DEFINITIONS' => ['FLAG=1'] }
behaves_like 'warn_about_xcconfig_overrides'
UI.warnings.should.include 'The `SampleProject [Release]` target ' \
'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)
@integrator.send(:warn_about_xcconfig_overrides)
@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
it 'allows build settings which inherit the settings form the CocoaPods xcconfig' 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)
@integrator.send(:warn_about_xcconfig_overrides)
@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
it "ignores certain build settings which don't inherit the settings form the CocoaPods xcconfig" do
@user_target_build_settings = { 'CODE_SIGN_IDENTITY' => "Mac Developer" }
behaves_like 'warn_about_xcconfig_overrides'
UI.warnings.should.not.include 'CODE_SIGN_IDENTITY'
end
end
end
#-----------------------------------------------------------------------#
......
......@@ -50,6 +50,7 @@ module Pod
before do
@installer.stubs(:resolve_dependencies)
@installer.stubs(:download_dependencies)
@installer.stubs(:determine_dependency_product_types)
@installer.stubs(:generate_pods_project)
@installer.stubs(:integrate_user_project)
@installer.stubs(:run_plugins_post_install_hooks)
......@@ -75,6 +76,7 @@ module Pod
@installer.stubs(:run_pre_install_hooks)
@installer.stubs(:install_file_references)
@installer.stubs(:install_libraries)
@installer.stubs(:set_target_dependencies)
@installer.stubs(:write_lockfiles)
@installer.stubs(:aggregate_targets).returns([])
@installer.unstub(:generate_pods_project)
......@@ -116,6 +118,37 @@ module Pod
#-------------------------------------------------------------------------#
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
#-------------------------------------------------------------------------#
describe 'Dependencies Resolution' do
describe '#analyze' do
......@@ -417,9 +450,12 @@ module Pod
pod_target.stubs(:should_build? => false)
target = AggregateTarget.new(target_definition, config.sandbox)
mock_target = mock
mock_target = mock('PodNativeTarget')
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(:pod_targets).returns([pod_target])
@installer.stubs(:aggregate_targets).returns([target])
......
......@@ -215,7 +215,7 @@ module Pod
file_patterns = ['Classes/*.{h,m,d}', 'Vendor']
options = {
: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,
}
@spec.exclude_files = options[:exclude_patterns]
......
......@@ -51,6 +51,10 @@ module Pod
@target.copy_resources_script_path.to_s.should.include?('Pods/Target Support Files/Pods/Pods-resources.sh')
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
@target.target_environment_header_path.to_s.should.include?('Pods/Target Support Files/Pods/Pods-environment.h')
end
......@@ -71,9 +75,17 @@ module Pod
@target.copy_resources_script_relative_path.should == '${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh'
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
@target.xcconfig_relative_path('Release').should == 'Pods/Target Support Files/Pods/Pods.release.xcconfig'
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
describe 'Pod targets' do
......@@ -86,20 +98,29 @@ module Pod
@target.pod_targets = [@pod_target]
end
it 'returns pod targets by build configuration' do
pod_target_release = PodTarget.new([@spec], @target_definition, config.sandbox)
pod_target_release.expects(:include_in_build_config?).with('Debug').returns(false)
pod_target_release.expects(:include_in_build_config?).with('Release').returns(true)
@target.pod_targets = [@pod_target, pod_target_release]
describe 'with configuration dependent pod targets' do
before do
@pod_target_release = PodTarget.new([@spec], @target_definition, config.sandbox)
@pod_target_release.expects(:include_in_build_config?).with('Debug').returns(false)
@pod_target_release.expects(:include_in_build_config?).with('Release').returns(true)
@target.pod_targets = [@pod_target, @pod_target_release]
@target.user_build_configurations = {
'Debug' => :debug,
'Release' => :release,
}
expected = {
end
it 'returns pod targets for given build configuration' do
@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),
'Release' => (@pod_target.specs + @pod_target_release.specs),
}
@target.specs_by_build_configuration.should == expected
end
end
it 'returns the specs of the Pods used by this aggregate target' do
......@@ -115,5 +136,103 @@ module Pod
consumer_reps.should == [['BananaLib', :ios]]
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
......@@ -34,6 +34,10 @@ module Pod
@pod_target.pod_name.should == 'BananaLib'
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
@pod_target.dependencies.should == ['monkey']
end
......@@ -92,6 +96,12 @@ module Pod
)
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
@pod_target.xcconfig_path.to_s.should.include?(
'Pods/Target Support Files/Pods-BananaLib/Pods-BananaLib.xcconfig'
......@@ -100,6 +110,104 @@ module Pod
'Pods/Target Support Files/Pods-BananaLib/Pods-BananaLib-Private.xcconfig'
)
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
......
......@@ -3,5 +3,20 @@ require File.expand_path('../../spec_helper', __FILE__)
module Pod
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
......@@ -295,18 +295,37 @@ module Pod
s.ios.deployment_target = '7.0'
end
validator.spec.stubs(:subspecs).returns([subspec])
validator.expects(:podfile_from_spec).with(:osx, nil).once
validator.expects(:podfile_from_spec).with(:ios, nil).once
validator.expects(:podfile_from_spec).with(:ios, '7.0').once
validator.expects(:podfile_from_spec).with(:osx, nil, nil).once
validator.expects(:podfile_from_spec).with(:ios, nil, nil).once
validator.expects(:podfile_from_spec).with(:ios, '7.0', nil).once
podfile = validator.send(:perform_extensive_analysis, validator.spec)
end
it 'respects the local option' do
sut = Validator.new(podspec_path, SourcesManager.master.map(&:url))
sut.stubs(:validate_url)
podfile = sut.send(:podfile_from_spec, :ios, '5.0')
deployment_target = podfile.target_definitions['Pods'].platform.deployment_target
deployment_target.to_s.should == '5.0'
describe '#podfile_from_spec' do
before do
@sut = Validator.new(podspec_path, SourcesManager.master.map(&:url))
@sut.stubs(:validate_url)
end
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
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