Get array from response in swift - ios

i'm trying to get response in my model object, but facing an issue that it is showing me only first item of response, this is my code,
func getUserBalanceAPI()
{
APIService.getUserBalance{ (responseObject) in
if (responseObject?.status)! {
self.balanceArray.removeAll()
let user = UserCompleteBalance(JSON: (responseObject?.data as! [[String : Any]]).first!)
self.balanceArray.append(user!)
//Reload Collection View
self.currencyCVC.reloadData()
}
else if !(responseObject?.status)! {
Utilities.showBar(text: responseObject?.errorObject?.message)
}
}
}
How can i get all the items in an array? This is my response,
"responseBody": {
"data": [
{
"auction_deposit": 4083.63,
"currencyCode": "USD",
"userCurrencyId": 1,
"availableBalance": 64555.1,
"currentBalance": 68638.73
},
{
"auction_deposit": 0.0,
"currencyCode": "AED",
"userCurrencyId": 2,
"availableBalance": 198000.0,
"currentBalance": 198000.0
},
{
"auction_deposit": 0.0,
"currencyCode": "EUR",
"userCurrencyId": 3,
"availableBalance": 50000.0,
"currentBalance": 50000.0
}
]
}
This is my model class,
class UserCompleteBalance : Mappable {
var auctionDeposit : Int?
var availableBalance : Int?
var currencyCode : Int?
var currentBalance : Int?
var userCurrencyId : Int?
required init?(map: Map) {
}
func mapping(map: Map) {
auctionDeposit <- map["auction_deposit"]
currencyCode <- map["currencyCode"]
userCurrencyId <- map["userCurrencyId"]
availableBalance <- map["availableBalance"]
currentBalance <- map["currentBalance"]
}
}
Now i want to store all the response in this.

Your issue is related to the fact that you are getting only the first dictionary on the dictionary array, so you need to loop over your dictionary array to convert each of them on UserCompleteBalance model
Updated
You need to use [weak self] and strongSelf inside closure to avoid retain cycles
try with this code
func getUserBalanceAPI()
{
APIService.getUserBalance{ [weak self] (responseObject) in
guard let strongSelf = self else {
return
}
if (responseObject?.status)! {
strongSelf.balanceArray.removeAll()
if let usersDataArray = responseObject?.data as? [[String : Any]] {
for userData in usersDataArray {
strongSelf.balanceArray.append(UserCompleteBalance(JSON:userData))
}
}
//Reload Collection View
strongSelf.currencyCVC.reloadData()
}
else if !(responseObject?.status)! {
Utilities.showBar(text: responseObject?.errorObject?.message)
}
}
}

let user = UserCompleteBalance(JSON: (responseObject?.data as! [[String : Any]]).first!)
self.balanceArray.append(user!)
Above lines should be in a loop through all objects in responseObject.data
Your getUserBalanceAPI functions should be
APIService.getUserBalance{ (responseObject) in
if (responseObject?.status)! {
self.balanceArray.removeAll()
if let jsonObjects = responseObject?.data as? [[String : Any]] {
for jsonObject in jsonObjects {
if let user = UserCompleteBalance(JSON: jsonObject) {
self.balanceArray.append(user)
}
}
}
//Reload Collection View
self.currencyCVC.reloadData()
} else if !(responseObject?.status)! {
Utilities.showBar(text: responseObject?.errorObject?.message)
}
}
}

Related

Is there a library for swift that lets you custom the order of the keys in a JSON?

