Swift: passing params - unexpectedly found nil while unwrapping an Optional value - swift

I'm trying to use Alamofire and passing my parameters to a function I made.
Here's my code:
let msisdn : AnyObject = textFields[0].text!
//let msisdn = textFields[0].text!
let userId = "MyID"
let params = [
"msisidn":msisdn /*as AnyObject*/,
"denom_id":self.selectedGameDetail.Id /*as AnyObject*/,
"game_id":self.selectedGameDetail.GameId /*as AnyObject*/
]
print(params)
showEZLoading(true)
su.postEpins(userId, params: params, completion:{ (result, error) -> Void in
self.hideEZLoading()
if (error != nil){
print("DEBUG: API Response Error")
Utility.displayAlert(self, msg: "There's an errror fetching data from server.")
}
else {
print("DEBUG: API Response Success")
}
})
Everything in selectedGameDetail is a String.
And here's the postEpins function:
func postEpins(msisdn: String, params: [String: AnyObject]?, completion:(result: JSON, error: NSError?) -> Void) {
print("POST EPINS")
}
Doesn't do anything yet. But, whenever the app gets to the function call, I get this:
fatal error: unexpectedly found nil while unwrapping an Optional value
I'm positive it has something to do with the params, but I'm 100% sure. Yes, params has stuff in it. Check below.
What I've tried:
Well, if you see a comment in my code, I've pretty much tried those. And I have no idea what else to do.
For reference, that exact same code works on another part of the app, minus the userID - and I also tried removing that, but it still gave me the above error.
I found this regarding the params:
I noticed that one of the values is an NSTaggedPointerString, whereas everything else is an AnyObject. Is this an issue?

Related

Completion handler not being called - Alamofire / stripe

My completion handler is never called.
This completion handler is written by stripe:
(end of step 1)
https://stripe.com/docs/mobile/ios/standard#ephemeral-key
Ive tried simplifying the function as much as possible (i made it more complex before), and putting a completion() in the end of every .case .
When i shift click on the STPJSONResponseCompletionBlock it tells me the parameters required are this:
jsonResponse
The JSON response, or nil if an error occured.
error
The error that occurred, if any.
if i call completion(nil, nil) i can crash my app, and it gives me this error:
2019-07-30 10:38:42.917984+0100 foodfactory[27595:2238502] *** Assertion failure in -[STPEphemeralKeyManager _createKey],
2019-07-30 10:38:42.929757+0100 foodfactory[27595:2238502] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not parse the ephemeral key response following protocol STPCustomerEphemeralKeyProvider. Make sure your backend is sending the unmodified JSON of the ephemeral key to your app. For more info, see https://stripe.com/docs/mobile/ios/standard#prepare-your-api'
But, if i pass in json as in the example below, it simply never calls..
Furthermore, i have copied the NodeJS code which sent the json to this completion handler as closely as possible (im on google cloud functions so there had to be some changes, and i checked my JSON im receiving and the ephemeral key is there, and i can access it if i write my own method to decode the json).
Also, as i can make it crash, i guess the completion handler must be being called?
This code calls the function with completion handler:
func changePaymentOptionsButtonTapped() {
// Setup customer context
let apiVersion = String(Stripe.version())
MyAPIClient.sharedClient.createCustomerKey(withAPIVersion: apiVersion) { (ephemeralKeyJson, err) in
print("ThisIsNeverCalled")
if let ephemeralKey = ephemeralKeyJson?["id"] as? String {
} else {
print("Error parsing JSON from customer creation: See: MyAPIClient - func createCustomerKey")
}
}
}
The function with completion handler:
func createCustomerKey(withAPIVersion apiVersion: String, completion: #escaping STPJSONResponseCompletionBlock) {
guard let customerId = KeychainWrapper.standard.string(forKey: Stripe_Customer_Id) else {
print("Keychain wrapper not retrieving stripe customer Id at MyAPIClient")
// completion(nil, nil)
return
}
let url = "https://us-central1-foodfactory-813ab.cloudfunctions.net/request_ephemeral_key"
Alamofire.request(url, method: .post, parameters: [
"api_version": apiVersion,
"customerId": customerId,
])
.validate(statusCode: 200..<300)
.responseJSON { responseJSON in
switch responseJSON.result {
case .success(let json):
completion(json as? [String: AnyObject], nil)
case .failure(let error):
print("Error creating customer Key (retrieving ephemeral key) with Alamofire. See: MyAPIClient - func createCustomerKey")
completion(nil, error)
}
}
}
Anyone got any suggestions? My guess is it's something to do with what im passing into the completion handler.
There's definitely an error here:
json as? [String: AnyObject]
change it to
json as? [String: Any]
The reason for it is that a JSON dictionary can contain things that aren't AnyObject - for instance, String is a struct, Int or Bool aren't objects either

