How do we populate pointers in the User object? In my Parse User table, I have the following array of pointers: "toys", that points to an object of type "Toy".
In my iOS client:
PFUser.currentUser()?.toys will return an array of pointers to toys, not full Toy objects. How do I retrieve and persists the full objects into PFUser.currentUser()?.toys so that the next time I access PFUser.currentUser()?.toys, I get full objects?
Thanks.
UPDATE:
Sample code that shows what i mean (thanks Chaitanya Shah):
let query = PFUser.query()!
query.includeKey("toys")
query.getObjectInBackgroundWithId(PFUser.currentUser()!.objectId!, block: {
(object: PFObject?, error: NSError?) -> Void in
if object != nil {
if let toys = object!["toys"] as? [PFObject] {
println(toys) // full Toy objects
println(PFUser.currentUser()?.toys) // Toy pointers
// the two toys above won't be the same. What I want is
// to have PFUser.currentUser() contains fully fetched toys
// objects so I can use it throughout my app later without
// have to re-fetch them.
})
}
})
let query = PFUser.query()!
query.includeKey("toys")
query.getObjectInBackgroundWithId(PFUser.currentUser()!.objectId!, block: {
(object: PFObject?, error: NSError?) -> Void in
if object != nil {
if let toys = object!["toys"] as? [PFObject] {
// toys!
})
}
})
Related
Im very new with CoreData fetching/display and so far able to save into CoreData from a JSON fetch.
The fetched data is an array of Airport info with only three items; airport_code, access_point and image_url.
I need to add two more values to each fetched item - a lat and lon coordinate which is stored in another CoreData entity with a matching airport_code item/attribute.
Can anyone provide some guidance as to how to create a separate function to query this other CoreData during the loop sequence by using the predicate value of the airport_code? I have attached the code I have so far:
func saveData(context: NSManagedObjectContext){
xArray.forEach { (data) in
let entity = Airports(context: context)
entity.airport_code = data.airport_code
entity.access_points = data.access_points
entity.image_url = data.image_url
entity.lat = getLat()
entity.lon = getLon()
}
do{
try context.save()
print("Success Saving to CoreData: \(xArray.count)")
}
catch{
print("Error Saving to CoreData \(error.localizedDescription)")
}
}
func getLat() -> String {
#FetchRequest(entity: AllAirports.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \AllAirports.airport_code, ascending: true)])
var results: FetchedResults<AllAirports>
//this is where Im lost as to how to query this CoreData to fetch the LON value when there is a match to the data.airport_code in the loop above.
return latResults
}
func getLon() -> String {
#FetchRequest(entity: AllAirports.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \AllAirports.airport_code, ascending: true)])
var results: FetchedResults<AllAirports>
//this is where Im lost as to how to query this CoreData to fetch the LON value when there is a match to the data.airport_code in the loop above.
return lonResults
}
func fetchData(context: NSManagedObjectContext) {
// this function performs a JSON parse and returns the xArray above
….
First you want to take a look at the response here What is the best way to do a fetch request in CoreData? to get an idea how a programmatic fetch request may look like.
As I understand your problem now, you want to write two methods getLat() and getLon() which are going to fetch the coordinates for an airport.
I would recommend to write a single function as shown below which looks up your airport in AllAirports using the given code and returns the tuple with the coordinates found.
func getCoord(airport_code: String) -> (lon: Double, lat: Double)? {
let request: NSFetchRequest<AllAirports> = AllAirports.fetchRequest()
request.predicate = NSPredicate(format: "airport_code == %#", airport_code)
if let result = try? viewContext.fetch(request) {
print("Found \(result.count) airports matching \(airport_code)")
// Just return the first matched airport
if let first = result.first {
return (first.longitude, first.latitude)
}
}
return nil
}
A better solution would be to add an extension to you AllAirports which returns an entry for a given (hopefully unique!) airport_code:
extension AllAirports {
static func airport(byCode airportCode: String, in context: NSManagedObjectContext) -> AllAirports? {
let request: NSFetchRequest<AllAirports> = fetchRequest()
request.predicate = NSPredicate(format: "airport_code == %#", airportCode)
if let result = try? context.fetch(request) {
return result.first
}
return nil
}
}
This could then be used in your code as follows:
func saveData(context: NSManagedObjectContext){
xArray.forEach { (data) in
let entity = Airports(context: context)
entity.airport_code = data.airport_code
entity.access_points = data.access_points
entity.image_url = data.image_url
if let airport = AllAirports.airport(byCode: data.airport_code, in: context) {
entity.lat = airport.latitude
entity.lon = airport.longitude
}
}
func reloadFriendList() {
var query = PFQuery(className:"userFriendClass")
query.whereKey("username", equalTo:user!.username!)
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object in objects! {
self.friendList = object["friends"] as! [String]
print(self.friendList)
self.reloadTableView()
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
}
i want to save object["friends"] that is an array from parse with usernames into
var friendList = [String]()
but i get the error: "fatal error: unexpectedly found nil while unwrapping an Optional value",
when the array is empty which means the user doesn't have any friends yet it works fine when the user have at least 1 or more friends.
You need to have your code ready to handle nil cases and cases where "objects" is an empty array.
If this were my code, I would do something like:
for object in objects {
if let friendList = object["friends"]
{
self.friendList = friendList
} else {
// make sure that your class's `friendList` var is declared as an optional
self.friendList = [String]()
}
}
Since objects is optional and may be nil, you need to unwrap it safely. One way to do that is to use the nil coalescing operator to unwrap it or to substitute an empty array if objects is nil. You can use it again to safely unwrap the list of friends:
for object in objects ?? [] {
self.friendList = (object["friends"] as? [String]) ?? []
You can also use optional binding if let to safely unwrap things:
if let unwrappedObjects = objects {
for object in unwrappedObjects {
if let friends = object["friends"] as? [String] {
self.friendsList = friends
} else {
// no friends :-(
self.friendsList = []
}
}
}
I'm trying to pass data from iPhone -> Watch via Watch Connectivity using background transfer via Application Context method.
iPhone TableViewController
private func configureWCSession() {
session?.delegate = self;
session?.activateSession()
print("Configured WC Session")
}
func getParsePassData () {
let gmtTime = NSDate()
// Query Parse
let query = PFQuery(className: "data")
query.whereKey("dateGame", greaterThanOrEqualTo: gmtTime)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let objectsFromParse = objects as? [PFObject]{
for MatchupObject in objectsFromParse
{
let matchupDict = ["matchupSaved" : MatchupObject]
do {
try self.session?.updateApplicationContext(matchupDict)
print("getParsePassData iPhone")
} catch {
print("error")
}
}
}
}
}
}
I'm getting error twice printed in the log (I have two matchups in Parse so maybe it knows there's two objects and thats why its throwing two errors too?):
Configured WC Session
error
error
So I haven't even gotten to the point where I can print it in the Watch app to see if the matchups passed correctly.
Watch InterfaceController:
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
let matchupWatch = applicationContext["matchupSaved"] as? String
print("Matchups: %#", matchupWatch)
}
Any ideas? Will post any extra code that you need. Thanks!
EDIT 1:
Per EridB answer, I tried adding encoding into getParsePassData
func getParsePassData () {
let gmtTime = NSDate()
// Query Parse
let query = PFQuery(className: "data")
query.whereKey("dateGame", greaterThanOrEqualTo: gmtTime)
query.findObjectsInBackgroundWithBlock { (objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil
{
if let objectsFromParse = objects as? [PFObject]{
for MatchupObject in objectsFromParse
{
let data = NSKeyedArchiver.archivedDataWithRootObject(MatchupObject)
let matchupDict = ["matchupSaved" : data]
do {
try self.session?.updateApplicationContext(matchupDict)
print("getParsePassData iPhone")
} catch {
print("error")
}
}
}
}
}
}
But get this in the log:
-[PFObject encodeWithCoder:]: unrecognized selector sent to instance 0x7fbe80d43f30
*** -[NSKeyedArchiver dealloc]: warning: NSKeyedArchiver deallocated without having had -finishEncoding called on it.
EDIT 2:
Per EridB answer, I also tried just pasting the function into my code:
func sendObjectToWatch(object: NSObject) {
//Archiving
let data = NSKeyedArchiver.archivedDataWithRootObject(MatchupObject)
//Putting it in the dictionary
let matchupDict = ["matchupSaved" : data]
//Send the matchupDict via WCSession
self.session?.updateApplicationContext(matchupDict)
}
But get this error on the first line of the function:
"Use of unresolved identifer MatchupObject"
I'm sure I must not be understanding how to use EridB's answer correctly.
EDIT 3:
NSCoder methods:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
//super.init(coder: aDecoder)
configureWCSession()
// Configure the PFQueryTableView
self.parseClassName = "data"
self.textKey = "matchup"
self.pullToRefreshEnabled = true
self.paginationEnabled = false
}
Error
You are getting that error, because you are putting a NSObject (MatchupObject) which does not conform to NSCoding inside the dictionary that you are going to pass.
From Apple Docs
For most types of transfers, you provide an NSDictionary object with
the data you want to send. The keys and values of your dictionary must
all be property list types, because the data must be serialized and
sent wirelessly. (If you need to include types that are not property
list types, package them in an NSData object or write them to a file
before sending them.) In addition, the dictionaries you send should be
compact and contain only the data you really need. Keeping your
dictionaries small ensures that they are transmitted quickly and do
not consume too much power on both devices.
Details
You need to archive your NSObject's to NSData and then put it in the NSDictionary. If you archive a NSObject which does not conform to NSCoding, the NSData will be nil.
This example greatly shows how to conform a NSObject to NSCoding, and if you implement these things then you just follow the code below:
//Send the dictionary to the watch
func sendObjectToWatch(object: NSObject) {
//Archiving
let data = NSKeyedArchiver.archivedDataWithRootObject(MatchupObject)
//Putting it in the dictionary
let matchupDict = ["matchupSaved" : data]
//Send the matchupDict via WCSession
self.session?.updateApplicationContext(matchupDict)
}
//When receiving object from the other side unarchive it and get the object back
func objectFromData(dictionary: NSDictionary) -> MatchupObject {
//Load the archived object from received dictionary
let data = dictionary["matchupSaved"]
//Deserialize data to MatchupObject
let matchUpObject = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! MatchupObject
return matchUpObject
}
Since you are using Parse, modifying an object maybe cannot be done (I haven't used Parse in a while, so IDK for sure), but from their forum I found this question: https://parse.com/questions/is-there-a-way-to-serialize-a-parse-object-to-a-plain-string-or-a-json-string which can help you solve this problem easier than it looks above :)
(I'm new to programming and totally new to Parse, so simplified explanations are certainly appreciated)
I imagine this is pretty straightforward; I'm just stuck. I have a PFObject saved in Parse.com that contains an array containing strings. I'm trying to set an array in my Swift app with the values in the Parse array.
var query = PFQuery(className:"ParseHat")
query.getObjectInBackgroundWithId("xxxxxxxxxx")
submittedNames = query["theHat"] as [String]
// submittedNames is declared elsewhere in the code. "theHat" is the key where
the array is stored in the PFobject
I get the error 'PFQuery' does not have a member named 'subscript'. I've tried doing a few things I didn't fully understand to the code but have gotten other errors so I'm just posting this as it seems to most closely resemble the method for retrieving objects in the Parse docs.
Try this out...
var query = PFQuery(className: "ParseHat")
query.getObjectInBackgroundWithId("XXXXXXXXX") {
(objects: PFObject?, error: NSError?) -> Void in
if error == nil {
//fetching users
for object in objects{ //looping through returned data
self.resultsArray.append(object.objectForKey("XXXXXXXX") as! String)
//adding the string to var resultsArray = [String]()
}
} else {
println("error :(")
}
}
I have a class in Parse called "WorkoutExercise" with pointers to other objects and i am trying to retrieve the workout exercises with the pointed objects as part of the objects too.
But when i execute the following code, none of the pointed objects show up in the workoutexercise object.
I tried includeKey("Exercise"), but that does not seem to work. Any clues?
Code:
// Return pfobjects casted as exercises for a given workout
func getExercisesForWorkout(workout:Workout!, completion: ([WorkoutExercise]!, NSError!) -> Void ){
var query = PFQuery(className:"WorkoutExercise")
query.orderByAscending ("sequence")
query.whereKey("Workout", equalTo: workout)
query.includeKey("Exercise")
query.includeKey("Workout")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
completion(objects as? [WorkoutExercise], error)
}
}
In Parse:
Result:
[<WorkoutExercise: 0x7feef1c57700, objectId: Q9KeD4WiuH, localId: (null)> {
Workout = "<Workout: 0x7feef1e89150, objectId: 1PQCrl3f8L>";
reps = 10;
sequence = 1;
weight = 20;
}]
Ok, i know the mistake.
You are not supposed to name your pointers with the uppercase.
Changed the pointers to lower case and all is well.