class TranslateModel : ObservableObject {
func translateCall() {
guard let url = URL(string: "https://openapi.naver.com/v1/papago/n2mt") else { return }
print(1)
let param = "source=en&target=kr&text=hi"
let paramData = param.data(using: .utf8)
var request = URLRequest(url: url)
print(2)
request.httpMethod = "POST"
request.httpBody = paramData
print(3)
request.addValue("application/x-www-form-urlencoded; charset=UTF-8", forHTTPHeaderField: "Content-Type")
request.addValue("3Bwy8lMkuAgZOyDHm1Z3", forHTTPHeaderField: "X-Naver-Client-Id")
request.addValue("gg391Jc1Ge", forHTTPHeaderField: "X-Naver-Client-Secret")
print(4)
let data = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else { fatalError() }
print(5)
guard error == nil else { fatalError()}
print(6)
guard let response = response as? HTTPURLResponse, response.statusCode >= 200 && response.statusCode < 300 else {return}
print(7)
print(data)
}
}
}
Firstly, Please check my code and screenshot.
I tried to do POST API with parameters and headers.
However, when I call the method, It's not working.
So, I checked the step of method, And found that the URLsession with request was not working.
But, I don't know what the problem is.
I think maybe parameters or headers faults.
could you let me know how I can solve this?
I'm using the new Swift 4 Codable interfaces to do a simple fetch of JSON data from a web service. I've tried to implement a generic type method to handle decoding (so I don't need custom methods) but I keep getting an error. Here is the code
extension StarWarsAPI {
public func decodeJson<T: Codable>(fetchUrl: URL, modelType: T, completion: #escaping (_ modelObject: Codable?, _ error:StarWarsErrorType?) -> Void){
//guard modelType is Codable else {return completion(nil,nil)}
var fetchRequest = URLRequest(url: fetchUrl, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
fetchRequest.httpMethod = "GET"
fetchRequest.allHTTPHeaderFields = [
"content-type": "application/json",
"cache-control": "no-cache",
]
let session = URLSession.shared
let fetchDataTask = session.dataTask(with: fetchRequest) { (data, response, error) in
guard error == nil else {
return completion(nil, StarWarsErrorType.urlResponseError(error: error))
}
guard let httpResponse = response as? HTTPURLResponse else {
return completion(nil, StarWarsErrorType.NilUrlResponseError())
}
guard let data = data else {
return completion(nil, StarWarsErrorType.noDataFound)
}
guard httpResponse.statusCode > 199 && httpResponse.statusCode < 300 else {
return completion(nil, StarWarsErrorType.httpErrorCode(code: httpResponse.statusCode))
}
var modelObject:Codable?
do {
let jsonDecoder = JSONDecoder()
modelObject = try jsonDecoder.decode(modelType.self, from: data)
return completion(modelObject, nil)
}catch{ // do nothing }
DispatchQueue.main.async {
completion(nil, nil)
}
}
}
fetchDataTask.resume()
}
The Error reads "Cannot invoke 'decode' with an argument list of type '(T, from: Data)'" . Here is a screenshot of the error.
What am I missing? Thanks!
The type passed in to decode(_:from:) needs to be known statically. modelType.self is a dynamic instance of modelType, but its type isn't constrained statically; you'll need to use T.self, which is the statically known type.
This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 5 years ago.
I am new in swift and creating a network call with below code but getting an "fatal error: unexpectedly found nil while unwrapping an Optional value" please help. Objective C version of this code is working fine for me
class func request(withUrlString urlString: String) -> NSMutableURLRequest
{
let request = NSMutableURLRequest(url: URL(string: urlString)!)
request.addValue("application/json", forHTTPHeaderField:"Accept")
request.httpMethod = "GET"
request.timeoutInterval = 60
return request
}
class func send(_ request: URLRequest, completion callback: #escaping (_: Data, _: Error) -> Void)
{
if WebServiceManager.isInternetAvailable() == false
{
let data: NSData? = nil
callback(data as! Data, NSError(domain:kNetworkErrorMeassage, code:0, userInfo:nil))
return
}
// print("Request# \n URL : \(request.url?.absoluteString) \n Headers : \(request.allHTTPHeaderFields!.description) \n Request Method : \(request.httpMethod) \n Post body : \(request.httpBody ? try? JSONSerialization.jsonObject(withData: request.httpBody, options: []) : request.httpBody)\n")
// print("Request# \n URL : \(request.url?.absoluteString) \n Headers : \(request.allHTTPHeaderFields!.description) \n Request Method : \(request.httpMethod) \n Post body : \(request.httpBody ? String(data: request.httpBody!, encoding: String.Encoding.ascii) : request.httpBody)\n")
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let dataTask : URLSessionDataTask? = URLSession.shared.dataTask(with: request) { (responseData, response, error) in
UIApplication.shared.isNetworkActivityIndicatorVisible = false
let httpResponce: HTTPURLResponse? = (response as? HTTPURLResponse)
let responseStatusCode: Int? = httpResponce?.statusCode
if responseStatusCode == 200 || responseStatusCode == 201 || responseStatusCode == 202
{
DispatchQueue.main.async(execute: {() -> Void in
callback(responseData!, error!)
})
}
else
{
DispatchQueue.main.async(execute: {() -> Void in
let data: NSData? = nil
callback(data as! Data, error!)
})
}
}
dataTask?.resume()
}
responseData will be nil if the dataTask method encounters an error. In this case error will be non-nil. You should make sure to check for an error before force-unwrapping responseData in your callback.
Here’s a code sample:
class func send(_ request: URLRequest, completion callback: #escaping (_: Data, _: Error) -> Void) {
let dataTask : URLSessionDataTask? = URLSession.shared.dataTask(with: request) { (responseData, response, error) in
if let error = error {
// handle error
} else {
// now we know responseData is non-nil
UIApplication.shared.isNetworkActivityIndicatorVisible = false
let httpResponce: HTTPURLResponse? = (response as? HTTPURLResponse)
let responseStatusCode: Int? = httpResponce?.statusCode
if responseStatusCode == 200 || responseStatusCode == 201 || responseStatusCode == 202 {
DispatchQueue.main.async {
callback(responseData!, nil)
}
} else {
// ...
}
}
}
}
The first thing that happens here is the error check. After that you can safely unwrap the optional responseData. Since error is nil in this case I changed the Error variable of your callback function to an optional.
I also used the trailing closure syntax for the Dispatch.main.async call as it makes it more readable.
Finally, in your else clause (which is not contained in my sample) you create a variable data which is nil only to force-cast it one line later... this will always crash. Please think again about what you want to achieve here.
I had this code in my Swift App
func parseJSON() {
let urlString = "www.websitethatlinkstoJSONfile.com"
if NSURL(string: urlString) == true {
let url = NSURL(string: urlString)
let data = try? NSData(contentsOfURL: url!, options: []) as NSData
let json = NSData(data: data!)
// more code
However, even though the link actually worked and was true, the if statement was never met and it kept skipping it and moving to else. So I changed the code to
if NSURL(string: urlString) != false
and it worked perfectly. I'm not sure why though?
As already explained in the other answers, comparing the optional
NSURL? against true or false is not what you want, and you should
use optional binding instead.
But why does it compile at all? And how can the result be interpreted?
In NSURL(string: urlString) == true, the left-hand side has the type
NSURL?, and NSURL is a subclass of NSObject.
There is a == operator taking two optional operands:
public func ==<T : Equatable>(lhs: T?, rhs: T?) -> Bool
The compiler uses the implicit conversion of Bool to NSNumber
to make that compile. So your code is equivalent to
if NSURL(string: urlString) == NSNumber(bool: true)
and that will always be false, and
if NSURL(string: urlString) != NSNumber(bool: false)
will always be true, simply because the left-hand side is not a
number.
Here is a demonstration of the effect:
func foo(x: NSObject?) {
print(x == true, x == false)
}
foo(NSNumber(bool: true)) // true, false
foo(NSNumber(bool: false)) // false, true
foo(NSObject()) // false, false !!!
The last case is what you observed: Both x == true and x == false
return false.
For classes not inheriting from NSObject it would not compile:
class A { }
let a: A? = A()
if a == true { } // cannot convert value of type 'A?' to expected argument type 'Bool'
Remark: This is another argument for not comparing boolean values
against true or false, i.e.
if a == true && b == false { ... }
is better written as
if a && !b { ... }
Applied to your case, you would get a compiler error indicating
the problem:
let urlString = "http://www.websitethatlinkstoJSONfile.com"
if NSURL(string: urlString) { }
// error: optional type '_' cannot be used as a boolean; test for '!= nil' instead
You don't really want to check for a boolean value when creating a NSURL, but rather make sure the NSURL you create is non-nil. Try wrapping it it in an if let statement like so to make sure whatever URL's you create are non-nil before executing further code.
let urlString = "www.websitethatlinkstoJSONfile.com"
if let url = NSURL(string: urlString) {
if let data = try? NSData(contentsOfURL: url, options: []) {
let json = NSData(data: data)
} else {
//Handle case where data is nil
}
} else {
//Handle case where url is nil
}
Using if let statements in this way makes sure that the NSURL and NSData objects you are creating are non-nil and valid objects and then you can add an else statement to them to handle cases where your url or data objects are nil. This will save you from unwanted crashes due to force unwrapping with the ! operator.
Yes there is a different take a look at some of the documentation. In this init method it is fallible.
public convenience init?(string URLString: String)
the question mark indicates it is fallible.
So it will return a NSURL object or nil.
convenience init?(parameter: AnyObject) {
if parameter == nil {
return nil
}
self.init()
}
So in a specific example like your example. You can test it in playground
let urlString = "100"
if NSURL(string: urlString) == true {
print("true")
//Never prints in any circumstance//
}
if NSURL(string: urlString) != false {
print("true")
//always prints//
}
if NSURL(string: urlString) != nil {
print("true")
//object was created//
}
The == operator checks whether both side value is equal or not.For example:
var data:Int = 6
if data == 5 {
//if this block is executed that means `data` is exactly equal to 5
//do something
}
else {
//In this situation, this block of code will be executed.
//if this block is executed that means data is anything other than 5.
//do something
}
The != operator checks that two values are not equal to each other.For example:
var data:Int = 6
if data != 5 {
//In this situation, this block of code will be executed.
//If this block is executed that means `data` is anything other than 5.
//do something
}
else {
//if this block is executed that means data is exactly equal to 5.
//do something
}
In your case code if NSURL(string: urlString) == true checks that if NSURL(string: urlString) return true then it should excute if block otherwise else block.
NSURL(string: urlString) is convenience initializer which creates NSURL object and returns. In case if it fails to do so then it returns nil
In any case, it does not return either true or false.So when you compare this with true it always fails and goes to else block.
And when you check that it is not equal to false (!= false) becomes true because NSURL(string: urlString) returning NSURL object and that is not equal to false.
So if you want to check that whether NSURL object is created or not you can check whether the return value is nil or not.
if NSURL(string: urlString) != nil {
//Object is created successfully.Now you can do whatever you want this object.
}
else {
//It failed to create an object.
}
I wonder if it's possible to directly send an array (not wrapped in a dictionary) in a POST request. Apparently the parameters parameter should get a map of: [String: AnyObject]?
But I want to be able to send the following example json:
[
"06786984572365",
"06644857247565",
"06649998782227"
]
You can just encode the JSON with NSJSONSerialization and then build the NSURLRequest yourself. For example, in Swift 3:
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let values = ["06786984572365", "06644857247565", "06649998782227"]
request.httpBody = try! JSONSerialization.data(withJSONObject: values)
AF.request(request) // Or `Alamofire.request(request)` in prior versions of Alamofire
.responseJSON { response in
switch response.result {
case .failure(let error):
print(error)
if let data = response.data, let responseString = String(data: data, encoding: .utf8) {
print(responseString)
}
case .success(let responseObject):
print(responseObject)
}
}
For Swift 2, see previous revision of this answer.
For swift 3 and Alamofire 4 I use the following ParametersEncoding and Array extension:
import Foundation
import Alamofire
private let arrayParametersKey = "arrayParametersKey"
/// Extenstion that allows an array be sent as a request parameters
extension Array {
/// Convert the receiver array to a `Parameters` object.
func asParameters() -> Parameters {
return [arrayParametersKey: self]
}
}
/// Convert the parameters into a json array, and it is added as the request body.
/// The array must be sent as parameters using its `asParameters` method.
public struct ArrayEncoding: ParameterEncoding {
/// The options for writing the parameters as JSON data.
public let options: JSONSerialization.WritingOptions
/// Creates a new instance of the encoding using the given options
///
/// - parameter options: The options used to encode the json. Default is `[]`
///
/// - returns: The new instance
public init(options: JSONSerialization.WritingOptions = []) {
self.options = options
}
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let parameters = parameters,
let array = parameters[arrayParametersKey] else {
return urlRequest
}
do {
let data = try JSONSerialization.data(withJSONObject: array, options: options)
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = data
} catch {
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
}
return urlRequest
}
}
Basically, it converts the array to a Dictionary in order to be accepted as Parameters argument, and then it takes back the array from the dictionary, convert it to JSON Data and adds it as the request body.
Once you have it, you can create request this way:
let values = ["06786984572365", "06644857247565", "06649998782227"]
Alamofire.request(url,
method: .post,
parameters: values.asParameters(),
encoding: ArrayEncoding())
Here is an example of encoding an Array of type Thing to JSON, using a router, and Ogra to do the JSON encoding:
import Foundation
import Alamofire
import Orga
class Thing {
...
}
enum Router: URLRequestConvertible {
static let baseURLString = "http://www.example.com"
case UploadThings([Thing])
private var method: Alamofire.Method {
switch self {
case .UploadThings:
return .POST
}
}
private var path: String {
switch self {
case .UploadThings:
return "upload/things"
}
}
var URLRequest: NSMutableURLRequest {
let r = NSMutableURLRequest(URL: NSURL(string: Router.baseURLString)!.URLByAppendingPathComponent(path))
r.HTTPMethod = method.rawValue
switch self {
case .UploadThings(let things):
let custom: (URLRequestConvertible, [String:AnyObject]?) -> (NSMutableURLRequest, NSError?) = {
(convertible, parameters) in
var mutableRequest = convertible.URLRequest.copy() as! NSMutableURLRequest
do {
let jsonObject = things.encode().JSONObject()
let data = try NSJSONSerialization.dataWithJSONObject(jsonObject, options: NSJSONWritingOptions.PrettyPrinted)
mutableRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableRequest.HTTPBody = data
return (mutableRequest, nil)
} catch let error as NSError {
return (mutableRequest, error)
}
}
return ParameterEncoding.Custom(custom).encode(r, parameters: nil).0
default:
return r
}
}
}
Swift 2.0
This code below post object array.This code is tested on swift 2.0
func POST(RequestURL: String,postData:[AnyObject]?,successHandler: (String) -> (),failureHandler: (String) -> ()) -> () {
print("POST : \(RequestURL)")
let request = NSMutableURLRequest(URL: NSURL(string:RequestURL)!)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
var error: NSError?
do {
request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(postData!, options:[])
} catch {
print("JSON serialization failed: \(error)")
}
Alamofire.request(request)
.responseString{ response in
switch response.result {
case .Success:
print(response.response?.statusCode)
print(response.description)
if response.response?.statusCode == 200 {
successHandler(response.result.value!)
}else{
failureHandler("\(response.description)")
}
case .Failure(let error):
failureHandler("\(error)")
}
}
}
#manueGE 's answer is right. I have a similar approach according to alamofire github's instruction:
`
struct JSONDocumentArrayEncoding: ParameterEncoding {
private let array: [Any]
init(array:[Any]) {
self.array = array
}
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = urlRequest.urlRequest
let data = try JSONSerialization.data(withJSONObject: array, options: [])
if urlRequest!.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest!.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
urlRequest!.httpBody = data
return urlRequest!
}
}
`
Then call this by customize a request instead of using the default one with parameter. Basically discard the parameter, since it is a dictionary.
let headers = getHeaders()
var urlRequest = URLRequest(url: URL(string: (ServerURL + Api))!)
urlRequest.httpMethod = "post"
urlRequest.allHTTPHeaderFields = headers
let jsonArrayencoding = JSONDocumentArrayEncoding(array: documents)
let jsonAryEncodedRequest = try? jsonArrayencoding.encode(urlRequest, with: nil)
request = customAlamofireManager.request(jsonAryEncodedRequest!)
request?.validate{request, response, data in
return .success
}
.responseJSON { /*[unowned self] */(response) -> Void in
...
}
Also, the way to handle error in data is very helpful.
let url = try Router.baseURL.asURL()
// Make Request
var urlRequest = URLRequest(url: url.appendingPathComponent(path))
urlRequest.httpMethod = "post"
// let dictArray: [[String: Any]] = []
urlRequest = try! JSONEncoding.default.encode(urlRequest, withJSONObject: dictArray)
Something I do in my project to upload a JSON array
func placeOrderApi(getUserId:String,getDateId:String,getTimeID:String,getAddressId:String,getCoupon:String)
{
let data = try! JSONSerialization.data(withJSONObject: self.arrOfServices, options: [])
let jsonBatch : String = String(data: data, encoding: .utf8)!
//try! JSONSerialization.data(withJSONObject: values)
let params = [
"user_id":getUserId,
"time_id":getTimeID,
"date_id":getDateId,
"address_id":getAddressId,
"services":jsonBatch,
"payment_mode":paymentVia,
"coupon":getCoupon
] as [String : Any]
print(params)
self.objHudShow()
Alamofire.request(BaseViewController.API_URL + "place_order", method: .post, parameters: params, encoding: JSONEncoding.default)
.responseJSON { response in
debugPrint(response)
switch response.result {
case .success (let data):
print(data)
self.objHudHide()
if response.result.value != nil
{
let json : JSON = JSON(response.result.value!)
if json["status"] == true
{
}
else
{
self.view.makeToast(NSLocalizedString(json["msg"].string ?? "", comment: ""), duration: 3.0, position: .bottom)
}
}
break
case .failure:
self.objHudHide()
print("Error in upload:)")
break
}
}
}
There are 2 approach to send send JSON content as parameter.
You can send json as string and your web service will parse it on server.
d["completionDetail"] = "[{"YearOfCompletion":"14/03/2017","Completed":true}]"
You can pass each value within your json (YearOfCompletion and Completed) in form of sequential array. And your web service will insert that data in same sequence. Syntax for this will look a like
d["YearOfCompletion[0]"] = "1998"
d["YearOfCompletion[1]"] = "1997"
d["YearOfCompletion[2]"] = "1996"
d["Completed[0]"] = "true"
d["Completed[1]"] = "false"
d["Completed[2]"] = "true"
I have been using following web service call function with dictionary, to trigger Alamofire request Swift3.0.
func wsDataRequest(url:String, parameters:Dictionary<String, Any>) {
debugPrint("Request:", url, parameters as NSDictionary, separator: "\n")
//check for internete collection, if not availabale, don;t move forword
if Rechability.connectedToNetwork() == false {SVProgressHUD.showError(withStatus: NSLocalizedString("No Network available! Please check your connection and try again later.", comment: "")); return}
//
self.request = Alamofire.request(url, method: .post, parameters: parameters)
if let request = self.request as? DataRequest {
request.responseString { response in
var serializedData : Any? = nil
var message = NSLocalizedString("Success!", comment: "")//MUST BE CHANGED TO RELEVANT RESPONSES
//check content availability and produce serializable response
if response.result.isSuccess == true {
do {
serializedData = try JSONSerialization.jsonObject(with: response.data!, options: JSONSerialization.ReadingOptions.allowFragments)
//print(serializedData as! NSDictionary)
//debugPrint(message, "Response Dictionary:", serializedData ?? "Data could not be serialized", separator: "\n")
}catch{
message = NSLocalizedString("Webservice Response error!", comment: "")
var string = String.init(data: response.data!, encoding: .utf8) as String!
//TO check when html coms as prefix of JSON, this is hack mush be fixed on web end.
do {
if let index = string?.characters.index(of: "{") {
if let s = string?.substring(from: index) {
if let data = s.data(using: String.Encoding.utf8) {
serializedData = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments)
debugPrint(message, "Courtesy SUME:", serializedData ?? "Data could not be serialized", separator: "\n")
}
}
}
}catch{debugPrint(message, error.localizedDescription, "Respone String:", string ?? "No respone value.", separator: "\n")}
//let index: Int = text.distance(from: text.startIndex, to: range.lowerBound)
debugPrint(message, error.localizedDescription, "Respone String:", string ?? "No respone value.", separator: "\n")
}
//call finised response in all cases
self.delegate?.finished(succes: response.result.isSuccess, and: serializedData, message: message)
}else{
if self.retryCounter < 1 {//this happens really frequntly so in that case this fn being called again as a retry
self.wsDataRequest(url: url, parameters: parameters)
}else{
message = response.error?.localizedDescription ?? (NSLocalizedString("No network", comment: "")+"!")
SVProgressHUD.showError(withStatus: message);//this will show errror and hide Hud
debugPrint(message)
//call finised response in all cases
self.delay(2.0, closure: {self.delegate?.finished(succes: response.result.isSuccess, and: serializedData, message:message)})
}
self.retryCounter += 1
}
}
}
}
I think based on Alamofire documentation you can write the code as following:
let values = ["06786984572365", "06644857247565", "06649998782227"]
Alamofire.request(.POST, url, parameters: values, encoding:.JSON)
.authenticate(user: userid, password: password)
.responseJSON { (request, response, responseObject, error) in
// do whatever you want here
if responseObject == nil {
println(error)
} else {
println(responseObject)
}
}