Unwrapping and checking multiple optionals on single line - ios

I currently use this pattern
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
let valid: Int? = 1
let status: String? = "ok"
if let v = jsonResult["valid"] as? Int, s = jsonResult["status"] as? String {
if v == valid && s == status{
//Do something
}
}
}
Is this the optimal method to check that v == 1 and s == "ok"
Or is it possible to do something like this answer, would it be better ?
Answer(Unwrapping multiple optionals in a single line)
if let v = jsonResult["valid"] as? Int, s = jsonResult["status"] as? String
where is(v, valid && s, status)
Any help is appreciated, thank you.

If you don't need v and s inside of the body of the if, you can just do the comparison directly:
if jsonResult["valid"] as? Int == 1 && jsonResult["status"] as? String == "ok" {
// Do something
}

Try:
if let v = jsonResult["valid"] as? Int, s = jsonResult["status"] as? String where (v == valid && s == status) {}

You should try guard statement
something like this
let dict = NSDictionary()
dict.setValue(Int(1), forKey: "one")
dict.setValue("String", forKey: "two")
guard let one = dict["one"] as? Int, two = dict["two"] as? String where one == 1 && two == "String" else {
print ("no")
return
}
print ("one is \(one) two is \(two)")

Related

How can I make swift code that accesses arrays better?

I have the following code
if let unwrappedDict = snapshot.value as? NSDictionary {
for (index, dd) in unwrappedDict {
let dd = dd as? NSDictionary ?? [:]
id = dd["id"] as? String ?? ""
let ccode = dd["code"] as? String ?? ""
if (ccode == code) {
if id.count > 0 {
found = true;
}
}
}
}
How can I make this code better? I'm specifically talking about this line let dd = dd as? NSDictionary ?? [:]?
You can make the code more compact, Swift-like and functional by doing something like this:
let found = (snapshotValue as? NSDictionary)?.compactMap { $1 as? NSDictionary }.contains { $0["code"] as? String == ccode && ($0["id"] as? String)?.isEmpty == false } ?? false
Explaining piece by piece:
(snapshotValue as? NSDictionary)?.compactMap { $1 as? NSDictionary } // This tries to cast snapshotValue as an NSDictionary, and only if that succeeds, takes the array of (key, value)'s from the dictionary and tries to cast each value as an NSDictionary. The output of this segment is an array of only values that succeeded in being cast to NSDictionary.
.contains { $0["code"] as? String == ccode && ($0["id"] as? String)?.isEmpty == false } // In the array of NSDictionary values that are also NSDictionaries themselves, check if any of those child dictionaries meet the condition of having the right code and a non-empty id string. If any one of them do, return true and early exit the loop
?? false // If the original conditional cast of snapshotValue as? NSDictionary fails, return false for the value found

How to get array key values from loop in Swift 3

I'm updating my code from Swift 2.3 to Swift 3, and I am facing these difficulties to get an array key value from a loop in Swift:
var countryarray = NSMutableArray()
self.GetCountriesResult = (responseJSON.objectForKey("GetCountriesResult") as? NSArray)!
for i in 0 ..< self.GetCountriesResult.count {
self.countryarr = self.GetCountriesResult.objectAtIndex(i).objectForKey("countryname") as? String ?? ""
self.countryarray.addObject(self.countryarr)
}
map is better suited than for-in in this case:
guard let json = responseJSON["GetCountriesResult"] as? [String : AnyObject]
else { return }
self.GetCountriesResult = json
let countryArray = self.GetCountriesResult.map {
return $0["countryname"] as? String ?? ""
}
// OR
guard let json = responseJSON["GetCountriesResult"] as? [String : AnyObject]
else { return }
let countries = json.map {
return $0["countryname"] as? String ?? ""
}
Suggestion: Read the following Swift style guides:
https://swift.org/documentation/api-design-guidelines/
https://github.com/raywenderlich/swift-style-guide
https://github.com/linkedin/swift-style-guide
https://github.com/github/swift-style-guide

swift how to make a search from firebase database

