Trying to load Parse PFFiles into UITableView from query - xcode

I'm a new, in training, swift programmer and have run into an issue with an app that I'm trying to put together.
I've searched through the forum and have found really helpful information but haven't been able to resolve my issue based on the results unfortunately.
I have an app set up to upload user images as PFFiles to parse but can't seem to have them download to the tableview in my table view controller.
A majority of the code I have tried to implement hasn't caused any errors but also hasn't downloaded the desired images.
I apologize if this is a very basic issue but I've exhausted my researching outlets.
I have the images uploading to a single class called Post and want to be able to pull images based on the current user's ID.
I've tried querying the information but always have an error returned that says that the system was unable to find anything for the query. I'm not sure if there is a better way to upload and classify the information on parse or if I'm not coding the call back correctly. I'm essentially wanting to be able to retrieve the user's uploaded images and descriptions and display them on an "Account" page.
I'm working in Xcode 7.2 in Swift 2.

So there are three things we have to care about:
1. structure of post
It would be better if you store the User in a pointer. A pointer is easier to save and retrieve. (Pointers is a way to present one to many Relations, but in our case this means One to One).
So in the data browser(parse.com) replace the column "userID" with "user" type: Pointer, target class: User.
Then in xCode
post["user"] = PFUser.currentUser()
2. Get All posts of a User
Add this code:
let query = PFQuery(className: "Post")
query.whereKey("user", equalTo: PFUser.currentUser()!)
query.getFirstObjectInBackgroundWithBlock { (obj: PFObject?, err: NSError?) -> Void in
if err != nil{
// DO something with the objets:
myArray = obj
}
}
3. Get the image
Lets say you have a ImageView like imageView
let file: PFFile = post["imageFile"] as! PFFile
file.getDataInBackgroundWithBlock { (data: NSData?, err: NSError?) -> Void in
if data != nil{
imageView.image = UIImage(data: data!)
}
}
Now you're done:)
But just a little thing to improve your code:
You don't need to dismiss the Alert after OK is pressed because if you set style to .Cancel this is automatically done! So change
alert.addAction((UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil) })))
to
alert.addAction((UIAlertAction(title: "OK", style: .Cancel, handler: nil)))

Related

How do I implement drag-and-drop from Photos to my Cocoa app with full quality?

In my drag destination view (NSView subclass), I have implemented:
override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool {
// check for Photos promises
var gotPromised = false
draggingInfo.enumerateDraggingItems(options: [], for: self, classes: [NSFilePromiseReceiver.self], searchOptions: [:], using: {(draggingItem, idx, stop) in
let filePromiseReceiver = draggingItem.item
print("got a file promise receiver: \(filePromiseReceiver)")
gotPromised = true
// Use filePromiseReceiver here for your task.
})
return gotPromised
}
When I run this and drag something over from Photos.app, I get this warning:
How do I fix my drag destination to not have this warning? I would like to get full-quality photos into my app.
Well. Figured it out myself after some investigation. You need to do multiple things (register for correct types, enumerate the items correctly etc), and the warning is not there if you just implement the promise receiving completely and correctly. Apple has provided an example project which implements everything correctly.

iOS Swift 3 Deleting a row from TableView Using Parse

I am building an iOS app and I am trying to delete a row from the UITableView. I am also using Parse as the mobile-backend of the app. Here is code for the delete method:
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
let query = PFQuery(className: "Receipts")
let currReceipt = receipts[indexPath.row]
query.whereKey("objectId", equalTo: currReceipt.objectId!)
query.findObjectsInBackground(block: { (objects, error) in
if error != nil {
self.createAlert(title: "Oops! Something went wrong number 1!", message: "We could not delete the receipt")
print("THERE WAS AN ERROR DELETING THE OBJECT")
}else{
for object in objects!{
self.receipts.remove(at: indexPath.row)
object.deleteInBackground()
self.table.reloadData()
}
}
})
}
}
Just to clarify, there will only be one receipt in the database with any given "objectId", so the query.findObjectsInBackground should only be returning a single object.
When I try to delete a row in the simulator, I get the error "Object not found" even though I can see that the object exists in the database. What am I doing wrong?
UPDATE
Found my solution after a long time looking. For anyone interested, it had to do with the default ACL values for Read and Write permissions. Here is the link to the answer: Parse weird bug in Swift that causes ACL write permissions to change to an objectId
It might be a typo, but why do you have ! in this line?
query.whereKey("objectId", equalTo: currReceipt.objectId!)

CloudKit Get actual Object from Record ID in OSX

I am trying to use CKSubscription to subscribe to changes. I am following Apple's docs which seems to be very general and incomplete.
Link to Apple Doc
I have got the point of getting Record ID sent to my app via the didReceiveRemoteNotification method in AppDelegate and I have got my Record Id: using this code:
func application(application: NSApplication, didReceiveRemoteNotification userInfo: [String : AnyObject]) {
let cknNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String:NSObject])
if cknNotification.notificationType == .Query,
let queryNotification = cknNotification as? CKQueryNotification {
let recordId = queryNotification.recordID
print(recordId)
}
How do I convert the CKNotification into the Actual Object I stores in Cloudkit? Do I need to perform another fetch or is the data contained in the CKNotification that I just need to Cast.
Looks like I worked it out.
The Notification only lets you know that they have changed. You need to do the work to pull the new records and update them. ( Makes sense really)
privateCloudDatabase().fetchRecordWithID(recordId!, completionHandler: { (recordfetched, error) in
//Check the Record Type if multiple.. I only have one type.
let myRecordType = recordfetched?.recordType
let myObject = mySuperObject(record: recordfetched!)
print("done")
})
// I should probably add more optional checking - but above just illustrates the solution and how simple it is to get.

