Swift 3 which is the best way to store of an Object with array etc - swift

I started to develop with Swift 3 and i´m getting crazy. Following Situation:
class subObject
{
var name : String
var list : [Int]
init( _name : String, _list : [Int] ){
self.name = _name
self.list = _list
}
}
class mainObject
{
var subObjectList : [subObject]
init( _list : [subObject] ){
self.subObjectList = _list
}
}
var data : [mainObject]
Which way is state of the art to store var data : [mainObject] persistently. I've already unsuccessfully tried .plistand NSKeyedArchiver.
Sorry but my english is worse.

NSCoding cannot be used because the classes aren't subclasses of NSObject.
Since all properties in both classes are property list compliant you could add a computed property propertyListRepresentation and an appropriate initializer.
Class names are supposed to start with a capital letter and parameters starting with an underscore are unusual in Swift.
class SubObject
{
var name : String
var list : [Int]
init(name : String, list : [Int] ){
self.name = name
self.list = list
}
init?(dictionary : [String:Any]) {
guard let name = dictionary["name"] as? String,
let list = dictionary["list"] as? [Int] else { return nil }
self.name = name
self.list = list
}
var propertyListRepresentation : [String:Any] {
return ["name" : name, "list" : list]
}
}
class MainObject
{
var subObjectList : [SubObject]
init(list : [SubObject] ){
self.subObjectList = list
}
init(propertyList : [[String:Any]] ){
self.subObjectList = propertyList.flatMap{ SubObject(dictionary: $0) }
}
var propertyListRepresentation : [[String:Any]] {
return subObjectList.map{ $0.propertyListRepresentation }
}
}
To use it:
let subs = [SubObject(name: "Foo", list: [1, 2, 3]), SubObject(name: "Bar", list: [4, 5, 6])]
let main = MainObject(list: subs)
let list = main.propertyListRepresentation
let data = try! PropertyListSerialization.data(fromPropertyList: list, format: .xml, options: 0)
print(String(data:data, encoding: .utf8)!)
let restoredList = try! PropertyListSerialization.propertyList(from: data, format: nil) as! [[String:Any]]
let restoredMain = MainObject(propertyList: restoredList)

Related

How to convert Array of Struct to List Realm?

i want to convert Array from struct to List Realm .
static func mapGenreResponsetoGenreEntity( input genre: [GenreModel]) -> List {
var list = List<GenreEntity>()
return genre.map { result in
let newGenre = GenreEntity()
newGenre.gamesCount = result.gamesCount ?? 0
newGenre.id = result.id ?? 0
newGenre.imageBackground = result.imageBackground ?? "Unknown"
newGenre.name = result.name ?? "Unknown"
newGenre.slug = result.slug ?? "Unknown"
list.append(newGenre)
return list
}
}
and the genre is
struct GenreModel: Codable {
let gamesCount : Int?
let id : Int?
let imageBackground : String?
let name : String?
let slug : String?
}
How can i convert from array genre (Struct) to List realm which is GenreEntity ?
This should just be a matter of adding the new GenreEntity objects to an array and then return the entire array once done.
This should do it
func convertToList(genreArray: [GenreClass]) -> List<GenreEntityRealmModel> {
let genreEntityList = List<GenreEntityRealmModel>()
genreArray.forEach { model in
let genreEntity = GenreEntity()
genreEntity.gamesCount = model.gamesCount
genreEntityList.append(genreEntity)
}
return genreEntityList
}

How to sort JSON Data in Array in swift 4

