Swift Eureka Forms - How to assign values to field options? - swift

If I create a segmented row field like so:
<<< SegmentedRow<String>(){ row in
row.title = "Sex"
row.tag = "sex"
row.selectorTitle = "Select your sex"
row.options = ["Male","Female"]
}
How do I match the options to a specific value? For example if a user select Male, then is there a way to get M instead of Male in code, but still shows Male to the User?
Or if I have a list of countries for example:
<<< PushRow<String>(){ row in
row.title = "Passport Issuing Country"
row.tag = "passportIssuingCountry"
row.selectorTitle = "Passport Issuing Country"
row.options = ["AUSTRALIA","AUSTRIA","BELGIUM"]
}
can I assign each Country name to a country code, like AUSTRALIA will return AU, AUSTRIA will return AT, etc.

Right now, there's no way to hold an internal value for an option - but this is what you can do:
1. Create an enum with all available options
enum Sex: Int {
case NotKnown = 0
case Male = 1
case Female = 2
case NotApplicable = 9
static let all = [NotKnown, Male, Female, NotApplicable]
static let strings = ["Unknown", "Male", "Female", "Not Applicable"]
func string() -> String {
let index = Sex.all.index(of: self) ?? 0
return Sex.strings[index]
}
static func fromString(string: String) -> Sex {
if let index = Sex.strings.index(of: string) {
return Sex.all[index]
}
return Sex.NotKnown
}
}
2. Create your row with all the options you want to expose
<<< SegmentedRow<String>(){ row in
row.title = "Sex"
row.tag = "sex"
row.selectorTitle = "Select your sex"
row.options = [Sex.Female.string(), Sex.Male.string()]
}
3. Read the value
let row: SegmentedRow<String>? = self.form.rowBy(tag: "sex")
if let value = row?.value {
// Returns "Female"
print(value)
// Returns 2
print(Sex.fromString(string: value).rawValue)
}
The user sees and selects Strings, but you get an enum-value that you for example can save in your database as an integer (see: ISO/IEC 5218).
https://en.wikipedia.org/wiki/ISO/IEC_5218

Related

TableView Cell -> UISearchBar : Sort Data, where the records which have most keywords matched should be shown first and then go towards the least

Search should sort results based on a number of matched keywords. It should show the most matched keyword results on top of the list, then the rest records going in descending order. I have a UITableView wherein I have implemented UISearchBar which is going to search my tableview cells as per query inserted in the search-box. Let me now recreate a scenario to explain it. Lets say my Api fetched different key value datas and among them is a key named as "tags" which will contain a string of comma separated values as "dog, cat, pug" now whenever user enters dog into the search-box as this value is shown in one of my tableview cell it will be shown as my tags contain value dog, again if we insert second keyword as cat the above record needs to be fetched and shown to the top and after that if there exists a record which contains only dog in one of the tag values or cat as one of the tag values then it needs to be shown after it. Similarly for third word.
Query -> Cat Dog Pug
It should show on the top only those records which have all of the above keywords
After that, it should show records where any two of the above values are present
After that, it should show single single records if there exists any from above keywords.
Please help.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print("\(searchText)")
let arrSearchTexts = searchText.components(separatedBy: " ")
filteredStorage = originalStorage1.filter { (obj) -> Bool in
if let tags = obj["tags"] as? String{
var hasTag = false
for searchedTag in arrSearchTexts{
if tags.contains(searchedTag.lowercased()){
hasTag = true
break
}
}
return hasTag //tags.contains(searchText.lowercased())
}
return false
}
//Sorting data
filteredStorage.sort { (obj1, obj2) -> Bool in
if let tags1 = obj1["tags"] as? String{
var hasAllTags = true
for tagVal in arrSearchTexts{
if !tags1.contains(tagVal.lowercased()){
hasAllTags = false
break
}
}
return hasAllTags
}
return obj1.contains { (obj) -> Bool in
if let val = obj.value as? String,
let firstTag = arrSearchTexts.last{
return val.hasPrefix(firstTag)
}
return false
}
}
if searchText == "" {
filteredStorage = originalStorage1
}
tblView.reloadData()
}
for i in 0..<filteredStorage.count {
var dict = filteredStorage[i]
dict["priority"] = 0
filteredStorage[i] = dict
}
for i in 0..<filteredStorage.count{
let tagInArr : String = filteredStorage[i]["tags"] as! String
var priorityInArr : Int = 0
for tagVal in arrSearchTexts{
if tagInArr.contains(tagVal){
priorityInArr += 1
}
}
filteredStorage[i]["priority"] = priorityInArr
}
filteredStorage.sort{
((($0 as Dictionary<String, AnyObject>)["priority"] as? Int)!) > ((($1 as Dictionary<String, AnyObject>)["priority"] as? Int)!)
}

