Appending into an array from completion block - ios

I am attempting to append the results of the parse query into usersData
struct Data {
var FirstName:String!
var LastName:String!
var Gender:String!
var Age:String!
}
In the class I have
var usersData = [Data]()
I am using this to
func parseUsersData(completionHandler: [Data] -> Void) {
var usersDataArray = [Data]()
let query = PFQuery(className: "_User")
query.fromLocalDatastore()
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let user = objects as? [PFObject]! {
for object in user! {
var singleData = Data()
singleData.FirstName = object["firstName"] as! String
singleData.LastName = object["lastName"] as! String
singleData.Gender = object["gender"] as! String
singleData.Age = object["age"] as! String
usersDataArray.append(singleData)
}
}
completionHandler(usersDataArray)
}
}
}
finally, I am trying to do this:
Edit: To clarify, I need to pass the data from the queries, userDataArray, into the array usersData.
parseUsersData { (usersDataArray) -> Void in
usersData.append(usersDataArray)
}
The error I am getting is
Cannot convert value of type '[Data]' to expected argument type 'Data'

In your last code block you seem to appending a Data array to a Data object usersData.append(usersDataArray) which causes error I think. Did you mean to write usersDataArray.append(usersData) which makes a lot more sense?

You are attempting to add an array of Data to an array that is looking for Data Objects. Add a new struct that handles your Data Array and then change the usersData to look for DataArray's:
struct DataArray {
var array = [Data]()
}
And change the line:
var usersData = [DataArray]()

usersDataArray is an array. To add an array to an other array, prefer appendContentsOf :
usersData.appendContentsOf(usersDataArray)

Related

I'm only getting one object back from a Get Request. How to retrieve all of it?

