convert api string to JSON in swift or objective-c - objective-c

hi i'm getting following string(text/html) from my api request.
let request = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {(response, data, error) in
let res = NSString(data: data, encoding: NSUTF8StringEncoding)!
var jsonStr = res
//here 'res' will be {status: 1, userid: "447", store_code: "930"}
var data = jsonStr.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
var localError: NSError?
var json: AnyObject! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError)
println("json res \(json)") // 'json' here is nil
if let dict = json as? [String: AnyObject] {
let weather = dict["status"] as? [AnyObject]
println(weather)
}
}
//following is the text/html response from api request
{status: 1, userid: "447", store_code: "930"}
So how can i convert this text/html response string to JSON.
When i try to use NSJSONSerialization it gave fatal error or nil.
Can anybody help me how can i solve this issue in swift or objective-c?

JSON specification requires all keys of dictionaries as quoted strings.
This wraps all keys in double quotes with regex
let str = "{status: 1, userid: \"447\", store_code: \"930\"}"
let regex = NSRegularExpression(pattern: "\\w+(?=: )", options: nil, error: nil)!
let jsonString = regex.stringByReplacingMatchesInString(str, options: NSMatchingOptions(0), range: NSMakeRange(0, count(str)), withTemplate: "\"$0\"")
let jsonData = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
let jsonObject = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions(0), error: nil)

Personally I use JSONJoy to extract those json's elements to DAO.

Related

Swift SQL POST method - Why does it always create a response with <null> as the string?

I'm trying to set up POST requests to connect my Swift Project to my SQL server. I found a lot of good resources on how to create the POST requests, and my code seems to create the request fine. However, it always returns a null string instead of the desired output string. How can I get the request to submit with the correct string?
My POST function:
func addString() {
// declare the parameter as a dictionary that contains string as key and value combination. considering inputs are valid
let parameters: [String: Any] = ["textstring":"Testing..."]
// create the url with URL
let url = URL(string: "http://localhost:3000/strings")! // change server url accordingly
// create the session object
let session = URLSession.shared
// now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
// add headers for the request
request.addValue("application/json", forHTTPHeaderField: "Content-Type") // change as per server requirements
do {
// convert parameters to Data and assign dictionary to httpBody of request
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
return
}
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
print("Post Request Error: \(error.localizedDescription)")
return
}
// ensure there is valid response code returned from this HTTP response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode)
else {
print("Invalid Response received from the server")
return
}
// ensure there is data returned
guard let responseData = data else {
print("nil Data received from the server")
return
}
do {
// create json object from data or use JSONDecoder to convert to Model struct
if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
print(jsonResponse)
// handle json response
} else {
print("data maybe corrupted or in wrong format")
throw URLError(.badServerResponse)
}
} catch let error {
print(error.localizedDescription)
}
}
// perform the task
task.resume()
}
My Object TextString that emulates the SQL table (Table "strings" has two columns: "id" (SERIAL PRIMARY KEY) and "textstring" (VARCHAR(255))
struct TextString: Codable, Hashable, Identifiable {
var id: Int
var textString: String
enum CodingKeys: String, CodingKey {
case id = "string_id"
case textString = "textstring"
}}
My response output:
["string_id": 28, "textstring": <null>]
I'm thinking that the issue is with the serialization but the code is taken almost directly from a youtube video where the test cases are shown and they work. Does anyone know why the output returns "textstring" as null and not "Testing..."?
Thanks in advance!

How convert URL building code from Objective-C to Swift? [duplicate]

This question already has answers here:
Swift - encode URL
(19 answers)
Closed 3 years ago.
I have Objective-C code to build a URL. How to write in Swift 3 code?
NSString *urlString = [[NSString alloc]initWithFormat:#"http://smartbaba.in/Familynk/api/registration.php?phone_no=%#&email=%#&password=%#&first_name=%#&last_name=%#",txtmobileno.text,txtemail.text,txtpassword.text,txtfirstname.text,txtlastname.text];
Try This
"http://smartbaba.in/Familynk/api/registration.php?phone_no=\(txtmobileno.text)&email=\(txtemail.text)&password=\(txtpassword.text)&first_name=\(txtfirstname.text)&last_name=\(txtlastname.text)"
Swift conversion can be something like this
// I thing code requires no explanation, its self explanatory
func makeUrl(phoneNo: String, email: String, password: String, firstName: String, lastName: String) {
// first way but not recomended
let urlString = "http://smartbaba.in/Familynk/api/registration.php?phone_no=\(phoneNo)&email=\(email)&password=\(password)&first_name=\(firstName)&last_name=\(lastName)"
print("\(urlString)")
// second way, ASAIK this is good way for constructing URL's
var components = URLComponents()
components.scheme = "http"
components.host = "smartbaba.in"
components.path = "/Familynk/api/registration.php"
components.queryItems = [
URLQueryItem(name: "phone_no", value: phoneNo),
URLQueryItem(name: "email", value: email),
URLQueryItem(name: "password", value: password),
URLQueryItem(name: "first_name", value: firstName),
URLQueryItem(name: "last_name", value: lastName)
]
let url = components.url
print(url) // returns URL
print(url?.absoluteString) // returns url path in string
}
// call function
makeUrl(phoneNo: "12345", email: "test#gmail.com", password: "12345678", firstName: "test", lastName: "user")
You can simply use String interpolation to create a String with multiple parameters like,
let urlString = "http://smartbaba.in/Familynk/api/registration.php?phone_no=\(txtmobileno.text)&email=\(txtemail.text)&password=\(txtpassword.text)&first_name=\(txtfirstname.text)&last_name=\(txtlastname.text)"
Now, to get the URL using urlString,
if let url = URL(string: urlString) {
//use url here...
}
post method using URLSession:
let myUrl = URL(string: "http://smartbaba.in/Familynk/api/registration.php");
var request = URLRequest(url:myUrl!)
request.httpMethod = "POST"
let postString = "firstName=James&lastName=Bond";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
if error != nil
{
print("error=\(error)")
return
}
// You can print out response object
print("response = \(response)")
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
print(parseJSON)
}
} catch {
print(error)
}
}
task.resume()
If you are using Alamofire then,
let parameters: Parameters = [
"Subject": "hallo"
]
let url = "http://mydomain/mydb/mydb_tesT.NSF/api/data/documents/unid/DD026770D91AA23DC1257EF90035E1C4"
Alamofire.request(url, method:.post, parameters:parameters, headers:headers).responseJSON { response in
switch response.result {
case .success:
debugPrint(response)
case .failure(let error):
print(error)
}
}

