This is all purely for educational purposes, to help me get a better understanding of how Parse as well as Swift operates.
I would like to make it so a user is only able to like an item once (not being able to hit a button multiple times), as currently, I'm utilizing an anonymous system with Parse.
Would I essentially use an if method with PFUser.CurrentUser() in the likeButton method to halt a user from hitting like again or would I use NSUserDefaults?
I'm not able to post code currently as I'm not near my laptop, however I could later if it helps. Still curious if I could get some info before that however.
Sample code I found on here from a previous question, which essentially implements the same idea.
#IBAction func likeButton(sender: AnyObject) {
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = objectAtIndexPath(hitIndex)
object.incrementKey("count")
object.saveInBackground()
self.tableView.reloadData()
}
Would I call NSUsersDefaults to stunt the user from hitting it more than once?
Instead of calling saveInBackground(), you'd better call the method saveInBackgroundWithBlock: instead. So the strategy is very simple:
First of all, define a 'busy' flag for the object (For example: savingInBackground) and store it wherever you like ( If you are showing 1 item then simply declare a Bool property / If you are showing a list of Item then declare a Dictionary with format ["objectID/Index": Bool]). This flag should be set to true for the item being saved in the background.
Whenever use taps on a Like button
If current item's savingInBackground flag is true, then do nothing
Else:
Set item's savingInBackground to true
Increase Like count and Call saveInBackgroundWithBlock:
In the completion block of saveInBackgroundWithBlock:, set savingInBackground back to false.
I am on train now so I can't write example code, but I hope that it's clear enough to help you to achieve what you want.
Related
I am trying to create an application that will allow a user to cycle through next/previous text document files in a folder, the way photo-viewing apps will often allow next/previous picture view. It seems to me so far that the most effective way to do this is to replace the document in the currently open window. The edited answer to my previous question on the topic suggests that this is indeed possible. I want to be able to use the document architecture for opening and saving; I don't want to have to generalize the framework, I'm trying to keep it simple. I tried to use the code recommended in the previous question as follows:
let prevDocument = windowController.document
let newDocument = Document(contentsOf: newURL, ofType: myDocumentType) // add do-catch
NSDocumentController.shared.addDocument(newDocument);
newDocument.addWindowController(windowController)
prevDocument.close()
But when I do this, the prevDocument.close() command gives me the odd error "Ambiguous reference to member 'close()'". Another thread tells me that "This kind of error appears when there's more than one variable/method with the same name". OK, but which ones, and how do I fix it? Underneath the "ambiguous reference" error I get two messages: "Found this candidate (Foundation.Stream)" and "Found this candidate (AppKit.NSBezierPath)". A brief look at the docs for Foundation.Stream and NSBezierPath suggests that Foundation.Stream and not NSBezierPath is what I'm trying to work with, but I have no idea how to tell the system that (or why NSBezierPath would be involved in the first place).
The document property of an NSWindowController has type AnyObject?, hence why there's no close method and the compiler struggles to figure out what type of object it could be, such that it could have a close method.
Clicking through to the documentation of the document property in Xcode (ctrl-cmd click) shows a comment that the document property is usually of type NSDocument, which does have a close method. Typecasting is worth a try:
guard let prevDocument = windowController.document as? NSDocument else {
// ...
}
// Rest of your code
Alternatively, if you can guarantee that only one document at a time will be managed by your application:
guard let previousDocument = NSDocumentController.shared.documents.first else {
// ...
return
}
// Rest of your code
I'm getting the above layout from Parse. What I want is vid 1, 2, and 3 to be in the same row; associated with same object ID. How can I do this? My ultimate goal is to easily retrieve 10 video dictionary's per user on a table view. Will any of this make a difference? I'm saving like this.....
videoDict = ["id":videoId, "title":vidTitleText, "description":vidDescription, "image":vidIMG]
let videoSave = PFObject(className:"UserVideos")
videoSave["user"] = PFUser.currentUser()!.username
videoSave["userObjectId"] = PFUser.currentUser()!.objectId
videoSave["vid\(saveValueLBL.text!)"] = videoDict
videoSave.saveInBackgroundWithBlock { (success, error ) -> Void in
if success == true
{
print("Succesfull")
}
}
Where you have let videoSave = PFObject(className:"UserVideos") you are creating a new videoSave object each time. you need to move that outside of your loop so that you're accessing the same object each time instead of making a new one. However, the way you currently have your code set up you'll run into problems, because each object can only have one synchronous action called on it (in this case, your save), so the second, third, maybe even all the way to the 10th save may not occur because it needs the first one to finish before the next one can be called. You need to create your object outside your loop, run the loop, then call the save at the end to make sure it isn't saving until all of the data is updated.
If this isn't all inside of a loop, you need to get the videoSave object back each time, perhaps by storing it onto your user, and then fetching it from the user object.
Put everything outside the loop and keep just the code below inside the loop:
videoDict = ["id":videoId, "title":vidTitleText, "description":vidDescription, "image":vidIMG]
videoSave["vid\(saveValueLBL.text!)"] = videoDict
From what I understand although I saved information in Parse as a Dictionary this is in fact an invalid data type. That's why I'm having trouble retrieving because Parse doesn't recognize the info.
I have in two views that are connected to the menu, in one view I have a text field to enter your e-mail and I want to take that textfield text (that is the email address) to the other view, but I think I need to save that data to the phone so later when the user start the app again he/she will never have to type that address again, and then use that email address in the other view that will send an email to that address. Here is the code that I have:
let configCorreo = ConfigurarCorreo()
let mail = configCorreo.textField.text!
And then I set the recipient using that mail the user typed in the other viewController:
let mc: MFMailComposeViewController = MFMailComposeViewController()
mc.mailComposeDelegate = self
mc.setToRecipients(["\(mail)"])
But I get an error saying: "fatal error: unexpectedly found nil while unwrapping an Optional value"
PrepareForSegue, NSUserDefault and Singleton
You have a few possible options to pass your data to other views depending how you want that data to be handled, I will explain each for you and you can choose which one best fit your need.
prepareForSegue: Method
I recommend this method if you want to hold your data for 1 segue transition, it's a good cause to pass this again to another view afterward you need to create another prepareForSegue within the new view. here is an example on how to do this:
First, you create 2 variables in both views, 1 to send (currentViewController.swift) and 1 to receive (toViewyourGoingController.swift).
currentViewController.swift var dataToSend: AnyObject?
ViewYourGoingController.swift var dataToReceive: AnyObject?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
//Check your segue, this way you can transfer different data to different view. also make sure the identifier match your segue.
if segue.identifier == "toViewYourGoing" {
//Initial your second view data control
let ExchangeViewData = segue.destinationViewController as! toViewyourGoingController
//Send your data with segue
ExchangeViewData.dataToReceive = dataToSend
}
}
NSUserDefault
Now this method is good if you want to keep your data live as long as the app is installed, once the app is removed this will reset automatically. You also have the option to update the value of the key if you wish, here is how you do NSUserDefault:
I always like to register my NSUserDeafult to default setting, a lot of people just continue with the second step without registering.
Register NSUserDefault in AppDelgate.swift
NSUserDefaults.standardUserDefaults().registerDefaults(["valueName": AnyObject])
Set Value to your NSUserDefault, this depends on what type of data you're storing, should match the one with your registration if you did register. (Example of Boolean data type below)
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "valueName") //Bool Data Type
Available types:
Make sure you synchronize once you set the value to the NSUserDefault, this way it will update instantly, otherwise it will update when it get a chance.
NSUserDefaults.standardUserDefaults().synchronize()
Receive Value: this will receive boolean value since we set boolean and register boolean.
let Variable: Bool! = NSUserDefaults.standardUserDefaults().boolForKey("valueName")
Singleton
Now singleton is basically a global variable that you can use them in any views, but some developers experience some bugs and difficulties, use it at your own risk, I recommend this method when you're definite that you will use that data a lot (STILL RISKY), but this method is like goddess of data handling :).
Create a NSObject subclass and call it DataManager.swift (I call it data manager cause it handle data.) as following:
import UIKit
class DataManager: NSObject {
//Store Data Globally
static var someData: Boo! //This Boolean, you can choose whatever you want.
}
the static is what keep your data live.
Now you can store and receive someData from anywhere like you handle any data type like this.
//Store
DataManager.someData = true
//Receive
print(DataManager.someData)
Challenges:
You can also use
Keychain
Sergey Kargopolov will walk you through how to use a third party to use swift keychain. Otherwise, you can take even harder challenge and create one yourself :P .
Key-Value Data in iCloud
Use CoreData to save it in a database, then you can perform a fetch request to get it back out of the database.
Very simple look it up here:
https://www.youtube.com/watch?v=3IDfgATVqHw
I have a NSDocument-based app, and I'd like my window positions to be saved and restored when re-opening documents. Apple's documentation on this is pretty sparse, but what I've been able to piece together is that at some point, something in the app needs to call NSWindow.setFrameUsingName() and NSWindow.setFrameAutosaveName().
What I haven't quite figured out is what point this needs to happen at, and what things need to do this. For example, this doesn't work at all:
// In my NSDocument class
override func windowControllerDidLoadNib(aController: NSWindowController) {
super.windowControllerDidLoadNib(aController)
// Add any code here that needs to be executed once the windowController has loaded the document's window.
aController.window?.setFrameUsingName("MainWindow")
aController.window?.setFrameAutosaveName("MainWindow")
}
I've read various different pieces of documentation or forum answers that point to awakeFromNib() to be another area to do this, but I can't get that to work either.
I'm also confused / worried that this is somehow being affected by Auto Layout or something I've done wrong in Interface Builder - for example, this is how my window is set up in IB:
I don't particularly want my window centered, but the other options seem to lock it in place in fixed horizontal or fixed vertical positions, which I also don't really want. A side effect of having my window be centered is that my document windows no longer cascade, which I neither want nor can seem to stop from happening (note that windowController.shouldCascadeWindows = true isn't helping either).
So - what's going on here? I'm finding knowledge on this topic to be particularly unclear or misleading, and likely out of date for Cocoa development 2015, so a modern refresher on this would be great.
Step 1: In IB, give the window an autosave name.
Step 2: There is no step 2.
The easiest place to set the name of the autosave name for the window frame is probably in your implementation of NSDocument.
If you override makeWindowControllers() as part of the implementation, you are creating the window controller(s) manually, and thus can set the name there:
override func makeWindowControllers() {
let storyboard = NSStoryboard(name: NSStoryboard.Name("MyDocumentStoryboard"), bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("MyDocument Window Controller")) as! MyDocumentWindowController
// this assumes you have a stored property or a computed property `someUniqueIdentifier` that is as unique as possible for your document
// and that you store on disk together with the rest of the document properties
windowController.windowFrameAutosaveName = NSWindow.FrameAutosaveName(rawValue: someUniqueIdentifier)
self.addWindowController(windowController)
}
If you instantiate your document window by overriding windowNibName, you should instead override the method windowControllerDidLoadNib(_:), to set the autosave name on the window. I have not tested this code, but I assume it will work:
func windowControllerDidLoadNib(_ windowController: NSWindowController) {
// this assumes you have a stored property or a computed property `someUniqueIdentifier` that is as unique as possible for your document
// and that you store on disk together with the rest of the document properties
windowController.windowFrameAutosaveName = NSWindow.FrameAutosaveName(rawValue: someUniqueIdentifier)
}
I am trying to use swift's shouldPerformSegueWithIdentifier() method, but it accepts 2 arguments. These are (identifier: String!, sender:AnyObject)
My main goal is to execute the code when pressing a login button in my storyboard, and depending of a series of checks return TRUE or FALSE, depending if whether the correct username and password were provided. So here are my questions:
What am I supposed to use as the identifier? Apple's documentation it explains that the identifier is a string that identifies the triggered segue. So suppose that my segue had the name of loginSegueProcess. How could I use it in my ViewController tat is assigned to my UIView? The thing is that I declare the method in my code and it requires me to specify both arguments (identifier & sender). How could I provide the arguments?
Will this method actually fulfill my needs? By that I mean if it will indeed stop the segue transition whenever my Login button is clicked and depending on whether the correct credentials were provided it is going to take you to the next View or it will show, say for example, an AlertView.
Finally, I was thinking that the performSegueWithIdentifier(args) method would help me as well. Does anybody know the difference between them?
Thanks a lot in advance!
isn't it what you want to do?
override func shouldPerformSegueWithIdentifier(identifier: String!, sender: AnyObject!) -> Bool {
if identifier == "LoginSuccessSegue" { // you define it in the storyboard (click on the segue, then Attributes' inspector > Identifier
var segueShouldOccur = /** do whatever you need to set this var to true or false */
if !segueShouldOccur {
println("*** NOPE, segue wont occur")
return false
}
else {
println("*** YEP, segue will occur")
}
}
// by default, transition
return true
}
You may not invoke shouldPerformSegueWithIdentifier() method by yourself. It will be automatically called just before transition to the next view giving a chance to determine wether the transition should take place or. You may conditionally return YES/NO from this method. If your condition does't involve any sever call,a simple logical checking this method will be enough for you.
performSegueWithIdentifier() is used to invoke a segue programmatically. Consider the above case with a network call, you may return NO from shouldPerformSegueWithIdentifier() initially since authentication is going on. After getting the response from server if it success you can call the segue to execute with performSegueWithIdentifier (Here the identifier is the ID you have given in the storyboard). Before make sure you are supposed to return YES from shouldPerformSegueWithIdentifier().
Now a third case if your segue is connecting from the login button(You have to connect it from the controller itself). The checking of shouldPerformSegueWithIdentifier is no more required. You can just call the segue with performSegueWithIdentifier() after getting the success response from server