I want to store formatted text in Realm Swift database - swift

noob swift guy here.
I have this UITextView where the user writes something. I need to store his "note" to the database with all the formated text in it. That means if it has bold, I need to display it as bold later.
This text should be stored in local Realm Swift Database. Any solutions how can I achieve that? I head about "Markdown", but I didn't understand how can that be useful.
Much thanks! Have a great day!

Realm only stores ascii text and cannot directly store any formatting properties like BOLD, and that's true for most databases.
If you want to store formatted strings, one option is to archive it to a Data type, which Realm fully supports. Then, when reading data back in, unarchive it from Data back to a formatted string. (formatted strings are often called Rich Text or in code: attributedStrings)
The below code is macOS but iOS will be similar
A quick example - here's a class to hold a some attributed string data
class MyAttrStringClass: Object {
#Persisted var attrStringData: Data!
}
Suppose we have a (rich) text field with the following text
Hello, World
To store the class with the attributed string in Realm:
let s = self.myRichTextField.attributedStringValue
let data = try! NSKeyedArchiver.archivedData(withRootObject: s, requiringSecureCoding: false)
let myObject = MyAttrStringClass()
myObject.attrStringData = data
try! realm.write {
realm.add(myObject)
}
then when reading back from Realm and displaying the attributed string in that same field
let myObject = realm.objects(MyAttrStringClass.self).first!
let data = myObject.attrStringData!
let s = try! NSKeyedUnarchiver.unarchivedObject(ofClass: NSAttributedString.self, from: data)
self.myRichTextField.attributedStringValue = s!
Note: there's no error checking in the above. Please protect your code by safely unwrapping optionals.

Related

Looking for right approach to saving data with Keychain and data encryption - Swift 5

My app stores user's sensitive data and I wonder what is general/proper/the most secure way to secure the data.
My approach now is saving encoded data into Keychain, more specifically but simply :
I am encoding/decoding custom types in array with PropertyList
Then pass these to Keychain. I am using a wrapper called KeychainAccess thanks to Kishikawa Katsumi.
Retrieve data from Keychain then decode it to display on tableview.
Below is my code bits :
extension UserInformation {
static let propertyListEncoder = PropertyListEncoder()
static let propertyListDecoder = PropertyListDecoder()
static let keychain = Keychain(service: "com.xxxxx.DataSaving-App")
static func saveToKeychain(userInfo: [UserInformation]) {
let data = try? propertyListEncoder.encode(userInfo)
if let savingData = data {
dump(userInfo)
keychain[data: "encodedUserInfo"] = NSData(data: savingData) as Data
}
}
static func loadFromKeychain() -> [UserInformation]? {
guard let retrievedData = keychain[data: "encodedUserInfo"] else {
return nil }
let data = try? propertyListDecoder.decode(Array<UserInformation>.self, from: retrievedData)
return data
}
}
Above works fine and no problem found so far. However, I am not sure to use PropertyListEncoder is right way to encode/decode. Please kindly see below specified questions and giving advice to me should be very grateful.
Would above approach be secure enough?
Is PropertyListEncoder/Decoder just doing ENCODING/DECODING, NOT SAVING DATA TO ANY, like plist or any storage where is vulnerable?
If any other measure is needed, is encrypting data with Common Swift then passing it to Keychain making sense?
I spent few weeks to find detail solution but no luck yet. Please help a new developer.
Property list encoding is not encryption. It's plain text (possibly in binary format); it is just a form of serialization. The "encryption" in your example consists of the fact that the information is hidden in the keychain. And the keychain is generally regarded as sufficiently secure.

Can Codable APIs be used to decode data encoded with NSKeyedArchiver.archivedData?

I'm converting a codebase from using NSCoding to using Codable. I have run into an issue when trying to restore data encoded with NSCoding. I have an object that was encoded with the code:
let encodedUser = NSKeyedArchiver.archivedData(withRootObject: user)
let userDefaults = UserDefaults.standard
userDefaults.set(encodedUser, forKey: userKey)
userDefaults.synchronize()
It was previously being decoded with the code:
if let encodedUser = UserDefaults.standard.data(forKey: userKey) {
if let user = NSKeyedUnarchiver.unarchiveObject(with: encodedUser) as? User {
\\ do stuff
}
}
After updating the User object to be Codable, I attempted to update the archive/unarchive code to use PropertyListDecoder because I saw that archivedData(withRootObject:) returns data formatted as NSPropertyListBinaryFormat_v1_0. When running this code:
if let encodedUser = UserDefaults.standard.data(forKey: userKey) {
let user = try! PropertyListDecoder().decode(User.self, from: encodedUser)
\\ do stuff
}
I get a keyNotFound error for the first key I'm looking for, and breakpointing in my init(from: Decoder), I can see that the container has no keys. I also tried to use PropertyListSerialization.propertyList(from:options:format) to see if I could pass that result into PropertyListDecoder, but that function gave me an NSCFDictionary, which is structured in a really strange way but does appear to contain all the data I'm looking for.
So is it possible to decode an object using Codable APIs if it was encoded with NSKeyedArchiver.archivedData(withRootObject:)? How can I achieve this?
I decided that the answer is almost certainly no, you cannot use Codable to decode what NSCoding encoded. I wanted to get rid of all the NSCoding cruft in my codebase, and settled on a migration path where I have both protocols implemented in the critical data models to convert stored data from NSCoding to Codable formats, and I will remove NSCoding in the future when enough of my users have updated.

