Who is ultimately responsible for setting URLResourceValues for URLs on disk? - ios

Who is ultimately responsible for setting URLResourceValues for URL objects that go on disk? When I create a file on disk, creationDate and contentAccessDate are automatically set for me, but contentAccessDate doesn't appear to ever change on its own when I access their paths. I can update the values manually with setResourceValues() but am I supposed to? And why does creationDate have a setter? Shouldn't that value strictly be set by the system?
Quick links:
https://developer.apple.com/documentation/foundation/urlresourcevalues
https://developer.apple.com/documentation/foundation/urlresourcevalues/1779762-contentaccessdate

Related

What are ways to store complex dynamic objects locally (iOS, swift)?

I have iOS app that takes data from the server as json and then serializes them into objects of different types. Types can be complicated, can contain subtypes, can inherit, so there is no any limitations. Another thing that makes everything even more complicated is some of types are stored as AnyObject? and only in run time they are being serialized into real types accordingly to the specific rules. Something like that:
class A {
var typeName: String?
var b: AnyObject?
}
Then when it's serialized it can be done something like that:
if let someClass = NSClassFromString(typeName) as? SomeGenericType.Type{
b = someClass.init()
}
Also querying should be done on all the data. Currently I'm trying to store all of them locally, then load into memory and query there from the code. I'm using User defaults, but they have some limitations, also I needed to provide custom coding to make it work, and each time when I add a new field it turned out that I missed something in coding and nothing works. So it's pain.
Ideally I would just do some magic command and all the objects are sent to local storage no matter how complicated they are. The same to extract them from this storage. Also, user change data so I can't just store primary Json. And I don't want to covert objects back to Jason as for it's pain too.
Any suggestions?
If you want to use sqlite then You can store whole object in one row! I means you can create table with 2 columns one is id and second is your dataobject(it's data type should be blob). Then convert your whole object into data. Then store in sqlite table and retrieve it as data then convert it to object when want to use. By this way your object will remains in same format as you asked
Firebase while meant for online synching and storage can also cache everything locally in case you are offline and perform query's against the local cache. It uses JSON.
CouchDB also has a mobile version for iOS.
Both of those are over kill if your dataset is small; you can just store it as a text file and read the JSON back in. See performance characteristics here. The graph is for a 7MB file so if you are significantly less than that your load time may be minimal.
NSKeyedArchiver.archivedData(withRootObject:) is great for storing custom objects as Data objects. The only thing you need to do to be able to use this is to make your custom objects conform to NSCoding. A great example can be found here:
Save custom objects into NSUserDefaults
Once you have the Data version of the object, it can easily be stored in UserDefaults, as a property in CoreData, or even in the app's keychain entries. Depending on your use case, sensitivity of data, and how much data you intend to store, you might want to use any number of storage methods. NSKeyedArchiver.archivedData(withRootObject:) allows you to pretty much use any of them.

when to use globalIdField

As far as I can tell, relay relies on nodeDefitions for queries when variables are being changed.
It'd appear that all objects with an id field should be a valid node. However, if I have data like this:
type User {
id: globalIdField('User'),
name: String,
folders: [ Folder ]
}
type Folder {
id: ???,
...
}
The data is stored in a document based solution, and the Folder objects are nested in the User object. But Folder objects are given an id so that some other objects could reference the Folder objects under the context of a User.
If Folder implements the nodeInterface, and uses globalIdField, then I need to figure out a way to fetch the Folder object from a globalId, meaning that I might have to scan through all the Users to find it, have a data map that'd allow me to find the object, or normalize the data so that Folders are in their own table.
If it doesn't implement the nodeInterface, and just uses Strings as id field, what happens when I try to mutate some fields on the Folder object?
It's often useful for these objects to have ids, even if there's no real id directly in your database. For example, if you want to write a mutation to rename a folder, it'd be great to have a global ID to reference this folder. Relay also uses them internally when the UI requests some additional data on a node that's not loaded yet.
One way to generate a global ID for the folder could be to take a prefix and add the user id and a way to identify the folder within the user, for example:
var folderID = ['folder', userID, folderID].join(':');
Whenever you want to resolve this id on your server, you split at the :, see that you want to load a folder by looking at the first part and then go via user to the right folder.

Best practice to process big plists?

I'm using a plist file which contains all app my data. The file is quite big and currently I'm loading all the stuff into Arrays and Dictionaries at first launch and save them into UserDefaults so that I don't have to touch the plist again. As this takes about 10 secs (iP4) I wonder if there is an even faster (better) way to process the plist. I checked the whole startup with Instruments and going through the hundreds of entries is actually the fastest part. It takes very long to save these processed stuff into NSUserDefaults.
You might benefit from saving the plist to your own file. That way you control the reading/writing, don't have any overhead associated with NSUserDefaults, and, most importantly, can ensure the format. That is, if reading/writing is producing the slow down, then you'll have to minimize the plist file size. Likely using a plist format of NSPropertyListBinaryFormat_v1_0 will do that:
See:
+ (NSInteger) writePropertyList: (id) plist
toStream: (NSOutputStream *) stream
format: (NSPropertyListFormat)format
options: (NSPropertyListWriteOptions) opt
error: (NSError **) error
From Apple's Property List Programming Guide:
The first approach [using NSDictionary or NSArray writeToFile] is
simpler—it requires only one method invocation instead of two—but the
second approach [as above] has its advantages. It allows you to convert the
runtime property list to binary format as well as an XML property
list. When you convert a static representation of a property list
back into a graph of objects, it also lets you specify with more
flexibility whether those objects are mutable or immutable.
Several points.
NSUserDefaults is probably just a big plist, so why use it? Stick your entries into a singleton that holds the in-memory structure.
If you're doing this on first load because you want it to be mutable, put the defaults into your resource folder. When you want to load it, check if you have it in the documents folder, and if you don't ( first load), copy it from the resource bundle to the documents.
If you're using NSUserDefaults for persistence, just write out your data to your plist in applicationShouldResignActive, and at any other times where you make important changes.
Write it in a background thread, but you probably need to do some locking here.
Best practise when load and save times become to big is probably move to core data, but 1-4 should give you some more mileage before you need to do that.

Adding a `lastModified` record to a Core Data managed object

An object needs to be submitted to the server, and I want to indicate to the user that the object needs to be submitted by displaying the lastModified date/time, and lastSubmitted date/time.
(Yes, the record must be manually submitted.)
I'm currently listening for NSManagedObjectContextObjectsDidChangeNotification, checking if the object's entity is RetailLocation, and if so, setting its lastModified date/time (of course, only if lastModified is not the only property being modified). Since this seems to highly confuse the undo manager, I use performSelector:SOMESEL withObject:retailLocation afterDelay:0.0 to set the lastModified property.
Sadly, this is almost even worse: this results in two actions being added to the undo stack!
Can someone recommend a nice way to implement a lastModified attribute in a Core Data-managed record? Alternatively, what am I missing?
If you don't want the modification date to be undoable, you can call disableUndoRegistration on your NSUndoManager before making changes, and enableUndoRegistration when you're done.
If you need one, you can get a pointer to the NSUndoManager by calling undoManager on your NSManagedObjectContext, but if you're working in iOS you should have one already.
Also, note Apple recommends using the NSManagedObjectContextWillSaveNotification notification for this, since changes might not necessarily be saved.

ASP.NET MVC - Sharing Session State Between Controllers

I am still mostly unfamiliar with Inversion of Control (although I am learning about it now) so if that is the solution to my question, just let me know and I'll get back to learning about it.
I have a pair of controllers which need to a Session variable, naturally nothing too special has happen because of how Session works in the first place, but this got me wondering what the cleanest way to share related objects between two separate controllers is. In my specific scenario I have an UploadController and a ProductController which work in conjunction with one another to upload image files. As files are uploaded by the UploadController, data about the upload is stored in the Session. After this happens I need to access that Session data in the ProductController. If I create a get/set property for the Session variable containing my upload information in both controllers I'll be able to access that data, but at the same time I'll be violating all sorts of DRY, not to mention creating a, at best, confusing design where an object is shared and modified by two completely disconnected objects.
What do you suggest?
Exact Context:
A file upload View posts a file to UploadController.ImageWithpreview(), which then reads in the posted file and copies it to a temporary directory. After saving the file, another class produces a thumbnail of the uploaded image. The path to both the original file and the generated thumbnail are then returned with a JsonResult to a javascript callback which updates some dynamic content in a form on the page which can be "Saved" or "Cancelled". Whether the uploaded image is saved or it is skipped, I need to either move or delete both it and the generated thumbnail from the temporary directory. To facilitate this, UploadController keeps track of all of the upload files and their thumbnails in a Session-maintained Queue object.
Back in the View: after the form is populated with a generated thumbnail of the image that was uploaded, the form posts back to the ProductsController where the selected file is identified (currently I store the filename in a Hidden field, which I realize is a horrible vulnerability), and then copied out of the temp directory to a permanent location. Ideally, I would like to simply access the Queue I have stored in the Session so that the form does not need to contain the image location as it does now. This is how I have envisioned my solution, but I'll eagerly listen to any comments or criticisms.
A couple of solutions come to mind. You could use a "SessionState" class that maps into the request and gets/sets the info as such (I'm doing this from memory so this is unlikely to compile and is meant to convey the point):
internal class SessionState
{
string ImageName
{
get { return HttpContext.Current.Session["ImageName"]; }
set { HttpContext.Current.Session["ImageName"] = value; }
}
}
And then from the controller, do something like:
var sessionState = new SessionState();
sessionState.ImageName = "xyz";
/* Or */
var imageName = sessionState.ImageName;
Alternatively, you could create a controller extension method:
public static class SessionControllerExtensions
{
public static string GetImageName(this IController controller)
{
return HttpContext.Current.Session["ImageName"];
}
public static string SetImageName(this IController controller, string imageName)
{
HttpContext.Current.Session["ImageName"] = imageName;
}
}
Then from the controller:
this.SetImageName("xyz");
/* or */
var imageName = this.GetImageName();
This is certainly DRY. That said, I don't particularly like either of these solutions as I prefer to store as little data, if any, in session. But if you're intent is to hold onto all of this information without having to load/discern it from some other source, this is the quickest (dirtiest) way I can think of to do it. I'm quite certain there's a much more elegant solution, but I don't have all of the information about what it is you're trying to do and what the problem domain is.
Keep in mind that when storing information in the session, you will have to dehydrate/rehydrate the objects via serialization and you may not be getting the performance you think you are from doing it this way.
Hope this helps.
EDIT: In response to additional information
Not sure on where you're looking to deploy this, but processing images "real-time" is a sure fire way to be hit with a DoS attack. My suggestion to you is as follows -- this is assuming that this is public facing and anyone can upload an image:
1) Allow the user to upload an image. This image goes into the processing queue for background processing by the application or some service. Additionally, the name of the image goes into the user's personal processing queue -- likely a table in the database. Information about background processing in a web app can be found # Schedule a job in hosted web server
2) Process these images and, while processing, display a "processing graphic". You can have an ajax request on the product page that checks for images being processed and trys to reload them every X seconds.
3) While an image is being "processed", the user can opt out of processing assuming they're the one that uploaded the image. This is available either on the product page(s) that display the image or on a separate "user queue" view that will allow them to remove the image from consideration.
So, you end up with some more domain objects and those objects are managed by the queue. I'm a strong advocate of convention over configuration so the final destination of the product image(s) should be predefined. Something like:
images/products/{id}.jpg or, if a collection, images/products/{id}/{sequence}.jpg.
You then don't need to know the destination in the form. It's the same for all images.
The queue then needs to know where the temp image was uploaded and what the product id was. The queue worker pops items from the queue, processes them, and stores them accordingly.
I know this sounds a little more "structured" than what you originally intended, but I think it's a little cleaner.
Is there complete equivalence between the UploadController and ProductController?
As files are uploaded by the UploadController, data about the upload is stored in the Session. After this happens I need to access that Session data in the ProductController.
As I read that the UploadControl needs read and write access to Upload data, the ProductController needs only read.
If that's true then you can make it clear by using an immuatable wrapper around the upload information and have the UploadController put that into the session.
The Session itself is by definiton a public shared noticeboard, decouples explicit relationships at the cost of allowing anyone to get and put. You could allow the ProductController to know about the UploadController and hence remove the need for passing the upload information via the session. My instinct is that the upload info is interesting to the public, so using Session is reasonable.
I don't see any DRY violation here, we are explicitly trying to separate responsibilities.

Resources