I have the following situation:
struct RequestCommand: Codable {
var event: String
var uuid: String
var type: String
var data: [String]
}
var data = try! JSONEncoder().encode(RequestCommand(event: "click", uuid: "123456", type: "Button", data: ["A", "B", "C"]))
print(String(data: data, encoding: String.Encoding.utf8)!)
The result of the print statement is the following:
{
"data": [
"A",
"B",
"C"
],
"event": "click",
"type": "Button",
"uuid": "123456"
}
As you can see the order of the keys in the JSON isn't the same as the fields in the RequestCommand.
I know that JSONEncoder doesn't guarantee the order, also using .sortedKeys would not work for me since I must keep the exact order of the fields from the RequestCommand and I cannot rename them.
My question is, is there a library in swift that gives me the option of keeping the exact same order from the swift object in the JSON?
for (key, value) in OrderedDictionary will maintain the order.
struct OrderedDictionary<KeyType:Hashable, ValueType> {
private var _dictionary:Dictionary<KeyType, ValueType>
private var _keys:Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity:Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary:Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = map(dictionary.keys) { $0 }
}
subscript(key:KeyType) -> ValueType? {
get {
return _dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key)
}
else {
self.updateValue(newValue!, forKey: key)
}
}
}
mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key:KeyType) {
_keys = _keys.filter { $0 != key }
_dictionary.removeValueForKey(key)
}
mutating func removeAll(keepCapacity:Int) {
_keys = []
_dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
}
var count: Int { get { return _dictionary.count } }
// keys isn't lazy evaluated because it's just an array anyway
var keys:[KeyType] { get { return _keys } }
// values is lazy evaluated because of the dictionary lookup and creating a new array
var values:GeneratorOf<ValueType> {
get {
var index = 0
return GeneratorOf<ValueType> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return self._dictionary[key]
}
}
}
}
}
extension OrderedDictionary : SequenceType {
func generate() -> GeneratorOf<(KeyType, ValueType)> {
var index = 0
return GeneratorOf<(KeyType, ValueType)> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return (key, self._dictionary[key]!)
}
}
}
}
func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}

Call func with various struct

I want to create one func which i can used with various struct.
I have several struct and I want use one func with all my struct.
I work with Firestore and want use this one func to access the Firestore.
My first struct:
struct Profile {
var name = ""
var surname = ""
var email = ""
var dictionary: [String: Any] {
return [
"name": name,
"surname": surname,
"email": email
]
}
}
extension Profile: DocumentSerializable {
init?(dictionary: [String: Any], id: String) {
let name = dictionary["name"] as? String ?? ""
let surname = dictionary["surname"] as? String ?? ""
let email = dictionary["email"] as? String ?? ""
self.init(name: name,
surname: surname,
email: email)
}
}
My second struct:
struct FavoriteList {
var favoriteList: [String]
var id: String
var dictionary: [String: Any] {
return [
"favoriteList": favoriteList,
"id": id
]
}
}
extension FavoriteList: DocumentSerializable {
init?(dictionary: [String : Any], id: String) {
let favoriteList = dictionary["favorite"] as? [String] ?? [""]
let id = id
self.init(favoriteList: favoriteList, id: id)
}
}
And my func which I used now to load data from firestore:
func observeQuery() {
guard let query = query else { return }
let time = DispatchTime.now() + 0.5
listener = query.addSnapshotListener { [unowned self] (snapshot, error) in
if let snapshot = snapshot {
DispatchQueue.main.asyncAfter(deadline: time) {
let profileModels = snapshot.documents.map { (document) -> Profile in
if let profileModel = Profile(dictionary: document.data(), id: document.documentID) {
return profileModel
} else {
fatalError("Error!")
}
}
self.profile = profileModels
self.document = snapshot.documents
self.tableView.reloadData()
}
}
}
}
So how I can make func observeQuery to use my structs Profile or FavouriteList?
You can use Generic Functions :
func observeQuery<T>(someObject: T) {
if someObject is Profile {
//do something
} else if someObject is FavouriteList {
//do something
}
}

How to parse Google Distance Matrix API JSON in IOS SWIFT 3 the correct way