Retrieving NSOrderedSet from Core Data and casting it to entity managedObjectSubclasss

Im making a Fitness app to learn Core data, and I have found that I need to let the user store every performed workout as a WorkoutLog item, and in there, there should be a series of ExerciseLogs which represents performances of that exercise (it contains each lift and also a reference to the actual exercise design).
Problem is that after a while i realize that i need to have these ordered, so that the next time i want to show the user their workout, the order that the exercisese were performed should be the same.
So I checked "ordered" in the top right of the image there, and now my code is in dire need of an update. I have tried to read as much as I could about working with NSOrderedSet and how to fetch them from core data and then manipulate them, but I havent really found much of use to me. (I have no experice in objective-c)
For example my code that used to be:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
guard let exerciseLogsToDelete = workoutLogToDelete.loggedExercises as? Set<ExerciseLog> else {
print("error unwrapping logged exercises in deleteWorkoutLog")
return
}
I get the error: .../DatabaseFacade.swift:84:77: Cast from 'NSOrderedSet?' to unrelated type 'Set' always fails
So what ive learned about sets and core data no longer seems applicable.
Im far from an expert in programming, but im very eager to learn how to get access to the loggedExercises instances.
TLDR; Is there a way to cast NSOrderedSet to something I can work with? How do we usually work with NSManagedSets from core data? Do we cast them to Arrays or MutableSets? I would very much appreciate an example or two on how to get started with retrieving and using these ordered sets!
Thanks
For anyone else wondering how to get started with orderedSets in core data:
After setting my the WorkoutLog.loggedExercises "to-many" relationship to be ordered, I managed to access them through the mutableOrderedSetValue function like this:
static func deleteWorkoutLog(_ workoutLogToDelete: WorkoutLog) {
let orderedExerciseLogs: NSMutableOrderedSet = workoutLogToDelete.mutableOrderedSetValue(forKey: "loggedExercises")
let exerciseLogsToDelete = orderedExerciseLogs.array
for exerciseLog in exerciseLogsToDelete {
guard let exerciseLog = exerciseLog as? ExerciseLog else {
return
}
Works great so far.
And to rearrange the NSOrderedSet I ended up doing something like this:
// Swap the order of the orderedSet
if let orderedExerciseLogs: NSOrderedSet = dataSourceWorkoutLog.loggedExercises {
var exerciseLogsAsArray = orderedExerciseLogs.array as! [ExerciseLog]
let temp = exerciseLogsAsArray[indexA]
exerciseLogsAsArray[indexA] = exerciseLogsAsArray[indexB]
exerciseLogsAsArray[indexB] = temp
let exerciseLogsAsOrderedeSet = NSOrderedSet(array: exerciseLogsAsArray)
dataSourceWorkoutLog.loggedExercises = exerciseLogsAsOrderedeSet
}

Best way to display swiftyjson parsed data?

After already having parsed my data through swiftyjson I am looking to display the parsed information in a uitableview. Let's say I have a list of names that I parsed out of the json file. I would like those names to display each in their own respective cells to allow the user to view them all.
I would also assume that if they selected the selected cells which are being displayed strings now after being parsed that I can actually have those stored into a core data object storing them as returnable strings? The thing is I used the .append function onto an array that I created we'll call names = [String]() to actually allow the values to actually come in order for the cells.
But when doing this I noticed a huge spike in cpu usage doing this as it keeps appending them in order lets say first name is matt second name is john and so forth then it creates a new situation of the array every time. so the first time it reads just matt then the next println would print out matt, john then the next one prints out matt, john, tom and so forth until the list of json names is completed.
If I have 500 names or more it takes the cpu awhile to process all those names into the array. I know there has to be another way but all references I see all use the .append method. So my code would be names.append(Name). Name is a parsed string from swiftyjson. From there I am actually able to populate the tableview which is fine it displays correctly in the end but it takes the device awhile to populate as it first needs to process adding all 500 names each time the parse is called. I am hoping to allow the 500 names be rechecked every time in case new ones have been added. This all stores currently as a local json file already.
The code I am using is below I can figure out the UItableview part but it's more a question of whether appending an array is the best and most efficient way of displaying the parsed data from swiftyjson.
let path = NSBundle.mainBundle().pathForResource("List", ofType: "json")
let jsonData = NSData(contentsOfFile: path!, options: NSDataReadingOptions.DataReadingMappedIfSafe, error: nil)
let json = JSON(data: jsonData!)
for (key, subJson) in json["array"] {
if let Name = subJson["Name"].string {
NameList.append(Name)
println(NameList)
}
}
maybe the Swift map function is more effective
…
let json = JSON(data: jsonData!)
var nameList = [String]()
if let subJson = json["array"] as [Dictionary<String,NSObject>]? {
nameList = subJson.map { $0["name"] as! String }
}

How to read unicode characters from parse.com database in Swift?

I am storing the unicode character in the following form in my Parse.com database.
The results I am getting are these
This is how I am fetching the results
if let objects = objects as? [PFObject] {
for object in objects {
println(object.objectId)
let pf = object as PFObject
self.question = pf["Question"] as String
}}
and I am storing self.question in a UILabel
How can I show these the unicode characters in simulator as well.
The only way out is directly store the characters instead of unicode of characters in the Parse database. It supports UTF-8 characters.