Creating a PDF (Or Other viewable format, such as .docx) from CoreData using swift - ios

Description
Hello! So i am saving several strings of user inputed data into CoreData using a handful of NSManagedObject subclasses. Since the application is actually a machine inspection, it is necessary to print/email the customer entered information out in a somewhat structured format. Below you will find pictures/examples of how i am saving the data. Please be kind to me, for it is my birthday <3
Code examples
Below is an example of how i am saving the information. In the example, the CoreData entity is "CrawlerThree" and the data is crawlerDistance.
#IBAction func save(_ sender: AnyObject) {
let appDel:AppDelegate = (UIApplication.shared().delegate as! AppDelegate)
let context:NSManagedObjectContext = appDel.managedObjectContext
let entity1 = NSEntityDescription.insertNewObject(forEntityName: "CrawlerThree", into:context) as NSManagedObject as! CrawlerThree
entity1.crawlerDistance = distance.text
}
The rest of the saves are almost identical to this, with varying entities and data, all of which are strings. I know this is a rather vague post, but i really do need to know what i need to use to format this information. It is worth noting that i already know how to actually retrieve the string's from CoreData, I am simply ignorant on how to actually process them to some other form.
The Simplest solution will be the best solution for my problem! Thanks in advance!

I would divide the problem into two.
Output from Core Data to screen
Export as PDF
It sounds as if you know how to get your strings from Core Data already, so you just have to display them in a suitable way.
Fortunately whatever you use on screen: UIView, UITableView etc. can be saved as a PDF using e.g. the (Swift) answer to How to Convert UIView to PDF within iOS?

Related

Should I use NSUserDefault, dictionaries, core data - or something else?

I'm having some issues with the app, that I'm making, which I thought would be a lot easier to explain with some photos, so ... :
Ofcourse the "Create New Person-button" in nr. 1 leads you to number two.
Now, I'm having issues figuring out how to save this data about the person in the "People Diary". The goal is, that when you enter a person's name, add a photo (an enable-camera feature, I will struggle with at a later time...) and add an answer to the question - then you only need to press "Save this person", and then you will be redirected to the AllPersonsInYourDiaryViewController, where there is now a new tableViewCell with this new person's name (maybe with a subtitle containing the answer and the photo shown in miniature in the cell too).
(Naturally you can then enter this cell with the data about the person too - but that comes next.)
So far in the app, I have used NSUserDefault, when allowing the user to create this specifik Diary by the Name "Antons Diary" with the specifik question and so on. But now it came to my attention, that maybe it is smarter to use something else? I tried with dictionaries, but couldn't get this to work properly.
So...: Before I spend hours and hours playing around with one of these ways, will someone smarter than me, tell me what the best approach would be?
If I can give my two cents, the first thing you have to do is to “design” how to represent a person programmatically. You can create a struct or class to do so, even though a struct is more suitable:
struct Person {
var name: String?
var answer: String?
var photo: String?
}
Then you can decide how to save the data of such an object persistently. If you want to use a database, then I would recommend using SQLite with FMDB library. It’s really easy and fast to learn how to use it, and it's also quite handy. I've used it big projects and it works smoothly. I find CoreData too complicated and an overkill based on what you need.
If you don’t want to use a database, your only other way is to save to files, but still, you’ve got options here too. If you encode (see Codable protocol in Swift), you can use NSKeyedArchiver to convert to Data object and write then to disk. If you like using dictionaries, and since the properties you’re going to have for a person are not going to be too many, you could create a dictionary by assigning the properties and their values, and then convert and save as JSON data, or even Plist files. Without any intension to do promotion here, but just to provide some additional help, if you want take a look to a library that I’ve written and that can do all these automatically for you. It’s a protocol that you have to adopt, and then you can instantly convert your struct to a dictionary, JSON or plist and save to files.
No matter which way you’re going to select, save the images as single files to documents directory, and keep their file names only stored to database/file. Based on them, you can build the path to each image (or the URL) easily when needed. Warning: Do not save the full path to the documents directory, especially if you’re testing on Simulator; paths are changing on each build. Save the file name only.
Additionally, if you’re going to use a struct like the one shown above, you could implement small but super convenient functions that will be responsible for saving, loading, or updating your data to the solution (database/file) you’ll eventually select. That way, you’ll have related stuff gathered in one place, and easily accessible (i.e., person.save()).
struct Person {
var name: String?
var answer: String?
var photo: String?
func save() {
…
}
func load() {
…
}
// More functions…
}
Lastly, avoid using UserDefaults, or at least keep just a few non-critical data there. UserDefaults are not meant to keep all data produced by your app. Most importantly, do not use it for saving sensitive data, especially passwords or other stuff like that.
I hope the above will help you make your mind.
I can give you the logic behind coreData and NSUserDefaults, but you will decide which one should be used.
CoreData is usually used as a database. you can create entities and attributes for every entity. Moreover, you can create relations between these entities.
When extracting data from coreData, you can arrange this data using NSSortDescriptor or select a specific record using NSPredicate.
So as you can see CoreData is a database.
While NSUserDefaults is usually used to save a password, username, userID... and such issues that you will regularly use in the app. NSUserDefaults gives you a direct access to the saved variables at any time. However, CoreData will take more time and lines of code to access the entity and make the query.
Now, check which method suits your case more.

