How can i extract data from an anyObject in Swift - ios

I'm using the TwitterKit SDK and listing a group of Tweets. The function has an error handler that stores any tweets that have been removed by the user and thus not shown. I am attempting to retrieve these particular ID's from the NSError user info dictionary. I can find them but end up with an anyObject.
This code is getting the tweet objects and filtering out the bad ones...
// load tweets with guest login
Twitter.sharedInstance().logInGuestWithCompletion {
(session: TWTRGuestSession!, error: NSError!) in
// Find the tweets with the tweetIDs
Twitter.sharedInstance().APIClient.loadTweetsWithIDs(tweetIDs) {
(twttrs, error) - > Void in
// If there are tweets do something magical
if ((twttrs) != nil) {
// Loop through tweets and do something
for i in twttrs {
// Append the Tweet to the Tweets to display in the table view.
self.tweetsArray.append(i as TWTRTweet)
}
} else {
println(error)
}
println(error)
if let fails: AnyObject = error.userInfo?["TweetsNotLoaded"] {
println(fails)
}
}
}
The println(error) dump is...
Error Domain=TWTRErrorDomain Code=4 "Failed to fetch one or more of the following tweet IDs: 480705559465713666, 489783592151965697." UserInfo=0x8051ab80 {TweetsNotLoaded=(
480705559465713666,
489783592151965697
), NSLocalizedDescription=Failed to fetch one or more of the following tweet IDs: 480705559465713666, 489783592151965697.}
and refining the results from the error "error.userInfo?["TweetsNotLoaded"]" I can end up with...
(
480705559465713666,
489783592151965697
)
My question is, is there a better way to get this data?
If not, how am I able to convert the (data, data) anyObject into an array of [data, data] ?

