Text and table view data storage in swift - swift

Could anyone tell me what the way of storing a long text in a swift app is. Let's suppose I have an app that has a table view and when I chose a row I go to a new scene where I have a big page filled by text.
The question is where do I have to store the data of the table's row and the whole text? And how?
Do I have to make a model? Is it just one for both the table and the text? Or more than one. Is there any tutorial that explains this exact situation or close to it?

You could use Parse.com framework to retrieve data from a database. There is a lot of documentation on this.
In this link you can find a tutorial that will explain you how to load data from Parse and show it in your UITableView.
Storing Local (in-memory store):
For storing without a internet connection you could use Core Data Stack with NSInMemoryStoreType as storeType. This tutorial will give you a nice idea on how it works.
You can declare a model like it follows:
struct CoreDataModel {
let name: String
let bundle: NSBundle
init(name: String, bundle: NSBundle)
// other properties & methods
}
And then manage it with:
let model = CoreDataModel(name: "MyModel", bundle: myBundle)
let stack = CoreDataStack(model: model,
storeType: NSInMemoryStoreType,
concurrencyType: .MainQueueConcurrencyType)
// Use context
stack.managedObjectContext

Related

Fetch data from CoreData for iOS 14 widget

I want to display data fetched from Core Data in a widget. But #FetchRequest doesn’t work on widgets.
As I understand, we have to create an app group and make a shared persistent container.
What I want to know is how to read (fetch) data on widgets from that shared persistent container or simply, how to display data fetched from Core Data in widgets.
First you need to create an AppGroup which will be used to create a Core Data Persistent Container (here is a good explanation how to do it)
Then you need to create your own CoreData stack (an example can be found when you create a new empty project with CoreData enabled).
Accessing Core Data Stack in MVVM application
Assuming you have already created your Core Data model (here called DataModel), you now need to set the container url to your custom shared container location:
Share data between main App and Widget in SwiftUI for iOS 14
let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: <your_app_group>)!
let storeURL = containerURL.appendingPathComponent("DataModel.sqlite")
let description = NSPersistentStoreDescription(url: storeURL)
let container = NSPersistentContainer(name: "DataModel")
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { ... }
Now you can get the managedObjectContext from your shared Persistent Container:
let moc = CoreDataStack.shared.managedObjectContext
and perform a fetch request with it (more information here)
let predicate = NSPredicate(format: "attribute1 == %#", "test")
let request = NSFetchRequest<SomeItem>(entityName: "SomeItem")
let result = try moc.fetch(request)
Apart from all the links above I recommend you also read this tutorial about Core Data:
Core Data with SwiftUI Tutorial: Getting Started
Here is a GitHub repository with different Widget examples including the Core Data Widget.
For people who did all the work above, and finally can get the connection to your Core Data (e.g. you can get the count of request), but can't fetch the request, is mostly because that the entity you're fetching contains transformable type, and for some reason this error occurred: Cannot decode object of class, try fix this.

Passing data from a tab bar switch right into another view controller