How to associate existing file type for Mac OS application in XCode 7?

I am making a simple viewer for jpeg pictures in Xcode 7, and trying to associate Jpeg file type with my application. This is a Cocoa application for OS X in Swift that uses storyboards, and is not a document-based application.
Tutorials that I found online suggest going through Info tab, adding a new document type there. Now that's where things start to look different from those in tutorials: in them there is only 3 field to fill out (Name, Types, Icon), but I have many more. I tried to experiment around and here is what I put in fields that I got:
Name: JPEG image
Class: left it blank
Extensions: .jpg, .jpeg, .JPEG, .JPG
Icon: left it blank
Identifier: public.jpeg
Role: Viewer
Mime types: image/jpeg
"Document is distributed as a bundle" is partially checked by default; I just left it as is.
Did not touch additional document type properties either.
As a result I got my application showing in a list in "Open with" when I secondary-click a Jpeg file along with other installed applications, but once I try to open it that way, I get a popup saying The document "picture.jpg" could not be opened. MyApp cannot open files in the "JPEG image" format.
What am I doing wrong?
The Class field is compulsory, and you have to have corresponding implementation.
Try:
Class: $(PRODUCT_MODULE_NAME).Document
And then add the Document.swift:
import Cocoa
class Document : NSDocument
{
override init() {
super.init()
// Add your subclass-specific initialization here.
}
override class func autosavesInPlace() -> Bool {
return true
}
override func makeWindowControllers() {
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: "Document Window Controller") as! NSWindowController
self.addWindowController(windowController)
}
override func data(ofType typeName: String) throws -> Data {
// Insert code here to write your document to data of the specified type. If outError != nil, ensure that you create and set an appropriate error when returning nil.
// You can also choose to override fileWrapperOfType:error:, writeToURL:ofType:error:, or writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
override func read(from data: Data, ofType typeName: String) throws {
// Insert code here to read your document from the given data of the specified type. If outError != nil, ensure that you create and set an appropriate error when returning false.
// You can also choose to override readFromFileWrapper:ofType:error: or readFromURL:ofType:error: instead.
// If you override either of these, you should also override -isEntireFileLoaded to return false if the contents are lazily loaded.
throw NSError(domain: NSOSStatusErrorDomain, code: unimpErr, userInfo: nil)
}
}
^ Modify as you need.
Also, you specified the role "Viewer", means you can open it with the space bar, and not the double click (open) - which is the role "Edit" right?

'queryForTable' cannot return 'nil' In Swift

I am using PFQueryTableViewController as part of ParseUI to load a table of objects based on the currentUser's geolocation. I have seen several other (older) forum posts (like here) detailing that the queryForTable function should return nil if a value like currentLocation:PFGeoPoint? is nil. Then wait on the background process in viewDidLoad to get the PFGeoPoint and loadObjects(), thus calling the queryForTable function again.
I am seeing others say that Swift's ParseUI libraries may not have a queryForTable function that allows nil returns.
var currentLocation:PFGeoPoint? = nil
override func viewDidLoad() {
if currentLocation == nil {
PFGeoPoint.geoPointForCurrentLocationInBackground {
(geoPoint: PFGeoPoint?, error: NSError?) -> Void in
if error == nil {
print("Got the user's current location!")
self.currentLocation = geoPoint
print("Reloading table using user's location")
self.loadObjects()
}
}
}
}
override func queryForTable() -> PFQuery {
if self.currentLocation != nil {
print("Generating query based on user's location!")
let query = PFQuery(className:self.parseClassName!)
query.whereKey("taskLocation", nearGeoPoint: self.currentLocation!, withinMiles: 50)
if(self.objects?.count == 0)
{
query.cachePolicy = PFCachePolicy.CacheThenNetwork
}
return query
}
print("User's current location is not known")
return nil
}
Obviously, this fails to build because the function is not (note the question mark):
override func queryForTable() -> PFQuery? {
...
}
My attempted workaround was to return PFQuery() instead of nil, but I believe it returns after the viewDidLoad self.loadObjects(). The behavior I see is a momentary flash of a table with cell results, and then an empty table.
Here is a link to the Google Group discussion about this very issue, with Hector Ramos saying that it works with Objective-C but not Swift (...yet?). I'm running the latest ParseUI as of this posting (1.1.6).
Is there an option yet to do this in Swift? If not, when?
If this isn't an option, what are some workarounds people have tried successfully?
I actually figured this issue out - nothing to do with the code itself. Apparently, when you set a fake location on the iOS Simulator, it also enforces that different location when you use a real iOS device plugged in. My results were not showing up because there legitimately wasn't an object with a PFGeoPoint near my location (because it thought I was in London!)
Anyway, moral of the story is to make sure you always know your preset location in both Simulator and physical iOS devices.
PS - The code above does work when my location is set correctly.

Resources