I have searched a lot to correctly parse a multi dimensional JSON arrays in swift. From the search results , what i could grasp was that it some what similar to retro-fit parsing in Android. i.e creating a parsing class for each json response. Please excuse me if i am making a mistake. I am new to IOS swif.
This is the distance matrix api json output for my distance call
{
"destination_addresses" : [ "My Destination" ],
"origin_addresses" : [
"My Source"
],
"rows" : [
{
"elements" : [
{
"distance" : {
"text" : "23.3 km",
"value" : 23333 //lastly i take this
},
"duration" : {
"text" : "49 mins",
"value" : 2938
},
"status" : "OK" //then i check this
}
]
}
],
"status" : "OK" //first i check this
}
This how i did it
This is my api call(works fine)
http://maps.googleapis.com/maps/api/distancematrix/json?origins=latitude,longitude&destinations=latitude,longitude&mode=driving&language=en-EN&sensor=false
func extract_Validate(jsonData:NSData)
{
var distInMeters = 0
var chkVarOne = "notOK"
let json1 = NSString(data: jsonData as Data, encoding: String.Encoding.utf8.rawValue)
let data = json1!.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)
print(data!)
do{
print("Inside do")
let json = try JSONSerialization.jsonObject(with: jsonData as Data, options:.allowFragments) as! NSObject
if let dictionary = json as? [String: Any] {
print("inside dictionary ")
if let detailsDict = dictionary as? NSDictionary {
print("Parse Data")
for (key, value) in detailsDict {
print("Property: \"\(key as! String)\"")
if key as! String == "status" && value as! String == "OK"{
chkVarOne = "OK"
}
if chkVarOne == "OK"
{
if key as! String == "rows"
{
if let elementsDict = value as? NSDictionary {
for(keyEle, valueEle) in elementsDict{
}
}
if let elementsDict = value as? NSArray
{
if let array = elementsDict as? [Any] {
if let firstObject = array.first {
if let distanceSet = firstObject as? NSDictionary{
for(keyDis, valueDis) in distanceSet{
// print("Property: \"\(keyDis as! String)\"")
if keyDis as! String == "elements"
{
if let DistDict = valueDis as? NSDictionary {
for(keyDict, valueDict) in DistDict{
print("Property: \"\(keyDict as! String)\"")
}
}
if let DistDict = valueDis as? NSArray {
// print(DistDict)
if let distArr = DistDict as?[Any]{
if let frst = distArr.first{
//print(frst)
if let distOne = frst as? NSDictionary{
var checkvar = "notOK"
for(ketOneDis, valOneDis) in distOne{
// print("Property: \"\(ketOneDis as! String)\"", valOneDis)
if ketOneDis as! String == "status" && valOneDis as! String == "OK"{
checkvar = "OK"
}
if checkvar == "OK"
{
if ketOneDis as! String == "distance"
{
// print(valOneDis)
if let valtwoDis = valOneDis as? NSDictionary{
for(kDis, vDis) in valtwoDis{
// print("Property: \"\(kDis as! String)\"", vDis)
if kDis as! String == "value"{
distInMeters = vDis as! Int
}
}
if let valTwoDis = valOneDis as? NSArray{
print(valTwoDis)
}
}
}
}
}
}
}
}
}
}
//print(distanceSet,"rttgtgtgtgtgtgtg")
}
if let distSet = firstObject as? NSArray{
print(distSet,"dfdffddfdfdfd")
}
}
}
}
}
//let rows
}
}
} //ending here
}
}
}
catch{
print("error in JSONSerialization")
}
print(distInMeters," is the resulting value")
}
This code is working fine fine. But i know this is not the way to do it.
So please help me
I think there might be some errors occur in this code later. Don't know for sure
The easiest and most effective way to do this is by using object mapping. Something like Gloss (https://github.com/hkellaway/Gloss) would do the trick. In your case you will have the following classes (objects):
Response
import Foundation
import Gloss
//MARK: - Response
public struct Response: Glossy {
public let destinationAddresses : [String]!
public let originAddresses : [String]!
public let rows : [Row]!
public let status : String!
//MARK: Decodable
public init?(json: JSON){
destinationAddresses = "destination_addresses" <~~ json
originAddresses = "origin_addresses" <~~ json
rows = "rows" <~~ json
status = "status" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"destination_addresses" ~~> destinationAddresses,
"origin_addresses" ~~> originAddresses,
"rows" ~~> rows,
"status" ~~> status,
])
}
}
Element:
import Foundation
import Gloss
//MARK: - Element
public struct Element: Glossy {
public let distance : Distance!
public let duration : Distance!
public let status : String!
//MARK: Decodable
public init?(json: JSON){
distance = "distance" <~~ json
duration = "duration" <~~ json
status = "status" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"distance" ~~> distance,
"duration" ~~> duration,
"status" ~~> status,
])
}
}
Row:
import Foundation
import Gloss
//MARK: - Row
public struct Row: Glossy {
public let elements : [Element]!
//MARK: Decodable
public init?(json: JSON){
elements = "elements" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"elements" ~~> elements,
])
}
}
Distance:
import Foundation
import Gloss
//MARK: - Distance
public struct Distance: Glossy {
public let text : String!
public let value : Int!
//MARK: Decodable
public init?(json: JSON){
text = "text" <~~ json
value = "value" <~~ json
}
//MARK: Encodable
public func toJSON() -> JSON? {
return jsonify([
"text" ~~> text,
"value" ~~> value,
])
}
}
After creating the classes, you map the JSON to the Object by doing the following:
let jsonResponse = ..//Your JSON response
guard let distanceResponse = Response(json: jsonResponse) else {
// handle decoding failure here
}
//You can use the distanceResponse object here as an object and access it's values.
Learn more about object mapping in this tutorial: https://www.raywenderlich.com/150322/swift-json-tutorial-2

