how to convert [NSManagedObject] to nsmutable array (Swift) - ios

i am using coredata in my app. i am storing 50 city in my core data. now i want to use search bar. which was working well with array. and i am using filtering for that. here is my code for that
arrcity.enumerateObjectsUsingBlock({ (obj, _, _) -> Void in
if let city : SelectCityModel = obj as? SelectCityModel
{
if city.city.lowercaseString.rangeOfString(self.searchtext.lowercaseString) != nil
{
self.searchcity?.addObject(city)
}
}
})
now i want to do same process but i have [nsmanagedobject] people. so how can i do this. it will be ease if is there any way to convert [NsmanagedObject] to nsmutablearry.
here is my code
if (people.count > 0)
{
people.enumerateObjectsUsingBlock({ (obj, _, _) -> Void in
if let city : SelectCityModel = obj as? SelectCityModel
{
if city.city.lowercaseString.rangeOfString(self.searchtext.lowercaseString) != nil
{
self.searchcity?.addObject(city)
}
}
})
}
but here var people = NSManagedObject so how can i convert it?

NSMutableArray is not related to a Swift Array.
But you could use the Swift Array with a native enumerator, for example
people.forEach { [unowned self] (object) -> () in
if object is SelectCityModel
{
if object.city.lowercaseString.rangeOfString(self.searchtext.lowercaseString) != nil
{
self.searchcity?.addObject(city)
}
}
}
If people is always [SelectCityModel] cast it to that type in the fetch line and omit the type check in the forEach closure. The check if people > 0 is not needed either.
A more convenient way is to use the filter and map functions.
self.searchcity = people.filter({$0.city.lowercaseString.rangeOfString(self.searchtext.lowercaseString) != nil}).map{ $0.city }

Related

Cannot cast/decode data from server into Swift data model?

