returning something from a cocoa method / waiting for the method

quite simply,

I’m starting to farm more and more functions out into Cocoa methods, using, for example:

set myThread to NSThread's detachNewThreadSelector_toTarget_withObject_("isInternetAvail:", current application's networkTest, missing value)

This goes off and logs something currently…

+(bool)isInternetAvail:(NSString *)passedString
{
NSLog@"Here!"
bool success = true;
return success;
}

The actual cocoa stuff is unimportant right now. I’ve been experimenting, and we have everything working as it should.

However, what I’d like to do is for the method called in cocoa to return something back to a property in AppleScript

It doesn’t matter how long I wait, myThread never gets populated with anything.

Ideas?

Hi,

first of all, using Cocoa Grand Central Dispatch (GCD) is the better choice to perform asynchronous tasks.
The block ^{} is executed asynchronously, you can store the result into a property or instance variable
or call a handler / method as a callback. The handler can also be implemented by using ASOC code on the AppleScript side


- (void)checkInternetConnection
{
    __weak id weakSelf = self;
    dispatch_async(dispatch_get_current_queue(), ^{
        SCNetworkReachabilityRef target;
        SCNetworkConnectionFlags flags = 0;
        Boolean result;
        target = SCNetworkReachabilityCreateWithName(NULL, "[url=http://www.google.com]www.google.com[/url]");
        result = SCNetworkReachabilityGetFlags(target, &flags);
        CFRelease(target);
        [weakSelf didCheckInternetConnection:(result && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired))];
    });

}

- (BOOL)didCheckInternetConnection:(BOOL)result
{
    NSLog(@"Here! : %d", result);
    return result;
}


Or NSOperationQueue…

different name “ same technology :wink:

I’m using NSOperationQueue only if there is a real queue with multiple task blocks

Ironically, the cocoa stuff is the stuff I actually had working!

I’m looking at chapter “From Shell Script to Task” of the book, as it appears to be the most relevant. The issue I’m having is that I can’t seem to start to understand how I would mould that around the

set myThread to NSThread's detachNewThreadSelector_toTarget_withObject_("isInternetAvail:", current application's networkTest, missing value)

The nature of asynchronous tasks is to do several things (apparently) at the same time.
Therefore the detachNewThreadSelector method cannot return anything directly because the main “timeline” moves on instantly.
Asynchronous tasks usually return their result in a callback or delegate method

That makes sense, and is what I may have understood subconsciously

This brings me to a different possible resolution then:

How can I set a applescript property from Cocoa (as I’m struggling with implementing a cocoa / ASOC callback bridge)

In the Cocoa .m file, create a protocol outside the implementation block

@interface NSObject (ASOCClass)
- (void)didCheckInternetConnection:(NSNumber *)isUP;
@end

@implementation CocoaClass
.

and change my example handler to


- (void)didCheckInternetConnection:(BOOL)isUP
{
id ASOCClass = NSClassFromString(@"nameOfASOCClass"); // change nameOfASOCClass to the appropriate name
[ASOCClass didCheckInternetConnection:@(isUP)];
}

the name of the Cocoa method and the delegate method can be different

in the ASOC class implement


property myAppleScriptProperty : false

on didCheckInternetConnection_(isUP)
	set my myAppleScriptProperty to isUP as boolean
end didCheckInternetConnection_

Stefan,

firstly, I apologise for not replying sooner… Be4en on my holidays :slight_smile:

So, after struggling for a couple of hours on this, I had a coffee, and sat down again.

I found this thread (http://macscripter.net/viewtopic.php?id=37162) which works as expected.

However, and I think this is down to my lack of understanding, I just can’t get the examples you’ve given above working :frowning:

I’d love to do this asynchronously, rather than synchronously, and get this working

Righty, I’ve resolved some of the issues but it’s still erring…

--  AppDelegate.applescript

property NSThread : class "NSThread"
property ConnectivityMethods : class "ConnectivityMethods"
property myAppleScriptProperty : false

script AppDelegate
	property parent : class "NSObject"
    property myAppleScriptProperty : false
    
    on dintConn_(isUP)
        set my myAppleScriptProperty to isUP as boolean
        log "Yo!" & my myAppleScriptProperty
    end dintConn_
	
	on applicationWillFinishLaunching_(aNotification)
	end applicationWillFinishLaunching_
    
    
    on testConn_(sender)
        log "Here"
        set myString to current application's NSString's stringWithString_("www.google.com")
        set myThread to NSThread's detachNewThreadSelector_toTarget_withObject_("hostIsReachable:", current application's ConnectivityMethods, myString)
        log "returned"
    end testConn_
	
	on applicationShouldTerminate_(sender)
		return current application's NSTerminateNow
	end applicationShouldTerminate_
	
end script
//  ConnectivityMethods.h

#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>

@interface ConnectivityMethods : NSObject {}

+(void)hostIsReachable:(NSString *)host;
+(void)didCheckInternetConnection:(BOOL)isUP;