I'm only getting one object back from a Get Request. How do I retrieve all of them? Here is my Data Model.
class DataClass {
// MARK VARIABLES
init(price: Double, title: String, firstImage: String, allImages: [String]) {
_price = Price
_title = title
_allImages = allImages
_firstImg = firstImage
}
var _title: String!
var _firstImg: String!
var _allImages: [String]!
var _propertyPrice: Double!
func downloadHandMProperties(completed: #escaping downloadComplete) {
Alamofire.request(propertyListing).responseJSON { response in
if let result = response.result.value {
let dict = JSON(result)
if let data = dict["data"].dictionary {
if let listingResultDict = data["listings"]?.array {
for list in listingResultDict {
if let propertyName = list["data"]["name"].string {
self._title = propertyName
/etc..
// I parsed all of the data and passed them to the variables.
completed()
}
}
Here is the ViewController from where I'm receiving it.
Class ViewController: UIViewController {
let array = [DataClass]()
var property = DataClass(price: 0, title: "", firstImage: "", allImages: [])
override func viewDidLoad() {
super.viewDidLoad()
let data = DataClass(price: property.price, title: property.title, firstImage: property.firstImage, allImages: property.allImages)
self.array.append(data)
print(array)
}
}
There is only one object in that array. How do I get all of them. I thought of looping through the results, but I can only loop through Arrays and Dictionaries, and the Object is neither. Any suggestions as to how to retrieve all of the objects and put them in to the array?
Try casting your result object as collection type that you can loop through. You'll have to figure out what you can cast your result object as with some trial and error but it could look something like this:
Alamofire.request(propertyListing).responseJSON { response in
if let result = response.result.value as? [[String: Any]] {
//here the result object is casted as an array of [String: Any] dictionaries
// I parsed all of the data and passed them to the variables.
}
completed()
}
Then you could loop through the array of dictionaries since each dictionary would represent one of your objects.
What you could do to test out what type you could cast your result object as is add a break point on the if let result = response.result.value { line of code and then run po response.result.value as? [[String: Any]] in the xcode console. If you get a good looking result printed out you're set!

Retrieve data from first only item in for loop

I have a query that gets objects from the server I'm then reducing the number of objects by matching "packName" to "className" which should just give me the children of "packName".
from this i am populating an array of struct items and pulling out the data for the first index of the array.
this is fine but I'm just a bit concerned that if the number of children increases this may slow processing down. so i was wondering if there was a way to just retrieve the first item of the for loop, which is all I'm after as the query has been sorted in ascending order.
this is the function code below.
class func createHistory(packName: String, completeBlock: ((Bool) -> Void)? = nil) {
struct initialDataStruct {
var packNameStruct : String
var packIdStruct : String
var partNameStruct : String
var partIdStruct : String
var partIndexStruct : Int
}
var initialDataArray = [initialDataStruct]()
let historyClass = PFObject(className: packName)
let query = PFQuery(className: "Part")
query.includeKey("fromPack")
query.order(byAscending: "partName")
query.fromLocalDatastore()
query.findObjectsInBackground { (objects, error) in
if error != nil {
print(error!)
}
else if let parts = objects {
for object in parts {
// if the fromPack column has data
if let fromPack = object.object(forKey: "fromPack") as? PFObject {
// create the class name from the pack name
if let className = (fromPack.object(forKey: "packName") as? String) {
// packName was sent from JVC
// this will limit array items to how ever many children packName has
if packName == className {
// because its sorted could probably just get the first item here
let packName = fromPack.object(forKey: "packName") as! String
let packId = fromPack.objectId as String!
let partName = object.object(forKey: "partName") as! String
let partId = object.objectId as String!
let partIndex = 0
initialDataArray.append(initialDataStruct(packNameStruct: packName,
packIdStruct: packId!,
partNameStruct: partName,
partIdStruct: partId!,
partIndexStruct: partIndex))
}
}
}
} // for
historyClass.add(initialDataArray[0].packNameStruct, forKey: "packName")
historyClass.add(initialDataArray[0].partIdStruct, forKey: "packId")
historyClass.add(initialDataArray[0].partNameStruct, forKey: "partName")
historyClass.add(initialDataArray[0].partIndexStruct, forKey: "partIndex")
print(historyClass)
PFObject.pinAll(inBackground: [historyClass])
}
} // query
}

how to retrive data from a user pointer on Parse using swift

On Parse I have users with Facebook profile and Email login profile. So I want to bury for users data in my twitter-like app.
In my "messages" class on Parse I have column "sender" that contains pointers to parse users.
I just want to retrive and show the name of users in class "messages" contained in the column "sender" wich contains pointers to PFUsers of which I need data for keys
"first_name"
"last_name"
"profile_picture"
How can I retrive their data like name and image in order to show them in a tableview?
these are the declarations of my arrays:
var sendersArray : [String] = []
var picturesArray : [NSData] = []
maybe I could use something like this tuple, but I can't understand how to grab data from pointers
for user in list {
let firstName = "fist_name"
let lastName = "last_name"
let oProfileImage = NSData() //"image_profile" as! NSData
otherUsers.append((oName: firstName, oLastName: lastName, oImageProfle: oProfileImage))
}
version - 1:
I started with printing the whole pf object
//******************************************************
func theSearch() {
let theSearchQuery = PFQuery(className: "Messages")
theSearchQuery.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
for object in objects! {
let theName = object.sender!
print(object)
print(theName)
sendersArray.append(theName)
let profilePicture = object["profile_pic"] as! PFFile
picturesArray.append(profilePicture)
}
self.tableView.reloadData()
})
}
//*******************************************************
version - 2:
then, found this solution, but still, doesn't
func theSearch() {
let theSearchQuery = PFQuery(className: "Messages" )
theSearchQuery.includeKey("sender")
theSearchQuery.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
for object in objects! {
let theName = object.sender!["first_name"] as? String
print(object)
print(theName)
sendersArray.append(theName)
let profilePicture = object["profile_pic"] as! PFFile
picturesArray.append(profilePicture)
}
self.tableView.reloadData()
})
}
errors:
seems to be a problem with sender, maybe I shouldn't use it
thanks in advance
let theName = object.objectForKey("sender")!.objectForKey("first_name") as! String
Complete Code:
func theSearch() {
let theSearchQuery = PFQuery(className: "Messages")
theSearchQuery.includeKey("sender")
theSearchQuery.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
for object in objects! {
let theName = object.objectForKey("sender")!.objectForKey("first_name") as! String
print(object)
print(theName)
self.sendersArray.append(theName)
let profilePicture = object["profile_picture"] as! PFFile
self.picturesArray.append(profilePicture)
}
self.tableView.reloadData()
})
}
Also, your picturesArray should be of type PFFile, like this:
var picturesArray = [PFFile]()
NOT NSData. change that at the top of your class.
-----EDIT------:
If you want to retrieve an image from a parse query, do this:
1) at the top of your class, declare the following arrays to store the results:
// your images will be stored in the file array
var fileArray = [PFFile]()
// your first and last names will be stored in String Arrays:
var firstNameArray = [String]()
var lastNameArray = [String]()
2) perform the query:
let query1 = PFQuery(className: "_User")
query1.orderByDescending("createdAt")
query1.findObjectsInBackgroundWithBlock({
(objects : [AnyObject]?, error : NSError?) -> Void in
if error == nil {
for x in objects! {
let firstName = x.objectForKey("first_name") as! String
let lastName = x.objectForKey("last_name") as! String
self.firstNameArray.append(firstName)
self.lastNameArray.append(lastName)
if x.objectForKey("profile_picture") as? PFFile == nil {
print("do nothing cause it's nil")
}
else {
let file:PFFile = x.objectForKey("profile_image") as! PFFile
self.fileArray.append(file)
}
}
self.tableView.reloadData()
}
})
Note I am using Swift 2 and Xcode 7. Syntax is slightly different in Xcode 6.4 and Swift 1.2.

