I need to pass data on subscribing to a notification, so, I've taken the example code in Module_07_04_iOSNativePush app and added the following code to the connect method (in ViewController.m):
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:
#"userName", #"DoronK",
#"password", #"testPwd", nil];
id pushit = [WLPushOptions new];
[pushit addSubscriptionParameter:#"test" :#"Test1"];
[pushit addSubscriptionParameters:dic];
[[WLPush sharedInstance] subscribe:readyToSubscribeListener.alias: pushit :connectListener];
This does not throw any errors, but, when I run the example PushAdapter code, and adding to the adapter using this:
var usub = 'json:'+JSON.stringify(userSubscription);
...
return { result: "Notification sent to user :: " + userId +
", wait " + waittime + " before sending another. UserSub:" + usub};
The result in the adapter is:
{
"isSuccessful": true,
"result": "Notification sent to user :: worklight, wait 0 before sending another.
UserSub:json:{\"userId\":\"worklight\",\"state\":{}}"
}
I would expect the variables that I passed in via the dictionary under "state", correct? Is there another way in the adapter to get the parameters that were passed in on the [WLPush subscribe] call?
As you can probably tell, I am VERY new to Objective C, so, don't assume the call to the subscribe is done correctly. Also, I'm using Worklight Studio 6 with Fix Pack 1.
The state will not contain the parameters passed during subscribe call. You need to first get the device subscription. The options object of the device subscription will contain the parameters passed during subscribe.
For eg.
var deviceSubscriptions=userSubscription.getDeviceSubscriptions();
var usub = 'json:'+JSON.stringify(deviceSubscriptions[0]);
Please refer to the following infocenter link
http://pic.dhe.ibm.com/infocenter/wrklight/v6r0m0/topic/com.ibm.worklight.help.doc/apiref/r_method_usersubscription_getdev.html
Related
Using the Nest API I am trying to set the nest thermostat's away status
Reading & Setting for temperature is working fine.
I have the read and write permissions correctly configured for both
thermostat temperature control and for setting thermostat away
I can read the status correctly. Does anyone with some experience of this API know how to go about setting this status?
in "FirebaseManager.h"
Firebase *newFirebase2 = [self.rootFirebase childByAppendingPath:#"structures"];
[newFirebase2 observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
// Put structures into a dictionary
NSMutableDictionary *dict = snapshot.value;
NSLog(#"\n\n\n1. Away Status = %#", [dict valueForKey:#"away"]);
NSLog(#"Dict Contents %#", dict); // <--- Reads thermostat status. A string either home or away
dict[#"away"] = #"away"; //<--- Changes status string but not a correct solution, and does not set the stat to away
//Changes status name but this is not parsed back to firebase
NSLog(#"new status = %#", [dict valueForKey:#"away"]);
}];
To update a child value
assume this structure
structures
structure_id_0
away: "home"
setting the away node to a string of away (this code is quite verbose so it's easy to follow)
Firebase *structuresRef = [self.rootFirebase childByAppendingPath:#"structures"];
//build a reference to where we want to write structures/structure_id/
Firebase *thisStructureRef = [structuresRef childByAppendingPath:#"structure_id_0"];
Firebase *awayRef = [thisStructureRef childByAppendingPath:#"away"];
[awayRef setValue:#"away"];
Now, if you want to do this for snapshots that have been retrieved via observing a node with FEventTypeChildAdded, the node name will be whatever is used in place of structure_id_0. The is the key of the key:value pair.
That can be obtained via snapshot.key.
So NSString *key = snapshot.key
Substitute the key variable in for #"structure_id_0" in the path.
Also check out Firebase Writing Data, and then the updateChildValues for another option.
I have an Objective-C application (https://github.com/NBICreator/NBICreator) with a privileged helper tool.
I have a few different privileged tasks the helper will need to perform during one build, and I want to have the user authenticate only once to perform those tasks.
The authorization works, but I can't seem to reuse the session in the helper. The user always have to authenticate for every step, even if I supply the exact same right to the Security Server and use the same AuthenticationRef.
I have read the docs and tested the pre-authentication methods in the main app first, and tried printing out (and retaining the auth session in the helper as well). But nothing I've tried have yet to work successfully.
I need som help figuring out why the Security Server feel the need to reauthenticate.
The code on GitHub in the Master Branch is current, and what I'm trying and testing by changing things back and forth. With that code, the user have to authenticate each time I call a helper function, even if I use the same authentication right.
This is what the right looks like in the authorization database:
class = rule;
created = "470329367.933364";
"default-prompt" = {
"" = "NBICreator is trying to start an Imagr workflow.";
};
identifier = "com.github.NBICreator";
modified = "470329367.933364";
requirement = "identifier \"com.github.NBICreator\" and anchor apple generic and certificate leaf[subject.CN] = \"Mac Developer: Erik Berglund (BXUF2UUW7E)\" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */";
rule = (
"authenticate-admin"
);
version = 0;
This is where I define the right: https://github.com/NBICreator/NBICreator/blob/master/NBICreator/Helper/NBCHelperAuthorization.m#L36-L43
NSStringFromSelector(#selector(authorizeWorkflowImagr:withReply:)) : #{
kCommandKeyAuthRightName : #"com.github.NBICreator.workflowImagr",
kCommandKeyAuthRightDefault : #kAuthorizationRuleAuthenticateAsAdmin,
kCommandKeyAuthRightDesc : NSLocalizedString(
#"NBICreator is trying to start an Imagr workflow.",
#"prompt shown when user is required to authorize to add a user"
)
},
And this is where I check if the user is authenticated:
https://github.com/NBICreator/NBICreator/blob/master/NBICreator/Helper/NBCHelperAuthorization.m#L222-L253
+ (NSError *)checkAuthorization:(NSData *)authData command:(SEL)command authRef:(AuthorizationRef)authRef {
#pragma unused(authData)
NSError * error;
OSStatus err = 0;
AuthorizationItem oneRight = { NULL, 0, NULL, 0 };
AuthorizationRights rights = { 1, &oneRight };
oneRight.name = [#"com.github.NBICreator.workflowImagr" UTF8String];
err = AuthorizationCopyRights(
authRef,
&rights,
NULL,
kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed,
NULL
);
if ( err != errAuthorizationSuccess ) {
NSString *message = CFBridgingRelease(SecCopyErrorMessageString(err, NULL));
error = [NSError errorWithDomain:[[NSProcessInfo processInfo] processName] code:err userInfo:#{ NSLocalizedDescriptionKey : message }];
}
return error;
}
As you can see there, I'm testing by setting a hardcoded right name to try and resue that right to the Security Server.
I'm stumped right now, and can't seem to find a way forward. Hoping someone here might know where to look.
I found the answer in how I set up the right in the rights database.
I was using the default code from the Apple example EvenBetterAuthorizationExample for creating and using the rights. But that only points to this documentation for the different rights to use.
None of those were helping me with authenticatin once and the having the helper be authenticated the next time I request authentication for the same right.
After some digging and looking into the actual rules in the authorization database I found a way to copy the rule that AuthenticateWithPrivileges uses, that works by authenticating for 5 minutes until requiring re-authentication.
So, I changed my code for creating my custom right from this:
NSStringFromSelector(#selector(addUsersToVolumeAtPath:userShortName:userPassword:authorization:withReply:)) : #{
kCommandKeyAuthRightName : NBCAuthorizationRightAddUsers,
kCommandKeyAuthRightDefault : #kAuthorizationRuleAuthenticateAsAdmin,
kCommandKeyAuthRightDesc : NSLocalizedString(
#"NBICreator is trying to add a user.",
#"prompt shown when user is required to authorize to add a user"
)
},
To this:
NSStringFromSelector(#selector(addUsersToVolumeAtPath:userShortName:userPassword:authorization:withReply:)) : #{
kCommandKeyAuthRightName : NBCAuthorizationRightAddUsers,
kCommandKeyAuthRightDefault : #{
#"class": #"user",
#"group": #"admin",
#"timeout": #(300),
#"version": #(1),
},
kCommandKeyAuthRightDesc : NSLocalizedString(
#"NBICreator is trying to add a user.",
#"prompt shown when user is required to authorize to add a user"
)
},
So, instead of using #kAuthorizationRuleAuthenticateAsAdmin as the RightDefault, i passed in this dict:
#{
#"class": #"user",
#"group": #"admin",
#"timeout": #(300),
#"version": #(1),
},
After that, my helper can request authorization from the user, and then I can reuse that session for the same right during 5 minutes without asking the user again.
Our project is supposed to re-use existing Adapters that expect JSON objects as input parameters into the Adapter procedures.
When we try to call the Adapter using:
WLProcedureInvocationData *myInvocationData = [[WLProcedureInvocationData alloc] initWithAdapterName:#"UserProfileAdapter" procedureName:#"getUserProfile"];
myInvocationData.parameters = [NSArray arrayWithObjects:#"{\"userEmail\" : \"xxx#xxx.com\"}", nil];
then the input object that is passed to the Adapter procedure is not a Javascript object, but a javascript string.
In order to make a object out of it, we would always need to parse the input from a string to an object using:
input = JSON.parse(input);
since we are reusing existing code that is in production with a hybrid app, this is not an option, since with the hyrbid app this works fine.
How can we pass JSON to a Javascript adapter so that the Adapter automatically creates an input object and not a string?
The answer we found was for Swift and for Android ... did not try ObjC anymore.
iOS:
//Create JSON Object with keys and values
let jsonObject: [String: AnyObject] = [
"userName": "xxx#xxx.com",
"password": "12345"
]
let procedureData = WLProcedureInvocationData(adapterName: "AuthAdapter", procedureName: "login")
procedureData.parameters = [jsonObject]
Android:
JSONObject jsonObj = new JSONObject("\"userName\":\"xxx#xx.com\",\"password\":\"1234\"}");
Object[] params = new Object[]{jsonObj};
WLProcedureInvocationData invocationData = new WLProcedureInvocationData("AuthAdapter", "login");
invocationData.setParameters(params);
We have not tried anything with deeper nested JSON structures yet and this might still be troublesome, but for a basic JSON this works fine for us now.
I've tried to use setTimeout in Worklight adapter procedure. It doesn't work.
WLSE0099E: An error occurred while invoking procedure [project BusinessBank]PushAdapter/submitNotificationFWLSE0100E: parameters: [project BusinessBank]
ReferenceError: "setTimeout" is not defined. (PushAdapter-impl.js#37)
I need to hold sending the push notification after invoking adapter procedure. It's need for the demo. My code example:
WL.Server.createEventSource({
name: 'PushEventSource',
onDeviceSubscribe: 'deviceSubscribeFunc',
onDeviceUnsubscribe: 'deviceUnsubscribeFunc',
securityTest:'AngularStarter-strong-mobile-securityTest'
});
function deviceSubscribeFunc(userSubscription, deviceSubscription){}
function deviceUnsubscribeFunc(userSubscription, deviceSubscription){}
function submitNotification(userId, notificationText) {
var userSubscription = WL.Server.getUserNotificationSubscription('PushAdapter.PushEventSource', userId);
if (userSubscription == null) {
return { result: "No subscription found for user :: " + userId };
}
var badgeDigit = 1,
notification = WL.Server.createDefaultNotification(notificationText, badgeDigit, {custom: "data"});
setTimeout(function() {
WL.Logger.debug("submitNotification >> userId :: " + userId + ", text :: " + notificationText);
WL.Server.notifyAllDevices(userSubscription, notification);
},5000);
return {
result: "Notification sent to user :: " + userId
};
}
If you're actually about about JavaScript method setTimeout, it will help if you'll add some code example of what exactly you're trying to do, given this is a programming Q&A website and all.
If you're demoing your app using Worklight Studio, there is no need to implement a timeout.
Open the app, login, subscribe to notifications, close the app. Then, right-click on the adapter and select Run As > Invoke Worklight procedure and add a username in text (for example: "myuser","mytext"). And that's it... the notification will be sent. Whenever you want it to be sent.
Otherwise,
There is no such thing as setTimeout for an adapter procedure...
See here: How to increase the adapter procedure timeout value in Worklight?
To set a timeout to a procedure, in the adapter XML file:
<procedure name="nameHere" requestTimeoutInSeconds="valueHere"/>
Please review the IBM Worklight Knowledge Center and training modules.
setTimeout API belongs to a global window object. basically using it the way you do is a shortcut to window.setTimeout().
Since adapter is not a browser but a server runnable code it doesn't have a global window object, therefore you have no setTimeout API.
In my -request:didLoad: delegate method I'm NSLoging the results but I can't figure out what's the content?
It looks like result is an NSArray but what is inside it? how do I parse the data?
A sample of the log looks like this:
result: (
{
"fql_result_set" = (
{
uid2 = 1234567;
},
{
uid2 = 12345678;
}
);
name = queryID;
},
{
"fql_result_set" = (
{
"birthday_date" = "05/12/1987";
name = "John Doe";
},
{
"birthday_date" = "03/01/1978";
name = "Jane Doe";
}
);
name = queryBirthday;
}
)
The Facebook iOS tutorial, in "Step 6: Using the Graph API", says
Note that the server response will be in JSON string format. The SDK uses an open source JSON library https://github.com/stig/json-framework/ to parse the result. If a parsing error occurs, the SDK will callback request:didFailWithError: in your delegate.
A successful request will callback request:didLoad: in your delegate. The result passed to your delegate can be an NSArray, if there are multiple results, or an NSDictionary if there is only a single result.
In your example, everything printed by NSLog inside "()" is part of an NSArray, while everything inside "{}" (which also have keys incidentally) is part of an NSDictionary and therefore accessible by key (name).
http://developers.facebook.com/docs/mobile/ios/build/
According to https://developers.facebook.com/docs/reference/api/, all 'responses' are JSON-Objects. To parse these, iOS 5 provides a class called NSJSONSerialization (NSJSONSerialization Class Reference)
You normally parse it as follows:
NSDictionary *dictionaryJSON = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];