Saving text in UITextView Swift 3 - ios

I am creating a To-Do App on IOS Platform Swift 3
I am trying to save note in UITextView so when i hit back or terminate application the data is saved.
StoryBoard Have a UITextView and a save button at the navigation bar
How to make user enter his text in UITextView and save it
class Details: UIViewController, UITextViewDelegate{
// MARK: - IB
#IBOutlet weak var noteText: UITextView!
#IBAction func addNote(_ sender: UIButton) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let addNote = Note(context: context)
addNote.details = noteText.text!
//Saving
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var Notes: [Note] = []
func getData() {
do {
Notes = try context.fetch(Note.fetchRequest())
} catch {
print("Fetching Failed")
}
}
override func viewWillAppear(_ animated: Bool) {
getData()
}
override func viewDidLoad() {
super.viewDidLoad()
let MyIcon = UIImageView(image: UIImage(named: "037_Pen"))
self.navigationItem.titleView = MyIcon
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Any idea how to display it ?
Created Entity called Note with Attribute details of type String

Dealing with data in iOS application
If you want to save your data inside application then you need to do something more inside your application for data saving purpose. This way you can save data inside application weather terminate application it will show your saved data and fetch again.
1.) For Short Date save can use UserDefaults
2.) By using SQLite
3.) By Using Coredata
4.) By Using Realm, For more details check Example.

You need to create database to save the text value every time as per your requirement.
You can create the database by using any one of the below :
Core dataGet tutorial from here
SQLite Get tutorial from here
Save your text data by using anyone these and then fetch the data and assign at the UI.

Related

Swift Core Data - Save, Populate, Edit an Entity Attribute/Relationship

I'm pretty new to iOS dev/Core Data and am having trouble implementing one part of my workflow within my app. See below:
Core Data Properties:
item
Attributes: title, amount, date, status, category (rel), note (rel)
note
Attributes: title, contents, createdAt, updatedAt, item (rel)
When a user creates a new item, all attributes are required, except for .note as I'd like to give the user the option to create a note at a later time if only needed.
What I want to accomplish:
User selects row to display item details
On item details view, user selects notes (highlighted in yellow above) to go to Add/Edit notes
The note is just a single object that the user can enter/update the note. So basically, one note per item.
MY CODE
Xcode 11.5, Swift 5
import UIKit
import CoreData
class NoteVC: UIViewController, UITextFieldDelegate, UITextViewDelegate {
//MARK: - Core Data
var item: Item?
var context: NSManagedObjectContext!
//MARK: - Outlets
#IBOutlet weak var headerContainer: UIView!
#IBOutlet weak var headerTitle: UILabel!
#IBOutlet weak var noteView: UIView!
#IBOutlet weak var noteTitleTextField: UITextField!
#IBOutlet weak var noteContentTextView: UITextView!
#IBOutlet weak var noteDataLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
override func viewWillDisappear(_ animated: Bool) {
super .viewWillDisappear(animated)
//Update note
if let title = noteTitleTextField.text, !title.isEmpty {
item?.notes?.title = title
item?.notes?.contents = noteContentTextView.text
}
item?.notes?.updatedAt = Date()
item?.notes?.contents = noteContentTextView.text
}
private func setupView() {
noteTitleTextField.text = item?.notes?.title
noteContentTextView.text = item?.notes?.contents
noteDataLabel.text = DateHelper.convertDate(date: Date())
}
//MARK: - Actions
#IBAction func doneButtonTapped(_ sender: UIButton) {
item?.notes?.title = noteTitleTextField.text
item?.notes?.contents = noteContentTextView.text
item?.notes?.createdAt = Date()
dismiss(animated: true)
}
}
MY PROBLEM
I'm having an issue creating the new note and assign it to that item and therefore populating the note details for editing. I was able to set the attributes for item.date, .category successfully to another modal view controller (so the passing of data is working), but to no avail with the Notes. Not sure if its because of the relationship or not. Again, I'm a n00b to Core Data so please forgive me for sounding simple.
Any help is appreciated.
Asking for a friend, =P
adrapp
Your problem seems to be that you are not creating a Note entity before trying to assign its properties.
The correct pattern to create a note and associate it with your item would be something like this:
if let note = NSEntityDescription.insertNewObject(forEntityName: "note", into: context) as? Note {
//set note properties
note.title = noteTitleTextField.text
note.contents = noteContentTextView.text
note.createdAt = Date()
//set relationship to item
item.note = note
}
Please verify the name of the entity ("note") and class ("Note") match what you defined in your project.
To allow updating an existing note, you need to check first if there is an existing note. You could modify the code above as follows:
// get the existing note, if any, or create a new one
let note = item.note ?? NSEntityDescription.insertNewObject(forEntityName: "note", into: context) as? Note
// if note existed or was successfully created...
if let note = note {
//set note properties
note.title = noteTitleTextField.text
note.contents = noteContentTextView.text
if item.note == nil {
note.createdAt = Date()
}
else {
note.updatedAt = Date()
}
//set relationship to item
item.note = note
}