I have JSON array like this
var json = NSArray() // array with json objects
//print json >>
json = (
{
Name = "Alen";
Score = 500;
},
{
Name = "John";
Score = 0;
},
{
Name = "Mark";
Score = 2000;
},
{
Name = "Steve";
Score = 300;
},
{
Name = "Ricky";
Score = 900;
}
)
and i can access its objects as
(json[0] as! NSDictionary).object(forKey: "Name")
(json[0] as! NSDictionary).object(forKey: "Score")
I want to sort this JSON array according to scores.
I found the answers like
let sortedArray = json.sorted(by: { $0.0 < $1.0 })
which gives error
Value of type 'Any' has no member '0'
Then I tried this
let sortedArray = (json as! NSDictionary).sorted {(aDic, bDic) -> Bool in
return aDic.key < bDic.key
}
It gave error
Binary operator '<' cannot be applied to two 'Any' operands
Can you please guide me to sort the array according to score in swift 4?
That's a very good example why you are strongly discouraged from using NSArray and NSDictionary in Swift.
Both collection types don't provide type information so everything is treated as Any. Most of the shared generic API of the Swift Standard library cannot be used with Any so you are not able to take advantage of the powerful generic functions unless you add a lot of ugly type casts.
If all values are String declare your array as
var json = [[String:String]]()
Then you can sort the array with
let sortedArray = json.sorted { $0["Score"]! < $1["Score"]! }
The most recommended solution is to decode the JSON directly into a custom struct
struct Player : Decodable {
let name : String
let score : String
private enum CodingKeys : String, CodingKey { case name = "Name", score = "Score" }
}
Then you get rid of all type casting and you can sort by the property name
var players = [Player]()
let jsonString = """
[{"Name" : "Alen", "Score" : "500"},
{"Name" : "John", "Score" : "0"},
{"Name" : "Mark", "Score" : "2000"},
{"Name" : "Steve", "Score" : "300"},
{"Name" : "Ricky", "Score" : "900"}]
"""
let data = Data(jsonString.utf8)
do {
players = try JSONDecoder().decode([Player].self, from: data)
let sortedPlayers = players.sorted{ $0.score.compare($1.score, options: .numeric) == .orderedAscending }
print(sortedPlayers)
} catch { print(error) }
Edit:
To load the JSON use an asynchronous way (URLSession)
Never load data from a remote URL with synchronous Data(contentsOf.
var players = [Player]()
let jsonUrl = URL(string: "url.json")!
let task = URLSession.shared.dataTask(with : url) { [unowned self] (data, _, error) in
if let error = error { print(error); return }
do {
players = try JSONDecoder().decode([Player].self, from: data!).sorted{ $0.score < $1.score }
DispatchQueue.main.async { // reload the table view if necessary
self.tableView.reloadData()
}
} catch { print(error) }
}
task.resume()
After parsing your json, you can sort your score array like this
var scoreArray = ["500", "0", "2000", "300", "900"]
array.sort { $0.compare($1, options: .numeric) == .orderedAscending }
I did something like this before
First I created two arrays of dictionary
var jsonArray = [(name:String, score:String)]()
var sortedscoreArray:[(name: String, score: String)] = []
and in getting json data you can create for loop
for I in 0..< jsonData.count{
Let jsonInfo = jsonData[i]
jsonArray.append((name: jsonInfo[“Name”].string!, score: jsonInfo[“Score"].string!))
}
and after you fill the json array pass it to sortedArray
sortedscoreArray = jsonArray.sorted(by: { $0.score < $1.score })
If array contains dictionary then you can use this code for sorting:
let sortedArray = json.sort { $0["Score"] as! Int < $1["Score"] as! Int }
print(sortedArray)
and if you are using bean class then you can use dot(.) properties for sorting:
let sortedArray = json.sort { $0.Score < $1.Score }
print(sortedArray)
let sortedResults = self.json?.sorted(by: {$0.name ?? EMPTY_STRING < $1.name ?? EMPTY_STRING }) ?? []

How to split string into Int:String Dictionary

So I'm trying to split a string that would look like this:
let Ingredients = "1:egg,4:cheese,2:flour,50:sugar"
and I'm attempting to get a dictionary output like this
var decipheredIngredients : [Int:String] = [
1 : "egg",
4 : "cheese",
2 : "flour",
50 : "sugar"
]
Here is the code that I am attempting this with
func decipherIngredients(input: String) -> [String:Int]{
let splitStringArray = input.split(separator: ",")
var decipheredIngredients : [String:Int] = [:]
for _ in splitStringArray {
decipheredIngredients.append(splitStringArray.split(separator: ":"))
}
return decipheredIngredients
}
When I try this I get an error saying I can't append to the dictionary. I've tried other methods like this:
func decipherIngredients(input: String) -> [String.SubSequence]{
let splitStringArray = input.split(separator: ",")
return splitStringArray
}
let newThing = decipherIngredients(input: "1:egg,4:cheese,2:flour,50:sugar").split(separator: ":")
print(newThing)
but I get this as the output of the function
[ArraySlice(["1:egg", "4:cheese", "2:flour", "50:sugar"])]
An alternative approach using Swift 4 and functional programming:
let ingredients = "1:egg,4:cheese,2:flour,50:sugar"
let decipheredIngredients = ingredients.split(separator: ",").reduce(into: [Int: String]()) {
let ingredient = $1.split(separator: ":")
if let first = ingredient.first, let key = Int(first), let value = ingredient.last {
$0[key] = String(value)
}
}
print(decipheredIngredients)
Swift 3
try this, assuming you want dictionary keys of type Int and values of type String
func decipherIngredients(_ input: String) -> [Int:String] {
var decipheredIngredients : [Int:String] = [:]
let keyValueArray = input.components(separatedBy: ",")
for keyValue in keyValueArray {
let components = keyValue.components(separatedBy: ":")
decipheredIngredients[Int(components[0])!] = components[1]
}
return decipheredIngredients
}

Array of structs: UserDefaults, how to use?

I've already check all of those topics:
How to save an array of custom struct to NSUserDefault with swift?
How to save struct to NSUserDefaults in Swift 2.0
STRUCT Array To UserDefaults
I have a struct containing some Strings and an other struct: MySection.
struct MySection {
var name: String = ""
var values: [MyRow] = []
}
And there is MyRow which is store in MySection.values
struct MyRow {
var value: String = ""
var quantity: String = ""
var quantityType: String = ""
var done: String = ""
}
Two arrays for use it
var arraySection: [MySection] = []
var arrayRow: [MyRow] = []
And in my application, I add dynamically some values in those arrays.
There is the delegate method for get datas from my second ViewController
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(MyRow())
arrayRow[arrayRow.count - 1] = newItem[0]
manageSection(item: sectionPick)
listTableView.reloadData()
}
And there is the manageSection function.
func manageSection(item: String) {
var i = 0
for _ in arraySection {
if arraySection[i].name == item {
arraySection.insert(MySection(), at: i + 1)
arraySection[i + 1].values = [arrayRow[arrayRow.count - 1]]
return
}
i += 1
}
arraySection.append(MySection())
arraySection[arraySection.count - 1].name = item
arraySection[arraySection.count - 1].values = [arrayRow[arrayRow.count - 1]]
}
My need is to store datas of the two arrays in UserDefaults (or CoreData maybe??) and use these datas when the user going back to the application.
I don't know how to do it, I've already try methods from the 3 topics but I'm not even doing a good job.
How can I do it?
Thanks guys!
Since both types contain only property list compliant types a suitable solution is to add code to convert each type to a property list compliant object and vice versa.
struct MySection {
var name: String
var values = [MyRow]()
init(name : String, values : [MyRow] = []) {
self.name = name
self.values = values
}
init(propertyList: [String: Any]) {
self.name = propertyList["name"] as! String
self.values = (propertyList["values"] as! [[String:String]]).map{ MyRow(propertyList: $0) }
}
var propertyListRepresentation : [String: Any] {
return ["name" : name, "values" : values.map { $0.propertyListRepresentation }]
}
}
struct MyRow {
var value: String
var quantity: String
var quantityType: String
var done: String
init(value : String, quantity: String, quantityType: String, done: String) {
self.value = value
self.quantity = quantity
self.quantityType = quantityType
self.done = done
}
init(propertyList: [String:String]) {
self.value = propertyList["value"]!
self.quantity = propertyList["quantity"]!
self.quantityType = propertyList["quantityType"]!
self.done = propertyList["done"]!
}
var propertyListRepresentation : [String: Any] {
return ["value" : value, "quantity" : quantity, "quantityType" : quantityType, "done" : done ]
}
}
After creating a few objects
let row1 = MyRow(value: "Foo", quantity: "10", quantityType: "Foo", done: "Yes")
let row2 = MyRow(value: "Bar", quantity: "10", quantityType: "Bar", done: "No")
let section = MySection(name: "Baz", values: [row1, row2])
call propertyListRepresentation to get a dictionary ([String:Any]) which can be saved to User Defaults.
let propertyList = section.propertyListRepresentation
Recreation of the section is quite easy, too
let newSection = MySection(propertyList: propertyList)
Edit
Use the propertyList initializer only if you get data from UserDefaults in all other cases use the other initializer.
For example replace
#IBAction func addButtonPressed(_ sender: Any) {
newProducts.append(MyRow(propertyList: ["":""]))
newProducts[newProducts.count - 1].value = nameTextField.text!
newProducts[newProducts.count - 1].quantity = quantityTextField.text!
newProducts[newProducts.count - 1].quantityType = type
newProducts[newProducts.count - 1].done = "No"
delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
navigationController?.popViewController(animated: true)
}
with
#IBAction func addButtonPressed(_ sender: Any) {
let row = MyRow(value: nameTextField.text!,
quantity: quantityTextField.text!,
quantityType: type,
done: "No")
newProducts.append(row)
delegate?.returnInfos(newItem: newProducts, sectionPick: typePick)
navigationController?.popViewController(animated: true)
}
and replace
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(MyRow(propertyList: ["":""]))
arrayRow[arrayRow.count - 1] = newItem[0]
manageSection(item: sectionPick)
listTableView.reloadData()
}
with
func returnInfos(newItem: [MyRow], sectionPick: String) {
arrayRow.append(newItem[0])
manageSection(item: sectionPick)
listTableView.reloadData()
}
Basically first create the object, then append it to the array. The other way round is very cumbersome.