Best guess is that TweetsNotLoaded is an NSArray of NSNumber (or maybe NSString, not sure how they're coding/storing message ids), so you can cast the result and go from there:
if let tweetsNotLoaded = error.userInfo?["TweetsNotLoaded"] as? [String] {
// here tweets not loaded will be [String], deal with them however
...
}
If that doesn't work, assume they're longs and use:
if let tweetsNotLoaded = error.userInfo?["TweetsNotLoaded"] as? [Int64] {
// here tweets not loaded will be [Int64], deal with them however
...
}

Related

CloudKit Sharing

I am having trouble understanding some of the CloudKit sharing concepts and the WWDC 2016 "What's new in CloudKit" video doesn't appear to explain everything that is required to allow users to share and access shared records.
I have successfully created an app that allows the user to create and edit a record in their private database.
I have also been able to create a Share record and share this using the provided sharing UIController. This can be successfully received and accepted by the participant user but I can't figure out how to query and display this shared record.
The app creates a "MainZone" in the users private database and then creates a CKRecord in this "MainZone". I then create and save a CKShare record and use this to display the UICloudSharingController.
How do I query the sharedDatabase in order to access this record ? I have tried using the same query as is used in the privateDatabase but get the following error:
"ShareDB can't be used to access local zone"
EDIT
I found the problem - I needed to process the accepted records in the AppDelegate. Now they appear in the CloudKit dashboard but I am still unable to query them. It seems I may need to fetch the sharedDatabase "MainZone" in order to query them.
Dude, I got it: First you need to get the CKRecordZone of that Shared Record. You do it by doing the following:
let sharedData = CKContainer.default().sharedCloudDatabase
sharedData.fetchAllRecordZones { (recordZone, error) in
if error != nil {
print(error?.localizedDescription)
}
if let recordZones = recordZone {
// Here you'll have an array of CKRecordZone that is in your SharedDB!
}
}
Now, with that array in hand, all you have to do is fetch normally:
func showData(id: CKRecordZoneID) {
ctUsers = [CKRecord]()
let sharedData = CKContainer.default().sharedCloudDatabase
let predicate = NSPredicate(format: "TRUEPREDICATE")
let query = CKQuery(recordType: "Elder", predicate: predicate)
sharedData.perform(query, inZoneWith: id) { results, error in
if let error = error {
DispatchQueue.main.async {
print("Cloud Query Error - Fetch Establishments: \(error)")
}
return
}
if let users = results {
print(results)
self.ctUsers = users
print("\nHow many shares in cloud: \(self.ctUsers.count)\n")
if self.ctUsers.count != 0 {
// Here you'll your Shared CKRecords!
}
else {
print("No shares in SharedDB\n")
}
}
}
}
I didn't understand quite well when you want to get those informations. I'm with the same problem as you, but I only can get the shared data by clicking the URL... To do that you'll need two functions. First one in AppDelegate:
func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {
let acceptSharesOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
acceptSharesOperation.perShareCompletionBlock = {
metadata, share, error in
if error != nil {
print(error?.localizedDescription)
} else {
let viewController: ViewController = self.window?.rootViewController as! ViewController
viewController.fetchShare(cloudKitShareMetadata)
}
}
CKContainer(identifier: cloudKitShareMetadata.containerIdentifier).add(acceptSharesOperation)
}
in ViewConroller you have the function that will fetch this MetaData:
func fetchShare(_ metadata: CKShareMetadata) {
let operation = CKFetchRecordsOperation(recordIDs: [metadata.rootRecordID])
operation.perRecordCompletionBlock = { record, _, error in
if error != nil {
print(error?.localizedDescription)
}
if record != nil {
DispatchQueue.main.async() {
self.currentRecord = record
//now you have your Shared Record
}
}
}
operation.fetchRecordsCompletionBlock = { _, error in
if error != nil {
print(error?.localizedDescription)
}
}
CKContainer.default().sharedCloudDatabase.add(operation)
}
As I said before, I'm now trying to fetch the ShareDB without accessing the URL. I don't want to depend on the link once I already accepted the share. Hope this helps you!

Parse query on array of pointers only retrieving first object

In the code below, self.arrayOfLikers is an array of Pointers to Parse User Objects. Which looks like this on Parse:
var query = PFQuery(className: "Item")
query.includeKey("likedBy")
query.getObjectInBackgroundWithId(theObjectIdOfTheItem!) { (returnedItem, error) in
if error != nil {
print(error)
}
else {
returnedItem?.fetchInBackgroundWithBlock({ (fetchedReturnedItem, error) in
if error != nil {
print(error)
}
else {
dispatch_async(dispatch_get_main_queue(),{
self.arrayOfLikers = fetchedReturnedItem!["likedBy"] as! [PFUser]
print("about to print the arrayOfLikers")
print(self.arrayOfLikers)
self.tableView.reloadData()
})
}
})
}
}
When I print my self.arrayOfLikers I can see that the first index contains a correctly retrieved PFUser object. Below is a print statement from Xcode:
In another viewController, I am storing these pointers when the user taps the "like" button. This is how I am storing those pointers:
Creation of the userPointer:
let userPointer = PFObject(outDataWithClassName: "User", objectId: user?.objectId)
(oddly, the Parse docs use withoutDataWithClassName, but when I put this in, Xcode keeps making me change it outDataWithClassName, and it compiles, so I guess this is correct?)
Storing the userPointer: (please note, theItemObject is an object set by a previous VC):
theItemObject!.addObject(userPointer, forKey: "likedBy")
Consequently, Parse via Xcode gives me this error:
2016-03-30 23:38:47.749 Trashly[5482:1512409] [Error]: object not found for get (Code: 101, Version: 1.12.0)
Optional(Error Domain=Parse Code=101 "object not found for get" UserInfo={code=101, temporary=0, error=object not found for get, NSLocalizedDescription=object not found for get})
Any ideas why my query is not getting any user objects but the first one?

How to create pointer to Parse PFUser from facebook ID

I'm doing a Facebook graph call to get friends of the user that are using my app. I get the facebook ID of the user's friends back from the graph call. Below is what I'm attempting to obtain from Parse with that ID, but's it's not getting all the users back, I believe since its an async call. How can I save an array of pointers of the user's fb friends that are using the app? Thanks in advance!!
graphConnection.addRequest(requestFriends, completionHandler: { (connection: FBSDKGraphRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if result.objectForKey("friends") != nil {
// parsing dictionary to get results
let firstDict = result.objectForKey("friends")
let dataArray = firstDict!.objectForKey("data")
let myFriendsUsingTheAppCount = dataArray!.count
print("\(myFriendsUsingTheAppCount)")
let friendsArray:NSMutableArray = []
for var i = 0; i < dataArray!.count; i++ {
let friend = dataArray![i]
let friendFbObjectID = friend.objectForKey("id")!
let query = PFUser.query()
query!.whereKey("facebookID", equalTo: friendFbObjectID)
query!.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
// this is where I was going to save this specific user as a pointer
} else {
// some error
}
})
// this is saving just the friend's name/fb id to an array, but I want an array of pointers to the PFUser
friendsArray.addObject(friend)
}
} else {
// fb friends is nil
print("FB friends came back nil")
}
})
This could be simplified into a single query by using whereKey:containedIn:
let facebookIDs = dataArray.map { $0.objectForKey("id")! }
let query = PFUser.query()!
query.whereKey("facebookID", containedIn: facebookIDs)
The query will now contain all users whose facebook id is in the array passed to the query.
(swift syntax may be incorrect did not double check)