How can I load all the data stored in the array, when the app starts again (Swift)?

I have to implement a function that loads the stored data into the shopping list array, when the app starts, and a function that stores the current contents of my list when the button is pressed. I used UserDefaults class and it works for the second function (when the button is pressed) but not for the first one (when the app starts). If I restart the app and press the button, I see that only the last input was stored. How can I fix the code if I want to store all data from the array?
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var inputEntered: UITextField!
// keyboard gives up the first responder status and goes away if return is pressed
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
inputEntered.resignFirstResponder()
return true
}
var shoppingList: [String] = []
#IBAction func buttonAddToList(_ sender: UIButton) {
if let item = inputEntered.text, item.isEmpty == false { // need to make sure we have something here
shoppingList.append(item) // store it in our data holder
}
inputEntered.text = nil // clean the textfield input
print(shoppingList.last!) // print the last element to avoid duplicates on the console
storeData()
}
// this function stores the current contents of my list when the button is pressed
func storeData () {
let defaults = UserDefaults.standard
defaults.set(inputEntered.text, forKey: "Saved array")
print(defaults)
}
// to call the function storeDate(), when the app restarts
override func viewDidLoad() {
super.viewDidLoad()
inputEntered.delegate = self
// Do any additional setup after loading the view.
storeData()
}
}
You can add a getter and a setter to your array and persist your values to user defaults. This way you don't need to call storeData and or remembering to load the data when initialising your array:
var shoppingList: [String] {
get {
UserDefaults.standard.stringArray(forKey: "shoppingList") ?? []
}
set {
UserDefaults.standard.set(newValue, forKey: "shoppingList")
}
}
You are calling storeData() in viewDidLoad, but inputEntered is empty then, so you are storing blank data.
Also, defaults.set(inputEntered.text, forKey: "Saved array") doesn't append new data onto the key -- it overwrites what is there. So you are not storing the array, you are only storing the last value.
You need to store shoppingList to store the array.
I'm unsure if I have got what you are asking for but have you tried looking into UITextFieldDelegate.
If you add this, it will ensure add the protocols of which I am sure there is a method that can be called when the user finishes editing text field.

How do I share data between iOS app and today view extension

I'm trying to show the persons name in the today view extension; but I can't seem to do this. I've watched so many YouTube video's but they never helped. So if you can, I want you to answer this.
How the app works: You type your name inside the app. And the it will show in the today view extension.
About the app: I have a text field and button. The textfield is the persons name and the button is the save the name.
I want to show the name in the today extension.
Thank you.
Add the group to the entitlements/capabilities.
Go to the capabilities tab of the app's target
Enable App Groups
Create a new app group, entitled something appropriate. It must start with group..
Let Xcode go through the process of creating this group for you.
Save data to NSUserDefaults with group ID and use it in your extension.
From Apple's App Extension Guide :
https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html
In the main app, save person name:
let defaults = UserDefaults(suiteName: "your group ID")
defaults!.set("person name", forKey: "key for person name")
defaults!.synchronize()
In the extension, you can use saved person name:
let defaults = UserDefaults(suiteName: "your group ID")
let savedPersonName = defaults!.string(forKey: "key for person name")
Just a quick notice as I too have missed it
In order to get it working create the group on app target
and then on Today's target add Group Capability as well and tick the one just created from the main app's target (it should be listed)
here is a simple example of today extension in this example, I am only showing and updating the user name
this my today extension storyboard image
and today-view-Controller code is:
import UIKit
import NotificationCenter
class TodayViewController: UIViewController, NCWidgetProviding {
#IBOutlet weak var lnameLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
if let name = UserDefaults.init(suiteName: "group.com.ahmad.widget")?.value(forKey: "name"){
lnameLabel.text = name as? String
}
else{
lnameLabel.text = "Wait..."
}
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResult.Failed
// If there's no update required, use NCUpdateResult.NoData
// If there's an update, use NCUpdateResult.NewData
completionHandler(NCUpdateResult.newData)
}
}
I create a storyboard and add a button in this than on his button action I update the user name on today extension
code of viewController is :
class ViewController: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var nameTextfield: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func SetNameOnWigetAction(_ sender: Any) {
nameLabel.text = nameTextfield.text
UserDefaults.init(suiteName: "group.com.ahmad.widget")?.setValue(nameTextfield.text, forKey: "name")
}
}

Saving data in UITextView