Getting error when trying to use Result type with delegate

Im tring to make a network call and instead of using callback I try to use delegate instead.using Result type where .Sucsess is T: Decodable and .failure is Error. passing my model in the .Sucsess is working but when trying to pass an error I get a compile error "Generic parameter 'T' could not be inferred" what am I missing ?
protocol NetworkServiceDelegate: class {
func decodableResponce<T: Decodable>(_ result: Result<T, NetworkError>)
}
let dataTask:URLSessionTask = session.dataTask(with: url) { (dataOrNil, responceOrNil, errOrNil) in
if let error = errOrNil {
switch error {
case URLError.networkConnectionLost,URLError.notConnectedToInternet:
print("no network connection")
self.delegate?.decodableResponce(Result.failure(.networkConnectionLost))
case URLError.cannotFindHost, URLError.notConnectedToInternet:
print("cant find the host, could be to busy, try again in a little while")
case URLError.cancelled:
// if cancelled with the cancelled method the complition is still called
print("dont bother the user, we're doing what they want")
default:
print("error = \(error.localizedDescription)")
}
return
}
guard let httpResponce:HTTPURLResponse = responceOrNil as? HTTPURLResponse
else{
print("not an http responce")
return
}
guard let dataResponse = dataOrNil,
errOrNil == nil else {
print(errOrNil?.localizedDescription ?? "Response Error")
return }
do{
//here dataResponse received from a network request
let decoder = JSONDecoder()
let modelArray = try decoder.decode([Movie].self, from:
dataResponse) //Decode JSON Response Data
DispatchQueue.main.async {
self.delegate?.decodableResponce(Result.success(modelArray))
}
} catch let parsingError {
print("Error", parsingError)
}
print("http status = \(httpResponce.statusCode)")
print("completed")
}
this line generates the error, it dosnt metter if I pass my enum that cumfirms to Error or trying to pass the error from the dataTask
self.delegate?.decodableResponce(Result.failure(.networkConnectionLost))
Well, you have two problems, having to do with the question "what type is this?" Swift is very strict about types, so you need to get clear about that.
.networkConnectionLost is not an Error. It is an error code. You need to pass an Error object to a Result when you want to package up the error. For example, URLError(URLError.networkConnectionLost) is an Error.
The phrase Result<T, NetworkError> makes no sense. Result is already a generic. Your job is to resolve the generic that it already is. You do that by specifying the type.
So for example, you might declare:
func decodableResponce(_ result: Result<Decodable, Error>)
It is then possible to say (as tests):
decodableResponce(.failure(URLError(URLError.networkConnectionLost)))
or (assuming Movie is Decodable):
decodableResponce(.success([Movie()]))
That proves we have our types right, and you can proceed to build up your actual code around that example code.

Swift 4 - How can I call a piece of code which returns out of the function in multiple functions without duplicating code?