How can I store a Dictionary with RealmSwift?

Considering the following model:
class Person: Object {
dynamic var name = ""
let hobbies = Dictionary<String, String>()
}
I'm trying to stock in Realm an object of type [String:String] that I got from an Alamofire request but can't since hobbies has to to be defined through let according to RealmSwift Documentation since it is a List<T>/Dictionary<T,U> kind of type.
let hobbiesToStore: [String:String]
// populate hobbiestoStore
let person = Person()
person.hobbies = hobbiesToStore
I also tried to redefine init() but always ended up with a fatal error or else.
How can I simply copy or initialize a Dictionary in RealSwift?
Am I missing something trivial here?
Dictionary is not supported as property type in Realm.
You'd need to introduce a new class, whose objects describe each a key-value-pair and to-many relationship to that as seen below:
class Person: Object {
dynamic var name = ""
let hobbies = List<Hobby>()
}
class Hobby: Object {
dynamic var name = ""
dynamic var descriptionText = ""
}
For deserialization, you'd need to map your dictionary structure in your JSON to Hobby objects and assign the key and value to the appropriate property.
I am currently emulating this by exposing an ignored Dictionary property on my model, backed by a private, persisted NSData which encapsulates a JSON representation of the dictionary:
class Model: Object {
private dynamic var dictionaryData: NSData?
var dictionary: [String: String] {
get {
guard let dictionaryData = dictionaryData else {
return [String: String]()
}
do {
let dict = try NSJSONSerialization.JSONObjectWithData(dictionaryData, options: []) as? [String: String]
return dict!
} catch {
return [String: String]()
}
}
set {
do {
let data = try NSJSONSerialization.dataWithJSONObject(newValue, options: [])
dictionaryData = data
} catch {
dictionaryData = nil
}
}
}
override static func ignoredProperties() -> [String] {
return ["dictionary"]
}
}
It might not be the most efficient way but it allows me to keep using Unbox to quickly and easily map the incoming JSON data to my local Realm model.
I would save the dictionary as JSON string in Realm. Then retrive the JSON and convert to dictionary. Use below extensions.
extension String{
func dictionaryValue() -> [String: AnyObject]
{
if let data = self.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
return json!
} catch {
print("Error converting to JSON")
}
}
return NSDictionary() as! [String : AnyObject]
} }
and
extension NSDictionary{
func JsonString() -> String
{
do{
let jsonData: Data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String.init(data: jsonData, encoding: .utf8)!
}
catch
{
return "error converting"
}
}
}
UPDATE 2021
Since Realm 10.8.0, it is possible to store a dictionary in a Realm object using the Map type.
Example from the official documentation:
class Dog: Object {
#objc dynamic var name = ""
#objc dynamic var currentCity = ""
// Map of city name -> favorite park in that city
let favoriteParksByCity = Map<String, String>()
}
Perhaps a little inefficient, but works for me (example dictionary from Int->String, analogous for your example):
class DictObj: Object {
var dict : [Int:String] {
get {
if _keys.isEmpty {return [:]} // Empty dict = default; change to other if desired
else {
var ret : [Int:String] = [:];
Array(0..<(_keys.count)).map{ ret[_keys[$0].val] = _values[$0].val };
return ret;
}
}
set {
_keys.removeAll()
_values.removeAll()
_keys.appendContentsOf(newValue.keys.map({ IntObj(value: [$0]) }))
_values.appendContentsOf(newValue.values.map({ StringObj(value: [$0]) }))
}
}
var _keys = List<IntObj>();
var _values = List<StringObj>();
override static func ignoredProperties() -> [String] {
return ["dict"];
}
}
Realm can't store a List of Strings/Ints because these aren't objects, so make "fake objects":
class IntObj: Object {
dynamic var val : Int = 0;
}
class StringObj: Object {
dynamic var val : String = "";
}
Inspired by another answer here on stack overflow for storing arrays similarly (post is eluding me currently)...

