Swift - Ternary conditional operator misunderstanding - ios

I have issues understanding something with ternary conditional operator in Swift 4.1. I hope someone can explain to me :)
I'm using Apollo library.
This library exposes me an object with this field (EDIT) :
public var discount: Swift.Optional<Bool?> {
get {
return graphQLMap["discount"] as! Swift.Optional<Bool?>
}
set {
graphQLMap.updateValue(newValue, forKey: "discount")
}
}
In my class, I'm trying to set this value to true or nil (if value is false, it should be nil to return all results).
Attempt #1 :
func setGoodDeals(_ goodDeals: Bool) {
filters.discount = goodDeals ? goodDeals : nil;
}
This doesn't work ; my app crashes with this error :
Could not cast value of type 'Swift.Optional'
(0x10a9fa298) to 'Swift.Bool' (0x10a9cddf8).
Attempt #2 :
if ( goodDeals ) {
filters.discount = goodDeals;
} else {
filters.discount = nil;
}
This works.
Attempt #3 :
func setGoodDeals(_ goodDeals: Bool) {
filters.discount = goodDeals ? true : nil;
}
This works.
Can anyone explain to me why ?
Thx in advance,

Very simple. It works like this.
Let's say we have to pas the string on the basis of a bool 'isNewUser'.
yourLable.text = isNewUser ? "You can get" : "You get"
if 'isNewUser' is true then it will pass "You can get otherwise it will pass "You get".
I hope it is crystal clear.

Related

Swift: how to detect the error type using the debugger?

