jpo
January 18, 2023, 2:46pm
1
Hi,
A nice addition to JUCE would be to support JUCEApplication::anotherInstanceStarted() on iOS. If I understand correctly that would mean implementing application: didFinishLaunchingWithOptions:
and application:openURL:sourceApplication:annotation:
so that when we click on a supported file format in the ios Files browser, it opens the app (this is already working) and calls anotherInstanceStarted()
jpo
January 18, 2023, 4:09pm
2
Here is an example of implementation, the NSURL file is copied in the app temp folder, and anotherInstanceStarted is called with the path of that file. It seems that I only need to define application:openUrl :
// This is an internal list of callbacks (but currently used between modules)
Array<AppInactivityCallback*> appBecomingInactiveCallbacks;
+
+#if JUCE_IOS_ANOTHERINSTANCESTARTED
+
+ //copied from juce_ios_FileChooser.mm
+ juce::URL nsURLToJuceURL(NSURL *url)
+ {
+ [url startAccessingSecurityScopedResource];
+
+ NSError* error = nil;
+
+ auto* bookmark = [url bookmarkDataWithOptions: 0
+ includingResourceValuesForKeys: nil
+ relativeToURL: nil
+ error: &error];
+
+ [bookmark retain];
+
+ [url stopAccessingSecurityScopedResource];
+
+ URL juceUrl (nsStringToJuce ([url absoluteString]));
+
+ if (error == nil)
+ {
+ setURLBookmark (juceUrl, (void*) bookmark);
+ }
+ else
+ {
+ [[maybe_unused]] auto desc = [error localizedDescription];
+ jassertfalse;
+ }
+ return juceUrl;
+ }
+
+ // download NSURL to a file in the sandbox temp folder
+ String nsURLToTempFile(NSURL *nsurl) {
+ juce::URL url = nsURLToJuceURL(nsurl);
+ File target = File::getSpecialLocation(File::tempDirectory).getChildFile(URL::removeEscapeChars(url.getFileName()));
+ auto input_stream = url.createInputStream(URL::InputStreamOptions (URL::ParameterHandling::inAddress));
+ auto output_stream = target.createOutputStream();
+ output_stream->setPosition(0);
+ MemoryBlock block;
+ while (input_stream->readIntoMemoryBlock(block, 65536)) {
+ output_stream->write(block.getData(), block.getSize());
+ }
+
+ return target.getFullPathName();
+ }
+#endif // JUCE_IOS_ANOTHERINSTANCESTARTED
}
#if JUCE_PUSH_NOTIFICATIONS
@@ -57,6 +105,11 @@ namespace juce
- (void) applicationWillResignActive: (UIApplication*) application;
- (void) application: (UIApplication*) application handleEventsForBackgroundURLSession: (NSString*) identifier
completionHandler: (void (^)(void)) completionHandler;
+#if JUCE_IOS_ANOTHERINSTANCESTARTED
+- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url
+ sourceApplication:(NSString *)sourceApplication
+ annotation:(id)annotation;
+#endif // JUCE_IOS_ANOTHERINSTANCESTARTED
- (void) applicationDidReceiveMemoryWarning: (UIApplication *) application;
#if JUCE_PUSH_NOTIFICATIONS
@@ -141,6 +194,19 @@ JUCE_END_IGNORE_WARNINGS_GCC_LIKE
}
}
+#if JUCE_IOS_ANOTHERINSTANCESTARTED
+- (BOOL) application:(UIApplication *)application openURL:(NSURL *)url
+sourceApplication:(NSString *)sourceApplication
+ annotation:(id)annotation
+{
+ String tgt = nsURLToTempFile(url);
+ if (auto* app = JUCEApplicationBase::getInstance()) {
+ app->anotherInstanceStarted(tgt.quoted());
+ }
+ return true;
+}
+#endif // JUCE_IOS_ANOTHERINSTANCESTARTED
+
- (void) applicationWillTerminate: (UIApplication*) application
{
ignoreUnused (application);
1 Like
jpo
April 24, 2023, 10:48am
3
hum the nsURLToTempFile was not handling files > 65536 bytes correctly… Here is a better one:
String nsURLToTempFile(NSURL *nsurl) {
juce::URL url = nsURLToJuceURL(nsurl);
File target = File::getSpecialLocation(File::tempDirectory).getChildFile(URL::removeEscapeChars(url.getFileName()));
auto input_stream = url.createInputStream(URL::InputStreamOptions (URL::ParameterHandling::inAddress));
target.deleteFile();
auto output_stream = target.createOutputStream();
if (output_stream->openedOk()) {
output_stream->setPosition(0);
output_stream->truncate();
MemoryBlock block;
int bytes = 0;
while (input_stream->readIntoMemoryBlock(block, 65536)) {
output_stream->write(block.getData(), block.getSize());
bytes += block.getSize();
block.reset();
}
}
output_stream.reset();
input_stream.reset();
return target.getFullPathName();
}