Avoid swift nested if let tests - swift

I am doing some swift with web services. I am using alamofire. The code is working but i think that it's not "readable" ( not a clean code). Do you have an idea how i can optimise this ? Thanks
static func checkBookValidity(serialNumber: String, callBack: Result<Bool?> -> ()) {
let router = Router.CheckBookLuggage(serialNumber: serialNumber)
let request = Alamofire.request(router)
request.validate()
request.response { (request, response, data, error) in
if let error = error {
if error.code == NSURLErrorNotConnectedToInternet {
callBack(.Failure(.NoConnection))
}
else {
if let data = data {
do
{
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String : AnyObject] {
let erroType = WSError.errorTypeWithJson(json, httpErroCode: error.code)
callBack(.Failure(erroType))
}
} catch {
callBack(.Failure(.ServerError))
}
}
else {
callBack(.Failure(.ServerError))
}
}
}
else {
if let data = data {
do
{
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String : AnyObject] {
if let resultDic = json["result"] as? [String:AnyObject], let exists = resultDic["exists"] as? Bool {
if exists {
if let owner = resultDic["email"] as? String {
// ...
}
}
else {
callBack(.Success(false))
}
}
}
} catch {
callBack(.Failure(.ServerError))
}
}
}
}
}

Try this
if let error = error, data = data {
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [String : AnyObject] {
let erroType = WSError.errorTypeWithJson(json, httpErroCode: error.code)
callBack(.Failure(erroType))
....
....
if let statements can be chained together with comma, as in error and data above.
The same can be done with guard.

Related

Cannot convert value of type '(key: String, value: Any)' to expected argument type '[String : Any]

I'm trying to get JSON but it shows an occurred issue
Line starting with if let deliveryObject
How to get rid of the problem?
code:
struct Tracking {
let receiver: String
let placeOfMail: String
let indexOfMail: Double
init(json:[String:Any]) throws {
guard let receiver = json["name"] as? String else {
throw SerializationError.missing("Receiver data has been missed")
}
guard let placeOfMail = json ["address"] as? String else {
throw SerializationError.missing("Place of delivery has been missed")
}
guard let indexOfMail = json ["postindex"] as? Double else {
throw SerializationError.missing("Index of postmail has been missed")
}
self.receiver = receiver
self.placeOfMail = placeOfMail
self.indexOfMail = indexOfMail
}
static let basePath = "https://track.kazpost.kz/api/v2/"
static func deliveryData (withTrackid trackid:String, completion: #escaping ([Tracking]) -> ()){
let url = basePath + trackid
let request = URLRequest(url: URL(string: url)!)
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response : URLResponse?, error: Error?) in
var deliveryArray: [Tracking] = []
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options:[]) as? [String:Any] {
if let deliveryInformation = json ["delivery"] as? [String:Any] {
if let deliveryPlace = deliveryInformation ["address"] as? [String:Any] {
for dataPoint in deliveryPlace {
if let dataPointValue = dataPoint.value as? [String: AnyObject],
let deliveryObject = try Tracking(json: dataPointValue) {
deliveryArray.append(deliveryObject)
}
}
}
}
}
}catch {
print(error.localizedDescription)
}
completion(deliveryArray)
}
}
task.resume()
}
}

just incompatibility or real error in swift code