I have a firebase database in my app, here is a screenshot of it:
And I need to filter my results using this controller
so I need to compare from
`ref.observe(.value, with: {snapshot in
if (snapshot.value is NSNull){
print("not found")
}else{
for child in (snapshot.children) {
let snap = child as! DataSnapshot
let dict = snap.value as! AnyObject
let latt = dict["lat"]
let lngt = dict["lng"]
let sext = dict["sex"]
let statust = dict["status"]
let aget = dict["age"]
if self.searchSex.selectedSegmentIndex == 0{
self.sexG = "Male"}
else if self.searchSex.selectedSegmentIndex == 1{
self.sexG = "Female"
}
if (self.sexG == sext as! String && Int(searchAgeFrom.value) <= Int(aget) <= Int(searchAgeTo.value)))
{
}
}
}
})
`
I don't know how to filter results from dataSnapshot, I always get an errors. Here, I perform my searching, in ????? I don't know how search for selected value from pickerView:
if (self.sexG == sext as! String && Int(searchAgeFrom.value) <= Int(aget) <= Int(searchAgeTo.value)) && ?????)
{
}
I believe you have erors in unwrapping of snapshot.
Try this:
let dict = snap.value as! [String: AnyObject]
let latt = dict["lat"] as! Double
let lngt = dict["lng"] as! Double
// etc..
Hope it helps
First of all your if condition is not correct as of with searchAgeTo.value you are not comparing with any value also instead of casting snap.value to AnyObject cast it to proper dictionary in your case it is [String:Any].
Now to get the pickerView selected value you can use instance method selectedRow(inComponent:) or also use UIPickerViewDelegate method didSelectRow.
let selectedIndex = yourPickerView.selectedRow(inComponent: 0)
let selectedValue = yourPikcerArray[selectedIndex]
Now compare this in if condition this way.
for child in (snapshot.children) {
let snap = child as! DataSnapshot
let dict = snap.value as! [String:Any]
let latt = dict["lat"]
let lngt = dict["lng"]
let sext = dict["sex"] as! String
let statust = dict["status"] as! String
let aget = dict["age"]
if self.searchSex.selectedSegmentIndex == 0 {
self.sexG = "Male"
}
else {
self.sexG = "Female"
}
let selectedIndex = yourPickerView.selectedRow(inComponent: 0)
let selectedValue = yourPikcerArray[selectedIndex]
if (self.sexG == sext && Int(searchAgeFrom.value) >= Int(aget) && Int(aget) <= Int(searchAgeTo.value) && selectedValue == statust) {
//Handle here
}
}
But instead of filtering Firebase data this way better way is you can store all the data in some main array and than filter that main array according to your filter so that you will not need to observe Firebase data every time when you change your filter value. Also make one custom class or struct to manage all your Firebase data with array of that custom object.

Swift parse json to struct