The question might sound complicated, so let me ease the stress*
Lets say I have 2 tab bar tabs (Home1, Home2)
Both (Home1, Home2) have navigational controllers with two view controllers each
Home1(Navigation controller -> VC11 -> VC12)
Home2 (Navigation controller -> VC21 -> VC22)
Easy right? Well, not quite
What I want to do is pass data from [Home1: VC12 to Home2: VC22].
My coding efforts:
tabBarController!.selectedIndex = 1
self.navigationController!.popToViewController(navigationCon‌troller!.viewControl‌​lers[1] as! (Home2: VC22), animated: false)
let navController = self.tabBarController?.viewControllers![1] as! UINavigationController
navController.popToViewController((Home2: VC22) as! UIViewController, animated: true)
for oneView in (self.navigationController?.viewControllers)! {
if (oneView.isKind(of: (Home2: VC22).classForCoder())) {
let friendView = oneView as! (Home2: VC22)
friendView.testing = "Worked?"
}
}
ANYHELP IS WELCOMED!! Total hours spent: 8hrs
Your pass data task is going though this process
V12 --(pop)--> V11 --(switch)--> V21 --(push)--> V22
If you choose to pass data, you will need to pass it though all these three transactions and that's lots of coding and it's hard to practice.
Instead of passing data around, a better way to accomplish your goal is to store the data in V12, and then load it from V22. So now all you need to worry about is how to transfer user from V12 to V22.
You only need two lines of code to store and read data from UserDefaults
On your V12, before you are doing the above transactions, first save your data like this
UserDefaults.standard.set("yourStringData", forKey: "data")
Then when you reach VC22, from viewDidLoad, read stored info in this way
UserDefaults.standard.string(forKey: "data")
For your view transaction part, popToViewController is for going from a child view controller to it's parent. So when you switch your tab and get the UINavigationController, the topViewController must be VC21. So to go to VC22 given that you connect them though a storyboard segue, simply call it like this
let navController = self.tabBarController?.viewControllers![1] as! UINavigationController
let VC21 = navController.topViewController!
VC21.performSegue(withIdentifier: "segue", sender: nil)
You could also subclass your tabbar and create a variable to store your data there. Then both VC's would have access to the data. This way also alleviates the possible need for a singleton.
It sounds like you need to rethink your approach. It depends on the way you setup your app but it makes sense to store this data somewhere else besides directly passing it from one VC to another. Here are some options:
Singleton that stores your data and you pass that around from one VC to another so that if VC12 stores data in the Singleton, then VC22 can access it easily since it also has access to the singleton.
Have the Container View Controller store the data to be shared amongst its children view controllers. So, in this example, the TabBarController can store this data.
Use UserDefaults as a poor-mans Singleton/Persistence solution.
Use a full blown Data Persistence layer using SQLite, Core Data, or Realm for example to store and retrieve data.
... there are others but I think one of these solutions can work well for you depending on your needs.

How to load NSUserDefaults data on a Today Extension in Swift?

I've already searched a lot but any answer fixed the problem.
I've created an app group, selected it on both app and today extension, they're linked to the same group now. I'm trying to load data from the app and display it on a table view on the today extension, the thing is that I'm getting a crash saying that my array is nil, and it cannot be nil. I don't know why tho, cause I'm putting the data into the array. Here is my code:
func handleData() {
let defaults = NSUserDefaults(suiteName: "MY APP GROUP HERE")
if let descriptionsArr = defaults?.valueForKey("descriptions") as? [String] {
descriptions = descriptionsArr
}
defaults?.synchronize()
}
So, I'm pretty sure the app group is spelled right, and the value key. What could it be that is making my array nil? Really need help!
Thanks.

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.

Swift bad access error on empty value even with check

I am in the process of transitioning over to Swift from Obj-C and I think I have a fairly good understanding of the ? ! concepts. I am however experiencing a bad access crash on a field that is nil.
I am using
var cell :ContactIInfoTableViewCell!
cell = tableView.dequeueReusableCellWithIdentifier("notes", forIndexPath: indexPath) as! ContactIInfoTableViewCell
if let clinic = receivedVisitDetails?.clinicNotes {
cell.textLabel?.text = clinic
}
Which produces the error if empty. My understanding was that the IF statement would stop this issue but it hasn't.
What am I doing wrong here?
UPDATE
This is a detail view for the core data fetch.
NSManagedObject is set as
var receivedVisitDetails: VisitDetails! = nil
The prepareForSeague on the previous view
let visits:VisitDetails = fetchedResultsController.objectAtIndexPath(indexPath!) as! VisitDetails
taskController.receivedVisitDetails = visits
The crash only happens when the clinicNotes is null.
After all edits and comments, the problem is in:
#NSManaged var clinicNotes: String
It's optional in your Core Data model, but not in your class. Should be changed to:
#NSManaged var clinicNotes: String?
Here's similar problem where Imanou suggests mogenerator, which can handle it for you. It automatically generates two classes for each managed object. Human part is suitable for editing (not touched by mogenerator when updating your model) and machine is not suitable for editing (rewritten every time you do update your model). Human part inherits from machine part.
Whenever I'm working with Core Data I do use it. I strongly suggest it as well. You can also add it to build phase, so, it automatically update your machine classes when you're building your project.
When using this construction, you don't have to use ? as it is unwrapped for you.
try
if let clinic = receivedVisitDetails.clinicNotes {
cell.textLabel?.text = clinic
}