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

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")
}
}

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
}

Store my custom Class in UserDefaults, and casting(parsing) this UserDefault to reach values (Swift 4.2)

I have created a dummy IOS Application to explain my questions well. Let me share it with all details:
There are 2 Pages in this dummy IOS Application: LoginPageViewController.swift and HomepageViewController.swift
Storyboard id values are: LoginPage, Homepage.
There is login button in Login page.
There are 3 labels in Homepage.
App starts with Login page.
And i have a class file: UserDetail.swift
And there is one segue from login page to home page. Segue id is: LoginPage2Homepage
UserDetail.swift file
import Foundation
class UserDetail {
var accountIsDeleted = false
var userGUID : String?
var userAge: Int?
}
LoginPageViewController.swift file
import UIKit
class LoginPageViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func loginButtonPressed(_ sender: UIButton) {
var oUserDetail = UserDetail()
oUserDetail.accountIsDeleted = true
oUserDetail.userAge = 38
oUserDetail.userName = "Dirk Kuyt"
UserDefaults.standard.set(oUserDetail, forKey: "UserCredentialUserDefaults")
UserDefaults.standard.synchronize()
performSegue(withIdentifier: "LoginPage2Homepage", sender: nil)
}
}
HomepageViewController.swift file
import UIKit
class HomepageViewController: UIViewController {
var result_userGUID = ""
var result_userAge = 0
var result_isDeleted = false
#IBOutlet weak var labelUserGuidOutlet: UILabel!
#IBOutlet weak var labelAgeOutlet: UILabel!
#IBOutlet weak var labelAccountIsDeletedOutlet: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.setVariablesFromUserDefault()
labelUserGuidOutlet.text = result_userGUID
labelAgeOutlet.text = String(result_userAge)
labelAccountIsDeletedOutlet.text = String(result_isDeleted)
}
func setVariablesFromUserDefault()
{
if UserDefaults.standard.object(forKey: "UserCredentialUserDefaults") != nil
{
// I need a help in this scope
// I have checked already: My UserDefault exists or not.
// I need to check type of the value in UserDefault if UserDefault is exists. I need to show print if type of the value in UserDefault is not belongs to my custom class.
// And then i need to cast UserDefault to reach my custom class's properties: userGUID, userAge, isDeleted
}
else
{
print("there is no userDefault which is named UserCredentialUserDefaults")
}
}
}
My purposes:
I would like to store my custom class sample(oUserDetail) in UserDefaults in LoginPageViewController with login button click action.
I would like to check below in home page as a first task: My UserDefault exists or not ( I did it already)
I would like to check this in home page as a second task: if my UserDefault exists. And then check type of the UserDefault value. Is it created with my custom class? If it is not. print("value of userdefault is not created with your class")
Third task: If UserDefault is created with my custom class. And then parse that value. Set these 3 variables: result_userGUID, result_userAge, result_isDeleted to show them in labels.
I get an error after I click the login button in Login Page. Can't I store my custom class in UserDefaults? I need to be able to store because I see this detail while I am writing it:
UserDefaults.standart.set(value: Any?, forKey: String)
My custom class is in Any scope above. Isn't it?
You can't store a class instance without conforming to NSCoding / Codable protocols
class UserDetail : Codable {
var accountIsDeleted:Bool? // you can remove this as it's useless if the you read a nil content from user defaults that means no current account
var userGUID : String?
var userAge: Int?
}
store
do {
let res = try JSONEncoder().encode(yourClassInstance)
UserDefaults.standard.set(value:res,forKey: "somekey")
}
catch { print(error) }
retrieve
do {
if let data = UserDefaults.standard.data(forKey:"somekey") {
let res = try JSONDecoder().decode(UserDetail.self,from:data)
} else {
print("No account")
}
}
catch { print(error) }

Accessing iOS's UserDefaults values in the WatchKit App and WatchKit Extension

I have one button, one text box and one button in my iOS app. The user inputs text, presses the button and the user input is shown in the label. Also, the user input is saved when the app is closed and ran again by UserDefaults. The code for the iOS app is as follows:
import UIKit
class ViewController: UIViewController {
// Connecting the front-end UI elements to the code
#IBOutlet weak var myTextField: UITextField!
#IBOutlet weak var myLabel: UILabel!
// Connecting the button to the code
#IBAction func myButton(_ sender: Any) {
UserDefaults.standard.set(myTextField.text, forKey: "myText")
myLabel.text = UserDefaults.standard.object(forKey: "myText") as? String
}
override func viewDidLoad() {
super.viewDidLoad()
// If there is any value stored previously, the stored data will be shown in the label and the text box to edit.
if (UserDefaults.standard.object(forKey: "myText") as? String) != nil {
UserDefaults.standard.set(myTextField.text, forKey: "myText")
myLabel.text = UserDefaults.standard.object(forKey: "myText") as? String
}
}
}
But now, I want to access the data stored in the key myText in the WatchKit Extension and display the text in the WatchKit App. I have inserted a label in the WatchKit App but want the WatchKit Extension to access the data stored in UserDefaults.standard.object(forKey: "myText").
import WatchKit
import Foundation
class InterfaceController: WKInterfaceController {
// Connecting the label to the code
#IBOutlet var watchKitLabel: WKInterfaceLabel!
override func awake(withContext context: Any?) {
super.awake(withContext: context)
watchKitLabel.setText((UserDefaults.standard.object(forKey: "myText")) as? String)
}
}
Can anyone please help me? I have read the documentations about App Groups on various sources, but the documentation is either of Swift v3.1 or Objective-C. Or any other solution except App Groups will work. Also, I want the data to be platform-independent. So, if the user inputs once in the iOS app and quits, I want the data to be accessible by the watch right away.
Thanks in advance... :)

Saving text in UITextView Swift 3

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.

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