IGListKit insert data to class from Alamofire request

So i have this model
class Event: NSObject {
var _eventName: String!
var _venueName : String!
var _eventImage: String!
var eventName: String {
if _eventName == nil {
_eventName = ""
}
return _eventName
}
var venueName: String {
if _venueName == nil {
_venueName = ""
}
return _venueName
}
var eventImage: String {
if _eventImage == nil {
_eventImage = ""
}
return _eventImage
}
init(eventsDict: Dictionary<String, AnyObject>) {
if let venue = eventsDict["venue"] as? Dictionary<String, AnyObject> {
if let venuname = venue["name"] as? String{
self._venueName = venuname
}
if let eventname = eventsDict["name"] as? String {
self._eventName = eventname
}
if let eventimage = eventsDict["coverPicture"] as? String {
self._eventImage = eventimage
}
}
}
And i make it IGListDiffable with this extension.
extension NSObject: IGListDiffable {
public func diffIdentifier() -> NSObjectProtocol {
return self
}
public func isEqual(toDiffableObject object: IGListDiffable?) -> Bool {
return isEqual(object)
}
}
So when I'm loading data from hardcoded code like this
var entries = [Event]()
func loadFakeEvents() {
let entries = [
Event(
eventName: "Ζωρζ Πιλαλι Και Η Soufra Band Στο AN Groundfloor - Live Stage!",
venueName: "AN Groundfloor - live stage",
eventImage: "https://scontent.xx.fbcdn.net/v/t31.0-8/s720x720/15936729_1867160333520142_8855370744955080264_o.jpg?oh=8198bc10a8ea61011d7ec1902b34aa01&oe=593D6BC4"
),
Event(
date: "2017-02-18T21:30:00+0200",
name: "Διονύσης Σαββόπουλος at Gazarte I Main Stage 18/02",
venuename: "Gazarte",
eventImage: "https://scontent.xx.fbcdn.net/v/t1.0-9/s720x720/16265335_1262826863809003_3636661375515976849_n.jpg?oh=5bb342321a65d33dbc1cc41de266b45e&oe=5907857C"
)
]
self.entries = entries
}
The events are loading fine. As they have to.
But when i'm making an alamofire request, of course, it takse some time to load the data and append them to the empty array of events.
This is the function that I have to call the events
func loadEvents() {
let parameters: Parameters = [
"Some" : "Parameters",
"Some" : "Parameters"
]
Alamofire.request(baseurl, method: .get, parameters: parameters)
.responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let result = responseData.result
if let dict = result.value as? Dictionary<String, AnyObject>{
print(dict) // <-- Check this out
if let list = dict["events"] as? [Dictionary<String, AnyObject>] {
for obj in list {
let event = Event(eventsDict: obj)
self.entries.append(event)
}
}
}
}
}
}
So in the above code i have a print, which prints the json.
And in my
extension LocationViewController: IGListAdapterDataSource {
func objects(for listAdapter: IGListAdapter) -> [IGListDiffable] {
let items: [IGListDiffable] = loader.entries as [IGListDiffable]
print(items.count) // <--- another print of items that should be displayed
return items
}
func listAdapter(_ listAdapter: IGListAdapter, sectionControllerFor object: Any) -> IGListSectionController {
return NormalSectionController()
}
func emptyView(for listAdapter: IGListAdapter) -> UIView? { return nil }
}
Adapter i also print the items that should be displayed.
So when i load the fakeEvents function it prints 2 but when i load them with the normal function it prints 0 and then the JSON from the dict var from the previous code.
Normally i would reloadData() of the collection view.
But with IGListKit what is the trick of sending the Event Class to the CollectionView?
Thanks a lot for your time and i hope i'm not off topic !
Pasting my answer from this same issue on Github in case anyone finds this.
https://github.com/Instagram/IGListKit/issues/468
It looks like you're missing a call to self.adapter.performUpdates(animated: true) after the for-loop when appending to the entries dict:
func loadEvents() {
// ...
Alamofire.request(baseurl, method: .get, parameters: parameters)
.responseJSON { (responseData) -> Void in
if responseData.result.value != nil {
let result = responseData.result
if let dict = result.value as? Dictionary<String, AnyObject>{
if let list = dict["events"] as? [Dictionary<String, AnyObject>] {
for obj in list {
let event = Event(eventsDict: obj)
self.entries.append(event)
}
// missing this!
self.adapter.performUpdates(animated: true)
// missing that!
}
}
}
}
}

