How to remove UserDefaults data swift? [duplicate] - swift

This question already has answers here:
How to auto clear NSUserDefault values in swift? [duplicate]
(5 answers)
Closed 3 years ago.
userdaeults does not delete the data in memory, although I do delete it. Keychain does the deletion. I share the code below. It calls the handlelogout function when exiting. The recording is fine, but when I exit, it does not delete the data that I have saved.
#objc func deleteee(){
do {
try keychain.remove("chipnumbernew")
} catch let error {
print("error: \(error)")
}
}
#objc func deleteeetimer(){
UserDefaults.standard.removeObject(forKey: "timertext")
UserDefaults.standard.removeObject(forKey: "timertext2")
UserDefaults.standard.synchronize()
}
#objc func handleLogout(){
do {
deleteee()
deleteeetimer()
try Auth.auth().signOut()
let mainTableVC = LoginViewController()
let navController = UINavigationController(rootViewController: mainTableVC)
self.present(navController, animated: true, completion: {
//
})
} catch let signOutError as NSError {
print ("Giriş Yapılamadı: %#", signOutError)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let token = try? keychain.getString("chipnumbernew")
chip1InfoString = token
if let strtimer = UserDefaults.standard.string(forKey: "timertext") {
print("strtimer", strtimer)
timertextNew.text = strtimer
}
if let strtimer2 = UserDefaults.standard.string(forKey: "timertext2") {
print("strtimer2", strtimer2)
timertext2New.text = strtimer2
} }

The removeObject in UserDefaults itself will clear the value. You can verify that directly in the plist. Please see this post for accessing plist.
Still, you want to clean up, you can try below code.
if let appDomain = Bundle.main.bundleIdentifier {
UserDefaults.standard.removePersistentDomain(forName: appDomain)
}

Related

Using CloudKit Containers Properly

I have an app that uses NSPersistentCloudKitContainer for iCloud Sync to users who want it and has worked for all platforms (iPhone/iPad/Mac). But now, trying to add Apple Watch support, I realize that I might have implemented CloudKit wrong this whole time.
Code:
final class CoreDataManager {
static let sharedManager = CoreDataManager()
private var observer: NSKeyValueObservation?
lazy var persistentContainer: NSPersistentContainer = {
setupContainer()
}()
private func setupContainer() -> NSPersistentContainer {
var useCloudSync = UserDefaults.standard.bool(forKey: "useCloudSync")
#if os(watchOS)
useCloudSync = true
#endif
let containerToUse: NSPersistentContainer?
if useCloudSync {
containerToUse = NSPersistentCloudKitContainer(name: "app")
} else {
containerToUse = NSPersistentContainer(name: "app")
}
guard let container = containerToUse, let description = container.persistentStoreDescriptions.first else {
fatalError("Could not get a container!")
}
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
if !useCloudSync {
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in }
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
container.viewContext.transactionAuthor = "app"
container.viewContext.automaticallyMergesChangesFromParent = true
NotificationCenter.default.addObserver(self, selector: #selector(type(of: self).storeRemoteChange(_:)), name: .NSPersistentStoreRemoteChange, object: container.persistentStoreCoordinator)
return container
}
}
//MARK: - History token
private lazy var historyQueue: OperationQueue = {
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
return queue
}()
private var lastHistoryToken: NSPersistentHistoryToken? = nil {
didSet {
guard let token = lastHistoryToken, let data = try? NSKeyedArchiver.archivedData(withRootObject: token, requiringSecureCoding: true) else { return }
do {
try data.write(to: tokenFile)
} catch {
print("###\(#function): Failed to write token data. Error = \(error)")
}
}
}
private lazy var tokenFile: URL = {
let url = NSPersistentCloudKitContainer.defaultDirectoryURL().appendingPathComponent("app", isDirectory: true)
if !FileManager.default.fileExists(atPath: url.path) {
do {
try FileManager.default.createDirectory(at: URL, withIntermediateDirectories: true, attributes: nil)
} catch {
print("###\(#function): Failed to create persistent container URL. Error = \(error)")
}
}
return url.appendingPathComponent("token.data", isDirectory: false)
}()
init() {
// Load the last token from the token file.
if let tokenData = try? Data(contentsOf: tokenFile) {
do {
lastHistoryToken = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSPersistentHistoryToken.self, from: tokenData)
} catch {
print("###\(#function): Failed to unarchive NSPersistentHistoryToken. Error = \(error)")
}
}
}
//MARK: - Process History
#objc func storeRemoteChange(_ notification: Notification) {
// Process persistent history to merge changes from other coordinators.
historyQueue.addOperation {
self.processPersistentHistory()
}
}
func processPersistentHistory() {
let backContext = persistentContainer.newBackgroundContext()
backContext.performAndWait {
let request = NSPersistentHistoryChangeRequest.fetchHistory(after: lastHistoryToken)
let result = (try? backContext.execute(request)) as? NSPersistentHistoryResult
guard let transactions = result?.result as? [NSPersistentHistoryTransaction], !transactions.isEmpty else {
print("No transactions from persistent history")
return
}
// Update the history token using the last transaction.
if let lastToken = transactions.last?.token {
lastHistoryToken = lastToken
}
}
}
What I've noticed:
I only have 10 items on my test device. When I boot up the watch app and look at the console, it looks like it's going through the entire history of every addition and deletion of items I've ever done, making it take a long time to download the 10 items that I actually have left.
I looked in my iCloud storage and found out that my app is taking up a lot of space (48 MB) when the 10 items are just entities with a few strings attached to them
Research:
I've done a lot of research and found that it could be from setting NSPersistentHistoryTrackingKey only when the user is not using iCloud Sync. But when I enable NSPersistentHistoryTrackingKey for iCloud users too, the watch app still takes a very long time to download the 10 items.
I know Core Data can be tricky, and changing persistentStoreDescriptions or other attributes of the container can be an app-breaking bug to existing users. So I need something that works for new and existing users.
Ask:
Does anyone know how to fix this problem or have had similar issues? I've been trying to figure this out for almost a week now, and any help would be greatly appreciated!