I have a struct like
struct Channel {
var id : Int = 0
var name = ""
}
and I am getting json from URL as
{"channel_list":[{"channel_id":0,"channel_name":"test1"},{"channel_id":0,"channel_name":"test2"}]}
However I am not able to get data as
func parseJson(anyObj:AnyObject) -> Array<Channel>{
var list:Array<Channel> = []
if anyObj is Array<AnyObject> {
var b:Channel = Channel()
for json in anyObj as! Array<AnyObject>{
b.id = (json["channel_id"] as AnyObject? as? Int) ?? 0
b.name = (json["channel_name"] as AnyObject? as? String) ?? ""
list.append(b)
}
}
return list
}
//read code
let anyObj: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0),error: nil) as AnyObject?
println(anyObj)
if let myobj=anyObj["channel_list"] as AnyObject {
self.Channellist=self.parseJson(anyObj!)
}
Whats wrong with this?
First, instead of using AnyObject, you should cast the JSON response as a Dictionary: [NSObject:AnyObject] then safe cast the result of anyObj["channel_list"] to an Array of Dictionaries [[NSObject:AnyObject]], because this is your JSON response format.
Then you need to use this type in your parseJSON function. We're also simplifying it while we're at it, because there's no need to do weird castings anymore.
Also, you were passing the wrong argument to your function (you used anyObj instead of myObj).
struct Channel {
var id : Int = 0
var name = ""
}
func parseJson(anyObj: [[NSObject:AnyObject]]) -> Array<Channel>{
var list: Array<Channel> = []
var b: Channel = Channel()
for json in anyObj {
b.id = (json["channel_id"] as? Int) ?? 0
b.name = (json["channel_name"] as? String) ?? ""
list.append(b)
}
return list
}
if let anyObj = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions(0),error: nil) as? [NSObject:AnyObject] {
if let myobj = anyObj["channel_list"] as? [[NSObject:AnyObject]] {
self.Channellist=self.parseJson(myobj)
}
}
There's still room for improvement: you could create an initializer for your Struct, for example, and also create a typealias for the response types, use map to create the list, etc.
Here's how I would do it with Swift 2:
struct Channel {
var id : Int
var name: String
init?(JSON: [NSObject: AnyObject]?) {
guard let channelID = json["channel_id"] as? Int, let channelName = json["channel_name"] as? String
else { name = ""; id = 0; return nil }
name = channelName
id = channelID
}
}
func parseJSON(array: [[NSObject:AnyObject]]) -> [Channel?] {
return array.map { Channel(JSON: $0) }
// If you don't want to return optionals to channel you can do this instead:
// return array.map { Channel(JSON: $0) }.filter { $0 != nil }.map { $0! }
}
// And in the caller
do {
guard let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? [NSObject : AnyObject]
else { throw NSError(domain ... // Setup error saying JSON wasn't parsed. }
guard let arrayContents = dict["channel_list"] as? [[NSObject:AnyObject]]
else { throw NSError(domain ... // Setup error saying array wasn't found. }
let channels = parseJSON(arrayContents)
}
catch {
print(error)
}

How to use reflection with Core Data and Swift

I'm trying to use reflection in Swift with Core Data entities, but when I execute the following code, my reflected var has only a reference for a super class, it didn't have a reference for any of it's attributes.
func printProperties() {
let mirror = reflect(self)
for var i = 0; i < mirror.count; i++ {
let (propertyName, childMirror) = mirror[i]
println("property name: \(propertyName)")
println("property value: \(childMirror.value)")
}
}
Does anyone have some idea why this happens?
Update: As suggested by Anderson in his answer I tried another approach and ended up with this code:
func loadFromJson(json: JSON) {
for attributeKey in self.entity.attributesByName.keys {
let attributeDescription = self.entity.propertiesByName[attributeKey]!
as! NSAttributeDescription
let attributeClassName = attributeDescription.attributeValueClassName
let jsonValue = json[(attributeKey as! String)]
var attributeValue: AnyObject? = attributeDescription.defaultValue
if jsonValue.type != .Null && attributeClassName != nil {
if attributeClassName == "NSNumber" {
attributeValue = jsonValue.number!
} else if attributeClassName == "NSString" {
attributeValue = jsonValue.string!
}
}
setValue(attributeValue, forKey: (attributeKey as! String))
}
}
I believe that this code can help you.
I wrote this extension to make a dictionary from a NSmanagedObject and it accesses all attributes and values of the object.
extension NSManagedObject {
func toDict() -> Dictionary<String, AnyObject>! {
let attributes = self.entity.attributesByName.keys
let relationships = self.entity.relationshipsByName.keys
var dict: [String: AnyObject] = [String: AnyObject]()
var dateFormater = NSDateFormatter()
dateFormater.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
for attribute in attributes {
if self.entity.propertiesByName[attribute]!.attributeValueClassName != nil && self.entity.propertiesByName[attribute]!.attributeValueClassName == "NSDate" {
let value: AnyObject? = self.valueForKey(attribute as! String)
if value != nil {
dict[attribute as! String] = dateFormater.stringFromDate(value as! NSDate)
} else {
dict[attribute as! String] = ""
}
} else {
let value: AnyObject? = self.valueForKey(attribute as! String)
dict[attribute as! String] = value
}
}
for attribute in relationships {
let relationship: NSManagedObject = self.valueForKey(attribute as! String) as! NSManagedObject
let value = relationship.valueForKey("key") as! String
dict[attribute as! String] = value
}
return dict
}
}
I hope to have helped you.

Resources