I am trying to subscribe new user to Mailchimp list using Alamofire.
Problem starts when I'm trying to subscribe new user with .post method and JSONObject as a parameter:
func subscribeMail(){
let credentialData = "<my_api_key>".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString(options: [])
let headers = ["Authorization": "Basic \(base64Credentials)"]
let url = "https://us11.api.mailchimp.com/3.0/lists/<my_list_id>/members/"
let jsonObj: [String: AnyObject] = [
"mail_address" : "testMailAddress#gmail.com" as AnyObject,
"status" : "subscribed" as AnyObject,
]
let valid = JSONSerialization.isValidJSONObject(jsonObj)
print(valid)
Alamofire.request(url, method: .post, parameters: jsonObj , encoding: URLEncoding.default , headers: headers).responseJSON{response in
if response.result.isFailure {
print("Failed")
}
else if (response.result.value as? [String: AnyObject]) != nil {
print(response)
}
}
}
I get back status code 400:
SUCCESS: {
detail = "We encountered an unspecified JSON parsing error.";
instance = "";
status = 400;
title = "JSON Parse Error";
type = "http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/";
}
In Mailchimp documentation:
JSONParseError
We encountered an unspecified JSON parsing error.
This error means that your JSON was formatted incorrectly or was considered invalid or incomplete.
As you can see I am checking my jsonObj if its valid. So I dont get this parsing error..
In Mailchimp API 3.0 its written that just email_address and status fields are needed to subscribe new mail.
If I try to to send request with Alamofire using .get method with some mail address that is already subscribed, everything works fine, I can receive all data from Mailchimp.
Is there really problem with my jsonObj or is it somewhere else?
Change the object key from 'mail_address' to 'email_address' and give a try.
let jsonObj: [String: AnyObject] = [
"email_address" : "testMailAddress#gmail.com" as AnyObject,
"status" : "subscribed" as AnyObject,
]
Since you're getting a JSONParseError, your issue is related to the format in which you're sending the parameters.
Try encoding: JSONEncoding.default instead of encoding: URLEncoding.default.
Related
I have a Vapor API that has a route to register users. This route recibes a nested object in JSON format like this:
{
"name": "Test",
"email": "test1#test.com",
"password": "Test1",
"phone": {
"numberString": "+52 999 999 9999",
"countryCode": 52,
"nationalNumber": 9999999999,
}
}
This JSON is converted into a Content/Codable Object:
final class Create: Codable, Content {
let name: String!
let email: String!
let password: String
let phone: PhoneNumber!
init(name: String, email: String, password: String, phone: PhoneNumber) {
self.name = name
self.email = email
self.password = password
self.phone = phone
}
}
I have tried this route sending the JSON string as raw via Postman and the route worked perfectly but the problem is when I try to send it via URLSession in my iOS counterpart the ErrorMiddleware throws a DecodingError:
DecodingError: Value of type 'String' required for key 'password'.
At first I thought that the problem was the JSON generation until, for test purpose I send the same JSON as in the example and the Vapor API is still throwing the Error.
let urlStr = "\(BaseURL)/api/student/register"
guard let url = URL(string: urlStr) else { return }
var urlRequest = URLRequest(url: url, cachePolicy:
.reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 30.0)
let raw = "{\"name\":\"Prueba\",\"email\":\"prueba1#hotmail.com\",\"password\":\"Prueba1\",\"phone\":{\"numberString\":\"\",\"countryCode\":,\"nationalNumber\":}}"
urlRequest.httpMethod = "POST"
urlRequest.httpBody = raw.data(using: .utf8)
urlRequest.addValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
...
}.resume()
Can you even send this types of JSON's via URLSession or do I need to change my logic so it will receive a flat array?
After hours of debugging and getting strange errors I realized that my error was simpler than I thought.
The error was:
{"error":true, "reason":"Value of type 'String' required for key 'password'."}
And I tried to send in Postman a request with out the key 'password' which returned:
{"error": true, "reason": "Value required for key 'password'."}
What got me to analyze my objects and then I saw the error, my Create object wasn't unwrapped correctly, more precisely this:
let password: String
Should be having a ! next to String like this.
let password: String!
The reason why this worked on Postman and not on URLSession is still uncleared.
UPDATE:
As proposed by #vadian the headers in this URLSession are also missing and even tho after I added the force unwrapped the API passed the request but with nil content
urlRequest.setValue("application/json", forHTTPHeaderField:"Accept")
urlRequest.setValue("application/json", forHTTPHeaderField:"Content-Type")
Set also the content-type and length header
urlRequest.setValue(String(Data(json.utf8).count), forHTTPHeaderField:"Content-Length")
urlRequest.setValue("application/json", forHTTPHeaderField:"Accept")
urlRequest.setValue("application/json", forHTTPHeaderField:"Content-Type")
And never, never, never declare properties in a class as implicit unwrapped optional which are initialized with non-optional values.
I know sending json body in get request is violating http rules, however according to client's requirement the server is accepting json body in get request.so i want to send json body in my get request using alamofire.
i tried various parameter encoding types.nothing worked.
func listNews( withCompletionHandler:#escaping (_ status:Bool, _ message: String, _ yearLevels: JSON) -> Void){
let parameters = [
"month": "07",
"year": "2019",
"record_start": 0,
"record_offset": 100
] as [String : Any]
let headers = [
"Content-Type": "application/json"
]
let url = STAFF_SERVICE_URL+"staff/2/news?api_key=\(api_key)"
print("URL > \(url)")
Alamofire.request(url, method: .get,parameters:parameters, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
let statusCode = response.response?.statusCode
print(statusCode ?? "Success Error Code")
if(statusCode==200){
let swiftyJsonVar = JSON(response.result.value!)
print(swiftyJsonVar)
withCompletionHandler(true,"Success",swiftyJsonVar)
}else if(statusCode==400){
withCompletionHandler(false,"System Error. Please try again later.",JSON.null)
}else{
withCompletionHandler(false,"System Error",JSON.null)
}
}
}
i expect array of json objects in response. but the actual output is time out error
Actually you should change encoding method for get request. You have set encoding: JSONEncoding.default. Instead of that, use encoding: URLEncoding.default.
Second thing, if you are sending parameters in json body, then send all parameters instead of some sending with body and some in url. So, your api_key=\(api_key) should be in json body.
I hope this will fix your issue.
I am trying to learn Swift. One of my projects is to try to retrieve JSON data from an internal web service (a group of Python CGI scripts) and convert it into a Swift object. I can do this easily in Python, but I am having trouble doing this in Swift. Here is my playground code:
import UIKit
import XCPlayground
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
let endpoint: String = "http://pathToCgiScript/cgiScript.py"
let url = NSURL(string: endpoint)
let urlrequest = NSMutableURLRequest(URL: url!)
let headers: NSDictionary = ["User-Agent": "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)",
"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"]
urlrequest.allHTTPHeaderFields = headers as? [String : String]
urlrequest.HTTPMethod = "POST"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlrequest) {
(data, response, error) in
guard data != nil else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("Error calling script!")
print(error)
return
}
do {
guard let received = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as?
[String: AnyObject] else {
print("Could not get JSON from stream")
return
}
print(received)
} catch {
print("error parsing response from POST")
}
}
task.resume()
I know making a 'POST' to retrieve data may look odd, but that is how the system is set up. I keep on getting:
Could not get data from JSON
I checked the response, and the status is 200. I then checked the data's description with:
print(data?.description)
I got an unexpected result. Here is a snippet:
Optional("<0d0a5b7b 22535441 54555322 3a202244 6f6e6522 2c202242 55535922...
I used Mirror, and apparently the type is NSData. Not sure what to make of this. I have tried to encode the data with base64EncodedDataWithOptions. I have tried different NSJSONReadingOptions as well to no avail. Any ideas?
Update:
I used Wireshark to double check the code in the Playground. Not only was the call made correctly, but the data being sent back is correct as well. In fact, Wireshark sees the data as JSON. The issue is trying to turn the JSON data into a Swift object.
I figured out what was wrong. I was casting to the wrong type. This is the new code:
guard let received = try! NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) as? [AnyObject]
The JSON was not returning an array of dictionaries but an array of objects.
I have tried a lot of things now and are still getting:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (_SwiftValue)'
UPDATE:
I scan a barcode and save the info:
let calendar = Calendar.current
let expiryDate = (calendar as NSCalendar).date(byAdding: .month, value: 3, to: Date(), options: [])?.description
let barcode = BarcodeData(barcodeValue: value,
datetime: dateTime,
expiryDate: expiryDate!,
latitude: latitude.description,
longitude: longitude.description,
status: txtStatus.text!,
type: txtType.text!,
extraText: "")
Object are then mapped to a JSON string, it seems that the slashes (/) are added by this function:
let jsonBarcode = Mapper<BarcodeData>().toJSONString(barcode)
The barcode are then added to a list of String:
barcodeDataList.append(jsonBarcode)
When I click a button I invoke the web service, that anticipate parameters in the form of:
let testParams : Parameters =
[ "udid": "my_udid",
"data": jsonArray
]
jsonArray consist of an array of the BarcodeData-object(s) as seen above.
Communication with the web service looks like:
Alamofire.request(url, method: .post, parameters: testParams, encoding: JSONEncoding.default).validate().responseJSON { response in
switch response.result {
case .success:
print("Validation successful")
if let json = response.result.value {
print("JSON: \(json)")
}
case .failure(let error):
print("Error: \(error)")
}
}
The following is passed to the ws:
["udid": "\"001-my_udid\"", "data": [
"{\"latitude\":\"0.0\",\"status\":\"Empty\",\"datetime\":\"2016-09-20 05:10\",\"longitude\":\"0.0\",\"type\":\"ABC123\",\"barcodevalue\":\"123456\"}"
]]
The json array for "data" validates at jsonlint.com and the response from the server is in the form of a json object like:
{result: "Data successfully received"}
Change your encoding in request from encoding: JSONEncoding.default to encoding: URLEncoding.default
I think you need to encode your array in json object try something like this
do{
let data = try NSJSONSerialization.dataWithJSONObject(mappingArray, options: NSJSONWritingOptions(rawValue: 0))
let mappedString : String = (String(data: data , encoding: NSUTF8StringEncoding)!)
return mappedString
}
catch{
print(error)
return error as! String
}
Append this string with your url
Sorry for my bad english :)
So here we go. I started to create iPhone Apps in Swift 2.
I would like to use an API. For the Request I have used the Alamofire 3.0 Library. And this works fine, but I am unable to handle the JSON. I tried to use SwiftyJSON but I have no idea how it works. There is my code:
let headers = [
"Content-Type": "application/json"
]
Alamofire.request(.GET, "API URL", headers: headers)
.responseJSON { response in
if response.result.isSuccess {
}
}
I hope someone can help me. ;) Thanks
My latest usage of Alamofire (3.0) and SwiftyJSON (2.3)
let parameters = ["param1" : param, "param2" : "stringParam"] // my params, not required
Alamofire.request(.POST, url, parameters: parameters)
.responseJSON{ response in
guard response.result.error == nil else {
print("Error. \(response.result.error?.localizedDescription)")
return
} // guard close
if response.result.isSuccess {
let post = JSON(response.result.value!) // JSON is stored in post variable
// Another check of result
//if let value: AnyObject = response.result.value {
// let post = JSON(value)
} // if close
} // responseJSON close
Then access the JSON as per the instructions in Github
Hope this helps
Jacko