iOS writing to asset library observations - alasset

I am attempting to write an app that reads images from the asset library, modifies the image's GPS data and writes it back to the asset library. I store the assets in a mutableArray via the "enumerating assets" methods. Most of the details on how to do the various steps, I got from searching this forum. Thanks!
I have found that when I write the first "asset" via the "writeimagedatatosavedphotosalbum" method, all the elements of the mutableArray associated with the assets' URL became null. Furthermore, I noticed that writing back an image does not replace the original image, but instead creates a second instance of the image.
Just thought I'd pass these results along, in case others had questions. And, of course, I'd be interested in other's comments, observations, etc.
This forum has provided me with great information. Thanks again.

Your ALAsset object is only as good for the amount of time that your ALAssetsLibrary object is around. You either need to do everything you want in the completion block when you get the ALAsset, or store the ALAssetsLibrary in an instance variable so ARC does not deallocate it.

An ALAsset is essentially a Core Data object who can have properties accessed from multiple threads but a NSManagedObject or a subclass of NSManagedObject does not make sense without a parent NSManagedObjectContext much in the same way an ALAsset doesn't make sense without an ALAssetsLibrary.
It is common practice to store the NSManagedObjectContext on the AppDelegate; and while I abstract that functionality into a wrapper/singleton there is a retained reference to the NSManagedObjectContext throughout the app lifecycle. Apply the same logic to the ALAssetsLibrary and everything will works as expected.

Related

Do category names get serialized with encodeWithCoder style marshalling?

I have an app that is storing some data using the Cocoa/ObjC initWithCoder / encodeWithCoder style marshaling. Over the life of the app one of the classes that has been removed is an NSDate+Utils category.
The question is - if an NSDate+Utils is serialized to disk, and then deserialized with a later codebase when NSDate+Utils no longer exists; will it come back as an NSDate, or will it crash??
It won't crash. You just need to check whether it's nil. If that' nil, then just skip it, otherwise populate the data to your property. By the way, there is nothing to do with the codebase. It's just whether that local data being stored in the device. Codes are just things that tell device what or how to do stuff.
I wrote a small program to test this. Categories seem to get written as the base class - I assume as long as they don't add fields and/or mess with the withCoder methods.

I'm having trouble applying a solution found in SO in my iOS app. Can someone show me the big picture?

in the Stack Overflow posting:
How do I create a global UIManagedDocument instance per document-on-disk shared by my whole application using blocks?
Alan asked how to create a global UIManagedDocument to be used throughout his entire app. He provided code slices of his attempt. Kevinpo provided an answer which made perfect sense to Alan.
But I started out with the same problem, and can't make heads or tails out of their collective postings.
Specifically:
Alan's code references an object called managedDocumentDictionary,
but does not explain how to create it so I get an 'undeclared
identifier' compilation error.
Alan starts out stating that he wants to create a helper method to
retrieve a UIManagedDocument, yet throughout both his and Kevin's
code, neither actually show defining a helper method with .h and .m
files.
So, if possible, can anyone make sense of what they are saying and help me understand how it all fits together? Perhaps:
A helper Class definition,
How does one get the ball rolling, i.e., where do I initially create this UIManagedDocument,
Once created, How do I get the document in other TableViewControllers?
A sample of where this should be invoked - in the AppDelegate? or each TableViewController?
Maybe even a sample project?
Thanks to all for any interpretations you can offer.
That post shows how to access a document, based on a name. The dictionary is a mapping from names to UIManagedDocument instances. Thus, he can ask for document #"Foo" and the code will go look up #"Foo" in the dictionary. If it is there, the UIManagedDocument will be returned. If it is not there, then a new one will be created and placed in the dictionary (and the passed-in completion block will be called).
His question was basically, how to pass a completion block to the function, and have that function call the completion block he passed in.

QTMovie opened movie successfully?