Sort Array of JSON objects

I need to sort an array of JSON objects.
but it is needs to be translated from a literal meaning to a numeric meaning.
for example
object["status"]
"New" = 1
"Open" = 2
"Closed" = 3
/// I need to translated in here some where
var sortedOrders = orders.sort { $0["status"].doubleValue < $1["status"].doubleValue }
You could do this with an enum.
enum Status: Int {
case new = 1
case open = 2
case closed = 3
case unknown = 99
init(string: String) {
switch string {
case "new", "New": self = .new
case "open", "Open": self = .open
case "closed", "Closed": self = .closed
default:
self = .unknown
}
}
}
To use the enum, you could initialize like this:
var myStatus = Status(string: object["status"])
And use it like this:
print(myStatus) // prints "new"
print(myStatus.rawValue) // prints "1"
Edit: Since the enum has an underlying type of Int, to accomplish your sort you can compare the resulting Status directly (Thanks to this question https://stackoverflow.com/a/27871946/1718685):
var sortedOrders = orders.sort { Status(string: $0["status"]) < Status(string: $1["status"]) }
Here's one way:
let statuses = [ "New", "Open", "Closed" ]
var sortedOrders = orders.sort { (statuses.index(of: $0["status"] ?? "") ?? 99) < (statuses.index(of: $1["status"] ?? "") ?? 99) }
This will determine the index of each status within the statuses array. 99 will be used for unknown or missing statuses so any unknown statuses will appear at the end of the list.

Optional value in swift 3 Optimised way

I have create class Testing model which has 4 dataMember it should not be null when accessing (means return default value)
extension Double {
/// Rounds the double to decimal places value
func roundTo(places:Int = 2) -> Double
{
let divisor = pow(10.00, Double(places))
return (self * divisor).rounded() / divisor
}
}
class TestingModel{
var id : String!
var name : String! = "abc" /*It is not working*/
var price : Double! = 0.00
var uniqueId : Int! = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? "" //I dont want to do like this way
name = dictionary["name"] as? String
price = (dictionary["price"] as? Double)?.roundTo() ?? 0.00
uniqueId = dictionary["unique_id"] as? Int
}
}
let t:TestingModel = TestingModel.init(dictionary: ["x id" : "x012y12345z45","x name":"test1","x price":100.0,"uniqueId":1236.0])
let testString = "Jd " + t.id
print(testString) //Perfect
print(t.name)
print(t.price) /* Only one decemal point is printed */
Getting Output
Jd
nil
0.0
Expected output
Jd
abc /Should return abc instead of nil/
0.00 /Two decimal point complulsury/
What i actually mean in
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor
price is a Double type and what you are asking to do is to print that double value to 2 decimal places. Then you should make use of the following.
let a = 0.0
print(String(format: "%.2f", a))
this prints:
0.00
If you are planning to round it to decimal places, then also the above code will return that. But if you need it to round and return a double type then you can check this answer
Based on your updated question, I suggest to use the model as follows:
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.0
var uniqueId : Int = 1
/**
* Instantiate the instance using the passed dictionary values to set the properties values
*/
init(dictionary: [String:Any])
{
id = (dictionary["id"] as? String) ?? ""
name = dictionary["name"] as? String ?? "abc"
price = (dictionary["price"] as? Double) ?? 0.0
uniqueId = dictionary["unique_id"] as? Int ?? 1
}
}
You seem to have asked two different questions here. The first one regarding doubles have already been answered by adev. I will answer the second one, which is:
if i assign nil value to variable then it should remain with its default value without writing this Optional chaining ?? "abc" in constructor
If you want to do this then it means that the variable shouldn't be optional at all, as nil isn't one of its valid values. Make the variable a non-optional type and give it a default value.
class TestingModel{
var id : String = ""
var name : String = "abc"
var price : Double = 0.00
var uniqueId : Int = 1
}
You can't really avoid using ?? in the constructor because of the nature of dictionaries. They will always return a nil value if the key does not exist. You have to check it. It does not make sense even if this is possible anyway. Imagine something like this:
someVariable = nil // someVariable is now 0
This is extremely confusing. someVariable is 0, even though it appears that nil is assigned to it.
A workaround will be to add a dictionary extension. Something like this:
extension Dictionary {
func value(forKey key: Key, defaultValue: Value) -> Value {
return self[key] ?? defaultValue
}
}
But I still recommend that you use ?? instead.