How to Change A Label's Text From Another View Controller and Save it With Core Data

I have a Input(UITextfied) and i want the data in it to change DesA in My other ViewController
My First ViewController: Facial (where the label DesA is)
My Second ViewController: ServiceAChangeViewController (where The Input and button is)
(sorry for the weird names)
Essentially i want to write a text in my UITextField, then click a button that saves it using core data and then changes the DesA label in my First View Controller
I followed some tutorials on youtube and wrote this code
Second ViewController(ServiceAChangeViewController):
var score = Facial(nibName: nil, bundle: nil).TitleChange
var OutputLabel = Facial(nibName: nil, bundle: nil).DesA
``` #IBAction func saveData(_ sender: Any) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Entity", in: context)
let newEntity = NSManagedObject(entity: entity!, insertInto: context)
newEntity.setValue(score, forKey: "titleA")
do {
try context.save()
print("saved")
} catch {
print("Failed savign")
}
}
func getData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Entity")
request.returnsObjectsAsFaults = false
do {
if let result = try? context.fetch(request) as? [NSManagedObject] {
for data in result {
score = data.value(forKey: "titleA") as! String
}
} else {
print("Failed")
}
}
}
#IBAction func SubmitAnswer(_ sender: Any) {
OutputLabel?.text = score
score = String(InputA.text!)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
getData()
OutputLabel?.text = score
}```
First ViewController(Facial):
#IBOutlet var TitleA: UILabel!
var TitleChange = String()
override func viewDidAppear(_ animated: Bool) {
}
But When I run the app and enter the text, it doesn't seem to change it nor save it.
try this for saving core data entity, you also can use "didset" for variable
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newInfo = Entity(entity: Entity.entity(), insertInto: context)
// do something with entity
do {
try context.save() } catch let error as NSError {
print("Error... \(error), \(error)")
}

Return response as object in swift