Bad Request: there is no photo in the request

I'm trying to send an image from my application to telegram bot like here
https://newfivefour.com/swift-form-data-multipart-upload-URLRequest.html
Here is the code
let BotToken = "12345"
let ChatID = "123"
func SendToTelegram()
{
var request = URLRequest(url: URL(string: "https://api.telegram.org/bot"+BotToken+"/sendPhoto")!)
request.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let params = [:] as [String: String]
UIGraphicsBeginImageContextWithOptions(ScreenImage.bounds.size, true, 0.0)
ScreenImage.image?.draw(in: CGRect(x: 0, y: 0, width: ScreenImage.frame.size.width, height: ScreenImage.frame.size.height))
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
request.httpBody = createBody(parameters: params,
boundary: boundary,
data: UIImageJPEGRepresentation(image!, 0.7)!,
mimeType: "image/jpg",
filename: "hello.jpg")
print(request.httpBody!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else { // check for fundamental networking error
return
}
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? AnyObject
if let parseJSON = json {
print("resp :\(parseJSON)")
}
} catch let error as NSError {
print("error : \(error)")
}
}
task.resume()
}
and get an error
Bad Request: there is no photo in the request";
"error_code" = 400;
ok = 0;
Where do i make a mistake? I'm new in SWIFT and sorry for my English
I know this question is old but the link provided pushed me in the right direction. This is my solution for server side Swift not iOS but you should be able to use it with minimal changes. Remember if you're using iOS, perform none of these operations on the main thread.
class NetworkManager {
func sendTelegramPhoto(_ photo: Data) {
let url = "https://api.telegram.org/bot\(Constants.Telegram.token)/sendPhoto"
let params: [String: Any] = [
"chat_id": Constants.Telegram.uid,
"photo": photo
]
let _ = sendMultiTypePostRequest(url, parameters: params)
}
private func sendMultiTypePostRequest(_ url: String, parameters: [String:String]) -> NetworkResponse {
var networkResponse = NetworkResponse()
let semaphore = DispatchSemaphore(value: 0)
guard let url = URL(string: url) else {
return networkResponse
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
let boundary = "Boundary-\(UUID().uuidString)"
urlRequest.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
let httpBody = createBody(parameters: parameters,
boundary: boundary,
mimeType: "image/jpeg",
filename: "snapshot.jpg")
let config = URLSessionConfiguration.default
config.requestCachePolicy = .reloadIgnoringLocalCacheData
let session = URLSession(configuration: config)
let task = session.uploadTask(with: urlRequest, from: httpBody) { (data, response, error) in
networkResponse.data = data
networkResponse.response = response
networkResponse.error = error
semaphore.signal()
}
task.resume()
_ = semaphore.wait(timeout: .distantFuture)
return networkResponse
}
private func createBody(parameters: [String: String],
boundary: String,
mimeType: String,
filename: String) -> Data {
var body = Data()
let boundaryPrefix = "--\(boundary)\r\n"
for (key, value) in parameters {
if let data = value as? Data {
body.appendString(boundaryPrefix)
body.appendString("Content-Disposition: form-data; name=\"\(key)\"; filename=\"\(filename)\"\r\n")
body.appendString("Content-Type: \(mimeType)\r\n\r\n")
body.append(data)
body.appendString("\r\n")
} else if let string = value as? String {
body.appendString(boundaryPrefix)
body.appendString("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n")
body.appendString("\(string)\r\n")
}
}
body.appendString("--".appending(boundary.appending("--")))
return body
}
}
private extension Data {
mutating func appendString(_ string: String) {
let data = string.data(using: String.Encoding.utf8, allowLossyConversion: false)
append(data!)
}
}

Get server response message from error

My server (CakePHP) is responding like so:
$this->response->statusCode('400');
$this->response->type('json');
$this->response->body(json_encode(array('message' => 'Bookmark already exists')));
The Postman output looks like what you would expect:
{"message":"Bookmark already exists"}
The problem is that I cannot find a way to access this message from the failure handler (Alamofire 3.1.3 + SwiftyJSON 2.3.2)
Alamofire.request(.POST...
.validate()
.responseJSON { response in
switch response.result {
case .Success(_):
// All good
case .Failure(let error):
// Status code 400
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result)
I cannot find a way to cast response.data to JSON as a I simply get nil and the result returns just FAILURE.
Is there a way to access this server message from the failure handler ?
The data is not parsed in the .Failure case per the Alamofire 3.0 migration guide. However, server data is still available in response.data and can be parsed.
Below should work to parse this manually:
Alamofire.request(.POST, "https://example.com/create", parameters: ["foo": "bar"])
.validate()
.responseJSON { response in
switch response.result {
case .Success:
print("Validation Successful")
case .Failure(_):
var errorMessage = "General error message"
if let data = response.data {
let responseJSON = JSON(data: data)
if let message: String = responseJSON["message"].stringValue {
if !message.isEmpty {
errorMessage = message
}
}
}
print(errorMessage) //Contains General error message or specific.
}
}
}
This uses SwiftyJSON which provides the JSON struct to convert NSData. Parsing NSData to JSON can done without SwiftyJSON, answered here.
Another cleaner option might be to write a Custom Response Serializer.
A method with the router and no SwiftyJSON:
Alamofire.request(APIRouter.Register(params: params)).validate().responseJSON { response in
switch response.result {
case .Success(let json):
let message = json["clientMessage"] as? String
completion(.Success(message ?? "Success"))
case .Failure(let error):
var errorString: String?
if let data = response.data {
if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
errorString = json["error"]
}
}
completion(.Error(errorString ?? error.localizedDescription))
}
}
I have used the following lines to read the response body from a Alamofire request.
Alamofire.request(.POST, serveraddress, headers: headers, encoding: .JSON)
.response{ request, response, data, error in
let responseData = String(data: data!, encoding: NSUTF8StringEncoding)
print(responseData)
}
With this body I can get my custom server response errormessage.
best regards
For Alamofire 4.0 and above :
Try this
response.response?.statusCode
url : "YOUR-URL"
parameters: "YOUR PARAMETER DICTIONARY"
headers: "YOUR HEADER DICTIONARY"
I am using SwiftyJSON for JSON Parsing
A sample request is here :
func createPostRequestWith(path: String?,
parameters: [String : Any]? = nil,
success : #escaping (Any?) -> (),
failure : #escaping (NSError) -> ()) {
if !(Alamofire.NetworkReachabilityManager()?.isReachable)! {
let error = NSError(domain: "", code: -1003, userInfo: nil)
failure(error)
} else {
guard let url = path else { return }
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: createCurrentHeader()).validate(statusCode: 200..<300).responseJSON {
response in
switch response.result {
//Remove loader here either after parsing or on error
case .success(let data):
success(data)
case .failure(let error):
print(error)
if let responseData = response.data {
var parsedResponseData = JSON.init(data: responseData)
let customError = NSError(domain: parsedResponseData["message"].stringValue, code: response.response?.statusCode ?? 555, userInfo: nil)
failure(customError as NSError)
} else {
failure(error as NSError)
}
}
}
}
}

