How to get attachment's (NSItemProvider) file path in Swift 'Share App Extension'? - swift

Note: I am very new to Swift programming (2 days only) and I am working on this piece of code as part of an ElectronJS project. So please don't mind my ignorance regarding the basics of the language. Thanks.
I have created a Swift app containing a Share App Extension.
Requirements:
The Share App Extension should be able to send the absolute file path of the shared files to the container app, i.e. If the user selects a file (abc.txt) from Desktop in Finder and Shares to my Application, then the Share App Extension should be able to get the file path as
/users/userName/Desktop/abc.txt
What I am struggling with here is how to get the file path of the files shared with the Share App Extension. What is the way to get file path of the attachments in NSExtensionItem that is available to the Share App Extension or is it available from some other object ?
(I am able to successfully use App Groups to share data between Share App Extension and the Application)
In the final project, the Share App Extension becomes a part of an ElectronJS project as mentioned earlier.
Is there a standard way to share the aforementioned information (file path of the attachments) from the Share App Extension to the main/renderer processes of the Electron application.

I am sharing the solutions below. Please bear in mind that these might not be the best possible solutions and I am open to suggestions.
Solution to Point #1:
Briefing: The user selects files from Finder to be shared via the Share App Extension of the application which is registered with the OS if the extension context of the selection matches to that of the Share App Extension. Upon doing so, the Share App Extension receives the extension context alongwith NSExtensionItem. The NSExtensionItem object contains the NSItemProvider object which is the object you'd get for all the files (attachments) shared via the Share App Extension.
Now, for each item type that you receive via the Share App Extension, after looking for the data that your function recognizes via hasItemConforminToTypeIdentifier(_:), you can use UTI (Uniform Type Identifier) to identify its data.
Remedy: Here, the crucial part is to understand that one should be treating their input files as firstly being of the type: kUTTypeURL. Then, in the completionHandler for the loadItem method of the NSItemProvider object one would get NSURL which is basically the file path I was looking for.
Solution to Point #2:
Briefing: The Share App Extension has the luxury of being written in Swift but the main app in our project does not ! The main application is written in ElectronJS which is far far far far from being integratable with Swift ! Except for the fact that the application written in ElectronJS has the ability to be packaged in the form of a dmg application, there is very little integratability between ElectronJS and Swift as far as the language and framework intertwining is concerned.
Premise:
So, the premise is to be able to share the filepaths extracted earlier to be passed from the Share App Extension (written in Swift) to the main application (written in ElectronJS). Now, if the main application was a Cocoa application, things would have been much easier. If both of them belong to the same App group, then using the Swift APIs they could have read/written synchronously to the Shared Memory. However, the problem arises as those APIs are not available in ElectronJS. One remedy can be to run the Swift code in a sandboxed environment within the ElectronJS application using nodeJS libraries. However, a sandboxed environment presents its own nuances in data sharing. So, I have kept this approach on hold for now.
So, the approach that I have chosen right now is to use App Data Directory to share this intermediary information. The Share App Extension would be writing the filepath information in the App Data directory of the application and the ElectronJS application would use nodeJs APIs to access this information. Keep in mind that this is a very primitive approach and requires menial efforts but the requirements for this particular case doesn't need stringent security measures anyhow.
However, I am positively looking for a better way to solve Problem #2.

Related

SwiftUI / AppKit Interaction between embedded app (e.g. extension) and main app

In my SwiftUI / AppKit application I have a main app and an extension (in my case a Finder Sync Extension) and I would like to send some data from my extension to the main application (call a method in the main app for example).
The extension is embedded in the main application.
I have actually found a working solution to this issue but its not really optimal. My solution works by opening a URL in the extension and accepting the data in the main app by using an URL Scheme. (This is far from optimal, though because I need to encode the object I actually want to pass a JSON, URL encode it and then decode it on the other side)
To sum up, what is the best/easiest way to send data/interact between an embedded application (e.g. an extension) and the main application?
There are a variety of ways to do this, but most likely, you'll want the main app to write data somewhere the extension can get to it, and then read it from the extension.
You should read through the docs for FileManager.containerURL(forSecurityApplicationGroupIdentifier:), which has some discussion of the app group's directory in MacOS: https://developer.apple.com/documentation/foundation/filemanager/1412643-containerurl
Once you've got a directory that both the main app and extension can read to, you can write whatever you need there and then read it at the appropriate time from the extension.

With Chrome Apps soon to be removed, is there another way to edit a local file?

The Chrome Apps API has the very useful FileSystem API which allows a user to select a file for an app to edit (read and write changes to). However, with the entire Apps API soon to be removed, what other ways exists to edit a file on the local file system?
This is not an opinion-based question, I am asking for all conceivable alternatives.
Per https://developers.chrome.com/apps/migration:
Q: My app uses the chrome.fileSystem API to read and write user-specified files and / or directories. Can this be done on the open web?
A: In general, no. The open web can read single files that the user opens, but cannot retain access to those files, write to those files, or have any access to directories.
If it is critical for your app to read and write directories (e.g. it is a text editor with a folder view), you will need to either have a native helper app and extension combo, or create a native app.

