Saving to specific core data object in Today Extension - swift

Working on a part of my iOS project that needs to refer to a specific object that the user selects in the main application that is set through a toggle switch that activates the specified object to be used in a Today Extension to record simple objects created by the user in the Today Extension. I am unsure how to go about doing this specifically. I thought about using NSUSerDefaults as the go to method for specifying that object but this is all entirely new to me. Has anyone gone down this path before on here? Does anyone know a way to refer to the specific object you want to store to in the Today Extension?

You can save a reference to a specific managed object by using the objectID property (an instance of NSManagedObjectID), which can be converted to a URI. The URI can then be converted to NSData, which you can save in user defaults.
To save the reference, get the object's ID as a URI:
let objectIDURI = newManagedObject.objectID.URIRepresentation()
Then convert to NSData and save:
let objectIDURIData = NSKeyedArchiver.archivedDataWithRootObject(objectIDURI)
NSUserDefaults.standardUserDefaults().setObject(objectIDURIData, forKey: "savedID")
To get the object back, load the NSData and convert back to an NSURL (the following is simplified, you'll have to handle optionals properly):
let savedObjectIDURIData = NSUserDefaults.standardUserDefaults().objectForKey("savedID") as? NSData
let savedObjectIDURI = NSKeyedUnarchiver.unarchiveObjectWithData(savedObjectIDURIData!) as? NSURL
Then convert the ID back to an NSManagedObjectID and get the managed object:
let savedObjectID = context.persistentStoreCoordinator?.managedObjectIDForURIRepresentation(savedObjectIDURI!)
let savedObject = context.objectWithID(savedObjectID!)
At this point savedObject is the managed object that was saved above.

Related

How to utilize default value in Root.plist for Swift app settings

I've set some default values for a swift app in the Root.plist file such as this:
My question is: how can I access the default value or utilize it? Once I load my app first time, the value is nil until I manually set it to something. As far as I can see, the only thing this does is visually give it that appearance in the settings app, but doesn't actually have a tangible value associated with it?
Side note: One thing I'd like to do if I have to manually set this is to do something like this:
UserDefaults.standard.set(DEFAULT_VALUE, forKey: "user_toggle_switch")
I'm not sure if there's any way to pull this information. Any clarification on how settings works with defaults values would be superb.
You are confusing the two. That plist is completely different from UserDefaults.
If you want to access the values from the plist, you need to access it
if let path = Bundle.main.path(forResource: "Root", ofType: "plist") {
if let rootDictionary = NSDictionary(contentsOfFile: path) {
// read here
}
}

Swift NSUnarchiver Error

I get the following error when i try to unarchive a custom object
'cannot decode object of class (PhotoList) for key (root); the class may be defined in source code or a library that is not linked'
I currently have a version on the app store (v1.0, have issued an update via version TestFlight (v2.0) and this is where the error happens. The error doesn't happen on the same version builds via Xcode. Nothing has changed (that I can see!) that would have caused this.
Here is the code I have for archiving the object
let data = NSKeyedArchiver.archivedDataWithRootObject(VehicleList.instance)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "archiveName")
And here is the code I have for unarchiving the object
if let data = NSUserDefaults.standardUserDefaults().objectForKey("archiveName") as? NSData {
let photoList : PhotoList = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! PhotoList
}
It turns out the issue was related to multiple targets. It seems when I duplicated targets, I swapped over the original target with another, so it was in fact a different target that couldn't unarchive the data.
So be careful when you create targets and make sure you don't mix them up! Hope this saves someone the amount of time it took me to figure this out!

App Hangs when saving in core data

I'm using swift core data to save user's info within the app but when its saving current users data (name,bio, 3-4 photos) it hangs for 2-3 seconds until it saves the data. What is the best way to avoid this hanging of the app? this is the code I'm using
for photo in photos {
let data = NSData(contentsOfURL: NSURL(string: photo.url!)!)
let newimg = UIImage(data: data!)
newusersImages.addObject(newimg!)
}
mayBeUser.name = currentuser!.objectForKey("name") as! String
let arrayData = NSKeyedArchiver.archivedDataWithRootObject(newusersImages)
let locationData = NSKeyedArchiver.archivedDataWithRootObject(currentuser!.objectForKey("location")!)
mayBeUser.photos = arrayData
mayBeUser.location = locationData
mayBeUser.about = currentuser!.objectForKey("about") as! String
mayBeUser.setValue(currentuser!.objectForKey("age") as! Int, forKey: "age")
mayBeUser.objectId = currentuser!.objectId!
mayBeUser.lastSeen = currentuser!.objectForKey("lastSeen") as! NSDate
try! context.save()
print("Current User Updated")
when the user has 10-15 friends , it takes hangs for a minute to save/update all the info/data of the users.
Here's how I'd do it. Now obviously you don't want the UI to hang, so you'll want to use a managed object context that has a private queue concurrency type. More info on that here. Specifically, look at the part at the end where he details how to create the export function.
Now, since you're saving data to the persistent store in this case, you might not want the user to go around changing stuff while their previous changes are being saved, in this case you might want an interim screen showing the progress of the save.
You (and your app) are having to work hard to save all that photo data into one attribute of your User entity. Instead, restructure your data model:
Create a separate Photo entity for the photos.
Don't store the photo image data in CoreData - just store a string with the URL.
Add a to-many relationship from User to Photo, instead of the photos attribute.
Incidentally, I would avoid using objectId as an attribute name: it's so close to CoreData's objectID it will cause confusion.

Save key-value pair in .GlobalPreferences using Swift

I'm writing a short Mac app (let's call it "myApp") and I need to read from and write to the .GlobalPreferences plist in ~/Library/Preferences/.
For the reading part, I use the following and it's working fine:
let boolValue = NSUserDefaults.standardUserDefaults().boolForKey("aKeyInGlobalPreferences")
However, I'm having some trouble in changing the value for that same key. I tried the following:
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "aKeyInGlobalPreferences")
NSUserDefaults.standardUserDefaults().synchronize()
Sadly, it didn't change the key value in .GlobalPreferences. Instead, it created a new plist com.myName.myApp with the key-value pair in there.
How can I make my app write to .GlobalPreferences instead of creating a new plist?
You need to write specifically into the global domain with setPersistentDomain().
NSUserDefaults.standardUserDefaults().setPersistentDomain(["aKeyInGlobalPreferences":true],
forName: NSGlobalDomain)

How do you save multiple fields to one row or objectId in Parse using Swift?

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.