how to save data in swift 4 with codable and file manager - swift4

hi every ones there anyone know how to save data in swift 4
i made a emoji app that i can describe the emojis and i have a future that i can save the new emoji in the app i wrote this code in my emoji class but as i want return the emoji i get an error please help me .
import Foundation
struct Emoji : Codable {
var symbol : String
var name : String
var description : String
var usage : String
static let documentsdirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
static let archiveurl = documentsdirectory.appendingPathComponent("emojis").appendingPathExtension("plist")
static func SaveToFile (emojis: [Emoji]) {
let propetyencod = PropertyListEncoder()
let encodemoj = try? propetyencod.encode(emojis)
try? encodemoj?.write(to : archiveurl , options : .noFileProtection)
}
static func loadeFromFile () -> [Emoji] {
let propetydicod = PropertyListDecoder()
if let retrivdate = try? Data(contentsOf: archiveurl),
let decodemoj = try?
propetydicod.decode(Array<Emoji>.self, from: retrivdate){
}
return decodemoj in this line i get error
}
}

The error occurs because decodemoj is out of scope. You need to write
static func loadeFromFile() -> [Emoji] {
let propetydicod = PropertyListDecoder()
if let retrivdate = try? Data(contentsOf: archiveurl),
let decodemoj = try? propetydicod.decode(Array<Emoji>.self, from: retrivdate) {
return decodemoj
}
return [Emoji]()
}
and return an empty array in case of an error. Alternatively declare the return value as optional array and return nil.
But why not a do - catch block?
static func loadeFromFile() -> [Emoji] {
let propetydicod = PropertyListDecoder()
do {
let retrivdate = try Data(contentsOf: archiveurl)
return try propetydicod.decode([Emoji].self, from: retrivdate)
} catch {
print(error)
return [Emoji]()
}
}

Related

File Exporter shows export sheet with no files

I'm trying to write JSON Data to a file, then export it.
Writing went fine:
let jsonData = try JSONEncoder().encode(JSON)
if let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let pathWithFileName = documentDirectory.appendingPathComponent("json.json")
filePath = pathWithFileName.path()
try jsonData.write(to: pathWithFileName)
exportFile = true
}
I also have a Doc struct which is necessary for .fileExporter()
struct Doc: FileDocument {
var url: String
static var readableContentTypes: [UTType] {[.json]}
init(url: String) {
self.url = url
}
init(configuration: ReadConfiguration) throws {
self.url = ""
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
let file = try! FileWrapper(url: URL(fileURLWithPath: url), options: .immediate)
return file
}
}
When I print out the file's contents from the function above, everything shows up.
However when I call .fileExporter():
.fileExporter(isPresented: $exportFile, document: Doc(url: filePath), contentType: .json) { (result) in
do {
let fileURL = try result.get()
print(fileURL)
} catch {
print(error.localizedDescription)
}
}
The export sheet shows up, but there are no items to be moved (see picture)
I have no idea what's going on here so good luck to everyone who tries to solve this.

Facing issues with ContentBlockerRequestHandler of Safari extension

I am currently working on a safari app extension that blocks content. I want the user to configure the rule (turning a rule on and off). Since I can’t overwrite the bundled JSON files and we can’t write to the documents folder, as it’s not accessible to the extension I decided to use App Groups. My approach looks like this:
Within the ContentBlockerRequestHandler I want to save the blockerList.json into the app group (Only when launched for the first time)
When this is done I want that the handler reads from the app group by taking the url of my json which is within the app group instead of taking the default json in the extension
Since I can not debug the handler I don't know if I am on the right path. The following shows my code:
class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
guard let rulesUrl = loadRules() else {
let clonedRules = cloneBlockerList()
save(rules: clonedRules)
return
}
guard let attachment = NSItemProvider(contentsOf: rulesUrl) else { return }
let item = NSExtensionItem()
item.attachments = [attachment]
context.completeRequest(returningItems: [item], completionHandler: nil)
}
private func cloneBlockerList() -> [Rule] {
var rules: [Rule] = []
if let url = Bundle.main.url(forResource: "blockerList", withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(ResponseData.self, from: data)
rules = jsonData.rules
} catch {
print("error:(error)")
}
}
return rules
}
private func save(rules: [Rule]) {
let documentsDirectory = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "my group identifier")
let archiveURL = documentsDirectory?.appendingPathComponent("rules.json")
let encoder = JSONEncoder()
if let dataToSave = try? encoder.encode(rules) {
do {
try dataToSave.write(to: archiveURL!)
} catch {
// TODO: ("Error: Can't save Counters")
return;
}
}
}
private func loadRules() -> URL? {
let documentFolder = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "my group identifier")
guard let jsonURL = documentFolder?.appendingPathComponent("rules.json") else {
return nil
}
return jsonURL
}
}
Thankful for any help