Missing argument for parameter #2 in call to Alamofire.request(URLRequestConvertible)

Trying to pass an Authentication header in, using the recommended approach of a custom URLRequestConvertible.
So here is my URLRequestConvertible object that conforms to the protocol:
class SecureJSONRouter : URLRequestConvertible {
var type: String
var token: String
var parameters: [String: AnyObject]
init(typevar: String, tokenvar: String, parametersvar: [String: AnyObject]) {
type = typevar
token = tokenvar
parameters = parametersvar
}
var URLRequest: NSURLRequest {
let URL = NSURL(string: da_url)!
let URLRequesting = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(type))
let encoding = Alamofire.ParameterEncoding.JSON
URLRequesting.setValue(token, forHTTPHeaderField: "Authorization")
return encoding.encode(URLRequesting, parameters: parameters).0
}
}
It's basically a place to store a few things, like a bit of URL, and a token, and a way to create a NSURLRequest with a full URL, the JSON parameters, and the header field for authorization.
When I try to call it like this:
let myUrlRequest: URLRequestConvertible = SecureJSONRouter(typevar: "locations", tokenvar: token!, parametersvar: parameters)
Alamofire.request(myUrlRequest).response{ (req, resp, data, error) in
if (error != nil) {
println(req)
println(resp)
} else {
println("saved \(data)")
}
}
it won't compile, gives me a "Missing argument for parameter #2 in call" error, at the request line.
Any ideas?
PS: I did have my SecureJSONRouter thingy as the recommended Enum instead of a Class, but I got the same error. I was looking at the protocol definition, and figured there's no reason it can't be a simpler (class) in my case, so I changed it. Still the same error.
I think there's a few possible culprits here. First off, are you sure you're calling Alamofire and not AlamoFire? I've seen people make that mistake before.
As for the sample code you posted, it didn't compile for a few different reasons. I couldn't reproduce exactly the same compiler errors you were seeing, but here's a slightly modified version of you original post that does compile.
class SecureJSONRouter : URLRequestConvertible {
var type: String
var token: String
var parameters: [String: AnyObject]
init(typevar: String, tokenvar: String, parametersvar: [String: AnyObject]) {
type = typevar
token = tokenvar
parameters = parametersvar
}
var URLRequest: NSURLRequest {
let URL = NSURL(string: "http://httpbin.org")!
let URLRequesting = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(type))
let encoding = Alamofire.ParameterEncoding.JSON
URLRequesting.setValue(token, forHTTPHeaderField: "Authorization")
return encoding.encode(URLRequesting, parameters: parameters).0
}
}
Then here's an example of calling your SecureJSONRouter.
let token: String? = "my_fancy_token"
let parameters: [String: AnyObject] = ["sample_parameter": "sample_parameter_value"]
let myUrlRequest: URLRequestConvertible = SecureJSONRouter(typevar: "locations", tokenvar: token!, parametersvar: parameters)
Alamofire.request(myUrlRequest).response{ (req, resp, data, error) in
if (error != nil) {
println(req)
println(resp)
} else {
println("saved \(data)")
}
}
That is compiling with Xcode 6.1.1 against the Alamofire 1.1.3 release. Hope that helps!