Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/apple-targets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,11 @@ Ideally, this would be generated automatically based on a fully qualified Xcode
| broadcast-upload | Broadcast Upload Extension |
| call-directory | Call Directory Extension |
| message-filter | Message Filter Extension |
| file-provider-ui | File Provider UI Extension |
| broadcast-setup-ui | Broadcast Setup UI Extension |
| classkit-context | ClassKit Context Provider Extension|
| unwanted-communication | Unwanted Communication Reporting |
| photo-editing | Photo Editing Extension |


<!-- | imessage | iMessage Extension | -->
Expand All @@ -323,11 +328,6 @@ The following iOS extension types exist in Xcode but aren't supported yet. Contr

| Extension Point Identifier | Xcode Template Name |
| --- | --- |
| `com.apple.fileprovider-actionsui` | File Provider UI Extension |
| `com.apple.broadcast-services-setupui` | Broadcast Setup UI Extension |
| `com.apple.classkit.context-provider` | ClassKit Context Provider Extension |
| `com.apple.identitylookup.classification-ui` | Unwanted Communication Reporting Extension |
| `com.apple.photo-editing` | Photo Editing Extension |
| `com.apple.printing.discovery` | Print Service Extension |
| `com.apple.quicklook.preview` | Quick Look Preview Extension |
| `com.apple.spotlight.index` | CoreSpotlight Delegate Extension |
Expand Down
21 changes: 21 additions & 0 deletions packages/apple-targets/e2e/__tests__/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,27 @@ const TARGET_REGISTRY: TargetEntry[] = [
},
{ type: "call-directory", dir: "call-directory", target: "calldirectory" },
{ type: "message-filter", dir: "message-filter", target: "messagefilter" },
{
type: "file-provider-ui",
dir: "file-provider-ui",
target: "fileproviderui",
},
{
type: "broadcast-setup-ui",
dir: "broadcast-setup-ui",
target: "broadcastsetupui",
},
{
type: "classkit-context",
dir: "classkit-context",
target: "classkitcontext",
},
{
type: "unwanted-communication",
dir: "unwanted-communication",
target: "unwantedcommunication",
},
{ type: "photo-editing", dir: "photo-editing", target: "photoediting" },
];

// Derived from the central TARGET_REGISTRY — no need to maintain by hand.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "type": "broadcast-setup-ui" }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "type": "classkit-context" }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "type": "file-provider-ui" }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "type": "photo-editing" }
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "type": "unwanted-communication" }
5 changes: 5 additions & 0 deletions packages/apple-targets/src/configuration-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,11 @@ function getConfigurationListBuildSettingsForType(
case "broadcast-upload":
case "call-directory":
case "message-filter":
case "file-provider-ui":
case "broadcast-setup-ui":
case "classkit-context":
case "unwanted-communication":
case "photo-editing":
return createNotificationContentConfigurationList(props);
default:
const exhaustiveCheck: never = props.type;
Expand Down
68 changes: 68 additions & 0 deletions packages/apple-targets/src/target.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,31 @@ export const TARGET_REGISTRY = {
frameworks: ["IdentityLookup"],
displayName: "Message Filter",
},
"file-provider-ui": {
extensionPointIdentifier: "com.apple.fileprovider-actionsui",
frameworks: ["FileProviderUI"],
displayName: "File Provider UI",
},
"broadcast-setup-ui": {
extensionPointIdentifier: "com.apple.broadcast-services-setupui",
frameworks: ["ReplayKit"],
displayName: "Broadcast Setup UI",
},
"classkit-context": {
extensionPointIdentifier: "com.apple.classkit.context-provider",
frameworks: ["ClassKit"],
displayName: "ClassKit Context Provider",
},
"unwanted-communication": {
extensionPointIdentifier: "com.apple.identitylookup.classification-ui",
frameworks: ["IdentityLookup", "IdentityLookupUI"],
displayName: "Unwanted Communication Reporting",
},
"photo-editing": {
extensionPointIdentifier: "com.apple.photo-editing",
frameworks: ["Photos", "PhotosUI"],
displayName: "Photo Editing",
},
} as const satisfies Record<string, TargetDefinition>;

