Commit 3cf03bbd authored by Samuel Giddins's avatar Samuel Giddins Committed by GitHub

Merge pull request #5502 from benasher44/basher_improved_extension_handling

Improve handling of app and watch os 1 extensions
parents 70487ea7 fe6f6521
...@@ -34,10 +34,18 @@ To install release candidates run `[sudo] gem install cocoapods --pre` ...@@ -34,10 +34,18 @@ To install release candidates run `[sudo] gem install cocoapods --pre`
##### Bug Fixes ##### Bug Fixes
* Improve handling of app extensions, watch os 1 extensions
and framework targets
[benasher44](https://github.com/benasher44)
[#4203](https://github.com/CocoaPods/CocoaPods/issues/4203)
* Fix local pod platform conflict error message. * Fix local pod platform conflict error message.
[Muhammed Yavuz Nuzumlalı](https://github.com/manuyavuz) [Muhammed Yavuz Nuzumlalı](https://github.com/manuyavuz)
[#5052](https://github.com/CocoaPods/CocoaPods/issues/5052) [#5052](https://github.com/CocoaPods/CocoaPods/issues/5052)
##### Bug Fixes
* Fix installing pods with `use_frameworks` when deduplication is disabled. * Fix installing pods with `use_frameworks` when deduplication is disabled.
[Samuel Giddins](https://github.com/segiddins) [Samuel Giddins](https://github.com/segiddins)
[#5481](https://github.com/CocoaPods/CocoaPods/issues/5481) [#5481](https://github.com/CocoaPods/CocoaPods/issues/5481)
......
...@@ -24,7 +24,7 @@ GIT ...@@ -24,7 +24,7 @@ GIT
GIT GIT
remote: https://github.com/CocoaPods/Xcodeproj.git remote: https://github.com/CocoaPods/Xcodeproj.git
revision: 1b15f4c79303843e8f370afa9dd74b8ad0329785 revision: 796dec1d5abcdfc02ca1b5b8748d91c3f3e943b0
branch: master branch: master
specs: specs:
xcodeproj (1.1.0) xcodeproj (1.1.0)
...@@ -86,7 +86,7 @@ GIT ...@@ -86,7 +86,7 @@ GIT
GIT GIT
remote: https://github.com/segiddins/json.git remote: https://github.com/segiddins/json.git
revision: d4011cabfaa80e9b0da81947b5e9b38dd47359db revision: a9588bc4334c2f5bf985f255b61c05eafdcd8907
branch: seg-1.7.7-ruby-2.2 branch: seg-1.7.7-ruby-2.2
specs: specs:
json (1.7.7) json (1.7.7)
......
...@@ -4,7 +4,7 @@ module Pod ...@@ -4,7 +4,7 @@ module Pod
# Generates the xcconfigs for the aggregate targets. # Generates the xcconfigs for the aggregate targets.
# #
class AggregateXCConfig class AggregateXCConfig
# @return [Target] the target represented by this xcconfig. # @return [AggregateTarget] the target represented by this xcconfig.
# #
attr_reader :target attr_reader :target
...@@ -61,7 +61,14 @@ module Pod ...@@ -61,7 +61,14 @@ module Pod
'FRAMEWORK_SEARCH_PATHS' => '$(inherited) ', 'FRAMEWORK_SEARCH_PATHS' => '$(inherited) ',
'LIBRARY_SEARCH_PATHS' => '$(inherited) ', 'LIBRARY_SEARCH_PATHS' => '$(inherited) ',
} }
if pod_targets.any?(&:uses_swift?) # For embedded targets, which live in a host target, CocoaPods
# copies all of the embedded target's pod_targets its host
# target. Therefore, this check will properly require the Swift
# libs in the host target, if the embedded target has any pod targets
# that use Swift. Setting this for the embedded target would
# cause an App Store rejection because frameworks cannot be embedded
# in embedded targets.
if !target.requires_host_target? && pod_targets.any?(&:uses_swift?)
config['EMBEDDED_CONTENT_CONTAINS_SWIFT'] = 'YES' config['EMBEDDED_CONTENT_CONTAINS_SWIFT'] = 'YES'
end end
@xcconfig = Xcodeproj::Config.new(config) @xcconfig = Xcodeproj::Config.new(config)
......
...@@ -226,6 +226,61 @@ module Pod ...@@ -226,6 +226,61 @@ module Pod
private private
# Copies the pod_targets of any of the app embedded aggregate targets into
# their potential host aggregate target, if that potential host aggregate target's
# user_target hosts any of the app embedded aggregate targets' user_targets
#
# @param [AggregateTarget] aggregate_target the aggregate target whose user_target
# might host one or more of the embedded aggregate targets' user_targets
#
# @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
# representing the embedded targets to be integrated
#
def copy_embedded_target_pod_targets_to_host(aggregate_target, embedded_aggregate_targets)
return if aggregate_target.requires_host_target?
# Get the uuids of the aggregate_target's user_targets' embedded targets if any
embedded_uuids = Set.new(aggregate_target.user_targets.map do |target|
aggregate_target.user_project.embedded_targets_in_native_target(target).map(&:uuid)
end.flatten)
return if embedded_uuids.empty?
embedded_aggregate_targets.each do |embedded_target|
next unless embedded_target.user_targets.map(&:uuid).any? do |embedded_uuid|
embedded_uuids.include? embedded_uuid
end
raise Informative, "#{aggregate_target.name} must call use_frameworks! because it is hosting an embedded target that calls use_frameworks!." unless aggregate_target.requires_frameworks?
pod_target_names = aggregate_target.pod_targets.map(&:name)
# This embedded target is hosted by the aggregate target's user_target; copy over the non-duplicate pod_targets
aggregate_target.pod_targets = aggregate_target.pod_targets + embedded_target.pod_targets.select do |pod_target|
!pod_target_names.include? pod_target.name
end
end
end
# Raises an error if there are embedded targets in the Podfile, but
# their host targets have not been declared in the Podfile
#
# @param [Array<AggregateTarget>] aggregate_targets the generated
# aggregate targets
#
# @param [Array<AggregateTarget>] embedded_aggregate_targets the aggregate targets
# representing the embedded targets to be integrated
#
def verify_host_targets_in_podfile(aggregate_targets, embedded_aggregate_targets)
aggregate_target_uuids = Set.new aggregate_targets.map(&:user_targets).flatten.map(&:uuid)
embedded_targets_missing_hosts = []
embedded_aggregate_targets.each do |target|
host_uuids = target.user_targets.map do |user_target|
target.user_project.host_targets_for_embedded_target(user_target).map(&:uuid)
end.flatten
embedded_targets_missing_hosts << target unless host_uuids.any? do |uuid|
aggregate_target_uuids.include? uuid
end
end
unless embedded_targets_missing_hosts.empty?
raise Informative, "Unable to find host target for #{embedded_targets_missing_hosts.map(&:name).join(', ')}. Please add the host targets for the embedded targets to the Podfile."
end
end
# Creates the models that represent the targets generated by CocoaPods. # Creates the models that represent the targets generated by CocoaPods.
# #
# @return [Array<AggregateTarget>] # @return [Array<AggregateTarget>]
...@@ -236,6 +291,14 @@ module Pod ...@@ -236,6 +291,14 @@ module Pod
aggregate_targets = specs_by_target.keys.map do |target_definition| aggregate_targets = specs_by_target.keys.map do |target_definition|
generate_target(target_definition, pod_targets) generate_target(target_definition, pod_targets)
end end
if installation_options.integrate_targets?
# Copy embedded target pods that cannot have their pods embedded as frameworks to their host targets
embedded_targets = aggregate_targets.select(&:requires_host_target?).select(&:requires_frameworks?)
verify_host_targets_in_podfile(aggregate_targets, embedded_targets)
aggregate_targets.each do |target|
copy_embedded_target_pod_targets_to_host(target, embedded_targets)
end
end
aggregate_targets.each do |target| aggregate_targets.each do |target|
target.search_paths_aggregate_targets = aggregate_targets.select do |aggregate_target| target.search_paths_aggregate_targets = aggregate_targets.select do |aggregate_target|
target.target_definition.targets_to_inherit_search_paths.include?(aggregate_target.target_definition) target.target_definition.targets_to_inherit_search_paths.include?(aggregate_target.target_definition)
...@@ -277,6 +340,7 @@ module Pod ...@@ -277,6 +340,7 @@ module Pod
target.pod_targets = pod_targets.select do |pod_target| target.pod_targets = pod_targets.select do |pod_target|
pod_target.target_definitions.include?(target_definition) pod_target.target_definitions.include?(target_definition)
end end
target target
end end
......
...@@ -20,7 +20,10 @@ module Pod ...@@ -20,7 +20,10 @@ module Pod
# @return [Array<Symbol>] the symbol types, which require that the pod # @return [Array<Symbol>] the symbol types, which require that the pod
# frameworks are embedded in the output directory / product bundle. # frameworks are embedded in the output directory / product bundle.
# #
EMBED_FRAMEWORK_TARGET_TYPES = [:application, :unit_test_bundle, :ui_test_bundle, :app_extension, :watch_extension, :watch2_extension].freeze # @note This does not include :app_extension or :watch_extension because
# these types must have their frameworks embedded in their host targets
#
EMBED_FRAMEWORK_TARGET_TYPES = [:application, :unit_test_bundle, :ui_test_bundle, :watch2_extension].freeze
# @return [String] the name of the embed frameworks phase # @return [String] the name of the embed frameworks phase
# #
...@@ -54,6 +57,7 @@ module Pod ...@@ -54,6 +57,7 @@ module Pod
add_pods_library add_pods_library
add_embed_frameworks_script_phase add_embed_frameworks_script_phase
remove_embed_frameworks_script_phase_from_embedded_targets
add_copy_resources_script_phase add_copy_resources_script_phase
add_check_manifest_lock_script_phase add_check_manifest_lock_script_phase
end end
...@@ -110,6 +114,20 @@ module Pod ...@@ -110,6 +114,20 @@ module Pod
end end
end end
# Removes the embed frameworks build phase from embedded targets
#
# @note Older versions of CocoaPods would add this build phase to embedded
# targets. They should be removed on upgrade because embedded targets
# will have their frameworks embedded in their host targets.
#
def remove_embed_frameworks_script_phase_from_embedded_targets
native_targets.each do |native_target|
if AggregateTarget::EMBED_FRAMEWORKS_IN_HOST_TARGET_TYPES.include? native_target.symbol_type
remove_embed_frameworks_script_phase(native_target)
end
end
end
def add_embed_frameworks_script_phase_to_target(native_target) def add_embed_frameworks_script_phase_to_target(native_target)
phase = create_or_update_build_phase(native_target, EMBED_FRAMEWORK_PHASE_NAME) phase = create_or_update_build_phase(native_target, EMBED_FRAMEWORK_PHASE_NAME)
script_path = target.embed_frameworks_script_relative_path script_path = target.embed_frameworks_script_relative_path
...@@ -121,7 +139,7 @@ module Pod ...@@ -121,7 +139,7 @@ module Pod
# @param [PBXNativeTarget] native_target # @param [PBXNativeTarget] native_target
# #
def remove_embed_frameworks_script_phase(native_target) def remove_embed_frameworks_script_phase(native_target)
embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name == EMBED_FRAMEWORK_PHASE_NAME } embed_build_phase = native_target.shell_script_build_phases.find { |bp| bp.name && bp.name.end_with?(EMBED_FRAMEWORK_PHASE_NAME) }
return unless embed_build_phase.present? return unless embed_build_phase.present?
native_target.build_phases.delete(embed_build_phase) native_target.build_phases.delete(embed_build_phase)
end end
......
...@@ -21,7 +21,13 @@ module Pod ...@@ -21,7 +21,13 @@ module Pod
create_module_map create_module_map
create_umbrella_header create_umbrella_header
end end
create_embed_frameworks_script # Because embedded targets live in their host target, CocoaPods
# copies all of the embedded target's pod_targets to its host
# targets. Having this script for the embedded target would
# cause an App Store rejection because frameworks cannot be
# embedded in embedded targets.
#
create_embed_frameworks_script unless target.requires_host_target?
create_bridge_support_file create_bridge_support_file
create_copy_resources_script create_copy_resources_script
create_acknowledgements create_acknowledgements
......
...@@ -7,6 +7,9 @@ module Pod ...@@ -7,6 +7,9 @@ module Pod
# generated this target. # generated this target.
attr_reader :target_definition attr_reader :target_definition
# Product types where the product's frameworks must be embedded in a host target
EMBED_FRAMEWORKS_IN_HOST_TARGET_TYPES = [:app_extension, :framework, :messages_extension, :watch_extension].freeze
# Initialize a new instance # Initialize a new instance
# #
# @param [TargetDefinition] target_definition @see target_definition # @param [TargetDefinition] target_definition @see target_definition
...@@ -23,6 +26,22 @@ module Pod ...@@ -23,6 +26,22 @@ module Pod
@xcconfigs = {} @xcconfigs = {}
end end
# @return [Boolean] True if the user_target's pods are
# for an extension and must be embedded in a host,
# target, otherwise false.
#
def requires_host_target?
# If we don't have a user_project, then we can't
# glean any info about how this target is going to
# be integrated, so return false since we can't know
# for sure that this target refers to an extension
# target that would require a host target
return false if user_project.nil?
symbol_types = user_targets.map(&:symbol_type).uniq
raise ArgumentError, "Expected single kind of user_target for #{name}. Found #{symbol_types.join(', ')}." unless symbol_types.count == 1
EMBED_FRAMEWORKS_IN_HOST_TARGET_TYPES.include? symbol_types[0]
end
# @return [String] the label for the target. # @return [String] the label for the target.
# #
def label def label
......
//
// AppDelegate.swift
// Sample Extensions Project
//
// Created by Benjamin Asher on 6/10/16.
// Copyright © 2016 CocoaPods. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8150" systemVersion="15A204g" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8122"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</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>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
//
// ViewController.swift
// Sample Extensions Project
//
// Created by Benjamin Asher on 6/10/16.
// Copyright © 2016 CocoaPods. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="M4Y-Lb-cyx">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
</dependencies>
<scenes>
<!--Today View Controller-->
<scene sceneID="cwh-vc-ff4">
<objects>
<viewController id="M4Y-Lb-cyx" customClass="TodayViewController" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ft6-oW-KC0"/>
<viewControllerLayoutGuide type="bottom" id="FKl-LY-JtV"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" simulatedAppContext="notificationCenter" id="S3S-Oj-5AN">
<rect key="frame" x="0.0" y="0.0" width="320" height="37"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="top" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hello World" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" preferredMaxLayoutWidth="280" translatesAutoresizingMaskIntoConstraints="NO" id="GcN-lo-r42">
<rect key="frame" x="20" y="8" width="280" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" cocoaTouchSystemColor="lightTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="FKl-LY-JtV" firstAttribute="top" secondItem="GcN-lo-r42" secondAttribute="bottom" constant="20" symbolic="YES" id="0Q0-KW-PJ6"/>
<constraint firstItem="GcN-lo-r42" firstAttribute="leading" secondItem="S3S-Oj-5AN" secondAttribute="leading" constant="20" symbolic="YES" id="6Vq-gs-PHe"/>
<constraint firstAttribute="trailing" secondItem="GcN-lo-r42" secondAttribute="trailing" constant="20" symbolic="YES" id="L8K-9R-egU"/>
<constraint firstItem="GcN-lo-r42" firstAttribute="top" secondItem="Ft6-oW-KC0" secondAttribute="bottom" constant="20" symbolic="YES" id="mYS-Cv-VNx"/>
</constraints>
</view>
<extendedEdge key="edgesForExtendedLayout"/>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<size key="freeformSize" width="320" height="37"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="vXp-U4-Rya" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="516" y="285"/>
</scene>
</scenes>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>
<?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>CFBundleDisplayName</key>
<string>Today Extension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widget-extension</string>
</dict>
</dict>
</plist>
//
// TodayViewController.swift
// Today Extension
//
// Created by Benjamin Asher on 6/10/16.
// Copyright © 2016 CocoaPods. All rights reserved.
//
import UIKit
import NotificationCenter
class TodayViewController: UIViewController, NCWidgetProviding {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view from its nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.NewData)
}
}
...@@ -250,6 +250,12 @@ module Pod ...@@ -250,6 +250,12 @@ module Pod
@generator.send(:pod_targets).each { |pt| pt.stubs(:uses_swift?).returns(false) } @generator.send(:pod_targets).each { |pt| pt.stubs(:uses_swift?).returns(false) }
@generator.generate.to_hash['EMBEDDED_CONTENT_CONTAINS_SWIFT'].should.be.nil @generator.generate.to_hash['EMBEDDED_CONTENT_CONTAINS_SWIFT'].should.be.nil
end end
it 'does not set EMBEDDED_CONTENT_CONTAINS_SWIFT when there is swift, but the target is an extension' do
@target.stubs(:requires_host_target?).returns(true)
@generator.send(:pod_targets).first.stubs(:uses_swift?).returns(true)
@generator.generate.to_hash['EMBEDDED_CONTENT_CONTAINS_SWIFT'].should.be.nil
end
end end
#-----------------------------------------------------------------------# #-----------------------------------------------------------------------#
......
...@@ -671,6 +671,87 @@ module Pod ...@@ -671,6 +671,87 @@ module Pod
#-------------------------------------------------------------------------# #-------------------------------------------------------------------------#
describe 'extension targets' do
before do
SpecHelper.create_sample_app_copy_from_fixture('Sample Extensions Project')
@podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '6.0'
project 'Sample Extensions Project/Sample Extensions Project'
target 'Sample Extensions Project' do
pod 'JSONKit', '1.4'
end
target 'Today Extension' do
pod 'monkey'
end
end
end
it 'copies extension pod targets to host target, when use_frameworks!' do
@podfile.use_frameworks!
analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile)
result = analyzer.analyze
result.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == [
'Pods-Sample Extensions Project/JSONKit',
'Pods-Sample Extensions Project/monkey',
'Pods-Today Extension/monkey',
].sort
end
it 'does not copy extension pod targets to host target, when not use_frameworks!' do
analyzer = Pod::Installer::Analyzer.new(config.sandbox, @podfile)
result = analyzer.analyze
result.targets.flat_map { |at| at.pod_targets.map { |pt| "#{at.name}/#{pt.name}" } }.sort.should == [
'Pods-Sample Extensions Project/JSONKit',
'Pods-Today Extension/monkey',
].sort
end
it "raises when unable to find an extension's host target" do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
use_frameworks!
platform :ios, '6.0'
project 'Sample Extensions Project/Sample Extensions Project'
target 'Today Extension' do
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
should.raise Informative do
analyzer.analyze
end.message.should.match /Unable to find host target for Pods-Today Extension. Please add the host targets for the embedded targets to the Podfile/
end
it 'raises when the extension calls use_frameworks!, but the host target does not' do
podfile = Pod::Podfile.new do
source SpecHelper.test_repo_url
platform :ios, '6.0'
project 'Sample Extensions Project/Sample Extensions Project'
target 'Sample Extensions Project' do
pod 'JSONKit', '1.4'
end
target 'Today Extension' do
use_frameworks!
pod 'monkey'
end
end
analyzer = Pod::Installer::Analyzer.new(config.sandbox, podfile)
should.raise Informative do
analyzer.analyze
end.message.should.match /Pods-Sample Extensions Project must call use_frameworks! because it is hosting an embedded target that calls use_frameworks!/
end
end
#-------------------------------------------------------------------------#
describe 'Private helpers' do describe 'Private helpers' do
describe '#sources' do describe '#sources' do
describe 'when there are no explicit sources' do describe 'when there are no explicit sources' do
......
...@@ -147,22 +147,22 @@ module Pod ...@@ -147,22 +147,22 @@ module Pod
phase.nil?.should == true phase.nil?.should == true
end end
it 'adds an embed frameworks build phase if the target to integrate is an app extension' do it 'does not add an embed frameworks build phase if the target to integrate is an app extension' do
@pod_bundle.stubs(:requires_frameworks? => true) @pod_bundle.stubs(:requires_frameworks? => true)
target = @target_integrator.send(:native_targets).first target = @target_integrator.send(:native_targets).first
target.stubs(:symbol_type).returns(:app_extension) target.stubs(:symbol_type).returns(:app_extension)
@target_integrator.integrate! @target_integrator.integrate!
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name } phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
phase.nil?.should == false phase.nil?.should == true
end end
it 'adds an embed frameworks build phase if the target to integrate is a watch extension' do it 'does not add an embed frameworks build phase if the target to integrate is a watch extension' do
@pod_bundle.stubs(:requires_frameworks? => true) @pod_bundle.stubs(:requires_frameworks? => true)
target = @target_integrator.send(:native_targets).first target = @target_integrator.send(:native_targets).first
target.stubs(:symbol_type).returns(:watch_extension) target.stubs(:symbol_type).returns(:watch_extension)
@target_integrator.integrate! @target_integrator.integrate!
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name } phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
phase.nil?.should == false phase.nil?.should == true
end end
it 'adds an embed frameworks build phase if the target to integrate is a watchOS 2 extension' do it 'adds an embed frameworks build phase if the target to integrate is a watchOS 2 extension' do
...@@ -202,6 +202,42 @@ module Pod ...@@ -202,6 +202,42 @@ module Pod
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name } phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
phase.nil?.should == false phase.nil?.should == false
end end
it 'removes embed frameworks build phases from app extension targets' 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_framework_phase_name }
phase.nil?.should == false
target.stubs(:symbol_type).returns(:app_extension)
@target_integrator.integrate!
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
phase.nil?.should == true
end
it 'removes embed frameworks build phases from watch extension targets' 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_framework_phase_name }
phase.nil?.should == false
target.stubs(:symbol_type).returns(:watch_extension)
@target_integrator.integrate!
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
phase.nil?.should == true
end
it 'removes embed frameworks build phases from framework targets' 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_framework_phase_name }
phase.nil?.should == false
target.stubs(:symbol_type).returns(:framework)
@target_integrator.integrate!
phase = target.shell_script_build_phases.find { |bp| bp.name == @embed_framework_phase_name }
phase.nil?.should == true
end
end end
describe 'Private helpers' do describe 'Private helpers' do
......
...@@ -226,6 +226,25 @@ module Pod ...@@ -226,6 +226,25 @@ module Pod
dummy = support_files_dir + 'Pods-SampleProject-dummy.m' dummy = support_files_dir + 'Pods-SampleProject-dummy.m'
dummy.read.should.include?('@interface PodsDummy_Pods') dummy.read.should.include?('@interface PodsDummy_Pods')
end end
it 'creates an embed frameworks script, if the target does not require a host target' do
@pod_target.stubs(:requires_frameworks? => true)
@target.stubs(:requires_frameworks? => true)
@installer.install!
support_files_dir = config.sandbox.target_support_files_dir('Pods-SampleProject')
script = support_files_dir + 'Pods-SampleProject-frameworks.sh'
File.exist?(script).should == true
end
it 'does not create an embed frameworks script, if the target requires a host target' do
@pod_target.stubs(:requires_frameworks? => true)
@target.stubs(:requires_frameworks? => true)
@target.stubs(:requires_host_target? => true)
@installer.install!
support_files_dir = config.sandbox.target_support_files_dir('Pods-SampleProject')
script = support_files_dir + 'Pods-SampleProject-frameworks.sh'
File.exist?(script).should == false
end
end end
end end
end end
......
...@@ -193,6 +193,62 @@ module Pod ...@@ -193,6 +193,62 @@ module Pod
@target.requires_frameworks?.should == false @target.requires_frameworks?.should == false
end end
end end
describe 'Target might require a host target' do
before do
target_definition = Podfile::TargetDefinition.new('Pods', nil)
target_definition.abstract = false
@target = AggregateTarget.new(target_definition, config.sandbox)
project_path = SpecHelper.fixture('SampleProject/SampleProject.xcodeproj')
@target.user_project = Xcodeproj::Project.open(project_path)
@target.user_target_uuids = ['A346496C14F9BE9A0080D870']
end
it 'requires a host target for app extension targets' do
@target.user_targets.first.stubs(:symbol_type).returns(:app_extension)
@target.requires_host_target?.should == true
end
it 'requires a host target for watch extension targets' do
@target.user_targets.first.stubs(:symbol_type).returns(:watch_extension)
@target.requires_host_target?.should == true
end
it 'requires a host target for framework targets' do
@target.user_targets.first.stubs(:symbol_type).returns(:framework)
@target.requires_host_target?.should == true
end
it 'requires a host target for messages extension targets' do
@target.user_targets.first.stubs(:symbol_type).returns(:messages_extension)
@target.requires_host_target?.should == true
end
it 'does not require a host target for watch 2 extension targets' do
@target.user_targets.first.stubs(:symbol_type).returns(:watch2_extension)
@target.requires_host_target?.should == false
end
it 'does not require a host target for application targets' do
@target.user_targets.first.stubs(:symbol_type).returns(:application)
@target.requires_host_target?.should == false
end
it 'does not require a host target, if there is no user project (manual integration)' do
@target.user_project = nil
@target.user_target_uuids = []
@target.requires_host_target?.should == false
end
it 'raises an exception if more than one kind of user_target is found' do
@target.user_target_uuids << '51075D491521D0C100E39B41'
@target.user_targets.first.stubs(:symbol_type).returns(:app_extension)
@target.user_targets.last.stubs(:symbol_type).returns(:watch_extension)
should.raise ArgumentError do
@target.requires_host_target?
end.message.should.equal 'Expected single kind of user_target for Pods. Found app_extension, watch_extension.'
end
end
end end
describe 'With frameworks' do describe 'With frameworks' 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