How can I get data inside of completionHandler for Alamofire.responseJSon - swift

I'm currently making app for Destiny 2 API.
and I'm struggling with this issue
this is my whole function code that makes hash data to item name
I want to return that name variable which is guarded last, but that completion handler
runs after function is end.
I searched to internet, but almost everyone just used this data inside of it, not
function return.
that I only need is " Parse json and get returned data I want "
is there any idea please?
item parameter means " item hash to get name of it "
lang parameter means " language to return " if this is "en" then, it should return English.
here's the data I pared too " https://pastebin.com/1tV6fx9F "
func hashToName(item: String, lang: String = "ko") (" want to return String "-> String ){
let url = String(format: "https://www.bungie.net/platform/Destiny2/Manifest/DestinyInventoryItemDefinition/\(item)")
let param: [String: String] = ["lc": "\(lang)"]
let head: HTTPHeaders = ["x-api-key": "b21e4d2d33234b82be6e56893554974b"]
let doNetwork = AF.request(url, method:.get, parameters: param, encoder: URLEncodedFormParameterEncoder.default, headers: head)
doNetwork.responseJSON {
response in
switch response.result {
case .success(let site):
guard let dict = site as? NSDictionary else { return }
guard let res = dict["Response"] as? NSDictionary else { return }
guard let prop = res["displayProperties"] as? NSDictionary else { return }
guard let name: String = prop["name"] as? String else { return }
print(name) // data I want to return
case .failure(let err):
print(err.localizedDescription)
}
}
}

func hashToName(item: String, lang: String = "ko", returnString: #escaping (String)->()) {
let url = String(format: "https://www.bungie.net/platform/Destiny2/Manifest/DestinyInventoryItemDefinition/\(item)")
let param: [String: String] = ["lc": "\(lang)"]
let head: HTTPHeaders = ["x-api-key": "b21e4d2d33234b82be6e56893554974b"]
let doNetwork = AF.request(url, method:.get, parameters: param, encoder: URLEncodedFormParameterEncoder.default, headers: head)
doNetwork.responseJSON {
response in
switch response.result {
case .success(let site):
guard let dict = site as? NSDictionary else { return }
guard let res = dict["Response"] as? NSDictionary else { return }
guard let prop = res["displayProperties"] as? NSDictionary else { return }
guard let name: String = prop["name"] as? String else { return }
returnString(name)
print(name) // data I want to return
case .failure(let err):
print(err.localizedDescription)
}
}
}
//How to use
hashToName(item: "Your string") { str in
print(str)
}

Related

Image is not uploading in base64 format using Alamofire and swift

I am trying to upload image in base64string format. Here i am uploading images with some parameters using alamofire and swift. But my image is not uploading to the server. Please check my code and let me know where i am doing wrong. My code is:
func postRegistrationPlayerProfile(urlExtension: String, callback: #escaping(Bool,String?) -> Void,parameters:[String:Any]) -> Void {
let fullURL = URL(string: urlExtension)!
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = TimeInterval(Common.sharedInstance().REQUEST_TIME_OUT)
manager.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
}, usingThreshold: UInt64.init(), to: fullURL, method: .post) { (result) in
switch result {
case .success(let upload, _, _):
upload.responseJSON { (response : DataResponse<Any>) in
let responseDataString = Common.sharedInstance().getJSONStringFromData(data: response.data! as NSData)
print(responseDataString)
if let result = response.result.value {
let dictionary = result as! NSDictionary
let status = dictionary["status"] as! String
let message = dictionary["message"] as? String
if status == "success"{
callback(true,message)
return
}
else{
callback(false,message)
return
}
}
else{
callback(false,Common.sharedInstance().FAILED_BANNERIMG_UPLOAD)
return
}
}
case .failure(let encodingError):
print("encodingError: \(encodingError)")
callback(false,Common.sharedInstance().FAILED_BANNERIMG_UPLOAD)
}
}
}
my parameters are:
paramters.updateValue(phone!, forKey: "phone")
paramters.updateValue(state!, forKey: "state")
paramters.updateValue(city!, forKey: "city")
paramters.updateValue(zip!, forKey: "postcode")
paramters.updateValue(travel, forKey: "travel_distance")
paramters.updateValue(base64ImageString!, forKey: "profile_image")
paramters.updateValue(country!, forKey: "country")
I am converting my image to base64 string like:
if let selectedImage = chooseProfileImage.image{
if let data = selectedImage.jpegData(compressionQuality: 1){
base64ImageString = data.base64EncodedString(options: .lineLength64Characters)
}
}
Adding
url('data:image/jpeg;base64,)
to my Base64 converted string worked for me:
if let selectedImage = chooseProfileImage.image{
if let data = selectedImage.jpegData(compressionQuality: 1){
let imageString = selectedImage.jpegData(compressionQuality: 1.0)?.base64EncodedString() ?? ""
base64ImageString = "url('data:image/jpeg;base64,\(imageString)')"
profileData = data
}
}

Method to return value retrieved from HTTP request [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I have a method which performs an HTTP request and retrieves data from a website, it's working as expected, I'm getting the data correctly. What I haven't been able to do is return the retrieved value when the method is called.
Here is the code...
func myFunction(zipCode: String)->String{
var myData:String = ""
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
return
}
myData = jsonArray[0]["MyPropertyName"]!
// Here, myData outputs, "Info for zip code 52484 from HTTP request"
}
task.resume()
return myData
}
When I call myFunction I get and empty string...
myFunction(zipCode: "52484")// Outputs an empty string
What I was expecting to see is a return value of "Info for zip code 52484 from HTTP request" since the myData variable was modified inside the let task = before the return call. I tried returning inside the let task = but this cannot be done in Swift.
How can I return the retrieved value when the myFunction is called?
You need a completion as request is asynchronous
func myFunction(zipCode: String,completion:#escaping(_ str:String?) -> () ) {
let siteLink = "http://example.com/zip/" + zipCode
let url = URL(string: siteLink)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard error == nil else {
print(error!)
completion(nil)
return
}
guard let data = data else {
print("Data is empty")
completion(nil)
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = json as? [[String: String]] else {
completion(nil)
return
}
let myData = jsonArray[0]["MyPropertyName"]!
completion(myData)
}
task.resume()
}
Call
myFunction(zipCode: "52484") { (str) in
if let st = str {
print(st)
}
}

