Core data functions nicely. What might be a code snippet to reference the results of the core data and insert into MFMailComposeViewController directly inline (not as an attachment) using isHTML = Yes...
Currently I use code similar to this style (from one of my other apps) (and works)... not sure how to call the core data results to do similar...
NSString *emailBody = [NSString stringWithFormat:#"<p>Stats...</p>
<p><b>Laps Completed: %#</b></p><p>Goal: %# laps</p>
<p>Time To Complete: %# (h:m:s)</p>
<p>%f% completion ratio</p>",
self.lapLabel.text, self.goal.text, self.timeLabel.text,
(self.percentageDoughnut.percentage)*100];
[mail setMessageBody:emailBody isHTML:YES];
You can use UIActivityViewController with attaching file data from local storage, check below code
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view .
activityVC.popoverPresentationController?.sourceRect = sender.frame
self.present(activityVC, animated: true, completion: nil)
Related
I'm running my app using Xcode 12.4 and I'm using persisting my data using Core Data.
When I try to fetch the data and print it inside of a list, it prints the data correctly, but I'm having trouble finding the actual DB file on my mac.
Normally I would run print("Documents Directory: ", FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last ?? "Not Found!")and that would lead me to the correct file, but when I go to the destination now, the documents folder is empty.
Any ideas how I can retrieve the file?
If you're using NSPersistentContainer, try asking the container for the path it's using. You can do this any time after calling loadPersistentStores(completionHandler:):
print("Path to store file: \(container.persistentStoreCoordinator.persistentStores.first?.url?.path ?? "not found")")
Or you can put this in the completion closure you pass to loadPersistentStores(completionHandler:):
print("Loaded persistent store at \(storeDescription.url!.path)")
Keep in mind that the path will change every time you run the app, so be sure you have the current path. There are a couple of UUIDs in the path-- one of them doesn't change unless you delete the app, but the other changes all the time. For example once it might be this (wrapped here for readability)
/Users/[you]/Library/Developer/CoreSimulator/Devices/
445F0055-2A61-478A-9BA9-7F44CE038242/data/Containers/Data/Application/
4B21151B-4148-470B-88AC-F323CB88434D/Library/
Application Support/[AppName].sqlite
Then the next time it's this
/Users/[you]/Library/Developer/CoreSimulator/Devices/
445F0055-2A61-478A-9BA9-7F44CE038242/data/Containers/Data/Application/
E8889B57-41CB-4F14-AAB2-A02142DC47FF/Library/
Application Support/[AppName].sqlite
Which is kind of a pain, but you do need to check it every time.
I wrote the below to copy the location of core data database to the clipboard. If you are coding for IOS14 then you can comment out the appDelegate line and also swap the dbName line for the one commented.
func printCoreDataPath(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate // remove if IOS14
let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.applicationSupportDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let dbName = appDelegate.persistentContainer.name
// let dbName = PersistenceController.shared.container.name
let pasteboard = UIPasteboard.general
pasteboard.string = (path[0] + "/\(dbName).sqlite")
print (path[0] + "/\(dbName).sqlite")
}
I have one CoreData record that contains all of the app's settings. When I read that single record (using MagicalRecord), I get an array back. My question is: can I get addressabiltiy to the individual fields in the record without using "[0]" (field index), but rather using [#"shopOpens"]?
I was thinking something like this, but I don't think it's right:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"aMostRecentFlag == 1"]; // find old records
preferenceData = [PreferenceData MR_findAllWithPredicate:predicate inContext:defaultContext]; // source
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *preferencesDict = [[userDefaults dictionaryForKey:#"preferencesDictionary"] mutableCopy]; // target
// start filling the userDefaults from the last Preferences record
/*
Printing description of preferencesDict: {
apptInterval = 15;
colorScheme = Saori;
servicesType = 1;
shopCloses = 2000;
shopOpens = 900;
showServices = 0;
syncToiCloud = 0;
timeFormat = 12;
}
*/
[preferencesDict setObject: preferenceData.colorScheme forKey:#"shopOpens"];
UPDATE
This is how I finally figured it out, for those who have a similar question:
NSPredicate *filter = [NSPredicate predicateWithFormat:#"aMostRecentFlag == 0"]; // find old records
NSFetchRequest *freqest = [PreferenceData MR_requestAllWithPredicate: filter];
[freqest setResultType: NSDictionaryResultType];
NSDictionary *perferenceData = [PreferenceData MR_executeFetchRequest:freqest];
Disclaimer: I've never used magical record, so the very first part is just an educated guess.
I imagine that preferenceData is an instance of NSArray firstly because the method name uses findAll which indicates that it will return multiple instances. Secondly, a normal core data fetch returns an array, and there is no obvious reason for that find method to return anything different. Thirdly, you referenced using an index operation in your question.
So, preferenceData is most likely an array of all objects in the store that match the specified predicate. You indicated that there is only one such object, which means you can just grab the first one.
PreferenceData *preferenceData = [[PreferenceData
MR_findAllWithPredicate:predicate inContext:defaultContext] firstObject];
Now, unless it is nil, you have the object from the core data store.
You should be able to reference it in any way you like to access its attributes.
Note, however, that you can fetch objects from core data as dictionary using NSDictionaryResultType, which may be a better alternative for you.
Also, you can send dictionaryWithValuesForKeys: to a managed object to get a dictionary of specific attributes.
In my app I have a Core Data entity called Status with two attributes messageID as Integer32 and messageText as String.
I have a string stored in an SQL database which the app downloads on startup. The string from the database is broken down into two parts ID and text. An example message could be 011-Hello and the each part is stored in an array called messageParts. The first item in the array is the ID:
NSInteger newMessageID = [messageParts[0] integerValue];
I want to compare this ID with the one stored in Core Data such as:
if (messageID == newMessageID)
I get the newMessageID fine and I have a number to work with but I am totally confused as to how to handle the data type coming from Core Data. I can see that there is a number in the database using SQLlitebrowser and I have tried:
NSInteger *savedMessageID = [[self.status objectAtIndex:0] messageID];
and
NSInteger savedMessageID = [[self.status objectAtIndex:0] messageID];
But neither return the stored value. I think that this is a pointer issue but I am going around in circles here.
If you generate the NSManagedObject subclass from your xcdatamodeld (Xcode menu Editor > Create NSManagedObject Subclass…), you will find that the integer32 field is generated as an NSNumber...
This is maybe where you should take a look ?
I've been converting my own personal OGLES 2.0 framework to take advantage of the functionality added by the new iOS 5 framework GLKit.
After pleasing results, I now wish to implement the colour-based picking mechanism described here. For this, you must access the back buffer to retrieve a touched pixel RGBA value, which is then used as a unique identifier for a vertex/primitive/display object. Of course, this requires temporary unique coloring of all vertices/primitives/display objects.
I have two questions, and I'd be very grateful for assistance with either:
I have access to a GLKViewController, GLKView, CAEAGLLayer (of the GLKView) and an EAGLContext. I also have access to all OGLES 2.0
buffer related commands. How do I combine these to identify the color
of a pixel in the EAGLContext I'm tapping on-screen?
Given that I'm using Vertex Buffer Objects to do my rendering, is there a neat way to override the colour provided to my vertex shader
which firstly doesn't involve modifying buffered vertex (colour)
attributes, and secondly doesn't involve the addition of an IF
statement into the vertex shader?
I assume the answer to (2) is "no", but for reasons of performance and non-arduous code revamping I thought it wise to check with someone more experienced.
Any suggestions would be gratefully received. Thank you for your time
UPDATE
Well I now know how to read pixel data from the active frame buffer using glReadPixels. So I guess I just have to do the special "unique colours" render to the back buffer, briefly switch to it and read pixels, then switch back. This will inevitably create a visual flicker, but I guess it's the easiest way; certainly quicker (and more sensible) than creating a CGImageContextRef from a screen snapshot and analyzing that way.
Still, any tips as regards the back buffer would be much appreciated.
Well, I've worked out exactly how to do this as concisely as possible. Below I explain how to achieve this and list all the code required :)
In order to allow touch interaction to select a pixel, first add a UITapGestureRecognizer to your GLKViewController subclass (assuming you want tap-to-select-pixel), with the following target method inside that class. You must make your GLKViewController subclass a UIGestureRecognizerDelegate:
#interface GLViewController : GLKViewController <GLKViewDelegate, UIGestureRecognizerDelegate>
After instantiating your gesture recognizer, add it to the view property (which in GLKViewController is actually a GLKView):
// Inside GLKViewController subclass init/awakeFromNib:
[[self view] addGestureRecognizer:[self tapRecognizer]];
[[self tapRecognizer] setDelegate:self];
Set the target action for your gesture recognizer; you can do this when creating it using a particular init... however I created mine using Storyboard (aka "the new Interface Builder in Xcode 4.2") and wired it up that way.
Anyway, here's my target action for the tap gesture recognizer:
-(IBAction)onTapGesture:(UIGestureRecognizer*)recognizer {
const CGPoint loc = [recognizer locationInView:[self view]];
[self pickAtX:loc.x Y:loc.y];
}
The pick method called in there is one I've defined inside my GLKViewController subclass:
-(void)pickAtX:(GLuint)x Y:(GLuint)y {
GLKView *glkView = (GLKView*)[self view];
UIImage *snapshot = [glkView snapshot];
[snapshot pickPixelAtX:x Y:y];
}
This takes advantage of a handy new method snapshot that Apple kindly included in GLKView to produce a UIImage from the underlying EAGLContext.
What's important to note is a comment in the snapshot API documentation, which states:
This method should be called whenever your application explicitly
needs the contents of the view; never attempt to directly read the
contents of the underlying framebuffer using OpenGL ES functions.
This gave me a clue as to why my earlier attempts to invoke glReadPixels in attempts to access pixel data generated an EXC_BAD_ACCESS, and the indicator that sent me down the right path instead.
You'll notice in my pickAtX:Y: method defined a moment ago I call a pickPixelAtX:Y: on the UIImage. This is a method I added to UIImage in a custom category:
#interface UIImage (NDBExtensions)
-(void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y;
#end
Here is the implementation; it's the final code listing required. The code came from this question and has been amended according to the answer received there:
#implementation UIImage (NDBExtensions)
- (void)pickPixelAtX:(NSUInteger)x Y:(NSUInteger)y {
CGImageRef cgImage = [self CGImage];
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
if ((x < width) && (y < height))
{
CGDataProviderRef provider = CGImageGetDataProvider(cgImage);
CFDataRef bitmapData = CGDataProviderCopyData(provider);
const UInt8* data = CFDataGetBytePtr(bitmapData);
size_t offset = ((width * y) + x) * 4;
UInt8 b = data[offset+0];
UInt8 g = data[offset+1];
UInt8 r = data[offset+2];
UInt8 a = data[offset+3];
CFRelease(bitmapData);
NSLog(#"R:%i G:%i B:%i A:%i",r,g,b,a);
}
}
#end
I originally tried some related code found in an Apple API doc entitled: "Getting the pixel data from a CGImage context" which required 2 method definitions instead of this 1, but much more code is required and there is data of type void * for which I was unable to implement the correct interpretation.
That's it! Add this code to your project, then upon tapping a pixel it will output it in the form:
R:24 G:46 B:244 A:255
Of course, you should write some means of extracting those RGBA int values (which will be in the range 0 - 255) and using them however you want. One approach is to return a UIColor from the above method, instantiated like so:
UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f];
I am trying to parse a lot of text files and organize their contents as managed objects. There are a lot of duplicates in the text files, so one of the "collateral" tasks is to get rid of them.
What i am trying to do in this respect is to check whether an entity with the given content exists, and if it doesn't, i create one. However, i have different entities with different attributes and relationships. What i want is a kind of function that would take a number of attributes as an input and return a new NSManagedObject instance, and i wouldn't have to worry if it was inserted into the data store or fetched from it.
Is there one?
I must also say that i am a noob at core data.
Some more detail, if you want:
I am trying to write a sort of dictionary. I have words (Word{NSString *word, <<-> Rule rule}), rules (Rule{NSString name, <->>Word word, <<->PartOfSpeech partOfSpeech, <<-> Ending endings}), parts of speech (PartOfSpeech{NSString name, <<-> Rule rule}) (i hope the notation is clear).
Two words are equal, if they have the same word property, and "linked" to the same rule. Two rules are the same, if they have the same endings and part of speech.
So far i've written a method that takes NSPredicate, NSManagedObjectContext and NSEntityDescription as an input, and first queries the datastore and returns an entity if it finds one, or creates a new one, inserts it into the datastore and returns it. However, in this case I cannot populate the new entity with the necessary data (within that method), so i have to either pass an NSDictionary with the names of attributes and their values and insert them, or return by reference a flag as to whether i created a new object or returned an old one, so that i could populate it with the data outside.
But it looks kind of ugly. I'm sure there must be something more elegant than that, i just couldn't find it. Please, help me if you can.
Your basically on the right path. Core Data is an object graph. There not a lot of dynamic built in. There's also no "upsert". like you surmise, you have to fetch and if it doesn't exist, you insert one.
Here is what I have just started using to handle a fetch-or-create scenario. I am using a top level managed object which contains a few to-many relationships to subordinate objects. I have a class that houses a few arrays of data (those are not shown here). This class is responsible for saving and retrieving to and from core data. When the class is created, I do a fetch-or-create to access my top level NSManagedObject.
#implementation MyDataManagerClass
...
#synthesize MyRootDataMO;
- (MyDataManagerClass *) init {
// Init managed object
NSManagedObjectContext *managedObjectContext = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
// Fetch or Create root user data managed object
NSEntityDescription *entityDescription = [NSEntityDescription entityForName:#"MyRootDataMO" inManagedObjectContext:managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];
NSError *error = nil;
NSArray *result = [managedObjectContext executeFetchRequest:request error:&error];
if (result == nil) {
NSLog(#"fetch result = nil");
// Handle the error here
} else {
if([result count] > 0) {
NSLog(#"fetch saved MO");
MyRootDataMO = (MyRootDataMO *)[result objectAtIndex:0];
} else {
NSLog(#"create new MO");
MyRootDataMO = (MyRootDataMO *)[NSEntityDescription insertNewObjectForEntityForName:#"MyRootDataMO" inManagedObjectContext:managedObjectContext];
}
}
return self;
}
...