I need to cast the below response from my server as [UserResult] but I cannot get it to work??
What am I doing wrong?
func userSearch(keyword: String, completion: #escaping (Result<[UserResult], ResponseError>) -> Void ) {
socket.emit("userSearch", keyword)
socket.on("userFound") { ( data, ack) in
print(data) // prints below NSArray
if !data.isEmpty {
if let response = data as? [UserResult] {
print("USERS \(response)") // WILL NOT WORK?
completion(.success(response))
}
} else {
completion(.failure(.badRequest("No users found")))
}
}
}
Data from server
[<__NSArrayM 0x60000040e5b0>(
{
profileUrl = "www.address1.com";
username = chrissmith;
},
{
profileUrl = "www.address2.com";
username = johnsmith;
},
{
profileUrl = "www.address3.com";
username = alicesmith;
}
)
]
UserResult Model
struct UserResult: Decodable {
let username: String
let profileUrl: String
}
Well you are using Socket.IO library and specifically method
socket.on(clientEvent: .connect) {data, ack in
...
}
defined as
#discardableResult
open func on(clientEvent event: SocketClientEvent, callback: #escaping NormalCallback) -> UUID
using typealias:
public typealias NormalCallback = ([Any], SocketAckEmitter) -> ()
So basically at the and you are being returned data of type [Any] according to documentation.
Since you do not know what is inside your data it is better for you to unwrap objects in your array one by one (instead casting it directly to [UserResult]) and try to find out what Type there are by comparing to some set of known types as some of answers from this question suggest.
I would start with verifying the data structure with example code below , and only move on with casting to various type afterwards:
Lets assume example data1 is your data:
let dict1 = ["profileUrl":"www.address1.com","username":"chrissmith"]
let data1: NSArray = [dict1]
//printed data1:
// (
// {
// profileUrl = "www.address1.com";
// username = chrissmith;
// }
// )
if data1[0] as? [String:String] != nil {
print("We found out that first object is dictionary of [String:String]!")
}
else if data1[0] as? Dictionary<NSObject, AnyObject> != nil {
print("We found out that first object is dictionary of mixed values!")
} else {
print("We found out that first object has different data structure")
}
Hopefully this answer was at least a little bit helpfull, even though not providing direct easy solution for your problem.

observeNext called more than once after insert element to MutableObservableArray in Bond Swift Framework

I have a MutableObservableArray object which has a binding with observeNext function of Bond framework. At the first opening of the app, I fetch array from user defaults and insert it to this empty array.
My problem is that after I insert element to array, observeNext function called three times not once. What can be the problem?
var list = MutableObservableArray<Task>([])
_ = self.list.observeNext(with: { element in
if element.diff.count != 0 {
if element.diff.deletes.count >= 1 && element.collection.count == 0 {
self.restoreUserDefaults(with: false)
} else {
self.saveListToUserDefaults(list: element.collection)
}
}
})
Insert Function:
if let savedTask = userDef.object(forKey: self.userDefaultsKeyForList) as? Data {
let decoder = JSONDecoder()
if let loadedList = try? decoder.decode([Task].self, from: savedTask) {
self.list.batchUpdate({ (a) in
a.insert(contentsOf: loadedList, at: 0)
})
}
}
EDIT: I think when It first initialize array with [] It also gets into observeNext. Is it normal?

How to access & get nested values from IOS Swift 'Any' type?

I am trying to read from Firestore into a Dictionary[Any] type using Struct. I can get the values loaded into variable "data" dictionary with Any type.
However I cannot loop thru it to access normal nested Dictionary variable.
I cannot get Key, values printed.
Following is my code:
class PullQuestions {
//shared instance variable
**public var data = [Any]()**
private var qdb = Firestore.firestore()
public struct questionid
{
let qid : String
var questions : [basequestion]
var answers: [baseans]
}
public struct basequestion {
let category : String
let question : String
}
public struct baseans {
let answer : String
}
class var sharedManager: PullQuestions {
struct Static {
static let instance = PullQuestions()
}
return Static.instance
}
static func getData(completion: #escaping (_ result: [Any]) -> Void) {
let rootCollection = PullQuestions.sharedManager.qdb.collection("questions")
//var data = [Any]()
rootCollection.order(by: "upvote", descending: false).getDocuments(completion: {
(querySnapshot, error) in
if error != nil {
print("Error when getting data \(String(describing: error?.localizedDescription))")
} else {
guard let topSnapshot = querySnapshot?.documents else { return }
// var questiondoc = [basequestion]()
for questioncollection in topSnapshot {
rootCollection.document(questioncollection.documentID).collection("answers").getDocuments(completion: {
(snapshot, err) in
guard let snapshot = snapshot?.documents else { return }
var answers = [baseans]()
for document in snapshot { //There should be only one Document for each answer collection
//Read thru all fields
for i in 0..<document.data().count
{
let newAns = baseans(answer: answer)
print("Answer Docs=>", (answer))
answers.append(newAns)
}
}
let qid = questioncollection.documentID
let category = questioncollection.data()["category"] as! String
let question = questioncollection.data()["question"] as! String
let newQuestions = basequestion(category: category ,question: question)
let newQuestionDict = questionid(qid: qid, questions: [newQuestions], answers: answers)
PullQuestions.sharedManager.data.append(newQuestionDict)
//Return data on completion
completion(PullQuestions.sharedManager.data)
})
}
}
})
}
}
I can print like this
print("Count =>", (PullQuestions.sharedManager.data.count))
// print(PullQuestions.sharedManager.data.first ?? "Nil")
print(PullQuestions.sharedManager.data[0])
for element in PullQuestions.sharedManager.data
{
print("Elements in data:=>", (element))
}
I could access only the key.. how do i go and get the nested values ?
First of all, consider using Swift code conventions (e.g. your structs are named with small letters, but you should start with capital), this will make your code more readable.
Returning to your question. You use an array instead of dictionary (this piece of code: public var data = [Any]()). And here you are trying to print values:
for element in PullQuestions.sharedManager.data
{
print("Elements in data:=>", (element))
}
In this context element is an Any object, thus you cannot access any underlying properties. In order to do this you have two options:
1. You should specify the type of array's objects in it's declaration like this:
public var data = [questionid]()
or you can user this:
public var data: [questionid] = []
These two are equals, use the one you prefer.
2. If for any reasons you don't want to specify the type in declaration, you can cast it in your loop. Like this:
for element in PullQuestions.sharedManager.data
{
if let element = element as? quetionid {
print("Elements in data:=>", (element))
// you can also print element.qid, element.questions, element.answers
} else {
print("Element is not questionid")
}
}
You could of course use the force cast:
let element = element as! questionid
and avoid if let syntax (or guard let if you prefer), but I wouldn't recommend this, because it (potentially) can crash your app if element will be nil or any other type.

Iterate through a custom Parse column in Swift app

I want to know how I could store the entire custom column (the user Pointer<_User> column from a custom class) and put them all in an array variable so that I can see if a the user exists in that class or not. This is what I have:
Old Code
var objectUserIdArray = [String]()
let objectUserIdQuery : PFQuery = PFQuery(className: "Scores")
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
var objectID = objects! as [PFObject]
for i in 0..<objectID.count {
objectUserIdArray.append(objectID[i].objectId!)
}
for _ in objectID {
print(objectUserIdArray)
}
New Code
func saveScoresOnParse() {
objectUserIdQuery.whereKey("User", equalTo: PFObject(withoutDataWithClassName: "_User", objectId: userID))
objectUserIdQuery.findObjectsInBackgroundWithBlock {
(objects : [PFObject]? , error : NSError?) -> Void in
if error == nil {
//var objectID = objects! as [PFObject]
/*for i in 0..<objectID.count {
self.objectUserIdArray.append( objectID[i].objectId! )
}*/
for _ in objects! {
print(objects)
}
// The score key has been incremented
for (var i = 0 ; i < self.objectUserIdArray.count ; i++) {
if self.userID != objects![i] {
print("New Scores")
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores["User"] = PFUser.currentUser()
self.scores["Right"] = self.rightAnswers
self.scores["Wrong"] = self.wrongAnswers
self.scores["Skipped"] = self.skippedQuestions
self.scores.saveInBackground()
} else if self.userID == objects![i] {
print("Updated Scores")
self.scores.incrementKey("Right", byAmount: 1)
self.scores.incrementKey("Wrong", byAmount: 1)
self.scores.incrementKey("Skipped", byAmount: 1)
print("R: \(self.rightAnswers)")
print("W: \(self.wrongAnswers)")
print("S: \(self.skippedQuestions)")
self.scores.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// The score key has been incremented
} else {
// There was a problem, check error.description
}
}
} else {
print("Error")
}
}
} else {
print(error)
}
}
But it only stores the objectId column and not the Pointer<_User> column. I know this because when I print the stuff that is inside, it prints out the objectIds.
This is what happens, instead of just updating the current user's scores, it just makes new ones. I want the if statement to check if the user already exists in that column and if it does updates the scores and if it doesn't, make new ones. (The new code's if statement doesn't work, i have to bring it out for it to save...)
Your updated question make clearer what you are actually wanting to do;
Save or update a user's scores in your Parse Score object. To do this, there is no reason to retrieve any object Ids or loop through any results. More often than not you don't do use Object Ids explicitly when using Parse; you can simply pass the object itself with Parse working out the references for you.
I am not sure how you exactly want to change the scores; in your code above you increment in one case but set the scores explicitly in another, but the code below shows the general approach.
If you are frequently or repeatedly going to update a score record then you could make your code more efficient by holding a reference to the Scores object in a property after you find it the first time and simply update & save it subsequently.
func saveScoresOnParse() {
if let currentUser=PFUser.currentUser() {
let scoreQuery= PFQuery(className: "Scores")
scoreQuery.whereKey("User",equalTo:currentUser)
scoreQuery.getFirstObjectInBackgroundWithBlock {
(object : PFObject? , error : NSError?) -> Void in
if error == nil {
var scoreObject=object ?? PFObject.objectWithClassName("Scores")
if (scoreObject["User"]==nil) {
scoreObject["User"]=currentUser
}
scoreObject["Right"]=self.rightAnswers
scoreObject.saveInBackground()
} else {
print(error)
}
}
} else {
print("No current user!")
}
}