Global array only returns 0

I have instantiated a global array called array and try append the values from dictionary to the array. I have successfully copied the values from the dictionary to the array but it seems to only locally. I am not sure where I have done wrong. Please help me figure it out. Here's the code :
class StudentTableViewController: UITableViewController {
var dict = Dictionary<String, String> ()
var array = [Dictionary<String, String>] ()
// Student
override func viewDidLoad() {
super.viewDidLoad()
queryData()
}
func queryData ()-> (Int, [Dictionary<String, String>]) {
var userQuery = PFUser.query()
//var arr = []
var count : Int
count = 0
userQuery?.whereKey("Type", equalTo:"student")
userQuery?.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects! {
self.dict["firstName"] = object["firstName"] as! NSString as String
self.dict["lastName"] = object["lastName"] as! NSString as String
self.dict["contact"] = object["contact"] as! NSString as String
self.array.append(self.dict)
}
println(self.array)
count = self.array.count
})
return(self.array.count, self.array)
// returns 0
// return empty array
}
Here, self.array.count return 0 but I want to return the count of the dictionary members and the self.array only return empty array where I want it to return the dictionary.
I have been stuck on this for a while, I am not sure why it doesn't want to be global
Thanks
findObjectsInBackgroundWithBlock is asynchronous, so when your method returns, the block hasn't even run yet. Instead, you need to call something in your block with the results. Here is an example of how to do this by passing a completion block.
override func viewDidLoad() {
super.viewDidLoad()
queryData {
// update view
}
}
func queryData(completionHandler: () -> Void) {
var userQuery = PFUser.query()
var count : Int
count = 0
userQuery?.whereKey("Type", equalTo:"student")
userQuery?.findObjectsInBackgroundWithBlock({ (objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects! {
self.dict["firstName"] = object["firstName"] as! NSString as String
self.dict["lastName"] = object["lastName"] as! NSString as String
self.dict["contact"] = object["contact"] as! NSString as String
self.array.append(self.dict)
}
println(self.array)
count = self.array.count
dispatch_async(dispatch_get_main_queue) {
completion()
}
})
return(self.array.count, self.array)
}

Resources