diff --git a/.buckconfig b/.buckconfig index 8ebc4558..aeed3a9c 100644 --- a/.buckconfig +++ b/.buckconfig @@ -8,7 +8,7 @@ [swift] version = 4.0 - compiler_flags = -DBUCK -enable-testing -g -Onone -whole-module-optimization $(config custom.other_swift_compiler_flags) + compiler_flags = -DBUCK -enable-testing -g -Onone -whole-module-optimization $(config custom.other_swift_compiler_flags) -DDISABLE_SIRI_SHORTCUT use_filelist = true [apple] @@ -41,4 +41,3 @@ code_coverage_cxxflags = -fprofile-instr-generate -fcoverage-mapping code_coverage_ldflags = -fprofile-instr-generate code_coverage_swift_compiler_flags = -profile-generate -profile-coverage-mapping - \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5b4a20e6..361c7ec0 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,9 @@ tools/buck # Xcode **/xcuserdata/ **/*.xcodeproj/ +!App/SiriShortcut/_IntentsCompiler/IntentsCompiler.xcodeproj **/*.xcworkspace/ +**/build/ # Make *.d diff --git a/App/AppDelegate.swift b/App/AppDelegate.swift index db787385..ae9514e1 100644 --- a/App/AppDelegate.swift +++ b/App/AppDelegate.swift @@ -13,10 +13,42 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let window = UIWindow(frame: UIScreen.main.bounds) func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: ArgType) -> Bool { - + window.makeKeyAndVisible() window.rootViewController = ViewController() return true } + + #if !DISABLE_SIRI_SHORTCUT + #if swift(>=4.2) + // Xcode 10 + func application( + _ application: UIApplication, + continue userActivity: NSUserActivity, + restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool + { + return continueWithUserActivity(userActivity) + } + #endif + #endif + + // MARK: Private + + private func continueWithUserActivity(_ userActivity: NSUserActivity) -> Bool { + guard #available(iOS 12.0, *) else { + return false + } + + #if !DISABLE_SIRI_SHORTCUT + if let buckPhotoIntent = userActivity.interaction?.intent as? BuckPhotoIntent { + print("Launched with BuckPhotoIntent: \(buckPhotoIntent)") + return true + } else { + return false + } + #else + return false + #endif + } } diff --git a/App/BUCK b/App/BUCK index 10df6e6d..21f1eadf 100644 --- a/App/BUCK +++ b/App/BUCK @@ -1,5 +1,5 @@ -load("//Config:configs.bzl", "app_binary_configs", "library_configs", "watch_binary_configs", "message_binary_configs", "pretty", "info_plist_substitutions", "bundle_identifier", "DEVELOPMENT_LANGUAGE") -load("//Config:buck_rule_macros.bzl", "apple_lib", "apple_test_lib", "apple_test_all") +load("//Config:configs.bzl", "app_binary_configs", "library_configs", "watch_binary_configs", "message_binary_configs", "intent_binary_configs", "pretty", "info_plist_substitutions", "bundle_identifier", "DEVELOPMENT_LANGUAGE") +load("//Config:buck_rule_macros.bzl", "apple_lib", "apple_test_lib", "apple_test_all", "intent_interface") apple_asset_catalog( name = "ExampleAppAssets", @@ -57,8 +57,10 @@ apple_library( swift_version = "4.0", srcs = [ "ViewController.swift", + "ViewController+INUIAddVoiceShortcutViewControllerDelegate.swift", "AppDelegate.swift", "LocalizationHelper.swift", + ":BuckPhotoIntentInterface", ], tests = app_tests, deps = [ @@ -69,10 +71,16 @@ apple_library( # Resources "//App/Resources:ExampleAppStringResources", + "//App/Resources:IntentsDefinition", + "//App/Resources:IntentsStringResources", "//App/Resources:StoryboardResources", ] + first_party_library_dependencies + build_phase_scripts, + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Intents.framework", + "$SDKROOT/System/Library/Frameworks/IntentsUI.framework", + ], ) apple_binary( @@ -82,6 +90,7 @@ apple_binary( "//App/...", ], configs = app_binary_configs("ExampleApp"), + entitlements_file = "ExampleApp.entitlements", swift_version = "4.0", srcs = [ "BuckSupportFiles/Dummy.swift", @@ -148,7 +157,9 @@ apple_bundle( deps = [ # For "#watch", https://buckbuild.com/rule/apple_bundle.html#deps ":ExampleWatchApp#watch", - ":ExampleMessageExtension" + ":ExampleMessageExtension", + ":ExampleIntentExtension", + ":ExampleIntentUIExtension", ] + prebuilt_frameworks, ) @@ -158,6 +169,129 @@ apple_package( bundle = ":ExampleApp", ) +### Siri Shortcut Begin ### + +# Set up `BuckPhotoIntent` by handling the .intentdefinition file. +intent_interface( + "BuckPhotoIntentInterface", + "BuckPhoto", + "SiriShortcut/_IntentsCompiler/IntentsCompiler.xcodeproj") + +# The non-UI code for processing `BuckPhotoIntent`. +intent_bundle_identifier = bundle_identifier("ExampleApp.IntentExtension") +intent_binary_name = "ExampleIntentBinary" +intent_bundle_name = "ExampleIntentExtension" +intent_info_plist_substitutions = { + "DEVELOPMENT_LANGUAGE": "en-us", + "EXECUTABLE_NAME": intent_bundle_name, + "PRODUCT_BUNDLE_DISPLAY_NAME": intent_bundle_name, + "PRODUCT_BUNDLE_IDENTIFIER": intent_bundle_identifier, + "PRODUCT_NAME": intent_bundle_name, + # The Swift module name for `IntentHandler` is the binary name. + "NS_EXTENSION_PRINCIPAL_CLASS": "%s.IntentHandler" % intent_binary_name, +} + +apple_binary( + name = intent_binary_name, + srcs = glob([ + "SiriShortcut/ExampleIntent/**/*.swift", + ]) + [":BuckPhotoIntentInterface"], + configs = intent_binary_configs(intent_info_plist_substitutions), + + # "-e _NSExtensionMain" tell linker this binary is app extension, so it won't fail due to + # missing _main "-Xlinker -rpath -Xlinker @executable_path/../../Frameworks" tells the + # executable binary to look for frameworks in ExampleApp.app/Frameworks instead of PlugIns, so + # that we don't need to have the libSwift*.dylib in ExampleApp.app/PlugIns/*.appex/Frameworks + linker_flags = [ + "-e", + "_NSExtensionMain", + "-Xlinker", + "-rpath", + "-Xlinker", + "@executable_path/../../Frameworks", + ], + deps = [ + "//App/Resources:IntentsDefinition", + "//App/Resources:IntentsStringResources", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Intents.framework", + ], +) + +apple_bundle( + name = intent_bundle_name, + binary = ":" + intent_binary_name, + extension = "appex", + info_plist = "SiriShortcut/ExampleIntent/Info.plist", + info_plist_substitutions = intent_info_plist_substitutions, + xcode_product_type = "com.apple.product-type.app-extension", +) + +# The UI code for displaying the response to `BuckPhotoIntent`. +intentui_bundle_identifier = bundle_identifier("ExampleApp.IntentUIExtension") +intentui_binary_name = "ExampleIntentUIBinary" +intentui_bundle_name = "ExampleIntentUIExtension" +intentui_resources_name = intentui_bundle_name + "Resources" +intentui_info_plist_substitutions = { + "DEVELOPMENT_LANGUAGE": "en-us", + "EXECUTABLE_NAME": intentui_bundle_name, + "PRODUCT_BUNDLE_DISPLAY_NAME": intentui_bundle_name, + "PRODUCT_BUNDLE_IDENTIFIER": intentui_bundle_identifier, + "PRODUCT_NAME": intentui_bundle_name, +} + +apple_binary( + name = intentui_binary_name, + srcs = glob([ + "SiriShortcut/ExampleIntentUI/**/*.swift", + ]) + [":BuckPhotoIntentInterface"], + configs = intent_binary_configs(intentui_info_plist_substitutions), + + # "-e _NSExtensionMain" tell linker this binary is app extension, so it won't fail due to + # missing _main "-Xlinker -rpath -Xlinker @executable_path/../../Frameworks" tells the + # executable binary to look for frameworks in ExampleApp.app/Frameworks instead of PlugIns, so + # that we don't need to have the libSwift*.dylib in ExampleApp.app/PlugIns/*.appex/Frameworks + linker_flags = [ + "-e", + "_NSExtensionMain", + "-Xlinker", + "-rpath", + "-Xlinker", + "@executable_path/../../Frameworks", + ], + deps = [ + "//App/Resources:IntentsDefinition", + "//App/Resources:IntentsStringResources", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Intents.framework", + "$SDKROOT/System/Library/Frameworks/IntentsUI.framework", + ], +) + +apple_resource( + name = intentui_resources_name, + dirs = [], + # WORKAROUND: Buck CLI requires you to explicitly set the "Module Name" of the + # `IntentViewController` class to `ExampleIntentUIBinary` in MainInterface.storyboard. + files = glob(["SiriShortcut/ExampleIntentUI/**/*.storyboard"]) +) + +apple_bundle( + name = intentui_bundle_name, + binary = ":" + intentui_binary_name, + extension = "appex", + info_plist = "SiriShortcut/ExampleIntentUI/Info.plist", + info_plist_substitutions = intentui_info_plist_substitutions, + xcode_product_type = "com.apple.product-type.app-extension", + deps = [ + ":" + intentui_resources_name, + ], +) + +### Siri Shortcut End ### + ### Watch App Begin ### # Define the watch app in the same BUCK file as the binary into which the watch app will be installed. # Xcode is finicky when it comes to how it embeds watch apps into main app bundles. Watch apps are diff --git a/App/ExampleApp.entitlements b/App/ExampleApp.entitlements new file mode 100644 index 00000000..21d95c45 --- /dev/null +++ b/App/ExampleApp.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.developer.siri + + + diff --git a/App/Info.plist b/App/Info.plist index d08e8bd2..b0084189 100644 --- a/App/Info.plist +++ b/App/Info.plist @@ -29,6 +29,10 @@ 1 LSRequiresIPhoneOS + NSUserActivityTypes + + BuckPhotoIntent + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/App/Resources/BUCK b/App/Resources/BUCK index 96886192..1ec2bc93 100644 --- a/App/Resources/BUCK +++ b/App/Resources/BUCK @@ -1,3 +1,5 @@ +load("//Config:buck_rule_macros.bzl", "intentdefinition_resource") + apple_resource( name = "ExampleAppStringResources", visibility = ["//App:"], @@ -7,6 +9,15 @@ apple_resource( ]), ) +apple_resource( + name = "IntentsStringResources", + visibility = ["//App:"], + files = [], + variants = glob([ + "*.lproj/Intents.strings", + ]), +) + apple_resource( name = "StoryboardResources", visibility = ["//App:"], @@ -14,3 +25,5 @@ apple_resource( "*.lproj/*.storyboard", ]), ) + +intentdefinition_resource("IntentsDefinition", "./en.lproj/", "Intents", "en") diff --git a/App/Resources/en.lproj/ExampleApp.strings b/App/Resources/en.lproj/ExampleApp.strings index 61fd1ae3..48f1a243 100644 --- a/App/Resources/en.lproj/ExampleApp.strings +++ b/App/Resources/en.lproj/ExampleApp.strings @@ -1,4 +1,4 @@ -/* +/* ExampleApp.strings ExampleApp @@ -7,3 +7,4 @@ */ "Hello, world" = "Hello, world"; +"Show me a buck" = "Show me a buck"; diff --git a/App/Resources/en.lproj/Intents.intentdefinition b/App/Resources/en.lproj/Intents.intentdefinition new file mode 100644 index 00000000..30ef1282 --- /dev/null +++ b/App/Resources/en.lproj/Intents.intentdefinition @@ -0,0 +1,93 @@ + + + + + INEnums + + INIntentDefinitionModelVersion + 1.0 + INIntentDefinitionSystemVersion + 18E226 + INIntentDefinitionToolsBuildVersion + 10E1001 + INIntentDefinitionToolsVersion + 10.2.1 + INIntents + + + INIntentCategory + information + INIntentDescription + Shows a photo of a buck. + INIntentDescriptionID + WWnWNH + INIntentLastParameterTag + 0 + INIntentName + BuckPhoto + INIntentParameterCombinations + + + + INIntentParameterCombinationIsPrimary + + INIntentParameterCombinationSubtitle + See a buck. + INIntentParameterCombinationSubtitleID + Skm7E3 + INIntentParameterCombinationSupportsBackgroundExecution + + INIntentParameterCombinationTitle + + INIntentParameterCombinationTitleID + ejGvr8 + + + INIntentParameters + + INIntentResponse + + INIntentResponseCodes + + + INIntentResponseCodeFormatString + No bucks in the area today. + INIntentResponseCodeFormatStringID + HfNLHd + INIntentResponseCodeName + failure + INIntentResponseCodeSuccess + + + + INIntentResponseCodeFormatString + Here is a wild buck. + INIntentResponseCodeFormatStringID + 0U61v4 + INIntentResponseCodeName + success + INIntentResponseCodeSuccess + + + + INIntentResponseLastParameterTag + 0 + INIntentResponseParameters + + + INIntentRestrictions + 0 + INIntentTitle + Buck Photo + INIntentTitleID + 7wFgFZ + INIntentType + Custom + INIntentUserConfirmationRequired + + INIntentVerb + View + + + + diff --git a/App/Resources/es.lproj/ExampleApp.strings b/App/Resources/es.lproj/ExampleApp.strings index 017af457..9017fd37 100644 --- a/App/Resources/es.lproj/ExampleApp.strings +++ b/App/Resources/es.lproj/ExampleApp.strings @@ -1,4 +1,4 @@ -/* +/* ExampleApp.strings ExampleApp @@ -7,3 +7,4 @@ */ "Hello, world" = "Hola, mundo"; +"Show me a buck" = "Muéstrame un venado"; \ No newline at end of file diff --git a/App/Resources/es.lproj/Intents.strings b/App/Resources/es.lproj/Intents.strings new file mode 100644 index 00000000..c85fa4be --- /dev/null +++ b/App/Resources/es.lproj/Intents.strings @@ -0,0 +1,10 @@ +"0U61v4" = "Aqui hay un venado salvaje."; + +"7wFgFZ" = "Foto de un Venado"; + +"HfNLHd" = "No hay venados en esta área hoy."; + +"Skm7E3" = "Ve un venado."; + +"WWnWNH" = "Muestra la foto de un venado."; + diff --git a/App/SiriShortcut/ExampleIntent/BuckPhotoIntentHandler.swift b/App/SiriShortcut/ExampleIntent/BuckPhotoIntentHandler.swift new file mode 100644 index 00000000..a0865fe0 --- /dev/null +++ b/App/SiriShortcut/ExampleIntent/BuckPhotoIntentHandler.swift @@ -0,0 +1,24 @@ +// Created by Michael Bachand on 4/27/19. +// Copyright © 2019 Airbnb Inc. All rights reserved. + +import Foundation + +#if !DISABLE_SIRI_SHORTCUT +@available(iOS 12.0, *) +final class BuckPhotoIntentHandler: NSObject, BuckPhotoIntentHandling { + + func confirm( + intent: BuckPhotoIntent, + completion: @escaping (BuckPhotoIntentResponse) -> Void) + { + // In a real app, you would probably want to do some check here to make sure you're ready. + completion(BuckPhotoIntentResponse(code: .ready, userActivity: nil)) + } + + func handle(intent: BuckPhotoIntent, completion: @escaping (BuckPhotoIntentResponse) -> Void) { + completion(BuckPhotoIntentResponse(code: .success, userActivity: nil)) + } +} +#else +final class BuckPhotoIntentHandler {} +#endif diff --git a/App/SiriShortcut/ExampleIntent/Info.plist b/App/SiriShortcut/ExampleIntent/Info.plist new file mode 100644 index 00000000..379d0e6f --- /dev/null +++ b/App/SiriShortcut/ExampleIntent/Info.plist @@ -0,0 +1,44 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + $(PRODUCT_BUNDLE_DISPLAY_NAME) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + IntentsRestrictedWhileLocked + + IntentsSupported + + BuckPhotoIntent + + + NSExtensionPointIdentifier + com.apple.intents-service + NSExtensionPrincipalClass + $(NS_EXTENSION_PRINCIPAL_CLASS) + + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/App/SiriShortcut/ExampleIntent/IntentHandler.swift b/App/SiriShortcut/ExampleIntent/IntentHandler.swift new file mode 100644 index 00000000..c018e8e4 --- /dev/null +++ b/App/SiriShortcut/ExampleIntent/IntentHandler.swift @@ -0,0 +1,21 @@ +// Created by Michael Bachand on 4/20/19. +// Copyright © 2019 Airbnb Inc. All rights reserved. + +import Intents + +class IntentHandler: INExtension { + + override func handler(for intent: INIntent) -> Any { + if #available(iOS 12.0, *) { + #if !DISABLE_SIRI_SHORTCUT + guard intent is BuckPhotoIntent else { + fatalError("Unhandled intent type: \(intent)") + } + #endif + return BuckPhotoIntentHandler() + } else { + fatalError("Unexpectly executing code path on iOS < 12.0") + } + } + +} diff --git a/App/SiriShortcut/ExampleIntentUI/Base.lproj/MainInterface.storyboard b/App/SiriShortcut/ExampleIntentUI/Base.lproj/MainInterface.storyboard new file mode 100644 index 00000000..07777937 --- /dev/null +++ b/App/SiriShortcut/ExampleIntentUI/Base.lproj/MainInterface.storyboard @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/SiriShortcut/ExampleIntentUI/Info.plist b/App/SiriShortcut/ExampleIntentUI/Info.plist new file mode 100644 index 00000000..3499f328 --- /dev/null +++ b/App/SiriShortcut/ExampleIntentUI/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + $(PRODUCT_BUNDLE_DISPLAY_NAME) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + IntentsSupported + + BuckPhotoIntent + + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.intents-ui-service + + UIRequiredDeviceCapabilities + + arm64 + + + diff --git a/App/SiriShortcut/ExampleIntentUI/IntentViewController.swift b/App/SiriShortcut/ExampleIntentUI/IntentViewController.swift new file mode 100644 index 00000000..d73dd1bb --- /dev/null +++ b/App/SiriShortcut/ExampleIntentUI/IntentViewController.swift @@ -0,0 +1,31 @@ +// Created by Michael Bachand on 4/20/19. +// Copyright © 2019 Airbnb Inc. All rights reserved. + +import IntentsUI + +#if swift(>=4.0) +final class IntentViewController: UIViewController, INUIHostedViewControlling { + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .gray + // Do any additional setup after loading the view. + } + + // Prepare your view controller for the interaction to handle. + func configureView(for parameters: Set, of interaction: INInteraction, interactiveBehavior: INUIInteractiveBehavior, context: INUIHostedViewContext, completion: @escaping (Bool, Set, CGSize) -> Void) { + // Do configuration here, including preparing views and calculating a desired size for presentation. + completion(true, parameters, self.desiredSize) + } + + var desiredSize: CGSize { + guard let hostedViewMaximumAllowedSize = extensionContext?.hostedViewMaximumAllowedSize else { + fatalError("Unexpectedly could no extension context") + } + var result = hostedViewMaximumAllowedSize + result.height = 400 + return result + } + +} +#endif diff --git a/App/SiriShortcut/_IntentsCompiler/IntentsCompiler.xcodeproj/project.pbxproj b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler.xcodeproj/project.pbxproj new file mode 100644 index 00000000..7bb20353 --- /dev/null +++ b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler.xcodeproj/project.pbxproj @@ -0,0 +1,329 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + E43E3E4422700E1C00DBCD82 /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = E43E3E4322700E1C00DBCD82 /* Intents.intentdefinition */; }; + E4D2464522700F1C00587E6F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E4D2464422700F1C00587E6F /* LaunchScreen.storyboard */; }; + E4DA4374227008DC00DCB2E5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DA4373227008DC00DCB2E5 /* AppDelegate.swift */; }; + E4DA4379227008DC00DCB2E5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E4DA4377227008DC00DCB2E5 /* Main.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E43E3E4322700E1C00DBCD82 /* Intents.intentdefinition */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.intentdefinition; name = Intents.intentdefinition; path = ../../../Resources/en.lproj/Intents.intentdefinition; sourceTree = ""; }; + E4D2464422700F1C00587E6F /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + E4DA4370227008DC00DCB2E5 /* IntentsCompiler.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IntentsCompiler.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E4DA4373227008DC00DCB2E5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + E4DA4378227008DC00DCB2E5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + E4DA437F227008DD00DCB2E5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E4DA436D227008DC00DCB2E5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E4DA4367227008DC00DCB2E5 = { + isa = PBXGroup; + children = ( + E4DA4372227008DC00DCB2E5 /* IntentsCompiler */, + E4DA4371227008DC00DCB2E5 /* Products */, + ); + sourceTree = ""; + }; + E4DA4371227008DC00DCB2E5 /* Products */ = { + isa = PBXGroup; + children = ( + E4DA4370227008DC00DCB2E5 /* IntentsCompiler.app */, + ); + name = Products; + sourceTree = ""; + }; + E4DA4372227008DC00DCB2E5 /* IntentsCompiler */ = { + isa = PBXGroup; + children = ( + E4DA4373227008DC00DCB2E5 /* AppDelegate.swift */, + E4DA437F227008DD00DCB2E5 /* Info.plist */, + E43E3E4322700E1C00DBCD82 /* Intents.intentdefinition */, + E4D2464422700F1C00587E6F /* LaunchScreen.storyboard */, + E4DA4377227008DC00DCB2E5 /* Main.storyboard */, + ); + path = IntentsCompiler; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E4DA436F227008DC00DCB2E5 /* IntentsCompiler */ = { + isa = PBXNativeTarget; + buildConfigurationList = E4DA4382227008DD00DCB2E5 /* Build configuration list for PBXNativeTarget "IntentsCompiler" */; + buildPhases = ( + E4DA436C227008DC00DCB2E5 /* Sources */, + E4DA436D227008DC00DCB2E5 /* Frameworks */, + E4DA436E227008DC00DCB2E5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = IntentsCompiler; + productName = IntentsCompiler; + productReference = E4DA4370227008DC00DCB2E5 /* IntentsCompiler.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E4DA4368227008DC00DCB2E5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1020; + LastUpgradeCheck = 1020; + ORGANIZATIONNAME = Airbnb; + TargetAttributes = { + E4DA436F227008DC00DCB2E5 = { + CreatedOnToolsVersion = 10.2; + }; + }; + }; + buildConfigurationList = E4DA436B227008DC00DCB2E5 /* Build configuration list for PBXProject "IntentsCompiler" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E4DA4367227008DC00DCB2E5; + productRefGroup = E4DA4371227008DC00DCB2E5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E4DA436F227008DC00DCB2E5 /* IntentsCompiler */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E4DA436E227008DC00DCB2E5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E4DA4379227008DC00DCB2E5 /* Main.storyboard in Resources */, + E4D2464522700F1C00587E6F /* LaunchScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E4DA436C227008DC00DCB2E5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E43E3E4422700E1C00DBCD82 /* Intents.intentdefinition in Sources */, + E4DA4374227008DC00DCB2E5 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + E4DA4377227008DC00DCB2E5 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + E4DA4378227008DC00DCB2E5 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + E4DA4380227008DD00DCB2E5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + 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 = 12.2; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E4DA4381227008DD00DCB2E5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + 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 = 12.2; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E4DA4383227008DD00DCB2E5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 5LL7P8E8RA; + INFOPLIST_FILE = IntentsCompiler/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.IntentsCompiler; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E4DA4384227008DD00DCB2E5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 5LL7P8E8RA; + INFOPLIST_FILE = IntentsCompiler/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.airbnb.IntentsCompiler; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E4DA436B227008DC00DCB2E5 /* Build configuration list for PBXProject "IntentsCompiler" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E4DA4380227008DD00DCB2E5 /* Debug */, + E4DA4381227008DD00DCB2E5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E4DA4382227008DD00DCB2E5 /* Build configuration list for PBXNativeTarget "IntentsCompiler" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E4DA4383227008DD00DCB2E5 /* Debug */, + E4DA4384227008DD00DCB2E5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = E4DA4368227008DC00DCB2E5 /* Project object */; +} diff --git a/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/AppDelegate.swift b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/AppDelegate.swift new file mode 100644 index 00000000..bef7c804 --- /dev/null +++ b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/AppDelegate.swift @@ -0,0 +1,17 @@ +// Created by Michael Bachand on 4/23/19. +// Copyright © 2019 Airbnb Inc. All rights reserved. + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + +} diff --git a/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/Base.lproj/Main.storyboard b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/Base.lproj/Main.storyboard new file mode 100644 index 00000000..9a2bae08 --- /dev/null +++ b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/Base.lproj/Main.storyboard @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/Info.plist b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/Info.plist new file mode 100644 index 00000000..0af59823 --- /dev/null +++ b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/Info.plist @@ -0,0 +1,49 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSUserActivityTypes + + BuckPhotoIntent + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/LaunchScreen.storyboard b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/LaunchScreen.storyboard new file mode 100644 index 00000000..bfa36129 --- /dev/null +++ b/App/SiriShortcut/_IntentsCompiler/IntentsCompiler/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/App/SiriShortcut/_IntentsCompiler/README.md b/App/SiriShortcut/_IntentsCompiler/README.md new file mode 100644 index 00000000..fd85a639 --- /dev/null +++ b/App/SiriShortcut/_IntentsCompiler/README.md @@ -0,0 +1,7 @@ +# IntentsCompiler Xcode Project + +`IntentDefinitionCodegen` is internal to Xcode. As such, Apple does not expose a standalone command for creating the Swift (or Objective-C) interface from an .intentdefinition. + +Airbnb has filed a Radar requesting this functionality. + +Until then, we are using this Xcode project to generate code from the .intentdefinition. diff --git a/App/ViewController+INUIAddVoiceShortcutViewControllerDelegate.swift b/App/ViewController+INUIAddVoiceShortcutViewControllerDelegate.swift new file mode 100644 index 00000000..e06f1d77 --- /dev/null +++ b/App/ViewController+INUIAddVoiceShortcutViewControllerDelegate.swift @@ -0,0 +1,23 @@ +// Created by Michael Bachand on 4/27/19. +// Copyright © 2019 Airbnb Inc. All rights reserved. + +import IntentsUI +import UIKit + +#if !DISABLE_SIRI_SHORTCUT +@available(iOS 12.0, *) +extension ViewController: INUIAddVoiceShortcutViewControllerDelegate { + + func addVoiceShortcutViewController( + _ controller: INUIAddVoiceShortcutViewController, + didFinishWith voiceShortcut: INVoiceShortcut?, + error: Error?) + { + controller.dismiss(animated: true) + } + + func addVoiceShortcutViewControllerDidCancel(_ controller: INUIAddVoiceShortcutViewController) { + controller.dismiss(animated: true) + } +} +#endif diff --git a/App/ViewController.swift b/App/ViewController.swift index fda0380b..b06ab777 100644 --- a/App/ViewController.swift +++ b/App/ViewController.swift @@ -1,6 +1,7 @@ import ASwiftModule import Cpp1 import CryptoSwift +import IntentsUI import Objc1 import ObjcAndSwift import PromiseKit @@ -32,6 +33,17 @@ class ViewController: UIViewController { self.view.addSubview(label) label.sizeToFit() label.center = self.view.center + + #if !DISABLE_SIRI_SHORTCUT + if #available(iOS 12, *) { + let siriButton = INUIAddVoiceShortcutButton(style: .black) + siriButton.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(siriButton) + siriButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + siriButton.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -24).isActive = true + siriButton.addTarget(self, action: #selector(addToSiri(_:)), for: .touchUpInside) + } + #endif } override func viewDidAppear(_ animated: Bool) { @@ -117,4 +129,19 @@ class ViewController: UIViewController { print("AFNetworking's version is \(SwiftWithPrecompiledDependencyClass.networkingLibraryVersionNumber)") } + + @available(iOS 12.0, *) + @objc + dynamic private func addToSiri(_ sender: Any) { + #if !DISABLE_SIRI_SHORTCUT + let intent = BuckPhotoIntent() + intent.suggestedInvocationPhrase = localizedString("Show me a buck", "Invocation phrase for Siri Shortcut") + if let shortcut = INShortcut(intent: intent) { + let viewController = INUIAddVoiceShortcutViewController(shortcut: shortcut) + viewController.modalPresentationStyle = .formSheet + viewController.delegate = self + present(viewController, animated: true, completion: nil) + } + #endif + } } diff --git a/Config/buck_rule_macros.bzl b/Config/buck_rule_macros.bzl index 793f34cf..8d8ebce8 100644 --- a/Config/buck_rule_macros.bzl +++ b/Config/buck_rule_macros.bzl @@ -260,10 +260,12 @@ def logging_genrule( ) # Takes in a .mlmodel and produces a Swift interface and a compiled .mlmodelc. -# - parameter resource_source_name: The expected name of the Swift interface to be included in `srcs`. +# - parameter resource_source_name: The expected name of the Swift interface to be included in +# `srcs`. # - parameter resource_dependency_name: The expected name of the resource to add to `deps`. -# - parameter model_directory: The relative path to folder where the .mlmodel lives. Must include a trailing slash. -# - parameter model_name: The name of the .mlmodel. Do not include the .mlmodel suffix. +# - parameter model_directory: The relative path to the folder where the .mlmodel lives. Must +# include a trailing slash. +# - parameter model_name: The name of the .mlmodel. Do not include the ".mlmodel" suffix. # - parameter swift_version: The expected Swift version for the generated Swift interface file. def mlmodel_resource( resource_source_name, @@ -281,10 +283,10 @@ def mlmodel_resource( out = "%s.swift" % model_name, ) - modelc_resource = resource_dependency_name + "_compiled_model" + genrule_name = "compile_" + resource_dependency_name # Create a genrule to compile the mlmodelc from the mlmodel. logging_genrule( - name = modelc_resource, + name = genrule_name, srcs = [model_directory + model_name + ".mlmodel"], bash = 'xcrun coremlc compile "$SRCS" "\$(dirname "$OUT")"', out = "%s.mlmodelc" % model_name, @@ -294,7 +296,113 @@ def mlmodel_resource( native.apple_resource( name = resource_dependency_name, dirs = [ - ":" + modelc_resource, + ":" + genrule_name, ], files = [], ) + +# Takes in an .intentdefinition and produces the Swift interface for the specified intent. +# - parameter interface_source_name: The expected name of the Swift interface to be included in +# `srcs`. +# - parameter intent_name: The name of the intent in the .intentdefinition for which source should +# be generated. Do not include an "Intent" suffix. +# - parameter compiler_xcodeproj: The relative path to the .xcodeproj used to generate the Swift +# interface of the .intentdefinition. This project should reference the .intentdefinition that +# contains `intent_name`. +def intent_interface( + interface_source_name, + intent_name, + compiler_xcodeproj): + + script = """ + IFS=' ' read -ra SRCS_ARRAY <<< "$SRCS" + intents_compiler_xcodeproj="${SRCS_ARRAY[0]}" + + # We cannot upgrade CI to Xcode 10 yet. If we are still in Xcode 9, output a dummy Swift file. + # It doesn't matter since Siri Shortcuts don't work in Xcode 9 anyway. + # We can delete this alternate code path when https://github.com/airbnb/BuckSample/issues/102 + # is resolved. + if xcodebuild -version | grep "Xcode 1"; then + # `IntentDefinitionCodegen` is within Xcode. + xcodebuild \ + -configuration Release \ + -scheme 'IntentsCompiler' \ + -project "$intents_compiler_xcodeproj" \ + -derivedDataPath "$TMP" \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO \ + CODE_SIGNING_ALLOWED=NO + + intent_interface="`find "$TMP" -name %sIntent.swift`" + + if [[ -z "$intent_interface" ]]; then + echo "Compiler xcodeproj produced no Swift interface for the provided intent" + echo "Are you sure the .intentdefinition defines the intent?" + exit 1 + fi + else + intent_interface="$TMP/Dummy.swift" + echo "class Dummy { }" > "$intent_interface" + fi + + cp "$intent_interface" "$OUT" + """ % intent_name + + logging_genrule( + name = interface_source_name, + srcs = [ + compiler_xcodeproj, + ], + bash = script, + out = "%sIntent.swift" % intent_name, + ) + +# Proceses an .intentdefinition, creating a resource that can be added as a dependency. +# - parameter resource_dependency_name: The expected name of the resource to add to `deps`. +# - parameter definition_directory: The relative path to the folder where the .intentdefinition +# lives. Must include a trailing slash. +# - parameter definition_name: The name of the .intentdefinition. Do not include the +# ".intentdefinition" suffix. +# - parameter localization: A localization abbreviation, such as "en". The processed +# .intentdefinition will live in a .lproj directory of this name. +def intentdefinition_resource( + resource_dependency_name, + definition_directory, + definition_name, + localization): + + lproj_directory = "%s.lproj/" % localization + + genrule_name = "process_" + resource_dependency_name + # Create a genrule to process the .intentdefinition file. As far as we can tell, the + # `intentbuilderc` command makes no change to the .intentdefinition file. We nevertheless run it + # in an effort to be defensive. + logging_genrule( + name = genrule_name, + srcs = [definition_directory + definition_name + ".intentdefinition"], + bash = """ + lproj_dir=`dirname $OUT` + mkdir "$lproj_dir" + + # We cannot upgrade CI to Xcode 10 yet. If we are still in Xcode 9, do not process. + # It doesn't matter since Siri Shortcuts don't work in Xcode 9 anyway. + # We can delete this alternate code path when + # https://github.com/airbnb/BuckSample/issues/102 is resolved. + if xcodebuild -version | grep "Xcode 1"; then + xcrun intentbuilderc $SRCS $TMP "" + else + cp $SRCS $TMP + fi + definition_basename=`basename $SRCS` + cp "$TMP/$definition_basename" $OUT + """, + out = lproj_directory + definition_name + ".intentdefinition", + ) + + native.apple_resource( + name = resource_dependency_name, + visibility = ["PUBLIC"], + variants = [ + ":" + genrule_name, + ] + ) diff --git a/Config/configs.bzl b/Config/configs.bzl index 3d2934c3..3e5380ac 100644 --- a/Config/configs.bzl +++ b/Config/configs.bzl @@ -57,6 +57,7 @@ def app_binary_configs(name): "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES": "YES", "DEVELOPMENT_LANGUAGE": DEVELOPMENT_LANGUAGE, "PRODUCT_BUNDLE_IDENTIFIER": bundle_identifier(name), + "CODE_SIGN_ENTITLEMENTS": (name + ".entitlements"), } binary_config = SHARED_CONFIGS + binary_specific_config binary_config = config_with_updated_linker_flags(binary_config, ALL_LOAD_LINKER_FLAG) @@ -118,3 +119,10 @@ def message_binary_configs(name): } config = config_with_updated_linker_flags(config, ALL_LOAD_LINKER_FLAG) return configs_with_config(config) + +def intent_binary_configs(info_plist_substitutions): + config = { } + config.update(SHARED_CONFIGS) + config.update(info_plist_substitutions) + config = config_with_updated_linker_flags(config, ALL_LOAD_LINKER_FLAG) + return configs_with_config(config)