Mapping Realm objects to a Dict via Swift map

I have two (Realm.io) objects like this:
class Parent: Object {
var title = “”
var children = List<Child>()
var dict: [String: Any] {
print(“Dict 1”)
return ([“title”: self.title, “children”: self.children.map{ $0.dict }])
}
}
class Child: Object {
var name = “”
var dict: [String: Any] {
print(“Dict 2”)
return ([“name”: self.name])
}
}
I’m trying to convert them both to a dictionary in one swoop like this:
let parent_dict = parent.dict
It works fine for “title” and other such Parent attributes, but never actually goes to map the Child class object.
The output for the above example would be just: Dict 1
Be sure you add all the objects to the realm first. The following code works (try it in a playground inside your project):
import RealmSwift
class Parent: Object {
dynamic var title = ""
var children = List<Child>()
var dict: [String: Any] {
print("Dict 1")
return (["title": self.title, "children": self.children.map{ $0.dict }])
}
}
class Child: Object {
dynamic var name = ""
var dict: [String: Any] {
print("Dict 2")
return (["name": self.name])
}
}
let p1 = Parent(); p1.title = "John"
let c1 = Child(); c1.name = "Pam"
let c2 = Child(); c2.name = "Andrew"
let c3 = Child(); c3.name = "Barrack"
let realm = try! Realm()
try! realm.write {
realm.add(p1)
realm.add([c1, c2, c3])
p1.children.append(objectsIn: [c1, c2, c3])
}
print(p1.dict)
After running the code the console will read:
Dict 1
["title": "John", "children": Swift.LazyMapRandomAccessCollection<RealmSwift.List<Child>, Swift.Dictionary<Swift.String, Any>>(_base: List<Child> (
[0] Child {
name = Pam;
},
[1] Child {
name = Andrew;
},
[2] Child {
name = Barrack;
}
), _transform: (Function))]