I'm working on an Action Extension with no UI
NSExtensionPointIdentifier : com.apple.services
I'm trying to complete my extension returning an UIView
MYExternalView *viewTest = [[MYExternalView alloc] init];
NSItemProvider *resultsProvider = [[NSItemProvider alloc] initWithItem:viewTest typeIdentifier:kUTTypeMyCustomType];
NSExtensionItem *resultsItem = [[NSExtensionItem alloc] init];
resultsItem.attachments = #[resultsProvider];
[self.extensionContext completeRequestReturningItems:#[resultsItem] completionHandler:nil];
My returning view MYExternalView extends NSSecureCoding with very simple code:
MYExternalView.h
#interface MYExternalView : UIView < NSSecureCoding >
#end
MYExternalView.m
#implementation MYExternalView
+ (BOOL)supportsSecureCoding {
return YES;
}
#end
Running this code i had error message
<Warning>: <NSXPCConnection: 0x17410ae60> connection to service named com.*******.****.****: Warning: Exception caught during decoding of received message, dropping incoming message.
Exception: Exception while decoding argument 0 (#2 of invocation):
<NSInvocation: 0x174262580>
return value: {v} void
target: {#} 0x0
selector: {:} _completeRequestReturningItems:forExtensionContextWithUUID:completion:
argument 2: {#} 0x0
argument 3: {#} 0x0
argument 4: {#?} 0x0 (block)
Some ideas to solve my problem?
If i return another kind of object, a string for instance, everything work well.
Thanks.
Related
I created an objective-c method which will invoke a method via NSInvocation:
typedef void (^ScriptingEmptyBlock)();
typedef void (^ScriptingErrorBlock)(NSError *error);
- (void)scripting_execute:(NSString *)operation withParams:(nullable NSArray *)args {
SEL selector = [self scripting_selectorForOperation:operation];
Class class = [self class];
NSMethodSignature *signature = [class instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setSelector:selector];
[invocation setTarget:self];
for (int idx = 0; idx < args.count; idx ++) {
id arg = args[idx];
[invocation setArgument:&arg atIndex:idx + 2];
}
ScriptingEmptyBlock success = args[1];
// Breakpoint added on next line to test for nil
success(); // this is nil and would crash!
// (lldb) po args.count
// 3
// (lldb) po success
// Printing description of success:
// (ScriptingEmptyBlock) success = 0x0000000000000000
// (lldb) po args[1]
// (Function)
//[invocation getArgument:&success atIndex:2]; // also tried this and got nil as well
[invocation invoke];
}
The method takes an "operation" which is translated into a selector by overriding scripting_selectorForOperation: in subclasses and then performs the invocation.
All of that works, except when the invoked method has block arguments they are nil, I added the test for nil I describe with comments, when attempting to read the closure from the array it will be nil.
Called like:
let successClosure: ScriptingEmptyBlock = {
print("Renamed product")
}
let errorClosure: ScriptingErrorBlock = { error in
print("Failed to rename product: \(error)")
}
let params:[Any] = [ "testName", successClosure, errorClosure]
object.scripting_execute (ScriptOperation.updateProductName.rawValue, withParams: params)
Why is closure becoming nil?
success is not nil (in fact, NSArray cannot contain nils). If you print it like NSLog(#"%#", success);, it will say (Function), not (null). And if you print its class like NSLog(#"%#", [success class]);, it will say _SwiftValue. Basically, it is a Swift value that is bridged into Objective-C.
The problem is that the object success points to is not an Objective-C block. It is a Swift closure, and Swift closures are not the same as Objective-C blocks. Trying to use invoke it as if it were an Objective-C block causes undefined behavior. po in the debugger prints it wrong probably because it is printing it assuming it were type ScriptingEmptyBlock (a block type). If you do po (id) success, it will print (Function).
As to how you can explicitly put an Objective-C block into the array from Swift, the only way I figured out to do it something like:
let params:[Any] = [ "testName",
successClosure as (#convention(block) () -> Void)!,
errorClosure as (#convention(block) (NSError) -> Void)!]
object.scripting_execute (ScriptOperation.updateProductName.rawValue,
withParams: params)
I am not sure why it's necessary to put the function type inside a !, but it doesn't seem to work otherwise. Maybe someone else can find a better way.
I must admit that I don't fully understand why this is happening, but as far as I can tell this has nothing to do with using NSInvocation and would happen even if we just passed a Swift closure to an Objective-C function via a parameter of type id. Passing an Objective-C block via id works just fine, not sure why: Swift closures are supposed to be compatible with Objective-C blocks. As you know, elements of NSArray are of type id, so any Objective-C object can be an array element.
To work around this problem of accessing a Swift closure passed via id in Objective-C one can introduce a wrapper class.
// In a header:
#interface EmptyBlockWrapper : NSObject
#property EmptyBlock _blk;
#end
// In an implementation file (just an empty implementation):
#implementation EmptyBlockWrapper
#end
Then we can use a wrapper instance instead of a block as an array element in Swift:
let myBlock : EmptyBlock = {
print("In Swift EmptyBlock...")
}
let myBlockWrapper = EmptyBlockWrapper()
myBlockWrapper._blk = myBlock
In an Objective-C method we can call it as follows, assuming args is NSArray *:
EmptyBlockWrapper * emptyBlockWrapper = args[1];
emptyBlockWrapper._blk();
Hopefully this is helpful. Of course, this is just a simplified example to give you an idea; this could be made much fancier.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: [LeavesCache setDataSource:]: unrecognized selector sent to instance 0x7db1f30
Added Exception breakpoint and found that problem is at this line
pageCache = [[LeavesCache alloc] initWithPageSize:self.bounds.size];
- (void) initialize {
backgroundRendering = NO;
pageCache = [[LeavesCache alloc] initWithPageSize:self.bounds.size];
}
- (id) initWithPageSize:(CGSize)aPageSize
{
if (self = [super init]) {
pageSize = aPageSize;
pageCache = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void) setDataSource:(id<LeavesViewDataSource>)value {
pageCache.dataSource = value;
}
Have no idea how to fix this if some one can help me in this
I'm assuming you're using [this library][1], but it doesn't match up with what you've posted. Because the LeavesCache library on GitHub has no setDataSource method in the code - it's property declared instead. Have you made modifications to the source? Somebody has, because you seem to be setting the pageCache instance variable in one method to a NSMutableDictionary, and in another to a LeavesCache object.
Is there any particular reason why you're using this library? As far as I can tell, it hasn't been updated for three years, and iOS has supported iBooks like page turning interface since iOS 5 natively, using the UIPageViewController class.
Following code throws an exception.
vcClass is a Class object (inheritor from UIViewController). Self contains my implementation of viewWillAppear:
SEL viewWillAppearSEL = #selector(viewWillAppear:);
IMP viewWillAppearWithSuperIMP = [self methodForSelector:viewWillAppearSEL];
class_addMethod(vcClass, viewWillAppearSEL, viewWillAppearWithSuperIMP, #encode(BOOL));
NSMethodSignature *methodSignature = [vcClass instanceMethodSignatureForSelector:viewWillAppearSEL];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setSelector:viewWillAppearSEL];
With message:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSInvocation setArgument:atIndex:]: index (1) out of bounds [-1, -1]
Additional info: iOS5, ARC.
Can someone explain me what's wrong?
UPDATED:
This code code gives me responds message. So my class object is correct
[vcClass instancesRespondToSelector:viewWillAppearSEL] ? NSLog(#"responds") : NSLog(#"not responds");
Alse Im getting crash right after [invocation setSelector:viewWillAppearSEL];. That's why I called topic title as Unexpected exception with NSInvocation.
UPDATED2:
Also my implementation of viewWillAppear:
- (void)viewWillAppear:(BOOL)animated {
Class parentViewController = [self superclass];
void (*superViewWillAppear)(id, SEL, BOOL) =(void(*)(id, SEL, BOOL))class_getMethodImplementation(parentViewController, _cmd);
superViewWillAppear(self, _cmd, animated);
NSLog(#"view will appear with super");
}
One problem with your code is the type encoding that you are passing to class_addMethod(). This type encoding must include: 1) the return type, 2) the types for self and _cmd (the first two hidden parameters), and then 3) the types of all the other parameters.
For a method like - (void)viewWillAppear:(BOOL)animated, the type encoding should be the string
v#:c
v -- for void, return type
# -- for id, type for self
: -- for SEL, type for _cmd
c -- for char, this is what BOOL is. This is what you get when you did #encode(BOOL)
BOOL *arg1;
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setSelector:viewWillAppearSEL];
[invocation setArgument:&arg1 atIndex:2]; // argument indexing is offset by 2 hidden args
i'm using the generated acr code from sudzc in my iPhone project. i already imported the missing h files, fixed the body tag and everything works fine...for the first call of any function.
i call the functions in several positions in my code like in the viewDidLoad:
TestWsdlService_ManagerService* service = [TestWsdlService_ManagerService service];
[service getAttended:self action:#selector(getEventsHandler:) email: #"testmail#bla.com" password: #"testpass"];
first call works well. logging is enabled so i see the correct post and the correct response.
when i call the same functions again, i only see the correct post but it gives me that error with an empty response (no xml, nothing):
2012-08-14 10:26:55.574 Test[1768:11603] nserror Error Domain=CXMLErrorDomain Code=1
"Unknown error" UserInfo=0x6e5fea0 {NSLocalizedDescription=Unknown error}
2012-08-14 10:26:55.580 Test[1768:11603] Error: Unknown error
i also tried it with a singleton like this:
static TestSoapSingleton *sharedInstance = nil;
+ (TestSoapSingleton *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[super allocWithZone:NULL] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if (self) {
self.manager = [TestWsdlApi_ManagerService service];
self.manager.logging = YES;
self.username =#"testmail#blub.com";
self.password = #"test pass";
}
but at the end i got the same error.
the error is thrown in the CXMLDocument.m in the initWithData function, because the inData is empty (size=0).
also in the SoapRequest.m in the connectionDidFinishLoading function, the receivedData is empty.
i tried to debug the SoapRequest.m and detected, that the didReceiveResponse is called in the first and the second call of a function, but the didReceiveData is only called at the first time.
if i do two method calls right behind each other like this:
TestWsdlService_ManagerService* service = [TestWsdlService_ManagerService service];
[service getAttended:self action:#selector(getEventsHandler:) email: #"testmail#bla.com" password: #"testpass"];
TestWsdlService_ManagerService* service2 = [TestWsdlService_ManagerService service];
[service2 getAttended:self action:#selector(getEventsHandler:) email: #"testmail#bla.com" password: #"testpass"];
it also works without problems.
hope anybody has a solution, thanks
after several hours i found the bug...webserver problem / session problem. got issues with the zend::auth adapter
but i wonder why the ksoap2 api is working with the session things and the nsurlconnection not^^
I'm trying to implament a callback mechanism where I pass in a block to the init of a class, and after some work that class calls me back. The block gets called and most everything works, except when I call anything on "self" within the block. I get Program received signal: “EXC_BAD_ACCESS” unless I comment out any reference to self within the block.
Am I wrong to think that I can access self within the block?
any advice would be greatly appreciated. This code is in a brand new "Universal app" and I'm currently working on the IPad portion, so running under the IPad simulator.
Some code:
__block LoginViewController *blockSelf = self;
LoginAlertView *alert = [[LoginAlertView alloc]
intWithPinPrompt:NO
title:#"Mobile Login"
myCallback:^(LoginAlertView *v){
DLog(#"self %#", blockSelf);
NSString *u = v.userNameText;
NSString *p = v.passwordText;
NSString *i = v.pinText;
[self authenticateUser:u
password:p
pin:i];
}];
and here is the some code from the LoginAlertView
- (id) intWithPinPrompt:(BOOL)pinPromptEnabled title:(NSString*)aTitle myCallback:(loginClicked)aCallback{
if (self = [self initWithTitle:aTitle
message:nil
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Login", nil]) {
hasPinPrompt = pinPromptEnabled;
theCallback = aCallback;
}
return self;
}
and the callback call
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (theCallback) {
theCallback(self);
}
}
I changed to following line of code
theCallback = aCallback;
to
theCallback = [aCallback copy];
which is presenting me with the following error
Program received signal: “EXC_BAD_ACCESS”.
(gdb) bt
#0 0x029c8c97 in objc_msgSend ()
#1 0xbfffe560 in ?? ()
#2 0x00026f66 in -[LoginViewController show] (self=0x4a38450, _cmd=0x209c36c) at LoginViewController.m:18
#3 0x00026d3b in -[AuthenticatedViewController viewWillAppear:] (self=0x4c1fac0, _cmd=0x20b59ac, animated=0 '\0') at AuthenticatedViewController.m:17
one other thing, the definition of my block looks like this
typedef void(^loginClicked)(LoginAlertView*);
and the member variable is this
loginClicked theCallback;
also tried moving the declaration of the block up to a variable, and passing that in. this had the same results Anytime I use the "copy" on the bock I get the dreaded Program received signal: “EXC_BAD_ACCESS”. I thought maybe this was a IOS3.2 thing, so I tried running it under the IPhone 4.0 simulator, same results. Is there possibly a compiler setting that needs to be made in order to "Copy" the block onto the heap? I'm using LLVM1.5
First, what is blockSelf in your code?
Secondly, no, there is no reason why you can't use self and this is indicative of a bug in your code.
Specifically, you aren't copying the block. Blocks start out on the stack. Thus, when you say theCallback = aCallback;, you are storing a reference to an on-stack data structure. As soon as the defining stack frame goes away, that structure is no longer valid.
Change theCallback = aCallback; to theCallback = [aCallback copy]; (and add [theCallback release]; to your -dealloc method).