Ok, so I've got an application, and I want to make it scriptable. I set up the plist, I set up the sdef file.
So far I have only one apple Event command: gotoPage. it takes an integer. and returns a boolean.
The relevant XML is:
<command name="gotoPage" code="dcvwgoto" description="Goto a specified page">
<cocoa class="AEGoto"/>
<direct-parameter description="Page Number" type="integer"/>
<result description="True if the page exists, False othrewise" type="boolean"/>
</command>
I have an Objective-C class AEGoto.h:
#interface AEGoto :NSScriptCommand {
}
- (id)performDefaultImplementation;
- (id)performDefaultImplementation
{
int page = [[self directParameter] intValue];
Boolean retval = [gController setPage: page];
return retval? #"YES" : #"NO";
}
setPage: (int) is correct, and works fine.
When I call this, my program seems to work correctly. But then I get the error:
error "DocView got an error: 4 doesn’t understand the gotoPage message." number -1708 from 4
I also get, in my DocView output:
Error while returning the result of a script command: the result object... YES ...could not be converted to an Apple event descriptor of type 'boolean'. This instance of the class 'NSCFString' doesn't respond to -scriptingBooleanDescriptor messages.
However, if I return just the straight Boolean, I get:
Single stepping until exit from function -[NSScriptingAppleEventHandler handleCommandEvent:withReplyEvent:],
which has no line number information.
Program received signal: “EXC_BAD_ACCESS”.
so, I guess I've got 2 questions: 1) Why does it think it wants to tell 3 to goto a page? and 2) what is the correct way to return a Boolean from the applescript?
thanks.
return [NSNumber numberWithBool:retval];
Related
Some background: I'm building an app in react native that uses an Objective C library written about 6 or 7 years ago, maybe older. I'm writing swift code that has been set up to send callbacks to the react native application in JS. I have this function that I'm trying to use:
token = service.getUserToken(server,
port: P2PFunctions.tls_port,
appId: P2PFunctions.appID,
appSecret: P2PFunctions.appSecret,
phone: P2PFunctions.phone,
token: nil, errcode: errCode, errmsg: nil);
callback(["\(token!)"]);
And this is its definition:
- (NSInteger)getUserToken:(NSString*)ip_In port:(NSInteger)port_In appId:
(NSString*)appId_In appSecret:(NSString*)appSecret_In phone:
(NSString*)phoneNum_In token:(NSString**)accessTok_Out errcode:
(NSString**)strErrCode_Out errmsg:(NSString**)errMsg_Out;
These are the types I'm using (EDIT: I changed them from private to public, and they still are not being recognized):
The problem is, I'm getting nil back from the function. I believe I'm getting an HTTP response that is empty, and I notice that inside the debugger when I step to the Objective C function, I see nil for all my parameters inside of the Objective C function. I think... it is that I'm not passing the correct type. Or my Swift parameters are not visible in Objective C's memory space. If it is expecting an (NSString *), should I be passing a String?
How do I pass the correct types from Swift to Objective C? What would I change in my function call? Are my parameter types okay? I cannot edit the original Objective C library. They share a common memory space for all variables in the entire program, right?
Thank you so much!
I just ran this successfully:
// the objc part
#interface Test : NSObject
- (NSInteger)getUserToken:(NSString*)ip_In
port:(NSInteger)port_In
appId:(NSString*)appId_In
appSecret:(NSString*)appSecret_In
phone:(NSString*)phoneNum_In
token:(NSString* _Nonnull * _Nonnull)accessTok_Out
errcode:(NSString* _Nonnull * _Nonnull)strErrCode_Out
errmsg:(NSString* _Nonnull * _Nonnull)errMsg_Out;
#end
#implementation Test
- (NSInteger)getUserToken:(NSString*)ip_In
port:(NSInteger)port_In
appId:(NSString*)appId_In
appSecret:(NSString*)appSecret_In
phone:(NSString*)phoneNum_In
token:(NSString**)accessTok_Out
errcode:(NSString**)strErrCode_Out
errmsg:(NSString**)errMsg_Out {
*accessTok_Out = #"Token";
*strErrCode_Out = #"OK";
*errMsg_Out = #"msg";
return 42;
}
#end
// and the swift part
let t = Test()
var token: NSString = "t"
var errcode: NSString = "c"
var errmsg: NSString = "m"
let result = t.getUserToken("ip", port: 1,
appId: "2", appSecret: "3", phone: "4",
token: &token, errcode: &errcode, errmsg: &errmsg)
print(result)
and it works as expected.
Maybe this gives you a hint as to what's different in your situation.
So, after spending a week or so on this problem, I realize that it was not that the parameters are not actually being passed, but that the debugger is just not displaying the values of those parameters, at least on the main thread. Because I was getting the error code, I thought that something had to be wrong with the way I called the function - but actually, the function call is fine, the variables just didn't appear in the debugger for some reason:
Variables above appear to be nil - but in fact, they do have values.
Apple's guide on Using and Creating Error Objects gives the following code example:
NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL] ofType:#"html" error:&theError];
if (success == NO) {
// Maybe try to determine cause of error and recover first.
NSAlert *theAlert = [NSAlert alertWithError:theError];
[theAlert runModal]; // Ignore return value.
}
and accompanies it with the statement:
Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.
I have always been wondering why is this pattern so important? Why should we ALWAYS check the return value? What's wrong if we check whether the error is nil or not?
This design is not terribly unusual, compare also errno in standard C.
The design has a number of potential advantages:
The function does not have to write through a pointer on success. This does not only make the implementation of such functions easier and less error prone, it can also be a small performance advantage (e.g. this prevent CPU caches from being invalidated if the function succeeds).
If we always check that the function failed before accessing an error, we can use that same error pointer for multiple functions. Otherwise, we might get a previous failure rather than the failure of the most recent function.
This makes validation code easier to write. E.g. a function could set the error by default. If all validations pass, the function can simply return success instead of having to reset the error variable.
A function can use the same error pointer when calling other functions, but a failure of these helpers doesn't necessarily imply a failure of the top function.
In your specific case, the variable NSError *theError; has not been initialized. Accessing that variable without assigning to it first would invoke undefined behaviour. The documentation only guarantees that the variable will be set in case of an error.
Imagine you implement a method in terms of a few other methods:
-(BOOL)sendCachedRequestReturningError: (NSError**)err {
BOOL success = [self readCachedRequestReturningError:err];
if (!success && (*err).domain == MYFileDomain && (*err).errorCode == MYFileNotFoundCode) {
success = [self sendUncachedRequestReturningError:err];
}
return success;
}
Now there are 4 code paths here:
There is a cached request. We'll just return success == YES and all is good.
A non-recoverable error occurs trying to read from the cache. readCachedRequestReturningError: will set err and set success == NO and the caller will call presentError: or whatever
An error occurs trying to do the network request. Same as #2, err is set, and success == NO.
There is no cache, but we can make a network request. readCachedRequestReturningError: will set err to a valid NSError{MYFileDomain, MYFileNotFoundCode}, but then sendUncachedRequestReturningError: will succeed and set success == YES, and not touch err at all, leaving the previous error in it. If you now check err instead of checking the return value, you will think there was an error when all went well.
Note: The code above is grossly simplified, because we only care about errors. Of course in a real program, the methods would probably have another return parameter for the actual reply from the request, or would return the reply or nil instead of a success BOOL. It would also probably check whether err is NULL.
I just noticed a strange issue with my app using Core Data (which didn't happen before).
Here is a section of code:
NSArray *allTransactions = [CDMTransaction transactionsFrom:_dateFrom to:_dateTo bankAccounts:_displayedBankAccounts categories:categories recipients:recipients tags:tags unclosedOnly:unclosedTransactionsOnly];
for (CDMTransaction *transaction in allTransactions) {
if (transaction.inverseTransaction != nil) {
...
}
}
What causes the issue is the call to transaction.inverseTransaction.
CDMTransaction is a subclass of NSManagedObject, which contains several properties and relationships (like inverseTransaction).
Everything worked fine until now, but I get an crash when this property is called:
CoreData: error: warning snapshot_get_value_as_object called on NULL, and it sends me to the inverseTransaction property implementation.
But when I try to log the allTransactions array contents, before entering the "for" loop, it just work fine!
Here is the updated code:
NSArray *allTransactions = [CDMTransaction transactionsFrom:_dateFrom to:_dateTo bankAccounts:_displayedBankAccounts categories:categories recipients:recipients tags:tags unclosedOnly:unclosedTransactionsOnly];
NSLog(allTransactions: %#, allTransactions);
for (CDMTransaction *transaction in allTransactions) {
if (transaction.inverseTransaction != nil) {
...
}
}
And what I get in the log console:
2015-01-11 11:54:51.613 Cash[14383:1403274] allTransactions: (
"<CDMTransaction: 0x7b94d4a0> (entity: CDMTransaction; id: 0x7b935f30 <x-coredata://12493FBF-2D83-469C-9610-D7C29E21F12B/CDMTransaction/p1> ; data: <fault>)"
)
Without any issue after...
It doesn't seem to be an issue with the inverseTransaction property because it does the same with all other properties (NSString, XNSNumber, ...) and relationships...
What is wrong?
Edit 1: when it crashes I get the property implementation line highlighted in green with a EXC_BAD_INSTRUCTION error (and not an EXC_BAD_ACCESS)
Edit 2: The app doesn't crash anymore when I add request.returnsObjectsAsFaults = NO; to my NSFetchRequest... So it seems to be OK now, but why do I have to write that? It didn't need it before...
I would like to exit out the current method that I'm stepping through.
-(void)helloWorld {
NSLog(#"Hello");
// I would like to return here, so that "World" isn't printed.
NSLog(#"World");
}
I have tried the following, but without luck.
(lldb) expr return
<no result>
Is this possible with lldb?
Unfortunately in Xcode 4.5.x there is no way to force an early return from a function. In the current lldb sources over at http://lldb.llvm.org/ there is a newly added command, thread return, which does what you want - it includes the ability to specify the return value of the function. This won't be in Xcode until the next major release, though.
When you are debugging using Xcode and when your program is paused at a breakpoint, you can drag the little green arrow to any other line in the function. E.g. in the following code:
if I want to skip the NSLog(#"B"), I can simply drag the green arrow from line 20 to line 23, which means the function will simply "return" from anywhere I want.
I just added a breakpoint at the line mentioned below:
var computed: Bool {
return device.time == 10 // added breakpoint here
}
and got the following error:
error: Error returning from frame 0 of thread 1: We only support
setting simple integer and float return types at present..
Seems to work for only those two types
Ok, so I've been using NSLog in objective-C for awhile now to debug and I know it's supposed to print to the terminal whatever I put in the parentheses. For some reason, it just stopped printing to the terminal and I'm not sure how to fix this error. I was wondering what other people would suggest doing to fix this problem. I've only included part of my code because I don't want to scare away someone from answering this simple (or at least I hope it's simple to fix) problem. When I run the code, the only two statements that print are "serverButton - Stage 1" and "serverButton - Stage 2 - Complete" but nothing else in between. FYI -(void)startServer is in another class called "Server" and I have made "server" a pointer to that said class.
-(IBAction)serverButton {
NSLog(#"serverButton - Stage 1");
[server startServer];
NSLog(#"serverButton - Stage 2 - Complete");
}
-(void)startServer {
NSLog(#"serverButton - Stage 1");
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
NSLog(#"ERROR: serverButton - Stage 1");
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
serverError = 1;
NSLog(#"Error");
}
Add a check to make sure server is not nil before you call startServer, if it was nil then nothing would get called and no error would be generated.
You say:
FYI -(void)startServer is in another class called "Server" and I have made "server" a pointer to that said class.
If I read that right, your server variable is actually pointing to a class, like so:
Class server = [Server class];
...
NSLog(#"begin");
[server startServer];
NSLog(#"end");
If that is the case, then your startServer method would have to be a class method:
+ (void)startServer {
NSLog(#"hello!");
}
If that is not the case, can you please post a little bit more code? Specifically, the part where you assign to that server variable would be helpful.
It looks like the problem isn't NSLog, but rather that startServer isn't actually getting sent to the receiver like you think it is.
If the NSLog doesn't print to the terminal (explain what is terminal). I assume terminal is the Xcode debugger console. Then, you need to check the Console.app, whether it's printed there.
The new version of Xcode doesn't have this problem.