export type ExtensionType = keyof typeof TARGET_REGISTRY;
Expand Down Expand Up @@ -528,6 +553,49 @@ export function getTargetInfoPlistForType(type: ExtensionType) {
"$(PRODUCT_MODULE_NAME).MessageFilterExtension",
},
};
case "file-provider-ui":
return {
NSExtension: {
NSExtensionPointIdentifier,
NSExtensionPrincipalClass:
"$(PRODUCT_MODULE_NAME).FileProviderUIController",
},
};
case "broadcast-setup-ui":
return {
NSExtension: {
NSExtensionPointIdentifier,
NSExtensionPrincipalClass:
"$(PRODUCT_MODULE_NAME).BroadcastSetupViewController",
},
};
case "classkit-context":
return {
NSExtension: {
NSExtensionPointIdentifier,
NSExtensionPrincipalClass:
"$(PRODUCT_MODULE_NAME).ContextProvider",
},
};
case "unwanted-communication":
return {
NSExtension: {
NSExtensionPointIdentifier,
NSExtensionPrincipalClass:
"$(PRODUCT_MODULE_NAME).ClassificationViewController",
},
};
case "photo-editing":
return {
NSExtension: {
NSExtensionPointIdentifier,
NSExtensionPrincipalClass:
"$(PRODUCT_MODULE_NAME).PhotoEditingViewController",
NSExtensionAttributes: {
PHSupportedMediaTypes: ["Image"],
},
},
};
default:
// Default: used for widget and bg-download
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ module.exports = config => ({
});"
`;

exports[`getTemplateConfig should return a valid template for broadcast-setup-ui 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
type: \\"broadcast-setup-ui\\",
entitlements: { /* Add entitlements */ },
});"
`;

exports[`getTemplateConfig should return a valid template for broadcast-upload 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
Expand All @@ -50,6 +58,14 @@ module.exports = config => ({
});"
`;

exports[`getTemplateConfig should return a valid template for classkit-context 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
type: \\"classkit-context\\",
entitlements: { /* Add entitlements */ },
});"
`;

exports[`getTemplateConfig should return a valid template for clip 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
Expand Down Expand Up @@ -83,6 +99,14 @@ module.exports = config => ({
});"
`;

exports[`getTemplateConfig should return a valid template for file-provider-ui 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
type: \\"file-provider-ui\\",
entitlements: { /* Add entitlements */ },
});"
`;

exports[`getTemplateConfig should return a valid template for intent 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
Expand Down Expand Up @@ -131,6 +155,14 @@ module.exports = config => ({
});"
`;

exports[`getTemplateConfig should return a valid template for photo-editing 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
type: \\"photo-editing\\",
entitlements: { /* Add entitlements */ },
});"
`;

exports[`getTemplateConfig should return a valid template for quicklook-thumbnail 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
Expand Down Expand Up @@ -166,6 +198,14 @@ module.exports = config => ({
});"
`;

exports[`getTemplateConfig should return a valid template for unwanted-communication 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
type: \\"unwanted-communication\\",
entitlements: { /* Add entitlements */ },
});"
`;

exports[`getTemplateConfig should return a valid template for watch 1`] = `
"/** @type {import('@bacons/apple-targets/app.plugin').ConfigFunction} */
module.exports = config => ({
Expand Down
5 changes: 5 additions & 0 deletions packages/create-target/src/__tests__/createAsync.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ const ALL_TARGET_TYPES = [
"broadcast-upload",
"call-directory",
"message-filter",
"file-provider-ui",
"broadcast-setup-ui",
"classkit-context",
"unwanted-communication",
"photo-editing",
];

describe(getTemplateConfig, () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import ReplayKit

class BroadcastSetupViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
// Customize the UI for broadcast setup
}

@IBAction func cancelButtonTapped(_ sender: Any) {
userDidCancelSetup()
}

@IBAction func startButtonTapped(_ sender: Any) {
userDidFinishSetup()
}

func userDidFinishSetup() {
// URL of the resource where broadcast can be viewed
let broadcastURL = URL(string: "https://example.com/broadcast")!

// Dictionary with setup information that will be provided to the broadcast extension
let setupInfo: [String : NSCoding & NSObjectProtocol] = ["broadcastURL": broadcastURL as NSCoding & NSObjectProtocol]

// Tell ReplayKit that the extension is finished setting up and can begin broadcasting
self.extensionContext?.completeRequest(withBroadcast: broadcastURL, setupInfo: setupInfo)
}

func userDidCancelSetup() {
let error = NSError(domain: "com.example.broadcast", code: -1, userInfo: nil)
self.extensionContext?.cancelRequest(withError: error)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ClassKit

class ContextProvider: NSObject, CLSContextProvider {

func updateDescendants(of context: CLSContext, completion: @escaping (Error?) -> Void) {
// Called when ClassKit needs to update the context hierarchy.
// Add child contexts to the given context as appropriate.
completion(nil)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import UIKit
import FileProviderUI

class FileProviderUIController: FPUIActionExtensionViewController {

override func prepare(forAction actionIdentifier: String, itemIdentifiers: [NSFileProviderItemIdentifier]) {
// Prepare the UI for the given action
}

override func prepare(forError error: Error) {
// Present the error to the user
}

@IBAction func doneButtonTapped(_ sender: Any) {
extensionContext.completeRequest()
}

@IBAction func cancelButtonTapped(_ sender: Any) {
extensionContext.cancelRequest(withError: NSError(domain: FPUIErrorDomain, code: Int(FPUIExtensionErrorCode.userCancelled.rawValue)))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import UIKit
import Photos
import PhotosUI

class PhotoEditingViewController: UIViewController, PHContentEditingController {

var input: PHContentEditingInput?

override func viewDidLoad() {
super.viewDidLoad()
}

// MARK: - PHContentEditingController

func canHandle(_ adjustmentData: PHAdjustmentData) -> Bool {
// Return true if the adjustment data can be handled
return false
}

func startContentEditing(with contentEditingInput: PHContentEditingInput, placeholderImage: UIImage) {
// Present content for editing
input = contentEditingInput
}

func finishContentEditing(completionHandler: @escaping ((PHContentEditingOutput?) -> Void)) {
// Update the adjustment data and render the output
guard let input = input else {
completionHandler(nil)
return
}

let output = PHContentEditingOutput(contentEditingInput: input)
let adjustmentData = PHAdjustmentData(
formatIdentifier: "com.example.photo-editing",
formatVersion: "1.0",
data: Data()
)
output.adjustmentData = adjustmentData

completionHandler(output)
}

var shouldShowCancelConfirmation: Bool {
return false
}

func cancelContentEditing() {
// Clean up temporary files, etc.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import IdentityLookupUI

class ClassificationViewController: ILClassificationUIExtensionViewController {

override func viewDidLoad() {
super.viewDidLoad()
}

// Customize the view by reading classification request and response
override func prepare(for classificationRequest: ILClassificationRequest) {
// Configure the view for the classification request
}

override func classificationResponse(for request: ILClassificationRequest) -> ILClassificationResponse {
// Return a classification response for the given request
return ILClassificationResponse(action: .none)
}
}