hello i am working with swift 6.2 ( the performance of my pc doesn't support ++)
i have copied from open classroom this program in my file but there are many errors
i just want to know if it is just problem of swift compatibility or there are really errors in the code
//****************
import UIKit
class QuestionManager {
private let url = URL(string: "https://opentdb.com/api.php?amount=10&type=boolean")!
// exemple of message error: use of unresolved identifier 'URL'
static let shared = QuestionManager()
private init() {}
// FIX IT : replace static with class ...
// AND LOT OF MESSAGE ERROR LIKE THIS...
func get(completionHandler: #escaping ([Question]) -> ()) {
let task = URLSession.shared.dataTask(with: self.url) { (data, response, error) in
guard error == nil else {
completionHandler([Question]())
return
}
DispatchQueue.main.async {
completionHandler(self.parse(data: data))
}
}
task.resume()
}
private func parse(data: Data?) -> [Question] {
guard let data = data,
let serializedJson = try? JSONSerialization.jsonObject(with: data, options: []),
let parsedJson = serializedJson as? [String: Any],
let results = parsedJson["results"] as? [[String: Any]] else {
return [Question]()
}
return getQuestionsFrom(parsedDatas: results)
}
private func getQuestionsFrom(parsedDatas: [[String: Any]]) -> [Question]{
var retrievedQuestions = [Question]()
for parsedData in parsedDatas {
retrievedQuestions.append(getQuestionFrom(parsedData: parsedData))
}
return retrievedQuestions
}
private func getQuestionFrom(parsedData: [String: Any]) -> Question {
if let title = parsedData["question"] as? String,
let answer = parsedData["correct_answer"] as? String {
return Question(title: String(htmlEncodedString: title)!, isCorrect: (answer == "True"))
}
return Question()
}
}
extension String {
init?(htmlEncodedString: String) {
guard let data = htmlEncodedString.data(using: .utf8) else {
return nil
}
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
]
guard let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) else {
return nil
}
self.init(attributedString.string)
}
}

Trying to get an API / URL to show in my UILabel text?

I am try to get my UILabel text to equal the url text I am not getting any errors but it just is not doing anything not sure what I am doing wrong?
let tasks = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("error")
} else {
if let content = data {
do {
let Json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let data = Json as? NSString {
DispatchQueue.main.async {
self.myLabel.text = "\(data)"
}
}
} catch {
}
}
}
}
tasks.resume()
Your API Response is not String , It's Dictionary Dictionary in json is {}
API Response:
{
quote: "Travel is the most private of pleasures. There is no greater bore than the travel bore. We do not in the least want to hear what he has seen in Hong Kong.",
author: "Vita Sackville-West",
cat: "travel"
}
so you will not pass this condition at this line if let data = Json as? NSString because data is Dictionary not String
Correct solution :
let tasks = URLSession.shared.dataTask(with: URL(string: "https://talaikis.com/api/quotes/random/")!) { (data, response, error) in
if error != nil {
print("error")
} else {
if let content = data {
do {
let Json = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if let data = Json as? [AnyHashable:Any] {
if let quote = data["quote"] as? String {
print(quote)
}
if let author = data["author"] as? String {
print(author)
}
if let cat = data["cat"] as? String {
print(cat)
}
DispatchQueue.main.async {
self.myLabel.text = "\(quote)"
}
}
} catch {
}
}
}
}
tasks.resume()
}

SwiftyJson parse oData response

I am trying to parse a oData web service using SwiftyJSON
Here is my oData response:
{
"odata.metadata":"http://url.com/odata/$metadata#Updates","value":[
{
"ID":1,"msgTitle":"Testing","reportedBy":"testUser"
}
]
}
Here is my Swift code:
Alamofire.request(URL, method: .get).responseString { (responseData) -> Void in
if((responseData.result.value) != nil) {
self.activityIndicator.stopAnimating()
let swiftyJsonVar = JSON(responseData.result.value!)
print(swiftyJsonVar)
if let resData = swiftyJsonVar["value"].arrayObject {
if let dict = resData as? [Dictionary<String, AnyObject>] {
for obj in dict {
let announce = announcement(fileDict: obj)
self.Announcements.append(announce)
}
self.tableView.reloadData()
self.tableView.isHidden = false
}
}
}
}
The problem is that resData is returning null. What I am doing wrong to get the JSON within the value array?
I have also tried swiftyJsonVar[0]["value"].arrayObject without success.
After consulting the swiftyJSON documentation, I was able to figure this out using the following syntax:
Alamofire.request(URL, method: .get).responseString { (responseData) -> Void in
if((responseData.result.value) != nil) {
self.activityIndicator.stopAnimating()
//log.info("Response: \(responseData.result.value)")
let jsonObj = responseData.result.value!
if let dataFromString = jsonObj.data(using: .utf8, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
print(json)
if let resData = json["value"].arrayObject {
if let dict = resData as? [Dictionary<String, AnyObject>] {
for obj in dict {
let announce = announcement(fileDict: obj)
self.Announcements.append(announce)
}
self.tableView.reloadData()
self.tableView.isHidden = false
}
}
}
}
}