How do I open a file with unknown associations in iOS?

I think it's rather impossible, but will ask anyway. My application uses file association to open some types of files. What I need is to make file associations within my app. For example I have some files in my app's Documents folder and when user wants to open that it would be a great idea to ask him in which application he would like it to open (like Mail app does).
It can possibly be done with URL schemes, but if I don't know what applications user has, it can't be used. So, is there any way to use the device's file associations within an application?
You should take a look at Document Interaction Programming Topics for iOS. It explains how you can use the UIDocumentInteractionController class to present the user a list of apps which support a given file.

Is there a way to run a script on iOS?

I need to define a processing rule for web data in iOS and thought it would be a good idea to pull the processing rule as a script file from my server and execute it on the iOS device, since the web API I'm interacting with might change URLs or response syntax and I need to be able to fix such issues fast and cannot rely on pushing an update (takes forever).
I wanted to do it with a small JS file that is pulled from my server every once and a while, but unfortunately iOS doesn't include the JavaScriptCore framework.
Are there other options?
Apple developer agreement will not let you run a downloaded, interpreted script, on the device.
Your best bet is probably downloading a data structure (potentially in JSON format) and parse that and take some predefined actions in your client code based on that, rather than trying to execute the downloaded code directly.
You can let a UIWebView run a Javascript snippet, or you could use another scripting language like LUA (don't forget to add LUA for this). The real problem is: You are not allowed to download code from a webserver or somewhere else. Everything must either be already on the device, or calculated at runtime.
Depending on the information that you want, you could use an XML file that includes the new URLs and parse it, but I don't know if this fits your need.
You can compile JavaScriptCore into your app, evidently, and have it approved by Apple. However, as Mehrdad notes, any scripts run in the app must already be in the app at the time the app is reviewed.

iOS App-to-App Trasnmission of Data using new Document Support API

Problem:
Building Enterprise Applications of a Suite Nature, and need to be able to pass data from one application to another. Example: App1 is a barcode reader that produces and inventory list. App2 needs a "fresh" copy of the same inventory list information that App1 just produced in order to accomplish its goal of producing purchase orders. The two apps and databases are two large to squeeze together in single app, plus the suite will continue to grow with more and more apps.
Understanding:
I fully understand that "Each" application is in it's own sandbox. However, in reading through the documents regarding the new UIDocumentInteractionController API, it appears that an application can dip outside of the sandbox just a little to "Read-In", "View", or "Open-In" a document that was not apart of the bundle or created within the application.
Data Flow:
I'm trying to keep it simple. I have been using the DocInteraction sample application downloaded from Apple, and another application...called App1 to try and work with a simple text file. In App1, I create a simple txt file, and save it to the documents folder. (But this is still inside the app's sandbox?). in the DocInteraction modified sample, I have been trying to figure a way to "View", "Open-In", or better yet "Read-In" the created txt file. If I can pass a simple txt file between the two, I can include a CSV structure to update the databases on each side when ever the applications are opened.
I have tried to utilize the Launch Options Keys with no luck.
In short, I just can't seem to get my head around:
Where App1's data needs to go?
How to find the data in the other App, say App2?
How do you "Open" the file that exist inside another application's sandbox?
End Result:
I have tried to stay away from the
The Document Interaction docs outline:
Previewing a Document or Presenting Options
Registering Your Support of File Types
Opening Files From Other Apps
Displaying and Printing Quick Look Previews
It is the "Opening Files From Other Apps" that I am most interested with. It directs me to utilize the application:didFinishLaunchingWithOptions: method by passing in dictionary values for the keys. This is where I get lost?? How do I set the keys so that it knows "WHERE" and "WHAT" to look for? And I'm still not clear the proper director that App1 should be saving information to in order for the keys to point to the correct place?
Opening email file attachments and opening pdfs in iBooks can't be the only places where you can utilize this API or else Apple wouldn't have went through all the work, they are already allow to talk from App-To-App.
Note: I'm not trying to get App1 to directly transmit data into App2's files. I don't think that would be allowed by Apple at all! I'm trying to get App1 to zip up its data, save it in proper location, so when user decides to use App2, the data can then be available to App2 by "reading-in" the data.
If someone has a sample application, tutorial, or even a solid idea how to get this working I would really appreciate the help.
-Thanks!
P.S. Somebody with 1,500 or higher reputation please create a "UIDocumentInteraction" tag for stackoverflow!
I got it working last month. Here's my mental model:
App1 creates a file anywhere in its sandbox.
App1 calls docinteraction to display the "Open In" GUI for that file
User picks "Open in App2"
The iOS copies the file from one sandbox to the other and launches App2.
App2 implements didfinishlaunchingwithURL and loads the supplied URL (which is the copy in its sandbox)