Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add capabilities for detecting app extensions and host targets #382

Merged
merged 13 commits into from Jun 7, 2016
Merged
34 changes: 34 additions & 0 deletions lib/xcodeproj/project.rb
Expand Up @@ -526,6 +526,40 @@ def native_targets
root_object.targets.grep(PBXNativeTarget)
end

# Checks the native target for any targets in the project that are
# app extensions of that target
#
# @param [PBXNativeTarget] native target to check for app extensions
#
#
# @return [ObjectList<PBXNativeTarget>] A list of all targets that are
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will actually return an array

# app extensions of the passed in target.
#
def app_extensions_for_native_target(native_target)
return [] if native_target.app_extension?
native_targets.select do |target|
next unless target.app_extension?
host_targets_for_app_extension_target(target).map(&:uuid).include? native_target.uuid
end
end

# Returns the native target, in which the extension target is embedded.
# This works by traversing the targets to find those where the extension
# target is a dependency.
#
# @param [PBXNativeTarget] native target where target.app_extension?
# is true
#
# @return [ObjectList<PBXNativeTarget>] the native targets that hosts the extension
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returns an array

#
def host_targets_for_app_extension_target(extension_target)
raise ArgumentError, "#{extension_target} is not an app extension" unless extension_target.app_extension?
native_targets.find_all do |native_target|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Select

((extension_target.uuid != native_target.uuid) &&
(native_target.dependencies.map(&:target).map(&:uuid).include? extension_target.uuid))
end
end

# @return [PBXGroup] The group which holds the product file references.
#
def products_group
Expand Down
13 changes: 13 additions & 0 deletions lib/xcodeproj/project/object/native_target.rb
Expand Up @@ -416,6 +416,19 @@ class PBXNativeTarget < AbstractTarget
# @!group Helpers
#--------------------------------------#

# Checks product_type to determine whether or not the recevier is a
# an app extension
#
# @return True if the target would be an extension embedded in an
# app target, otherwise false
#
# @note watchOS 2 extensions are extensions of a watch target, not
# an app target, whereas this is not the case for watchOS 1
def app_extension?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there's already a helper for this?

Copy link
Member Author

@benasher44 benasher44 Jun 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see there's extension_target_type?, which I either didn't see or was newer than when I initially started this branch. That said, app_extension? specifically doesn't consider watch os 2 targets (unsure about tv targets), since the intent is to use this flag to determine if it's okay to embed frameworks in the target, which it's not for "app" extensions but I believe is okay to do for watch os 2 targets (cc @neonichu). Maybe I just need a better name here. How about allows_embed_frameworks? (and then I'll invert the condition)?

Copy link
Member Author

@benasher44 benasher44 Jun 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That naming (and maybe this method) is more targeted toward how it's going to be used in CocoaPods. Maybe this should just be a method/helper on AggregateTarget in CocoaPods that checks the user_target's product_type? The other methods in this PR could be refactored to use the existing helper extension_target_type?. Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, this feels like it belongs in CocoaPods

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @segiddins

@benasher44 you're correct that watchOS 2 extensions need their frameworks embedded, because the binaries are build for a different architecture.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okie doke. Thanks! I'll refactor and update soon.

extension_types = Set.new [:app_extension, :watch_extension]
extension_types.include? symbol_type
end

# @return [Symbol] The type of the target expressed as a symbol.
#
def symbol_type
Expand Down
@@ -0,0 +1,62 @@
{
"images" : [
{
"size" : "24x24",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"scale" : "2x",
"role" : "longLook",
"subtype" : "42mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="6221" systemVersion="13E28" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="AgC-eL-Hgc">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6213"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="3733"/>
</dependencies>
<scenes>
<!--Interface Controller-->
<scene sceneID="aou-V4-d1y">
<objects>
<controller id="AgC-eL-Hgc" customClass="InterfaceController" customModuleProvider="target"/>
</objects>
</scene>
</scenes>
</document>
@@ -0,0 +1,35 @@
<?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>Extensions</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>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>WKCompanionAppBundleIdentifier</key>
<string>cocoapods.Extensions</string>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>
@@ -0,0 +1 @@
Did you know that git does not support storing empty directories?
@@ -0,0 +1,36 @@
<?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>Extensions WatchKit 1 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>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>cocoapods.Extensions.watchkitapp</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
</dict>
</plist>
@@ -0,0 +1,31 @@
//
// InterfaceController.swift
// Extensions WatchKit 1 Extension
//
// Created by Benjamin Asher on 4/9/16.
// Copyright © 2016 CocoaPods. All rights reserved.
//

import WatchKit
import Foundation


class InterfaceController: WKInterfaceController {

override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)

// Configure interface objects here.
}

override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}

override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}

}