I am working with a poorly designed API (I don't have control over it) where even if the access token is expired, it still returns a HTTP success code but includes the 401 Unauthorized in the actual response body. So simply checking the HTTP status code isn't sufficient and I need to check the actual response.
I am making many network requests in my app to this API and when I receive the response, I need to first check whether the response is an array or a dictionary. If array, then we are good. If it's a dictionary, then I need to check the "error" field in the response dictionary which will have the 401 Unauthorized.
So every time I receive the JSON response, I have the following piece of code to return out of the function if it's an error dictionary:
guard !(myJSON is NSDictionary) && (myJSON as! NSDictionary).value(forKey: "error") != nil else {
print("Error: ", MyAppError.accessTokenExpired)
return
}
I am wondering if there is a way to prevent duplicating this piece of code in every network request function I have? Can I have this somewhere and simply call it without duplicating these lines of code each time?
Wrap it in a function like this one
func isErrorResponse(_ response: Any) -> Bool {
if let dict = response as? [String: Any], let error = dict["error"] {
print("Error: \(MyAppError.accessTokenExpired)")
return true
}
return false
}
You should use the swift data types if you are using Swift language. Although if myJSON validates with Dictionary then it will definitely be validated with [String: Any].
Create function like:
func isValidResponse(_ json: Any) -> Bool {
guard let jsonDict = json as? [String: Any], let let error = dict["error"] else { return true }
print("Error: \(error.localizedDescription)")
return false
}
Use it as:
guard YourClass.isValidResponse(myJSON) else { return }
// Valid Response - Code here...

How to check conditions of two parameters in my case

Say I have a closure with two optional parameters:
(data: MyData?, error: Error?) in
// I want to safely unwrap data & make sure error is nil
if let data = data, let error== nil {
}
The if condition above gives me error: Variable binding in a condition requires an initializer.
I understand I might have used wrong syntax with the let error==nil part. But, what is the correct way to do the condition check ?
For Swift 3 just drop let before error
if let data = data, error == nil {
// do stuff
}
Before Swift 3 the syntax was a little different
if let data = data where error == nil {
}
In your case it can be a good idea to use guard :
(data: MyData?, error: Error?) in
// I want to safely unwrap data & make sure error is nil
guard let data = data, error == nil else {
return
}
// code here
Another option is to use if case with pattern matching:
if case let (data?, nil) = (data, error) {
// ...
}
Here data? is the "optional pattern" and a shortcut for .some(data).

AlamoFire GET api request not working as expected

I am trying to get learn how to use AlamoFire and I am having trouble.
My method so far is as follows:
func siteInfo()->String?{
var info:NSDictionary!
var str:String!
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {(request, response, JSON, error) in
info = JSON as NSDictionary
str = info["access_key"] as String
//return str
}
return str
}
This returns nil which is a problem. From what I have read here, this is because the request can take a while so the closure doesn't execute till after the return. The suggested solution of moving the return into the closure does not work for me and the compiler just yells (adding ->String after (request,response,JSON,error) which gives "'String' is not a subtype of void"). Same goes for the other solution provided.
Any ideas? Even some source code that is not related to this problem, that uses AlamoFire, would be helpful.
Thanks!
One way to handle this is to pass a closure (I usually call it a completionHandler) to your siteInfo function and call that inside Alamofire.request's closure:
func siteInfo(completionHandler: (String?, NSError?) -> ()) -> () {
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {
(request, response, JSON, error) in
let info = JSON as? NSDictionary // info will be nil if it's not an NSDictionary
let str = info?["access_key"] as? String // str will be nil if info is nil or the value for "access_key" is not a String
completionHandler(str, error)
}
}
Then call it like this (don't forget error handling):
siteInfo { (str, error) in
if str != nil {
// Use str value
} else {
// Handle error / nil value
}
}
In the comments you asked:
So how would you save the info you collect from the get request if you
can only do stuff inside the closure and not effect objects outside of
the closure? Also, how to keep track to know when the request has
finished?
You can save the result of the get request to an instance variable in your class from inside the closure; there's nothing about the closure stopping you from doing that. What you do from there really depends on, well, what you want to do with that data.
How about an example?
Since it looks like you're getting an access key form that get request, maybe you need that for future requests made in other functions.
In that case, you can do something like this:
Note: Asynchronous programming is a huge topic; way too much to cover here. This is just one example of how you might handle the data you get back from your asynchronous request.
public class Site {
private var _accessKey: String?
private func getAccessKey(completionHandler: (String?, NSError?) -> ()) -> () {
// If we already have an access key, call the completion handler with it immediately
if let accessKey = self._accessKey {
completionHandler(accessKey, nil)
} else { // Otherwise request one
Alamofire.request(.GET, MY_API_END_POINT).responseJSON {
(request, response, JSON, error) in
let info = JSON as? NSDictionary // info will be nil if it's not an NSDictionary
let accessKey = info?["access_key"] as? String // accessKey will be nil if info is nil or the value for "access_key" is not a String
self._accessKey = accessKey
completionHandler(accessKey, error)
}
}
}
public func somethingNeedingAccessKey() {
getAccessKey { (accessKey, error) in
if accessKey != nil {
// Use accessKey however you'd like here
println(accessKey)
} else {
// Handle error / nil accessKey here
}
}
}
}
With that setup, calling somethingNeedingAccessKey() the first time will trigger a request to get the access key. Any calls to somethingNeedingAccessKey() after that will use the value already stored in self._accessKey. If you do the rest of somethingNeedingAccessKey's work inside the closure being passed to getAccessKey, you can be sure that your accessKey will always be valid. If you need another function that needs accessKey, just write it the same way somethingNeedingAccessKey is written.
public func somethingElse() {
getAccessKey { (accessKey, error) in
if accessKey != nil {
// Do something else with accessKey
} else {
// Handle nil accessKey / error here
}
}
}