I am working on extension for safari.I have checked we can communicate between host app and extension as we can run extension or close extension.But in my case I want to communicate with host app without closing extension app.
var MyExtensionJavaScriptClass = function() {};
MyExtensionJavaScriptClass.prototype = {
run: function(arguments) {
arguments.completionFunction({"baseURI": document.documentElement.innerHTML});
},
test: function(arguments) {
alert("Need to run without closing extension");
},
finalize: function(arguments) {
alert("Test Done");
// arguments contains the value the extension provides in [NSExtensionContext completeRequestReturningItems:expirationHandler:completion:].
// In this example, the extension provides a color as a returning item.
document.body.style.backgroundColor = arguments["bgColor"];
}
};
var ExtensionPreprocessingJS = new MyExtensionJavaScriptClass;
In above my JavaScript file I have run function that run at the time of extension run and finalize fun run as we call completeRequestReturningItems in objc side.I want to run my test function without closing extension
You don't.
To quote Apple's Extension Guidelines, from the section How an Extension Communicates.
There is no direct communication between a running extension and its containing app; typically, the containing app isn’t even running while its extension is running.
This isn't to say that you cannot, just that Apple doesn't want you, and the ability to do so is probably either private or non-existent.
Quick terminology level set:
Containing App = "an app that contains one or more extensions is called a containing app"
Host App = "An app that can let users choose an extension to help them perform a task is called a host app."
That being said, Apple does not supply a communication stream from Host App to extension. In your case, you can load data initially with the run() in the JS Preprocessing file and then respond with data on exit of the extension with finalize().
Related
I am working on an app where data (custom structs) can be transfered between instances of the app running on different phones. The app should be able to open Share Sheet and send the data to another device where it will automatically open the app for the data to be imported. (Having support for the share sheet is important because the app needs to be functional when there is not internet access and airdrop seems to be the only way to transfer data between phone when there is no internet.)
So far I have made the struct I would like to transfer between instances of the app conform to the Transferrable Protocol. I have also defined a custom Uniform Type Identifier in the code and the info.plist. With this, I am able to export the struct using the share sheet and it sends a json file ending in .stageresult. However, my issue is that when other devices receive the file they do not open it automatically nor do they give any way to do it manually. Also, I have been unable to find anything online about how to handle importing custom files. Is there a way I can call a function with the imported data to load it into my app? What is the proper way to handle importing custome universal type identifers using Swift/SwiftUI
import UniformTypeIdentifiers
import SwiftUI
import Foundation
extension UTType {
static var stageresult: UTType { UTType(exportedAs: "com.example.stageresult") }
}
struct StageResult: Codable {
var name: String
var start: Bool
var recordings: [Recording]
}
struct Recording: Codable {
var plate: String
var timestamp: Double
}
extension StageResult: Transferable {
static var transferRepresentation: some TransferRepresentation {
CodableRepresentation(contentType: .stageresult)
}
}
Defining Type Identifiers
Info.plist Supports Opening Documents In Place
TL;DR
On iOS 13 and Xcode 11, how can I configure an Intent to run in the background and just return the result, so it can be used as the input for other actions in the Shortcuts app?
Details of what I'm trying to achieve
My app has a list of songs that I want to expose via Shortcuts (actually, metadata about the song, not the song itself). The idea is to give advanced users a way to integrate this database of music with other apps or actions they want. For example, one may find useful to get a list of upcoming music for the next month, and use it to create Calendar events for each song. Having access to this list on the Shortcuts app can enable them to do this.
I have created an Intent called "List All Unread Music Releases" and defined its response as a list of objects that contains information about each song. The problem is, when I go to the Shortcuts app, create a new shortcut using this Intent, and run it, it opens my app instead of running in the background.
Steps I've done to create and configure Intents
Here's a high level definition of what I did to configure Intents in the project. The next section will have the actual source code and screenshots.
Created a new SiriKit Intent Definition File.
Created a new Intent.
Defined it's Title, Description, Parameters, and disabled the "Intent is eligible for Siri Suggestions" checkbox.
Defined the response property as an Array (because it's going to be a list of songs), and configured the Output to be this array property.
Created a new Intents Extension, with the checkbox "Include UI Extension" disabled. The idea here is to process the user request in the background and return a list with the results - no UI required.
In the Intents Extension target, defined the IntentsSupported array inside Info.plist with the name of the intent created in step 2.
Made the IntentHandler class implement the protocol generated for the intent created in step 2.
Code samples and screenshots
My SiriKit Intent Definition File and the GetUnreadReleases Intent:
The GetUnreadReleases Intent response:
The Intents Extension IntentHandler class:
import Intents
class IntentHandler: INExtension, GetUnreadReleasesIntentHandling {
func handle(intent: GetUnreadReleasesIntent, completion: #escaping (GetUnreadReleasesIntentResponse) -> Void) {
let response = GetUnreadReleasesIntentResponse(code: .success, userActivity: nil)
let release1 = IntentRelease(identifier: "1", display: "Teste 1")
release1.name = "Name test 1"
release1.artist = "Artist test 1"
response.releases = [release1]
completion(response)
}
func resolveMediaType(for intent: GetUnreadReleasesIntent, with completion: #escaping (IntentMediaTypeResolutionResult) -> Void) {
if intent.mediaType == .unknown {
completion(.needsValue())
} else {
completion(.success(with: intent.mediaType))
}
}
override func handler(for intent: INIntent) -> Any {
// This is the default implementation. If you want different objects to handle different intents,
// you can override this and return the handler you want for that particular intent.
return self
}
}
The Intents Extension Info.plist file:
Conclusion
So, I would like this intent to run in the background, assemble the list of songs based on the user defined parameters, and return this list to be used as an input to other actions in the Shortcuts app.
It looks like previous versions of the Intents editor (Xcode < 11 / iOS < 13.0) had a checkbox "Supports background execution" that did just that, but I can't find it anymore on Xcode 11.
Thanks to edford from Apple Developer Forums, I was able to make it work. In the intents definition file, the "Intent is eligible for Siri Suggestions" checkbox must be checked for the background execution to work.
I am implementing a NEPacketTunnelProvider and loading it from my view controller using:
var vpnManager: NETunnelProviderManager = NETunnelProviderManager()
...
let providerProtocol = NETunnelProviderProtocol()
providerProtocol.providerBundleIdentifier = "AA.BB.CC"
providerProtocol.serverAddress = "<something>"
...
self.vpnManager.localizedDescription = "My app"
self.vpnManager.protocolConfiguration = providerProtocol
self.vpnManager.isEnabled = true
self.vpnManager.connection.startVPNTunnel()
Parts marked with "..." seemed irrelevant.
My understanding (although it's really not clear in the documentation) is that when I do this, and I have a target that was created as type "NetworkExtension" with BundleId AA.BB.CC, that the extension would be loaded and executed properly. So, my understanding is that startTunnel (from NEPacketTunnelProvider) will implicitly be called from the code block above.
I put a NSLog("STARTING TUNNEL!!!!!") right at the top of the startTunnel method, but am not sure where to see that. So far, I have viewed the logs in:
Console
Window > Devices and simulators > View device logs
None of these appear to show the logs from within the extension. The problem is that the extension seems to crash before I can attach to the running process, so I have the feeling I'm just "missing" that log because I can't attach quickly enough.
Short question
How can I attach to a running network extension quickly enough so that I don't miss an NSLog that is run immediately?
The logs from Network extensions do not go to Xcode console. to see the logs from Network extension you have to follow the bellow steps.
Open the console application.
Select your iOS device running the network extension you will see all the logs from your device.
Filter the logs by Network extension target name now you will see the logs only from your network extension.
How can I attach to a running network extension quickly enough?
What I have been doing to solve this problem was, put the thread on sleep right before log statement.
sleep(60)
It will give you enough time to attach the Network Extension process for debug.
An object that helps a user share data from one place to another within your app, and from your app to other apps.
This is the statement written at the very beginning of UIPasteboard docs. But when I try to use it in two different apps accessing data set by other app I am getting nil everytime
DispatchQueue.global(qos: .background).async {
var i = 1
while(i > 0) {
let v = UIPasteboard.general.string
sleep(1)
print("Task : \(i)")
print("Value: \(v)")
i = i + 1
}
}
I am fetching data in above code and setting data as in below code.
UIPasteboard.general.string = "Hello"
NB: I have tested locally in this app it is setting data
Are you running iOS >=10? There was a privacy change regarding passing value between apps. Try reading the UIPasteBoard api doc : (https://developer.apple.com/documentation/uikit/uipasteboard).
Tl:dr You need to have both apps to be in the same app group (Communicating and persisting data between apps with App Groups)
To note: iOS apps are sandboxed. So the change in iOS 10 just enforces that feature.
EDITED: Since you can't use App Groups (different developer and/or products), you have to send data via a different channel. Try searching urlSchemes or store/fetch through a common server(tedious tho)
What I want to implement is as follow:
A-app (calling app) : request the return value of a-string sent as parameter : request(a-string) -> b-string.
B-app (plug-in installed separately by me or others, it plays the role of dictionary or database ) : search a-string from database and return the result (b-string).
With successful experiences of plug-in on android and with Apple's confident rhetoric of plug-in, I thought plug-in, of course, run on iOS. After a lot of hard work, however, I finally found out:
* Note : The creation and use of loadable bundles is not supported in iOS.*
Nonetheless, not giving up, I finally made it with custom URl and pasteboard:
A-app : write a-string and false state to pasteboard & call B-app via custom URL.
B-app : viewDidLoad runs following func and thereafter exit program ; func { read pasteboard and search from database & write the result(b-string) and true state to pasteboard }
A-app : while-loop detects whether state is false or true. if true, catch b-string from pasteboard.
Anyway it works but it's too long thus almost useless. Do you have any idea for better solutions? Why doesn't Apple allow plug-in for iOS? Any responses are welcome. Thank you.
I can't answer why Apple doesn't allow plug-ins, but I can offer some advice on what you're trying to achieve.
The common pattern for sending data back to your application is to implement a callback url, so the A-app would also implement a custom URI and add that to the uri sent to B-app.
B-app would then process the uri as you have already implemented, but then instead of exiting, it simply sends the data you requested in the uri passed to it.
See http://x-callback-url.com for more details and example implementations.