@end
//  ConnectivityMethods.m

#import "ConnectivityMethods.h"
#import <SystemConfiguration/SystemConfiguration.h>

@implementation ConnectivityMethods

+(void)didCheckInternetConnection:(BOOL)isUP
{
    id ASOCClass = NSClassFromString(@"dintConn"); // change nameOfASOCClass to the appropriate name
    [ASOCClass dintConn:@(isUP)];
}

+(void)hostIsReachable:(NSString *)host
{
    __weak id weakSelf = self;
    NSParameterAssert(nil != host);
    SCNetworkReachabilityRef    target;
    BOOL result;
    SCNetworkConnectionFlags flags = 0;
    target = SCNetworkReachabilityCreateWithName(NULL, [host UTF8String]);
    result = (BOOL)SCNetworkReachabilityGetFlags(target, &flags);
    CFRelease(target);
    NSLog(@"Here! : %d", result);
    [self didCheckInternetConnection:result];
    }
@end

Changes:

Renamed the ASOC method to dintConn, as xcode was complaining that I had duplicate class names.

I get:

Instance Method “-dintConn” not found (return type defaults to ‘id’) on line [ASOCClass dintConn:@(isUP)];

I created a simple test project.
Indeed the compiler complains if the method name is used twice.
The reason is the NSObject category which is needed as an interface declaration of the delegate method

Download: ConnectivityTest

:slight_smile:

Have learnt a lot today.

The only real differences I see if that I had

property ConnectivityMethods : class "ConnectivityMethods"
    property myAppleScriptProperty : false

Outside of the script / end script block in AS, and the fact I’d confused myseld between ASOC Class and Method

Humble thanks again Stefan!

Thanks StefanK for this simple project, very useful
I have been playing with this and noticed two puzzling things, probably touching the limits of ASOC :

  1. Your code sets the myAppleScriptProperty boolean value as confirmed by the log following immediately but somehow it seems to only change it on an instance of the script and does not modify the AppDelegate property. If you add a simple button in the interface and link it to a handler to simply log the value of that property it is still unchanged (It will report myAppleScriptProperty as false even tho you are connected and your callback to applescript just logged a true…)

  2. When inside the called back applescript handler (didCheckInternetConnection_) it seems we have a limited scope as we can do certain things, like set variables or call more handlers but a simple cocoa method like NSTextfield setStringValue_ will report as error -10000, unrecognized function.

Any idea or workaround those issues ?

Thanks again for your resourceful posts here

I am looking at this sample project also. StefanK might have something else in mind for his demo than what you wanted.

I see the same thing @mikaelectron mentions- the Cocoa class does a call back on the ASOC class not instance, effectively not really returning the value. Though that construct is nice, I don’t see it’s usefulness with respect to this project. If one were to make a project where the ASOC object only handled secondary functions, that might be pertinent. But since the ASOC object is the app delegate, you’d probably want that value returned.

Secondly, wrapping the main body of hostIsReachable in an async call is usually good form, it does actually preclude proper return value from being returned (when I was working with the code). In most cases, I would want to check for network reachability before presenting some other alert to the user, or proceeding with the function.

Here’s some changes I made (I added a button also):

script AppDelegate
	property parent : class "NSObject"
    property ConnectivityMethods : class "ConnectivityMethods"
    property myAppleScriptProperty : false
    property myButton : missing value
	
	on applicationWillFinishLaunching_(aNotification)
        set my myAppleScriptProperty to (ConnectivityMethods's hostIsReachable_("www.google.com") as boolean)
	end applicationWillFinishLaunching_
	
	on applicationShouldTerminate_(sender)
		-- Insert code here to do any housekeeping before your application quits 
		return current application's NSTerminateNow
	end applicationShouldTerminate_
    
    on pushButton_(sender)
        tell me to log "current value= " & my myAppleScriptProperty
    end
	
end script

ConnectivityMethods.m

+ (BOOL)hostIsReachable:(NSString *)host
{
    BOOL nsResult = NO;

    SCNetworkReachabilityRef target;
    SCNetworkConnectionFlags flags = 0;
    Boolean result;
    target = SCNetworkReachabilityCreateWithName(NULL, [host UTF8String]);
    result = SCNetworkReachabilityGetFlags(target, &flags);
    CFRelease(target);

    if ((result && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired))) {
        nsResult = YES;
    }

    NSLog(@"hostIsReachable= %d", nsResult);
    return nsResult;
}

This might not be right for everything, but it will set the appdelegate’s property to True.

Thanks SuperMacGuy

yes, your code will work and set the property fine.

I was interested in this “call back from cocoa” to AS delegate for another purpose (using GCDAsyncSocket) and since it uses another method that will return the result and not the one originally called by the AS I cannot use return to directly get the output.
I found another way around for the moment using UserDefaults but was wondering about this class / instance issue and if there was any possible fix or solution.