How to sort an array of Structures with/by dynamic property

Given an NSTableView that has an array of structures as its datasource. A user can click on any column heading to sort by that column. The column identifiers match the property names of the properties within the structure.
Given a structure
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
}
and an array of structures
var myArray = [MyStructure]()
The goal is that when a column heading is clicked, use that column's identifier to sort the array of structures by that column identifier/property
With an array of dictionaries, it was easy...
self.myArrayOfDictionaries.sortInPlace {
(dictOne, dictTwo) -> Bool in
let d1 = dictOne[colIdentifier]! as String;
let d2 = dictTwo[colIdentifier]! as String;
return d1 < d2 //or return d1 > d2 for reverse sort
}
The question is how to access the properties of the Structure dynamically, something like
let struct = myArray[10] as! MyStructure //get the 10th structure in the array
let value = struct["col0data"] as! String //get the value of the col0data property
If there is a better way, suggestions would be appreciated.
I should also note that the structure may have 50 properties so this is an effort to reduce the amount of code needed to sort the array by any one of those properties.
edit:
One solution is to change the structure to a class derived from NSObject. Then the properties could be accessed via .valueForKey("some key"). However, I am trying to keep this Swifty.
Maybe I have a solution to your problem. The advantage of this code over your solution is here you don't need to add a subscript method to your struct to create an hardcoded String-Property-Value map via code.
Here's my extension
extension _ArrayType {
func sortedBy(propertyName propertyName: String) -> [Self.Generator.Element] {
let mirrors = self.map { Mirror(reflecting: $0) }
let propertyValues = mirrors.map { $0.children.filter { $0.label == propertyName }.first?.value }
let castedValues = propertyValues.map { $0 as? String }
let sortedArray = zip(self, castedValues).sort { (left, right) -> Bool in
return left.1 < right.1
}.map { $0.0 }
return sortedArray
}
}
Usage
struct Animal {
var name: String
var type: String
}
let animals = [
Animal(name: "Jerry", type: "Mouse"),
Animal(name: "Tom", type: "Cat"),
Animal(name: "Sylvester", type: "Cat")
]
animals.sortedBy(propertyName: "name")
// [{name "Jerry", type "Mouse"}, {name "Sylvester", type "Cat"}, {name "Tom", type "Cat"}]
animals.sortedBy(propertyName: "type")
// [{name "Tom", type "Cat"}, {name "Sylvester", type "Cat"}, {name "Jerry", type "Mouse"}]
Limitations
The worst limitation of this solutions is that it works only for String properties. It can be change to work with any types of property by it must be at compile time. Right now I have not a solution to make it work with any king of property type without changing the code.
I already asked help for the core of the problem here.
I would definitely recommend simply embedding your dictionary into your struct. A dictionary is a much more suitable data structure for 50 key-value pairs than 50 properties – and you've said that this would be an acceptable solution.
Embedding the dictionary in your struct will give you the best of both worlds – you can easily encapsulate logic & you have have easy lookup of the values for each column ID.
You can now simply sort your array of structures like this:
struct MyStructure {
var dict = [String:String]()
init(col0Data:String, col1Data:String) {
dict["col0data"] = col0Data
dict["col1data"] = col1Data
}
}
var myArray = [MyStructure(col0Data: "foo", col1Data: "bar"), MyStructure(col0Data: "bar", col1Data: "foo")]
var column = "col0data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // [MyStructure(dict: ["col0data": "bar", "col1data": "foo"]), MyStructure(dict: ["col0data": "foo", "col1data": "bar"])]
column = "col1data"
myArray.sort {
$0.dict[column] < $1.dict[column]
}
print(myArray) // MyStructure(dict: ["col0data": "foo", "col1data": "bar"])], [MyStructure(dict: ["col0data": "bar", "col1data": "foo"])
Here's an answer (but not the best answer); use subscripts to return the correct property, and set which property you are sorting by within the array.sort:
struct MyStructure {
var col0data = "" //name matches the column identifier
var col1data = ""
subscript(key: String) -> String? { //the key will be the col identifier
get {
if key == "col0data" {
return col0data
} else if key == "col1data" {
return col1data
}
return nil
}
}
}
And then here's how the sort works:
let identifier = the column identifier string,say col0data in this case
myArray.sortInPlace ({
let my0 = $0[identifier]! //the identifier from the table col header
let my1 = $1[identifier]!
return my0 < my1
})
If you do not know what types the values of MyStructure can be you will have a hard time comparing them to sort them. If you had a function that can compare all types you can have in MyStructure then something like this should work
struct OtherTypeNotComparable {
}
struct MyStructure {
var col0data = "cat" //name matches the column identifier
var col1data: OtherTypeNotComparable
}
let structures = [MyStructure(), MyStructure()]
let sortBy = "col1data"
func yourCompare(a: Any, b: Any) -> Bool {
return true
}
var expanded : [[(String, Any, MyStructure)]]
= structures.map { s in Mirror(reflecting: s).children.map { ($0!, $1, s) } }
expanded.sortInPlace { (a, b) -> Bool in
let aMatch = a.filter { $0.0 == sortBy }.first!.1
let bMatch = b.filter { $0.0 == sortBy }.first!.1
return yourCompare(aMatch, b: bMatch)
}
source: https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Mirror_Structure/index.html