Fixed size array in swift

i have a fixed size array as
var fieldNameArray = [String?](count: 4, repeatedValue: nil)
i am doing this to search if there is the element in array or not
if let temp = find(fieldNameArray,"profile_picture"){//i get a compile error here
//remove the data
....
}else{
println(" //append the value")
.....
}
But i get a compile time error as
Cannot invoke 'find' with an argument list of type '([(String?)],
String)'
I think i should unwrap it? How can i do it
UPDATED
SRWebClient.POST(registerURl)
.data(registerImagesArray, fieldName: fieldNameArray, data: parametersToPost)
.send({(response:AnyObject!, status:Int) -> Void in//here compile time error
println("response object: \(response)")
Again after i changed my array to fixed size array i got this error
Cannot invoke 'send' with an argument list of type '((AnyObject!, Int)
-> Void, failure: (NSError!) -> Void)
For efficiency you should not do what Manav Gabhawala suggests but write a find function yourself:
func myFind(array: [String?], value: String) -> Int? {
for (i, av) in enumerate(array) {
if av != nil && av! == value {
return i
}
}
return nil;
}
As Swift compiles to machine code, you will have nearly the same performance as with the standard library’s find.
Try using this instead (Swift 2.0):
if let index = fieldNameArray.indexOf("profile_picture") {
//remove the data using the index
....
} else {
print("// append the value")
.....
}
In swift 1.2 (a little inefficient but it works) :
if let temp = find(fieldNameArray.filter { $0 != nil}.map { $0! },"profile_picture") {
// Then same code as question...

Resources