just incompatibility or real error in swift code

hello i am working with swift 6.2 ( the performance of my pc doesn't support ++)
i have copied from open classroom this program in my file but there are many errors
i just want to know if it is just problem of swift compatibility or there are really errors in the code
//****************
import UIKit
class QuestionManager {
private let url = URL(string: "https://opentdb.com/api.php?amount=10&type=boolean")!
// exemple of message error: use of unresolved identifier 'URL'
static let shared = QuestionManager()
private init() {}
// FIX IT : replace static with class ...
// AND LOT OF MESSAGE ERROR LIKE THIS...
func get(completionHandler: #escaping ([Question]) -> ()) {
let task = URLSession.shared.dataTask(with: self.url) { (data, response, error) in
guard error == nil else {
completionHandler([Question]())
return
}
DispatchQueue.main.async {
completionHandler(self.parse(data: data))
}
}
task.resume()
}
private func parse(data: Data?) -> [Question] {
guard let data = data,
let serializedJson = try? JSONSerialization.jsonObject(with: data, options: []),
let parsedJson = serializedJson as? [String: Any],
let results = parsedJson["results"] as? [[String: Any]] else {
return [Question]()
}
return getQuestionsFrom(parsedDatas: results)
}
private func getQuestionsFrom(parsedDatas: [[String: Any]]) -> [Question]{
var retrievedQuestions = [Question]()
for parsedData in parsedDatas {
retrievedQuestions.append(getQuestionFrom(parsedData: parsedData))
}
return retrievedQuestions
}
private func getQuestionFrom(parsedData: [String: Any]) -> Question {
if let title = parsedData["question"] as? String,
let answer = parsedData["correct_answer"] as? String {
return Question(title: String(htmlEncodedString: title)!, isCorrect: (answer == "True"))
}
return Question()
}
}
extension String {
init?(htmlEncodedString: String) {
guard let data = htmlEncodedString.data(using: .utf8) else {
return nil
}
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue
]
guard let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) else {
return nil
}
self.init(attributedString.string)
}
}

Writing data to a file with path using NSKeyedArchiver archivedData throws Unrecognized Selector for Swift 4.2