I have a function that connects to an API to retrieve data. The API takes two parameters accessCode (provided by user in a text box) and then UDID (UDID of their device). I can parse the data from within the function, but only locally. I need to store the values that are returned but am unsure on how to return them properly. Essentially I need this to return the json object as a dictionary (I think...) so it can be parsed outside of the async task. I've read through the swift documentation and that's where I found out how to do the requests, but I can't find a way to store the returned values in memory for access outside of the function.
func getResponse(accessCode:String, UDID:String, _ completion: #escaping (NSDictionary) -> ()) {
let urlPath = "https://apihosthere.com/api/validate?accessCode=" + accessCode + "&UDID=" + UDID
guard let url = URL(string: urlPath) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary {
let results = jsonResult as? NSDictionary
print(results)
completion(results!)
}
} catch {
//Catch Error here...
}
}
task.resume()
}
First of all don't use NSDictionary in Swift, use native [String:Any] and declare the type as optional to return nil if an error occurs.
And never use .mutableContainers in Swift, the option is useless.
func getResponse(accessCode:String, UDID:String, completion: #escaping ([String:Any]?) -> Void)) {
let urlPath = "https://apihosthere.com/api/validate?accessCode=" + accessCode + "&UDID=" + UDID
guard let url = URL(string: urlPath) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error else {
print(error)
completion(nil)
return
}
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!) as? [String:Any] {
print(jsonResult)
completion(jsonResult)
} else {
completion(nil)
}
} catch {
print(error)
completion(nil)
}
}
task.resume()
}
Your mistake is that you don't consider the closure, you have to execute the entire code inside the completion handler
#IBAction func StartWizard(_ sender: UIButton) {
//Store entered access code
let accessCode = AccessCodeField.text!
//Call API to validate Access Code
getResponse(accessCode:accessCode, UDID:myDeviceUDID) { [weak self] result in
if let accessCodeFound = result?["Found"] as? Bool {
print("Value of Found during function:")
//If access code is valid, go to License view
print(accessCodeFound)
if accessCodeFound {
//Load License View
DispatchQueue.main.async {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let licenseController = storyboard.instantiateViewController(identifier: "LicenseViewPanel")
self?.show(licenseController, sender: self)
}
}
}
}
}
Your completion closure should handle the obtained data. You would call the function like this:
getResponse(accessCode: "code", UDID: "udid", completion: { result in
// Do whatever you need to do with the dictionary result
}
Also, I'd recommend you to change your NSDictionary with a swift Dictionary.
This is what the API returns as a response
{
AccessCode = 00000000;
Client = "0000 - My Company Name";
EmailAddress = "brandon#brandonthomas.me";
FirstName = Brandon;
Found = 1;
LastName = Thomas;
Status = A;
UDIDregistered = 1;
}
And this is what calls the function. I am calling at after clicking a button after an access code is being entered in a text field.
#IBAction func StartWizard(_ sender: UIButton) {
//Store entered access code
let accessCode = AccessCodeField.text!
var accessCodeFound: Bool? = nil
//Call API to validate Access Code
getResponse(accessCode:accessCode, UDID:myDeviceUDID) { result in
accessCodeFound = result["Found"] as! Bool
print("Value of Found during function:")
print(accessCodeFound)
//accessCodeFound = true
}
//If access code is valid, go to License view
print("Value of Found after function:")
print(accessCodeFound)
//accessCodeFound = nil ???
//it seems the value is getting reset after the function completes
if accessCodeFound == true{
//Load License View
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let licenseController = storyboard.instantiateViewController(identifier: "LicenseViewPanel")
self.show(licenseController, sender: Any?.self)
}
}

presently delete core data item in uicollectionviewcell through tag

I am trying to permanently delete a single element from a core data set. Right now the code below removes the core data item but it does not permanently delete it. Once the class is called again it shows up again like nothing happened. I want it permanently deleted.
func loadData() {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let fetch = NSFetchRequest<Item>(entityName: "Item")
do {
self.itemName = try managedContext.fetch(fetch)
}
catch {
print("Could not load. \(error)")
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
var itemName : [Item] = []
#objc func elete(_ sender:UIButton){
itemName.remove(at: sender.tag)
collectionView.reloadData()
}

Deleting from Core Data Swift 4

I have a Favorites button that when clicked will add the image of the particular character to CoreData.
#IBAction func favButtonClicked(_ sender: UIButton) {
if sender.isSelected != true {
saveFav()
}
}
func saveFav() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newFav = NSEntityDescription.insertNewObject(forEntityName: "Favorite", into: context)
newFav.setValue((figure?.image)!, forKey: "favFig")
do {
try context.save()
print("Saved!")
} catch {
//Process Error
}
}
My question is how do I remove that image from CoreData when clicking the button again?
In coredata every object should have Id if you want to delete it , like this
let fetchRequest: NSFetchRequest<Favorite> = Favorite.fetchRequest()
fetchRequest.predicate = Predicate.init(format: "FavoriteID==\(ID)")
do {
let objects = try context.fetch(fetchRequest)
for object in objects {
context.delete(object)
}
try context.save()
} catch _ {
// error handling
}