Why the first completionHandler return the data before all methods are called?

Here is my API code
import UIKit
import Alamofire
import SwiftyJSON
class ItunesApi: NSObject
{
var artistArray:Array <Artist> = []
func downloadData(name:String, _ completionHandler: #escaping (_ result: Array<Artist>) -> Void)
{
Alamofire.request("https://itunes.apple.com/search?term=U2&entity=musicArtist").response
{ response in
if let data = response.data
{
let JsonResult = JSON(data)
self.findDiscography(data: JsonResult)
}
completionHandler(self.artistArray)
}
}
func findDiscography (data: JSON)
{
for (subJson):(String, JSON) in data["results"]
{
if let artistName = subJson.1["artistName"].string
{
print(artistName);
self.downloadDiscography(name: artistName)
}
}
}
func downloadDiscography (name: String)
{
Alamofire.request("https://itunes.apple.com/search?term=U2&entity=album").response
{ response in
if let data = response.data
{
let JsonResult = JSON(data)
self.createDataModel(name: name, data: JsonResult)
}
}
}
func createDataModel (name: String, data: JSON)
{
var albums:Array <Album> = []
for (subJson):(String, JSON) in data["results"]
{
var thumbnail:String = ""
var title:String = ""
var year:String = ""
if let thumbImage = subJson.1["artworkUrl60"].string
{
thumbnail = thumbImage;
}
if let titleString = subJson.1["collectionName"].string
{
title = titleString;
}
if let releaseDate = subJson.1["releaseDate"].string
{
year = releaseDate;
}
let album = Album(_thumbnail: thumbnail, _title: title, _year: year)
albums.append(album)
}
let artist = Artist(_name: name, _musicStyle: "Rock", _albums: albums as NSArray);
self.artistArray.append(artist);
}
}
And I call here in MyClassTableView.m
func searchBarSearchButtonClicked(_ searchBar: UISearchBar)
{
if let artist = searchBar.text
{
self.itunesApi.downloadData(name: artist, { (array) in
print(array);
})
}
}
Why the copmletionHandler return before all method are called? I want to return in first completionHandeler the result of all method but it return before. The self.itunesApi.downloadData return [] instead of an array filled

Resources