swift fetch a specific uiimage from core data

I'm pretty new to swift and core data. So if this logic doesn't make sense, please let me know.
I'm trying to save user's images to core data and be able to delete selected one.
I have Entity "Media", attribute "photo" type "binary data"
Here I convert UIImage to NSData
let data = UIImagePNGRepresentation(addedImage!) as NSData?
And save it.
let newPhoto = NSEntityDescription.insertNewObject(forEntityName: "Media", into: context)
newPhoto.setValue(imageData, forKey: "photo")
Here I try to retrieve that one selected image from core data using predicate
func deleteImage(imageData: NSData) {
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Media")
request.returnsObjectsAsFaults = false
request.predicate = NSPredicate(format: "photo =%#", imageData)
do {
let results = try context.fetch(request)
...
And I'm getting this error message.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[OS_dispatch_data _bytesPtrForStore]: unrecognized selector sent to instance
Saving and getting all images from entity "Media" is working perfectly. I just can't get the specific one.
Any thoughts/suggestions?
Thanks.
Saving images like this, with external storage enabled for the property, can be OK. But there are some very unusual things going on here.
The common approach would be to have the image be part of an entity with other information, but not as the primary value of the entity. For example an entity type that represents a person and has name, address, etc, and a picture. Storing just a picture in an entity is kind of weird. You're not getting much of anything from using Core Data over something simpler. If nothing else I'd guess you had a photo ID, or title, or some other data in the entity.
The second thing relates to that-- it's extremely unusual to try to fetch using a predicate based on a binary data property. I guess it might work but only if you can ensure that the predicate value is identical to one in the persistent store all the way through-- not a single bit changed in the binary blob. That might be true but I wouldn't rely on UIKit not doing something along the way that makes it subtly different. I've never seen that particular error message but I'm not surprised that this isn't working.
Finally, it's also unusual (though not quite so strange as the above) to delete an object based on a fetch request. You would have fetched these objects before to get the images. Why fetch the same object again just to delete it? Delete the one you already fetched.
A more reliable approach would be to at least include a unique ID in the entity, which might be an integer or a UUID or whatever else works for you. Fetch this entity and display images using the photo property. When you want to delete one, take the managed object from your fetch and tell the context to delete that one. Or if fetching is necessary for some reason, use the unique ID in the predicate instead of the binary data.
You better stop doing it. Saving heavy data (image, audio, video, ...) to core data will greatly decrease fetching speed when you have more records. Instead, save image to application folder, and then only save its url to core data.

Persisting application data

I'm fairly new to programming but at the moment. I can view my calculations in my stack on the second screen. Once the application project resets the stack is clear. My question is how I can keep the stack in the second view once the application has exited.
class ViewController: UIViewController {
var name = Array<String>()
#IBOutlet weak var labelDisplay: UILabel!
var calcEngine : CalculatorEngine?
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
if segue.identifier == "pushToNextView"
{
let secondVC: SecondVCViewController = segue.destinationViewController as! SecondVCViewController
secondVC.data = self.name
}
}
second view
class SecondVCViewController: UIViewController {
#IBOutlet weak var textData: UITextView!
var data = Array <String>()
override func viewDidLoad() {
super.viewDidLoad()
for(var i = 0; i < self.data.endIndex; i++ ){
self.textData.text = self.textData.text! + self.data[i] + "\n"
}
// Do any additional setup after loading the view.
}
There are many tools available to you to persist data from an app. Choosing an appropriate one requires some understanding of your needs, or at least making a guess. Here's where I would start but keep an eye out in case my assumptions don't match your needs.
What type of data do you want to persist?
Presumably you want to store the array of Strings seen in var name = Array<String>() but maybe you have some other data type in mind.
When do you need to read/write it (do you need to worry about multiple systems trying to write at the same time, is loading the data going to be very expensive because it is very large, do you only need to load a small piece at a time, ...)?
Sounds like you're fine reading/writing all the data at once and only on app launch/termination. There could be a reasonable maximum size to this calculation history to total storage size is probably fairly small.
How long does it need to be persisted?
Calculation histories are usually nice to have but not a catastrophic loss if they go missing. It would be nice to keep this data around but the app will work without it. Depending on use it may or may not cost the user time and frustration if the data is deleted unexpectedly.
Who needs access to it (is it also show on a web site, should it sync to all of a user's devices, ...)?
It's probably enough to keep this history just on the local device.
So what should we do?
For a small amount of data loaded all at once and only used locally I would write this data to a file.
Since you're just working with an array of strings we can use a plist (https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html)
NSArray (note: not a Swift array) has arrayWithContentsOfURL: and writeToURL:atomically: which can be a convenient way to read and write plist compatible data.
Using a file means we need to decide where to store this file. Take a look at Where You Should Put Your App’s Files. In this case it seems reasonable to write this data to either Documents or Library/Caches depending on how you plan to use it.
With all that covered we could save this stack something like:
let array = names as NSArray
guard let cachesURL = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first else {
print("Unable to find Caches directory, cannot save file.")
return
}
let fileURL = cachesURL.URLByAppendingPathComponent("history.plist")
array.writeToURL(fileURL, atomically: false)
Reading the file is similar. You'll need the same URL so decide which component is responsible for deciding where to save/load this data and let it supply the URL to use. You'll also need to check if any saved history exists or not (note that arrayWithContentsOfURL: can return nil).
At some point you might find it useful to model this data as a more specific data type, perhaps defining your own struct to capture these operations. A custom type can't be automatically written to or read from a file so you'll need to do a little more work. Apple has a nice example of how you might use NSCoding to do that in their tutorials: https://developer.apple.com/library/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/Lesson10.html
Preserving and Restoring State
Feature you are looking for is called State Restoration. From the docs:
State preservation records the configuration of your app before it is
suspended so that the configuration can be restored on a subsequent
app launch.
How to tackle it:
Tag your view controllers for preservation. Assign restoration identifiers.
Restore view controllers at launch time. Encode and decode their state.
Topic is very broad but the docs explain it very well.
What you're trying to do is keep persistent data between launches of your application, right?
For that you should use core data, there are many useful recourses on the web to help you with that, heres a few helpful ones.
https://developer.apple.com/library/watchos/documentation/Cocoa/Conceptual/CoreData/index.html
https://www.raywenderlich.com/934/core-data-tutorial-for-ios-getting-started

Core Data and a content manager

First time working with Core Data en its .xcdatamodeld
I am using a contentManager for a notes app.
But I am difficulties creating standerd notes.
Is a contentManager normally used for core data? My Problem is where the ?? is.
let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
let entity = NSEntityDescription.insertNewObjectForEntityForName("Note", inManagedObjectContext: ?? ) as! Note
}
It isn't exactly clear what the issue is, but it looks like you simply need to replace ?? with managedObjectContext. But, this code should be in a function, not just as variable definitions in the class itself. If you did want to make them variables then you can make them lazy so they're populated on demand the first time they're used.
As for your content manager, I'm guessing you mean some kind of data controller. That's generally a matter of personal preference and how long you've been developing. Generally you want to move the context ownership and management out of the app delegate as that's an inappropriate place to have it and using a data controller for that is clearly better.

Asynchronous Fetching Swift Xcode

I use a collection view to display images stored in CoreData, only a few (around 15) but when I scroll down it lags, It seams to be because I am not fetching my data asyncrhronously, is it possible with only 15 UIImages?
So here is my problem I cannot find a descent tutorial on asynchronous fetching in swift anywhere ,3 days I am looking. Sorry if I didn't search well.
This is what arrived to do with some bits from tutorials
let entity = NSEntityDescription.entityForName("Dreams", inManagedObjectContext: contxt)
let fetchRequest = NSFetchRequest()
fetchRequest.entity = entity
let asyncFetch = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) {
(result:NSAsynchronousFetchResult!) -> Void in
if result.finalResult!.count > 0 {
self.dreams = result.finalResult! as [Dream]
}
NSLog("Done.")
}
Dreams is an array of type [Dream] but when I display the collection view nothing appears
This is how I did it previously
let fetchRequest = NSFetchRequest(entityName: "Dreams")
if let fetchResults = contxt.executeFetchRequest(fetchRequest, error: nil) as? [Dream] {
dreams = fetchResults
}
It worked very well,
Thank you very much for your help
Unfortunately, I don't have time to offer specific code in this instance. I did want to present a basic strategy, however. Fetching on the main thread is exceedingly unlikely to be your problem here, but fetching everything at once definitely could be, particularly with large binary data involved. This should be sped up significantly if:
You start using an NSFetchedResultsController with a good batch size rather than a raw fetch request. NSFetchedResultsController is a little finicky about updates with collection views, in ways which are beyond the specific scope of this question, but they work very well once set up correctly.
You store the images as files instead of in Core Data itself, and store the path to the file instead. Then that image data won't get loaded into memory every time you fault in the object, and you can simply load and display the images as the cells come on screen using that file URL.
People who insist on storing binary data in Core Data usually find that storing the data as the only attribute in a separate "Image" entity with a relationship to the entity you are displaying helps. The image object (along with its binary image data) can then be kept a fault, and thus not in memory, even when the object you fetch for the collection view is in memory. I, however, prefer not to store them in Core Data, and simply handle the resulting atomicity gotchas and the possibility the file Core Data points to got deleted somehow.

Resources