Swift: cannot convert value of type string to expected argument type string

Need help with swift 3. I got
cannot convert value of type string to expected argument type string
func authKMA(text: String) {
let base = "http://api.kma1.biz/?method=auth"
let userlogin = "&username=vostok3r#gmail.com"
let userpass = "&pass=0000"
_ = ""
_ = ""
let auth = base + userlogin + userpass
let url = URL(string: auth)!
URLSession.shared.dataTask(with: url) { (data, _, _) in
guard let gotdata = data else {
print("Сервер не отвечает")
return
}
guard let jsonAny = try? JSONSerialization.jsonObject(with: gotdata, options: []) else {
print("Сервер не дал информацию по аккаунту")
return
}
guard let json = jsonAny as? [String: Any] else {
return
}
guard let authhasher = json.index(of: "authhash") else {
return
//ERROR: cannot convert value of type string to expected argument type string//
}
print(authhasher)
}.resume()
}
}
As Dávid Pásztor commented, Dictionary doesn't have index(of:) method. If all you're trying to do is extract the authhash value from your json dictionary, you can access your values using subscript notation:
guard let authhasher = json["authhash"] as? String else {
return
}
print(authhasher)

Mutable data pack into json

How could I send hashed password with sha256 via post request ?
func sha256(string: String) -> Data? {
guard let messageData = string.data(using:String.Encoding.utf8) else { return nil; }
var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes {digestBytes in
messageData.withUnsafeBytes {messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData
}
This is how I am hasing a password.
Then whenever I try to pack it into an array as a [String: Any] it throws an exception in JSONSErialization 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (Foundation._SwiftNSData)'
guard let loginURL = URL(string: LOGIN_URL) else {
print("Error: cannot create URL")
return
}
var loginURLRequest = URLRequest(url: loginURL)
loginURLRequest.httpMethod = "POST"
let content: [String: Any] = ["username": username, "passwordHash": password]
let json: Data
do {
json = try JSONSerialization.data(withJSONObject: content, options: [])
loginURLRequest.httpBody = json
} catch {
print("Error: Can not create JSON")
return
}
Thanks in advance!
What you probably want to do is encode the hash as a hex string. Given your sha256 function defined above, The following will do that:
let password = sha256(string: "myPassword")
.map { return String(format: "%02x", $0) }
.joined()
let content = [
"username": username,
"passwordHash": password
]
Alternatively, you could change your sha256 function to return a string (and make it an extension of string):
func sha256() -> String? {
guard let messageData = data(using:String.Encoding.utf8) else {
return nil
}
var digestData = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digestData.withUnsafeMutableBytes { digestBytes in
withUnsafeBytes { messageBytes in
CC_SHA256(messageBytes, CC_LONG(messageData.count), digestBytes)
}
}
return digestData.map { return String(format: "%02x", $0) }.joined()
}
Note: I'm winging this a little since CommonCrypto won't work in a playground and I'm too lazy to wrap a whole project around this, but the important parts are here.
Use data.map to iterate over each byte in the hash
convert it to a hex string with String(format: "%02x"...)
put it all back together with joined()

Sequence of actions with RxSwift

I'm using RxSwift to simply my code. For my current project I'd like to apply RxSwift's principles to a mess of completion blocks from the LayerKit:
layerClient.connectWithCompletion { (success, error) -> () in
if (!success) {
// Error
} else {
layerClient.requestAuthenticationNonceWithCompletion { (nonce, error) -> () in
// Even more blocks
}
}
}
I'm thinking about something like this:
// In extension
public func rx_connect() -> Observable<Bool> {
return create { observer in
self.connectWithCompletion { (success, error) -> ()in
if (success) {
observer.on(.Next(success))
observer.on(.Completed)
} else {
observer.on(.Error(error))
}
}
return NopDisposable.instance
}
}
public func rx_requestAuthenticationNonce() -> Observable<String> {
// Same for annother method
}
// In AppDelegate
self.layerClient.rx_connect()
.then() // requestAuthenticationNonceWithCompletion and use the nonce for next action
.then()
.subscribeNext(…
.onError(… // To catch all errors
RxSwift does not have a then() method. Is there another way to do this chaining stuff or am I thinking wrong on how to use ReactiveX in general?
Right, RxSwift doesn't have the then operator because the name is very risky.
then can be a lot of things in the Reactive Extensions world a map a flatMap or even a switchLatest, depending on the context.
In this case I would suggest to use a flatMap because that is going to be returned using a map is an Observable of Observables, that in most cases are hard to combine and manage. So I would do:
self.layerClient.rx_connect()
.flatMap(){ _ in
return rx_requestAuthenticationNonce()
.catchError(displayError)
}
.subscribeNext(…
.onError(… // To catch all errors
I would use flatMap instead of map in this case because I am then able to catch errors without terminating the sequence in case rx_requestAuthenticationNonce() fails.
Be careful in using the catchError operator, this operator will terminate the sequence after recovering and any extra event will be ignored after that.
This is authorization part in my project. Just an example for chaining requests. It turns the result of chained requests into a bool answer. It use 'flatMap' because result of old request is used in building new request. Otherwise, you can use 'concat' instead of 'flatMap'.
let client = RxSimpleLazyHttpClient.sharedInstance
let uuid = UIDevice().identifierForVendor!.UUIDString
let helloheaders = ["X-User-Identifier": "id:" + uuid]
let helloRequest: Observable<StringDictionary> = client.makeRequest(verb: .GET, url: NSURL(string: self.API_HOST + self.API_HELLO)!, parameters: [:], headers: helloheaders)
.map { result in
if (self.enableDebugOutput) {
self.debug(result)
}
let json = JSON(data: result.2!)
let userKey = json["user_key"].string
let userSecretHash = json["user_secret_hash"].string
return ["user_key": userKey ?? "", "user_secret_hash": userSecretHash ?? ""]
}
let jointAuthRequest: (StringDictionary -> Observable<StringDictionary>) = { [unowned self] stringDictionary in
Logger.D(stringDictionary.debugDescription)
let userKey = stringDictionary["user_key"]
let headers = ["X-User": userKey ?? ""]
return client.makeRequest(verb: .GET, url: NSURL(string: self.API_HOST + self.API_AUTH)!, parameters: [:], headers: headers)
.map { result in
if (self.enableDebugOutput) {
self.debug(result)
}
let json = JSON(data: result.2!)
let nonce = json["nonce"].string
var result = [String: String]()
result["nonce"] = nonce
return result.merge(stringDictionary)
}
}
let jointAuthNonceRequest: (StringDictionary -> Observable<StringDictionary>) = { [unowned self] stringDictionary in
Logger.D(stringDictionary.debugDescription)
let nonce = stringDictionary["nonce"] ?? ""
let userSecretHash = stringDictionary["user_secret_hash"] ?? ""
let headers = ["X-Pass": (userSecretHash + nonce).sha1().webSafeBase64()]
return client.makeRequest(verb: .GET, url: NSURL(string: self.API_HOST + self.API_AUTH + "/" + nonce)!, parameters: [:], headers: headers)
.map { result in
if (self.enableDebugOutput) {
self.debug(result)
}
let json = JSON(data: result.2!)
let sessionKey = json["session_key"].string
let sessionSecret = json["session_secret"].string
var result = [String: String]()
result["session_key"] = sessionKey
result["session_secret"] = sessionSecret
return result.merge(stringDictionary)
}
}
let jointResult: (StringDictionary -> Observable<Bool>) = { result in
let userKey = result["user_key"]
let userSecretHash = result["user_secret_hash"]
let sessionKey = result["session_key"]
let sessionSecret = result["session_secret"]
if userKey == nil || userSecretHash == nil || sessionKey == nil || sessionSecret == nil {
Logger.D("Auth Fail")
return just(false)
}
/* You can store session key here */
return just(true)
}
return helloRequest
.shareReplay(1)
.observeOn(RxScheduler.sharedInstance.mainScheduler)
.flatMap(jointAuthRequest)
.flatMap(jointAuthNonceRequest)
.flatMap(jointResult)
This is the 'makeRequest' method.
public func makeRequest(verb verb: Alamofire.Method, url: NSURL, parameters: [String : String]?, headers: [String : String]?) -> Observable<RxRequestResult> {
return create { observer in
Alamofire.request(verb, url, parameters: nil, encoding: ParameterEncoding.URL, headers: headers)
.response { request, response, data, error in
observer.onNext((request, response, data, error))
}
return AnonymousDisposable {
// when disposed
}
}
}