Nested dataTaskWithRequest in Swift tvOS

I'm a C# developer convert to Swift tvOs and just starting to learn. I've made some progress, but not sure how to handle nested calls to json. The sources are from different providers so I can't just combine the query.
How do I wait for the inner request to complete so the TVSeries has the poster_path? Is there a better way to add the show to the collection and then process the poster path loading in another thread so it doesn't delay the UI Experience?
func downloadTVData() {
let url_BTV = NSURL(string: BTV_URL_BASE)!
let request_BTV = NSURLRequest(URL: url_BTV)
let session_BTV = NSURLSession.sharedSession()
//get series data
let task_BTR = session_BTV.dataTaskWithRequest(request_BTV) { (data_BTV, response_BTV, error_BTV) -> Void in
if error_BTV != nil {
print (error_BTV?.description)
} else {
do {
let dict_BTV = try NSJSONSerialization.JSONObjectWithData(data_BTV!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results_BTV = dict_BTV!["results"] as? [Dictionary<String, AnyObject>]{
for obj_BTV in results_BTV {
let tvshow = TVSeries(tvDict: obj_BTV)
//for each tv series try to load a poster_path from secondary provider
if let str = obj_BTV["title"] as? String!{
let escapedString = str?.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())!
if let url = NSURL(string: self.SEARCH_URL_BASE + escapedString!) {
let request = NSURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print (error?.description)
} else {
do {
let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments) as? Dictionary<String, AnyObject>
if let results = dict!["results"] as? [Dictionary<String, AnyObject>] {
//iterate through the poster array
for obj in results {
if let path = obj["poster_path"] as? String {
tvshow.posterPath = path
break
}
}
}
} catch let error as NSError {
print(error.description)
}
}
}
task.resume()
}
}
self.tvSeries.append(tvshow)
}
dispatch_async(dispatch_get_main_queue()){
self.collectionView.reloadData()
}
}
} catch let error as NSError {
print(error.description)
}
}
}
task_BTR.resume()
}
Thanks for your help!
I would recommend breaking things apart into multiple methods, with callbacks to sequence the operations, and utilizing Swift's built-in throws error handling mechanism. Here's an example, not perfect, but might help as a starting point:
class TVSeries
{
let title: String
var posterPath: String?
enum Error: ErrorType {
case MalformedJSON
}
init(tvDict: [String: AnyObject]) throws
{
guard let title = tvDict["title"] as? String else {
throw Error.MalformedJSON
}
self.title = title
}
static func loadAllSeries(completionHandler: [TVSeries]? -> Void)
{
NSURLSession.sharedSession().dataTaskWithURL(NSURL(string: BTV_URL_BASE)!) { data, response, error in
guard let data = data else {
print(error)
completionHandler(nil)
return
}
do {
completionHandler(try fromJSONData(data))
}
catch let error {
print(error)
}
}.resume()
}
static func fromJSONData(jsonData: NSData) throws -> [TVSeries]
{
guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
throw Error.MalformedJSON
}
guard let results = dict["results"] as? [[String: AnyObject]] else {
throw Error.MalformedJSON
}
return try results.map {
return try TVSeries(tvDict: $0)
}
}
func loadPosterPath(completionHandler: () -> Void)
{
guard let searchPath = title.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet()) else {
completionHandler()
return
}
let url = NSURL(string: SEARCH_URL_BASE)!.URLByAppendingPathComponent(searchPath)
NSURLSession.sharedSession().dataTaskWithURL(url) { [weak self] data, response, error in
defer { completionHandler() }
guard let strongSelf = self else { return }
guard let data = data else {
print(error)
return
}
do {
strongSelf.posterPath = try TVSeries.posterPathFromJSONData(data)
}
catch let error {
print(error)
}
}.resume()
}
static func posterPathFromJSONData(jsonData: NSData) throws -> String?
{
guard let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject] else {
throw Error.MalformedJSON
}
guard let results = dict["results"] as? [[String: AnyObject]] else {
throw Error.MalformedJSON
}
for result in results {
if let path = result["poster_path"] as? String {
return path
}
}
return nil
}
}
It might also be worth your time to look into something like RxSwift or Alamofire, which help you with these kinds of data-conversion / sequencing operations.