Parse: How can I update all rows with the same key value

How can I update all rows with the same key value in a Parse class?
As my code, I think Parse would cost a lot of my money if there are 100 objects per user.
Is there any other way to do that?
let username = PFUser.currentUser()?.username
let objectQuery = PFQuery(className: "FriendList")
objectQuery.whereKey("username", equalTo: username!)
objectQuery.findObjectsInBackgroundWithBlock { (friendList:[AnyObject]?, error:NSError?) -> Void in
if let friendList = friendList as? [PFObject] {
for myInfo in friendList {
myInfo["contact"] = contact
myInfo["portrait"] = PFFile(data: portraitNSData)
myInfo["company"] = company
myInfo["position"] = position
myInfo.save() // create a request?
}
}
}
Form array of needed objects and than just
PFObject.saveAllInBackground(objectsArray)
Parse has a functions to object see to save many objects (see documentation here)
PFObject.saveAllInBackground(array, block: {
(succeeded: Bool, error: NSError!) -> Void in
if (error != nil) {
println("Error saving: \(error)")
}
})
Unfortunately the documentation is not update to Swift but you can see a list of the function in objective-c
saveAll:
saveAll:error:
saveAllInBackground:
saveAllInBackground:block:
It would be best to create a CloudCode function. Then call that function from iOS. It's not difficult, see their documentation here: Parse.com

How to prevent duplicate entry on parse?

I' trying to save song info to parse, but if the song already exist in parse I want my code just do nothing.
I've tried this code below:
var Music = PFObject(className:"Musics")
var query = PFQuery(className:"Musics")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
var songTitle = object.objectForKey("songTitle") as? String
if songTitle != title {
Music["createdBy"] = PFUser.currentUser()
Music["songTitle"] = title
Music["albumCover"] = imageFile
Music["songArtist"] = artist
Music.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
println("succeed")
} else {
// There was a problem, check error.description
println("error jeh")
}
}
}else{
println("song already exist")
}
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
the code above give below result on log:
Successfully retrieved 4 scores.
song already exist
Successfully retrieved 4 scores.
song already exist
Successfully retrieved 4 scores.
song already exist
Successfully retrieved 4 scores.
song already exist
succeed
succeed
succeed
succeed
succeed
succeed
succeed
succeed
succeed
succeed
succeed
succeed
Why my for loop , looping more than the Objects.count? and how can I prevent dupiclate entry on parse?
give me any advice, doesn't matter in obj c or swift
I suggest to implement a simple beforeSave trigger, on Parse Cloud code, in order to check if the new entry song already exist (basically you're going to make one or more field uniques. For example:
Parse.Cloud.beforeSave("Musics", function(request, response) {
var newEntrySong = request.object;
var querySongs = new Parse.Query("Musics");
querySongs.equalTo("title", newEntrySong.get("title"));
querySongs.equalTo("description", newEntrySong.get("description"));
// this could be a sort of signature for your song, to make more unique (skipping spaces and new lines for example)
querySongs.equalTo("md5Title", newEntrySong.get("md5Title"));
querySongs.first({
success: function(temp) {
response.error({errorCode:123,errorMsg:"Song already exist!"});
},
error: function(error) {
response.success();
}
});
});
Hope it helps.

Resources