Why Realm "to many" relationship having always the same reference?

Why is the realm-list containing the very same elements instead of different ones ?
As you can see in the picture below, there are two relam-objects (UndoMemoryNameEntry and NameEntry). The first one contains a list of 8 elements. The list's element-type is of type NameEntry !
My last NameEntry object is written with currentScorePlayer=1 and currentScoreMe=15 as you can see in the picture below:
The list in UndoMemoryNameEntry is correctly inserted the last NameEntry object. You find the insertion-code further down...
But now the problem: Why are all the existing list-elements as well changed to the newest inserted element ???? As you can see in the picture below, all the elements are unfortunately identical to the last one added - why ??????
If I change the NameEntry to the following :
And inserting at index=0 to the list, then the List changes to :
Why are all the elments changed ? And not just the inserted one ??? Thanks for any help on this !
My two realm-objects are :
class NameEntry: Object {
dynamic var playerName = ""
dynamic var isMyAdversary: Bool = false
dynamic var currentScorePlayer: Int = 0
dynamic var currentScoreMe: Int = 0
}
and the List :
class UndoMemoryNameEntry: Object {
dynamic var undoPlayerName = ""
let NameEntryList = List<NameEntry>()
}
The following code creates the Realm-List :
// query rlm for existing object (with name adversary
let undoPredicate = NSPredicate(format: "undoPlayerName == %#", adversaryName)
let undoPlayerName = rlm.objects(UndoMemoryNameEntry).sorted("undoPlayerName", ascending: true).filter(undoPredicate)
// if undoPlayerName object does not exist - then create it!
if (undoPlayerName.count < 1) {
rlm.beginWrite()
let undoEntry = UndoMemoryNameEntry()
undoEntry.undoPlayerName = adversaryName
rlm.add(undoEntry)
rlm.commitWrite()
}
The following code adds a "NameEntry"-Element in the List :
let undoPredicate = NSPredicate(format: "undoPlayerName == %#", plaNameLab)
let undoPlayerName = rlm.objects(UndoMemoryNameEntry).sorted("undoPlayerName", ascending: true).filter(undoPredicate)
if (undoPlayerName.count == 1) {
rlm.beginWrite()
println(entry)
var undoEntry = undoPlayerName[0] as UndoMemoryNameEntry
undoEntry.NameEntryList.insert(entry, atIndex: 0)
rlm.commitWrite()
}
The above code-excerts work perfectly - except that the realm-List always changes all its elements to the one just inserted.
I finally found a solution:
First of all rearrange the two realm objects as follows:
class NameEntry: Object {
dynamic var playerName = ""
dynamic var currentScorePlayer: Int = 0
dynamic var currentScoreMe: Int = 0
// the undo-list is better placed in the first object...
let undoEntryList = List<UndoMemoryNameEntry>()
override static func primaryKey() -> String? {
return "playerName"
}
}
class UndoMemoryNameEntry: Object {
dynamic var undoPlayerName = ""
dynamic var currentScorePlayer: Int = 0
dynamic var currentScoreMe: Int = 0
// no primary key here since the undoEntry will have several items with the same undoPlayerName
}
Then when adding a "NameEntry"-Element in the List :
let predicate = NSPredicate(format: "playerName == %#", plaNameLab)
let playerName = rlm.objects(NameEntry).sorted("playerName", ascending: true).filter(predicate)
if (playerName.count == 1) {
rlm.beginWrite()
var entry = playerName[0] as NameEntry
// you need to create a new list object first !!!!!!!!!!!!
// ...in my initial example, this creation was missing !!!!!!
var siblingEntry = UndoMemoryNameEntry()
siblingEntry.undoPlayerName = plaNameLab
siblingEntry.currentScorePlayer = entry.currentScorePlayer
siblingEntry.currentScoreMe = entry.currentScoreMe
// insert new list-element
entry.undoEntryList.insert(siblingEntry, atIndex: 0)
// alternatively choose append if you want to add the element at the end of the list
entry.undoEntryList.append(siblingEntry)
// or choose the "ringbuffer-solution" given in the add-on below if you want to restrict the number of list-elements to ringbuffer-size !
// ...
rlm.commitWrite()
}
Add-on: If you want to create a ringbuffer having only a limited number of list-elements:
// create ringbuffer of 20 elements (20th element will be newest)
let ringBufferSize = 20
let undoPredicate = NSPredicate(format: "undoPlayerName == %#", plaNameLab)
if (entry.undoEntryList.filter(undoPredicate).sorted("undoPlayerName").count < ringBufferSize) {
entry.undoEntryList.append(siblingEntry)
}
else {
// entry.undoEntryList.replace(ringBufferSize-1, object: siblingEntry)
entry.undoEntryList.removeAtIndex(ringBufferSize-1)
entry.undoEntryList.append(siblingEntry)
for index in 0..<ringBufferSize-1 {
let tempEntry1 = rlm.objects(UndoMemoryNameEntry).filter(undoPredicate).sorted("undoPlayerName")[index] as UndoMemoryNameEntry
let tempEntry2 = rlm.objects(UndoMemoryNameEntry).filter(undoPredicate).sorted("undoPlayerName")[index+1] as UndoMemoryNameEntry
tempEntry1.currentScorePlayer = tempEntry2.currentScorePlayer
tempEntry1.currentScoreMe = tempEntry2.currentScoreMe
}
let tempEntry = rlm.objects(UndoMemoryNameEntry).filter(undoPredicate).sorted("undoPlayerName")[ringBufferSize-1] as UndoMemoryNameEntry
rlm.delete(tempEntry)
}