I'm new in iOS development, so maybe I'm thinking in the wrong way. I coded a view model with a function that calls an API, and everything works fine.
class SearchCityViewModel : ViewModelProtocol {
//OBSERVABLES
var cities = PublishSubject<[City]>()
var networkError = PublishSubject<Void>()
var generalError = PublishSubject<Void>()
init(){
print("Init SearchCityViewModel")
reinit()
}
func reinit(){}
func searchCity(stringToSearch: String){
async {
do {
if stringToSearch.count>=2 {
let cities = try await(api.getCities(cityToSearch: stringToSearch)).payload!
self.cities.onNext(cities)
}
else {
self.cities.onNext([])
}
}
catch {
self.generalError.onNext(Void())
}
}
}
Now I want to handle errors. In the catch block I want to distinguish all the errors I want to handle gracefully, and for the other ones I just want to emit a general error. To do that, firstly I need to know which error is thrown when the situation I want to handle occurs. I usually do this with the debugger. For instance, I disable the internet connection, and i create a breakpoint inside the catch block. The idea is to check which error is thrown when the internet connection is disabled, in order to create a catch block for that kind of error.
Image of the debugger
I'm struggling because with the debugger I only see that is an AFError instance, but it's not telling me nothing more that can help me to catch it.
What is wrong with my workflow? Do I really need to read all the docs every time? For each library I use?
Thank you!
Perhaps you can read the articles and then you will know how to do it better, you can use the framework -oslog instead of using print function.
debugging your logging info
I found the way. What I was missing is casting the error as NSError. In this way, with the debugger is possible to see the domain and the code of the error. In the case of Alamofire, the real error is wrapped, and it's accessible through the underlyingError attribute. Once I had the domain and the code of the error, I wrote the following code:
class SearchCityViewModel : ViewModelProtocol {
//OBSERVABLES
var cities = PublishSubject<[City]>()
var networkError = PublishSubject<Void>()
var generalError = PublishSubject<Void>()
init(){
print("Init SearchCityViewModel")
reinit()
}
func reinit(){}
func searchCity(stringToSearch: String){
async {
do {
if stringToSearch.count>=2 {
let cities = try await(api.getCities(cityToSearch: stringToSearch)).payload!
self.cities.onNext(cities)
}
else {
self.cities.onNext([])
}
}
catch {
if let afError = asAFError, let underlyingError = afError.underlyingError as NSError?, underlyingError.domain == NSURLErrorDomain, underlyingError.code == NSURLErrorNotConnectedToInternet || underlyingError.code == NSURLErrorTimedOut {
self.networkError.onNext(Void())
}
else {
self.generalError.onNext(Void())
}
}
}
}

EXC_BAD_ACCESS for arrays in Xcode function

I have this function
func pickAttraction(attractionType: Array<Attraction>) -> Attraction {
let randAttr = attractionType[5]
if favoritesNames.contains(randAttr.attractionName) {
return pickAttraction(attractionType: attractionType)
} else {
return randAttr
}
}
and my program crashes (sometimes) on the line starting with "if favoritesNames". On the last time it crashed, the array favoritesNames had 1 string inside, and randAttr.attractionName had a different string. I expected it to return randAttr, but it crashed instead. Does anyone have any idea why?
I also tried
if favoritesNames.contains(randAttr.attractionName) || favoritesNames[0] == randAttr.attractionName {
and I got the same error
I've also tried
func pickAttraction(from attractions: [Attraction]) -> Attraction? {
attractions.filter { !favoritesNames.contains($0.attractionName) }
.randomElement()
}
instead of the other function and I still get the same error
Thank you
I believe let randAttr = attractionType[5] is accessing an index that doesn't exist. Can you verify that there is available data if you access the array at [5]?

Generic Swift struct: Segmentation fault: 11

I am trying to implement a struct with generic type that conforms to Hashable protocol. Can anybody help me understand why am I getting "Segmentation fault: 11" error with the following code.
I would really appreciate any insights regarding this.
struct Pmf<Element: Hashable> {
typealias Distribution = [Element : Float]
fileprivate var normalized = false
fileprivate var distribution:[Element : Float] = [ : ] {
didSet {
self.normalized = false
}
}
}
extension Pmf {
init(values: [Element], withProbs probs: [Float]) {
for pair in zip(values, probs) {
self.distribution[pair.0] = pair.1
}
}
var probDist: Distribution {
mutating get {
if !normalized {
self.normalize()
}
return self.distribution
}
}
subscript(value: Element) -> Float? {
mutating get {
if !normalized {
self.normalize()
}
return self.distribution[value]
}
set(prob) {
self.distribution[value] = prob
}
}
mutating func normalize() {
for (key, val) in self.distribution {
self.distribution[key] = val / Float(self.distribution.count)
}
}
}
var pp = Pmf<String>()
pp["One"] = 4
pp["Two"] = 5
pp["three"] = 5
print(pp)
Seems you need a little trick to define an initializer for a value type in an extension:
Add one line to your init(values:withProbs:) as shown below:
init(values: [Element], withProbs probs: [Float]) {
self.init() //<-
for pair in zip(values, probs) {
self.distribution[pair.0] = pair.1
}
}
Anyway compilers should not crash with SegFault 11. Even if the source code has some fault in it.
You'd better send a Bug Report to Apple, or to swift.org.
I starting writing a bug report for the similar situation (adding method deceleration using a generic with an associated type) and was getting segmentation faults every possible combination I tried.
I started to write a 'Minimum Verifiable Example' in a playground and found I couldn't replicate the fault.
The only difference between app and playground was that app had protocol and method in different source files.
I combined the two source files and no more segmentation fault!!
This took hours of my time to track down, hope it helps someone else.
Update: Submitted a bug for this fault, if you're encountering this too, please add a comment to let the team know you've encountered it: https://bugs.swift.org/browse/SR-3595

API - Microsoft Cognitive Services Face, how to return attributes

In swift I'm using the Microsoft Cognitive Services Face API function detectWithData and trying to use returnFaceAttributes which calls for [AnyObject]!. I need help with what to enter into the Array.
According to this link I assumed ["age", "gender"] would work but I receive an error saying:
unrecognized selector sent to instance 0x7f9b96043df0
And using [MPOFaceAttributeTypeAge, MPOFaceAttributeTypeGender] gives an error:
Value of type 'MPOFaceAttributeTypeAge' does not conform to expected element type 'AnyObject'
For some reason typing "true" in the array give me the age attribute but all other attributes show as nil.
I can't find any examples using swift online. Any advice or pointing me in the right direction would be appreciated.
#IBAction func battleBtn(sender: UIButton){
if !hasChoosenTop || !hasChoosenBottom{
showErrorAlert()
} else{
if let firstImg = topImg.image, let firstImgData = UIImageJPEGRepresentation(firstImg, 0.8), let secondImg = bottomImg.image, let secondImgData = UIImageJPEGRepresentation(secondImg, 0.8){
FaceService.instance.client.detectWithData(firstImgData, returnFaceId: true, returnFaceLandmarks: false, returnFaceAttributes: [MPOFaceAttributeTypeAge, MPOFaceAttributeTypeGender], completionBlock: { (face: [MPOFace]!, err: NSError!) in
if err == nil {
var topFace: String?
topFace = face[0].faceId
var top = face[0].attributes.age
print("my faceId: \(topFace)")
print("my faceId: \(top)")
}
})
}
}
}
screenshot of error
Include an array of Int for [AnyObject]!. For example: [1,4] returns age and smile.

Swift Parse: can not retrieve the value of a bool in _User class

I am developing an app using swift and Parse. For some reasons I have implemented a Bool named "modified" in the _User class. I have been playing around with swift and Parse for a few months but this just does not make sense.
When I try to retrieve the value of the "modified" Bool I keep on getting "false" value even though it is set on "true" on Parse server. Here is the code:
var modified: Bool = PFUser.currentUser().objectForKey("modified") as! Bool
println("User Modified Bool is set to: \(modified)")
I have also tried with
self.modified = PFUser.currentUser().valueForKey("modified") as! Bool
println("User Modified Bool is set to: \(modified)")
and
self.modified = PFUser.currentUser()["modified"] as! Bool
println("User Modified Bool is set to: \(modified)")
Do I have to make a specific query or is there a way to access this value directly?
Edit
I have implemented a specific query. Still get a "false" value though
var queryMainUser: PFQuery = PFUser.query()
queryMainUser.whereKey("username", equalTo: PFUser.currentUser().username)
queryMainUser.findObjectsInBackgroundWithBlock { (mainUsersObjects, mainUsersError) -> Void in
if (mainUsersError == nil) {
var theRef = mainUsersObjects[0] as! PFObject
self.modified = theRef["modified"] as! Bool
println("Any improvement? \(self.modified)")
}
}
Edit 2
Following #danh advices, I tried updating the currentuser instance on the device by implementing this code:
var currentUser = PFUser.currentUser()
currentUser.fetchInBackgroundWithBlock { (object, error) -> Void in
println("Refreshed")
currentUser.fetchIfNeededInBackgroundWithBlock { (result, error) -> Void in
self.modified = currentUser.objectForKey("modified") as! Bool
var idOfUser: String = currentUser.objectId
println("User \(idOfUser) UPDATED")
println(self.modified)
if self.modified == true {
self.deleteData()
self.fetchAllObjects()
}
}
}
When running the console gives me this:
Refreshed
User xTbBw6cNzK UPDATED
false
Here is a screenshot I just took of the server side:
Thank you all for your attention
I am not sure what version of swift you are using. But if you are using Swift 2.0 and Xcode 7, this SHOULD do the job.
This will not work:
let modifiedStatus = PFUser.currentUser()?["modified"] // return value will be nil
This will work for sure:
let modifiedStatus = PFUser.currentUser()?.objectForKey("modified")
print(modifiedStatus) // true as per your table
I know this may sound strange, but some I struggle with something for hours later realising my silly mistake. So it is always good to move back of the current task and later recheck after few hours. So just make sure you cross check the following:
The key "modified" in the main user class of parse
You can retrieve other key values (just to see if nothing else is wrong other than your current retrieving of a key bool value(
Though I am on Swift 2.0, but for sure there is no major change from in this specific code when it comes to move from Swift 1.2 to Swift 2.0.
Just see and if it still doesn't work, we can discuss more on your setup.
I have initialised a "doneSetUp" var as a local variable, it is an int.
then I query the user which just logged in
checks if it exists...
check if the variable userDidSetUp exists in parse and if it does I am converting it to an int and assign it to the local variable doneSetUp I made
then I am using the "doneSetUp" variable which now has a value of 0(false) or 1(true) to decide if the user already setup his account or not and then segue the user to the correct view controller.
mention that all of this code is inside of my logininbackgroundwithblock function.
I hope that helped.
query?.getObjectInBackgroundWithId(user!.objectId!, block: {
(user, error) -> Void in
if let user = user{
if var userDidSetUp: AnyObject = user["doneSetUp"] {
self.doneSetUp = userDidSetUp as! Int
if self.doneSetUp == 0 {
self.performSegueWithIdentifier("procceedToSetup", sender: self)
}else{
self.performSegueWithIdentifier("procceedToApp", sender: self)
}
}
}
})
I know it's an old post, but here's what worked for me.
This is inside the viewDidLoad method.
PFUser.currentUser()?.fetchInBackgroundWithBlock({ (object, error) -> Void in
var modified = PFUser.currentUser()?.objectForKey("modified")?.boolValue
if modified == true {
print(modified) // Output console displays "true"
Hope this helps.

Resources