Is there any way to detect whether QTMovie successfully loaded a movie? I.E. whether a valid component is found?
Or even better enumerate the components on launch and detect then what components have been loaded without having to provide a sample movie?
Thanks.
Have you read the QTMovie API reference? When you try to create a QTMovie object, you'll get either a valid movie object or nil. If you make use of the NSError argument (that's included on all the initializers/factory methods that create movies from files/URLs), you'll even get an explanation of what went wrong if the method returns nil. Also in that reference (handily categorized in the Tasks section), are a number of ways to get pretty much any information about the movie you want.
Regarding the "components" part of your question, I don't think QTKit gives you access to this directly. You might have to use the QuickTime.framework and dive deeper. You might be able to figure out whether your third-party-supported file type is actually supported by using the +[QTMovie movieFileTypes:] method (also found in the API reference I linked) and seeing if your file's extension appears there.

Adding multiple entries to a set while preserving the object-link

I have a question regarding the addition of multiple CoreData entries in multiple tables.
I have the following code:
while((item = [enumerator nextObject]))
{
DOCategory *cat;
cat = [[self categoryController] getNewCategory];
[cat setName:[item objectForKey: #"name"]];
[cat setDesc:[item objectForKey: #"description"]];
[cat setLastUpdate:updateTime];
//[[self categoryController] commitChanges];
}
I used to call the commitChanges after each enumeration, and that works, but takes a long time, especially on older devices as the initial load is quite substantial. Therefore I just want to save at the end of the whole operation.
What is the best way to add all those objectors to a NSSet (or NSArray) while preserving the link to the ManagedContext. Normally I would 'copy' them into the set, but that doesn't work here. A simple question, just have a block to seeing the best solution.
(I assume that I don't have to 'commit/save' after every new object I created, so the results are not written to the database yet, but are available for searches as there are different relational objects in the procedure)
Update:
After the suggestion below and more testing, it appears to me that when I haven't saved/committed the context it is not included NSFetchResultController. Is this right and supposed to be the way? Is there a way to do all the operations (including searches to create relations) in 'cache' and then commit once all is done?
Update 2:
In order to get the Managed Object I have a procedure in the Controller-class:
- (DOCategory *) getNewCategory{
return (DOCategory *)[NSEntityDescription insertNewObjectForEntityForName:#"Category" inManagedObjectContext:managedObjectContext];
}
It appears that the code runs fine, including cross references, until I come to adding the final object which uses all the other managed objects. There must be some problem in that code.
Your code is old school Objective-C 1.0 so it would be best to update it. You seldom use enumerators directly like that anymore.
The best way to handle this is to create the new managed objects by inserting them into the context using +[NSEntityDescription insertNewObjectForEntityForName:inManagedObjectContext:] this both creates the managed object and makes the context aware of it. The context will retain the managed object itself. That's were the "managed" of "managed object" comes from.
The normal practice when making a lot of changes is to change all the objects and then save the context only when you are done making all the changes.
Update:
It appears that the code runs fine,
including cross references, until I
come to adding the final object which
uses all the other managed objects.
There must be some problem in that
code.
That sounds like you might have a problem with the relationship being set properly in the data model itself. I can't say for certain without knowing what your data model looks like.
I discourage the use of cast with insertNewObjectForEntityForName: e.g.
(DOCategory *)[NSEntityDescription insertNewObjectForEntityForName:#"Category" inManagedObjectContext:managedObjectContext];
... because if you have a data model problem, such as not providing the name of the DOCategory class for the DOCatergory entity, the cast will obscure the problem. If there is no class defined the method will return an instance of the generic NSManagedObject class or if you accidentally define the wrong class for the entity e.g. MadeUpClassName instead of DOCategory then the cast will force the runtime to treat the returned object as a DOCategory object. That might work fine until way down in the code.
...it appears to me that when I haven't
saved/committed the context it is not
included NSFetchResultController. Is
this right and supposed to be the way?
Is there a way to do all the
operations (including searches to
create relations) in 'cache' and then
commit once all is done?
Not exactly sure what problem you are seeing. It is best practice to make all changes to all objects and then to save the context instead of saving after every change to any single object. Disk operations are expensive.
If your changes to the context are not reflected in the NSFetchResultsController (FRC) then you most likely did not implement the FRC delegate methods that update the tableview (see FRC docs.) Another possible error is that you've created two context accidentally. If you are using threads, each with a separate context (required practice) then you have to merge the background context with the front context before the changes made in the background are made known to the foreground.

Understanding NSManagedObject

In an existing project I have tried to introduce Core Data long after the project was created, so its model is already in place.
I have created the xcdatamodel and added my only class to it.
That class should act as a global storage for objects in my application.
The class properly implement NSManagedObject and I have verified it gets created and saved in context, also retrieved with a fetch result.
The way of saving data in this class is by means of NSMutableArray. But this is just not working. Here's a fragment of this class:
#interface WZMPersistentStore : NSManagedObject<NSCoding> {
NSMutableArray *persistentStorage;
}
#property(nonatomic,retain) NSMutableArray *persistentStorage;
-(void)add:(id)element;
-(void)remove:(id)element;
-(id)objectAtIndex:(NSUInteger)index;
-(NSUInteger)num;
#end
In the implementation I also override the initWithEntity like this:
- (id)initWithEntity:(NSEntityDescription*)entity insertIntoManagedObjectContext:(NSManagedObjectContext*)context {
NSLog(#"init with entity");
[super initWithEntity:entity insertIntoManagedObjectContext:context];
return [self init];
}
The init method only initialize the mutable array, and I can see from the log that it gets properly called by the app delegate when creating entity.
The add method just send message insertObject to persistentStorage.
The questions that come from this:
Am I doing "conceptually" right ? I
mean, is it correct to have instance
variable in managed object and
initialize like I did ?
when ns logging the size of the
persistentStorage I always get 0
even when logging a moment after the
addObject message (edit: that's not
true, I have verified again and I
correctly got 1 added).
The object stored in managed object
class trough persistentStorage are
normal class with attribute. Is
there something I need to do with
them ? I suppose not because I am
not getting any error at runtime.
No, that is not the "right" approach. You can perform initialization of instance variables in awakeFromFetch. Apple guidelines for NSManagedObject subclasses include the following:
You are also discouraged from
overriding
initWithEntity:insertIntoManagedObjectContext:,
dealloc, or finalize. Changing values
in the
initWithEntity:insertIntoManagedObjectContext:
method will not be noticed by the
context and if you are not careful,
those changes may not be saved. Most
initialization customization should be
performed in one of the awake…
methods. If you do override
initWithEntity:insertIntoManagedObjectContext:,
you must make sure you adhere to the
requirements set out in the method
description [...] (NSManagedObject Class Reference)
To really help, I'd need a deeper understanding of what you're trying to accomplish. Regardless, I strongly suggest combing through Apple's Core Data Programming Guide and sample code before proceeding.
I finally manage to solve this issue. Even if I am a newbie in objective-c, I think that introducing core data after the project is done, is not a good idea. Even if many claim it's easy. Unfortunately, all the people saying so, are showing as proof some really simple tutorial of one entity with one string attribute to change.
Instead for my project I ended up writing much code in addition to the existing one, plus some subclassing (NSManagedObject for example) which break the original model. This added code has also to be written carefully. Derived problem can be as simple as an attribute not saved, or dangerous as deleting wrong entities.
Infact, my problem was due to a wrong configuration in decode and encode method in the classes involved in the process of serialization.
For my questions:
-Point one still remain unanswered, because I am not yet confident in objective-c
-Point two, as I said the related object had some problem with encode/code.
-Point three, I was wrong, there's a lot of code to write, depending how complex is the relevant class.