Please provide the code for your ParseObjects when asking questions along with the code you tried… The playgrounds shows how to do this. You should know the “type” you expect to be returned:
So my swift code right now to call the cloud functions this:
let sendEmail_CloudFunction = Cloud(functionJobName: "sendEmail", arguments: myParameters)
sendEmail_CloudFunction.runFunction() { result in
switch result {
case .success(let response):
print("Response from cloud function: \(response)")
case .failure(let error):
assertionFailure("Error calling cloud function: \(error)")
}
}
And the node.js cloud function is this:
var args = request.params.arguments
var mailTemplateName = args.mailTemplateName
var mailSubject = args.mailSubject
var mailFromAddress = args.mailFromAddress
var mailRecipient = args.mailRecipient
var mailVariables = args.mailVariables
var mailReplyTo = args.mailReplyTo
var mailAttachmentName = args.mailAttachmentName
const mailgun = require("mailgun-js");
const DOMAIN = 'xxxxxx';
const mg = mailgun({apiKey: 'key', domain: DOMAIN});
const data = {
from: mailFromAddress,
to: mailRecipient,
subject: mailSubject,
template: mailTemplateName,
text: 'Testing some Mailgun mail!',
'h:X-Mailgun-Variables': mailVariables
};
mg.messages().send(data)
.then((res) => {
console.log(res);
console.log(`************************* Sending Mail - console.log - ${JSON.stringify(res)}`);
return res;
})
.catch((err) => {
console.error(err);
// console.log(`************************* Sending Mail - console.log - ${JSON.stringify(err)}`);
return err;
});
But from the error, it looks like parse is expecting a ‘result’ : Int.
I have tried returning { result : 1} as JSON and returning just the result above, plus returning the result above with the result property added to the JSON, but none seem to work.
I guess the question is, what is parse expecting, or do I define what the response is, like when I used dataTaskPublisher and .decode with my own response struct.
struct Cloud: ParseCloud
{
typealias ReturnType = String
var functionJobName: String
var arguments: [String : String] = [:]
}
Sorry about that. But that is the same as the playground sample. The playground sample cloud function only returns a string and does not show a version taking parms, but I figured that out.
The playground code also does not show anything about a return value that I could see. I would like to return the json returned from the function. It contains the ID and message. I was able to just return a string, like the playground code, but not the JSON, or string with the id and message.
Sorry about the confusion, I am new to SwiftUI and I do not know Node.js very well at all, BUT, I am learning, thank god!!
If your return format is: {"id":"<[email protected]>","message":"Queued. Thank you."}.
Then you can define a return type of:
struct EmailType: Decodable {
var id: String
var message: String
}
struct Cloud: ParseCloud
{
typealias ReturnType = EmailType // This can also be a [String: String], but the struct is probably more useful. If it's a specific ParseObject being returned, that should be here.
var functionJobName: String
var arguments: [String : String] = [:] // The comments in the playground and in the API documentation says how to create this
}
Ahh, ok yes, that makes sense and that is what I was missing and did not understand. Thank you. Everything works as expected now!!
So one other question, in my node.js cloud func, I use await as follows:
const result = await mg.messages().send(data)|
console.log(`************************* Sending Mail - result - ${JSON.stringify(result)}`);|
return result|
Is it ok to use await in my server node.js cloud function?
Hey cbaker6, one other quick question about this struct:
struct Cloud: ParseCloud
{
typealias ReturnType = EmailType
var functionJobName: String
var arguments: [String : String] = [:]
}
Is there a way to modify the arguments property so that I can use a [String : Any]?
Some of my parameters are a key with a value that is a list of string, or ints. so by using a string to any, I can pass that up and on the server it knows how to deal with the value. I used to pass string : any with the iOS parse sdk and PFCloud.
Can I have the arguments be a struct that is Encodable?
Is there a way to modify the arguments property so that I can use a [String : Any]?
No, Any isn’t Encodable, this only works with types that conform to the Encodable protocol. The dictionary for an argument was just an example. Your arguments can be separated into multiple properties of your cloud struct:
struct Cloud: ParseCloud {
typealias ReturnType = EmailType
var functionJobName: String
var argument1: String // Required parameter to your cloud function, `argument1` should match the name of your cloud function parameter.
var argument2: Int? // Optional parameter to your cloud function, `argument2` should match the name of your cloud function parameter.
// You can have as many parameters as your cloud function has
}
Can I have the arguments be a struct that is Encodable?
If you want something similar to Any, you can add the AnyCodable package via SPM and use type AnyEncodable. See more here and here. So it may look like:
struct Cloud: ParseCloud {
typealias ReturnType = EmailType
var functionJobName: String
var argument1: String // Required parameter to your cloud function, `argument1` should match the name of your cloud function parameter.
var argument2: Int? // Optional parameter to your cloud function, `argument2` should match the name of your cloud function parameter.
var argument3: AnyEncodable // Required parameter to your cloud function, `argument3` should match the name of your cloud function parameter.
// You can have as many parameters as your cloud function has
}
You can also do this with a mixed dictionary. Though I prefer the aforementioned methods as opposed to putting everything in a dictionary:
struct Cloud: ParseCloud {
typealias ReturnType = EmailType
var functionJobName: String
var arguments: [String: AnyEncodable]
}
Note that if you are using Xcode 13.2+, you probably want to start using the async/await instead of asynchronous callbacks. So instead of:
You would write:
let sendEmail = Cloud(functionJobName: "sendEmail", arguments: myParameters)
do {
let response = try await sendEmail.runFunction()
print("Response from cloud function: \(response)")
} catch {
print("Error calling cloud function: \(error)")
}
You can also improve the call of your Cloud function call by adding inits:
struct SendEmail: ParseCloud {
typealias ReturnType = EmailType
var functionJobName: String // You can also do `var functionJobName = "sendEmail"`
var arguments: String // Required parameter to your cloud function, `argument1` should match the name of your cloud function parameter.
}
extension SendEmail {
// Place all of your inits in an extension instead of the struct declaration
init(arguments: [String: String) {
functionJobName = "sendEmail"
self.arguments = arguments
}
}
let sendEmail = SendEmail(arguments: myParameters)
do {
let response = try await sendEmail.runFunction()
print("Response from cloud function: \(response)")
} catch {
print("Error calling cloud function: \(error)")
}
cbaker6, thanks for all the info, it has been most helpful. I have one question about a response. So as you can see from the above, there are two possibilities, right… success and error. I defined the ReturnType as EmailType and defined the required properties and it works great. However, in an error case, the properties are different. So, I used one method of making the properties in the struct optional so then f they do not exist it is ok. But, I try to steer away from optionals if I can. Is there a better way to define the error case return type? Or, is using optionals the only choice?