I am attempting to use the NSKeyedArchiver to write a Codable to disk.
All the questions I could find on the subject using deprecated methods. I can't find any SO questions or tutorials using the Swift 4 syntax.
I am getting the error
-[_SwiftValue encodeWithCoder:]: unrecognized selector sent to instance
Which I am guessing is the try writeData.write(to: fullPath) line in my UsersSession class.
What is the proper way to write data to a file in Swift 4.2?
struct UserObject {
var name : String?
}
extension UserObject : Codable {
enum CodingKeys : String, CodingKey {
case name
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
}
}
UserSession.swift
class UserSession {
static let shared = UserSession()
let fileName = "userdata.dat"
var user : UserObject?
lazy var fullPath : URL = {
return getDocumentsDirectory().appendingPathComponent(fileName)
}()
private init(){
print("FullPath: \(fullPath)")
user = UserObject()
load()
}
func save(){
guard let u = user else { print("invalid user data to save"); return}
do {
let writeData = try NSKeyedArchiver.archivedData(withRootObject: u, requiringSecureCoding: false)
try writeData.write(to: fullPath)
} catch {
print("Couldn't write user data file")
}
}
func load() {
guard let data = try? Data(contentsOf: fullPath, options: []) else {
print("No data found at location")
save()
return
}
guard let loadedUser = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? UserObject else {
print("Couldn't read user data file.");
return
}
user = loadedUser
}
func getDocumentsDirectory() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
}
Since you are using Codable, you should first encode to Data and then archivedData. Here is the code:
func save(){
guard let u = user else { print("invalid user data to save"); return}
do {
// Encode to Data
let jsonData = try JSONEncoder().encode(u)
let writeData = try NSKeyedArchiver.archivedData(withRootObject: jsonData, requiringSecureCoding: false)
try writeData.write(to: fullPath)
} catch {
print("Couldn't write user data file")
}
}
func load() {
guard let data = try? Data(contentsOf: fullPath, options: []) else {
print("No data found at location")
save()
return
}
guard let loadedUserData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? Data else {
print("Couldn't read user data file.");
return
}
// Decode Data
user = try? JSONDecoder().decode(UserObject.self, from: loadedUserData)
}

Codable to CKRecord

I have several codable structs and I'd like to create a universal protocol to code them to CKRecord for CloudKit and decode back.
I have an extension for Encodable to create a dictionary:
extension Encodable {
var dictionary: [String: Any] {
return (try? JSONSerialization.jsonObject(with: JSONEncoder().encode(self), options: .allowFragments)) as? [String: Any] ?? [:]
}
}
Then in a protocol extension, I create the record as a property and I try to create a CKAsset if the type is Data.
var ckEncoded: CKRecord? {
// Convert self.id to CKRecord.name (CKRecordID)
guard let idString = self.id?.uuidString else { return nil }
let record = CKRecord(recordType: Self.entityType.rawValue,
recordID: CKRecordID(recordName: idString))
self.dictionary.forEach {
if let data = $0.value as? Data {
if let asset: CKAsset = try? ckAsset(from: data, id: idString) { record[$0.key] = asset }
} else {
record[$0.key] = $0.value as? CKRecordValue
}
}
return record
}
To decode:
func decode(_ ckRecord: CKRecord) throws {
let keyIntersection = Set(self.dtoEncoded.dictionary.keys).intersection(ckRecord.allKeys())
var dictionary: [String: Any?] = [:]
keyIntersection.forEach {
if let asset = ckRecord[$0] as? CKAsset {
dictionary[$0] = try? self.data(from: asset)
} else {
dictionary[$0] = ckRecord[$0]
}
}
guard let data = try? JSONSerialization.data(withJSONObject: dictionary) else { throw Errors.LocalData.isCorrupted }
guard let dto = try? JSONDecoder().decode(self.DTO, from: data) else { throw Errors.LocalData.isCorrupted }
do { try decode(dto) }
catch { throw error }
}
Everything works forth and back except the Data type. It can't be recognized from the dictionary. So, I can't convert it to CKAsset. Thank you in advance.
I have also found there is no clean support for this by Apple so far.
My solution has been to manually encode/decode: On my Codable subclass I added two methods:
/// Returns CKRecord
func ckRecord() -> CKRecord {
let record = CKRecord(recordType: "MyClassType")
record["title"] = title as CKRecordValue
record["color"] = color as CKRecordValue
return record
}
init(withRecord record: CKRecord) {
title = record["title"] as? String ?? ""
color = record["color"] as? String ?? kDefaultColor
}
Another solution for more complex cases is use some 3rd party lib, one I came across was: https://github.com/insidegui/CloudKitCodable
So I had this problem as well, and wasn't happy with any of the solutions. Then I found this, its somewhat helpful, doesn't handle partial decodes very well though https://github.com/ggirotto/NestedCloudkitCodable