I'm writing notes app for iOS and I want all data which user enter in notes will be automatically saved when user typing automatically. I'm using Core Data and now I save data on viewWillDisappear, but I want the data also be saved if user terminate the app or the app will be automatically terminated in the background.
I use this code:
import UIKit
import CoreData
class AddEditNotes: UIViewController, UITextViewDelegate {
#IBOutlet weak var textView: UITextView!
var note: Note!
var notebook: Notebook?
var userIsEditing = true
var context: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
context = appDelegate.persistentContainer.viewContext
if (userIsEditing == true) {
textView.text = note.text!
title = "Edit Note"
}
else {
textView.text = ""
}
}
override func viewWillDisappear(_ animated: Bool) {
if (userIsEditing == true) {
note.text = textView.text!
}
else {
self.note = Note(context: context)
note.setValue(Date(), forKey: "dateAdded")
note.text = textView.text!
note.notebook = self.notebook
}
do {
try context.save()
print("Note Saved!")
}
catch {
print("Error saving note in Edit Note screen")
}
}
}
I understand what I can use applicationWillTerminate for this, but how I can pass there the data user entered? This functionality is in default notes app from Apple. But how it can be released?
There are two subtasks to saving the data: updating the Core Data entity with the contents of the text view and saving the Core Data context.
To update the contents of the Core Data entity, add a function to the AddEditNotes class that saves the text view contents.
func saveTextViewContents() {
note.text = textView.text
// Add any other code you need to store the note.
}
Call this function either when the text view ends editing or the text changes. If you call this function when the text changes, the Core Data entity will always be up to date. You won't have to pass the data to the app delegate because the app delegate has the Core Data managed object context.
To save the Core Data context, add a second function to the AddEditNotes class that saves the context.
func save() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate {
appDelegate.saveContext()
}
}
This function assumes you selected the Use Core Data checkbox when you created the project. If you did, the app delegate has a saveContext function that performs the Core Data save.
You can now replace the code you wrote in viewWillDisappear with the calls to the two functions to save the text view contents and save the context.
The last code to write is to go to your app delegate file and add the following line of code to the applicationDidEnterBackground and applicationWillTerminate functions:
self.saveContext()
By adding this code your data will save when someone quits your app.

What is causing this to happen? NSUserDefaults couldn't share the data between the IOS and Watch in a watchkit Project

I could get the data from the IOS,but watch. If I save data by watchkit extension, IOS couldn't get the data.So it's so strange. And I have added the Group and create the profile.
following is my Snippet:
class InterfaceController: WKInterfaceController {
#IBOutlet var outputLabel: WKInterfaceLabel!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
// load the data from the IOS
#IBAction func loadData() {
let userDefaults = UserDefaults.standard
userDefaults.synchronize()
let outputData = userDefaults.string(forKey: "share")
self.outputLabel.setText(outputData)
}
}
class ViewController: UIViewController {
#IBOutlet weak var inputData: UITextField!
#IBOutlet weak var inputLabel: UILabel!
#IBOutlet weak var outputLabel: UILabel!
// input string into the fieldText and save them in Group by NSUserDefaults
#IBAction func saveData(_ sender: AnyObject) {
let inputData1 = self.inputData.text
self.inputLabel.text = inputData1
let userDefaults = UserDefaults.standard
userDefaults.setValue(inputData1, forKey: "share")
userDefaults.synchronize()
self.outputLabel.text = userDefaults.string(forKey: "share")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Even though an app extension bundle is nested within its containing app’s bundle, the running app extension and containing app have no direct access to each other’s containers.
To enable data sharing, use Xcode or the Developer portal to enable app groups for the containing app and its contained app extensions. Next, register the app group in the portal and specify the app group to use in the containing app.
After you enable app groups, an app extension and its containing app can both use the NSUserDefaults API to share access to user preferences. To enable this sharing, use the initWithSuiteName: method to instantiate a new NSUserDefaults object, passing in the identifier of the shared group
Objective C
// Create and share access to an NSUserDefaults object
NSUserDefaults * sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName: #"com.example.domain.MyShareExtension"];
// Use the shared user defaults object to update the user's account
[sharedDefaults setObject:#"Your Value" forKey:#"Your Key"];
swift:
func saveUserDefaults() {
let sharedDefaults = NSUserDefaults(suiteName: "com.example.domain.MyShareExtension")
sharedDefaults?.setObject("Your Value", forKey: "Your Key")
sharedDefaults?.synchronize()
}
If you want to create a shared userdefaults between devices you need to use the following:
GET
UserDefaults(suiteName: "GROUP NAME")!.object(forKey: "share")
SET
UserDefaults(suiteName: "GROUP NAME")!.set("Test", forKey: "share")
Where the GROUP NAME is the group name you did set when you created your app group.

Resources