I am trying to get the value from a row, turn it into a string and then check to see if that string is in an array. So far I have this:
let row: IntRow? = form.rowBy(tag: "devicefield")
let stringvalue = String(describing: row?.value)
if (row?.value) != nil
{
if tableDevices.contains("\(stringvalue)")
{
self.dismiss(animated: false, completion: nil)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "filterData"), object: nil)
}
else
{
SCLAlertView().showError("Error", subTitle: "\(stringvalue) does not exist at your facility")
return
}
}
But for some reason it keeps printing out my variable stringvalue as an optional.
row?.value is an optional. That's pretty clear since you see the "?". So the print command you are using is simply printing out that it is an optional. The string isn't optional, the variable is. You can get a new variable like this:
if let printableRowNumberAsInt = row?.value {
// do your stuff like...
print("\(printableRowNumberAsInt)")
}
Related
I've encountered a problem I can't solve myself. I have tried the Internet without any luck.
I'm still pretty new to Swift and coding, and right now following a guide helping me create an app.
Unfortunately, as I can understand, the app was written for Swift 3, and is giving me some issues since I'm using Swift 4.
I have to lines that gives me this warning:
String interpolation produces a debug description for an optional value; did you mean to make this explicit?
Use 'String(describing:)' to silence this warning Fix
Provide a default value to avoid this warning Fix
However, when I click one of Xcode's solutions I get another problem.
If I use the first fix, the app crashes and I get the following message:
Thread 1: Fatal error: Unexpected Segue Identifier;
If I use the second fix I have to assign a default value. And I don't know what this should be.
The whole passage of code is as follows.
It's the line starting with guard let selectedMealCell and the last one after default: that is causing the issues.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
super.prepare(for: segue, sender: sender)
switch(segue.identifier ?? "") {
case "AddItem":
os_log("Adding a new meal.", log: OSLog.default, type: .debug)
case "ShowDetail":
guard let mealDetailViewController = segue.destination as? MealViewController else {
fatalError("Unexpected destination: \(segue.destination)")
}
guard let selectedMealCell = sender as? MealTableViewCell else {
fatalError("Unexpected sender: \(sender)")
}
guard let indexPath = tableView.indexPath(for: selectedMealCell) else {
fatalError("The selected cell is not being displayed by the table")
}
let selectedMeal = meals[indexPath.row]
mealDetailViewController.meal = selectedMeal
default:
fatalError("Unexpected Segue Identifier; \(segue.identifier)")
}
}
So, the first suggested fix worked for you. It quieted the compile time warning, although admittedly String(describing:) is a weak solution.
In both cases, you need to unwrap the optional value. For the first case you should use:
guard let selectedMealCell = sender as? MealTableViewCell else {
if let sender = sender {
fatalError("Unexpected sender: \(sender))")
} else {
fatalError("sender is nil")
}
}
and in the second case:
fatalError("Unexpected Segue Identifier; \(segue.identifier ?? "")")
Then you got a runtime error:
"Unexpected Segue Identifier;"
That is telling you that your switch didn't match the first 2 cases and it ran the default case. The crash is caused because your code is explicitly calling fatalError. Your segue.identifier is apparently an empty string.
So your problem is actually in your Storyboard. You need to assign identifiers to your segues. Click on the segue arrows between your view controllers, and assign identifiers "AddItem" and "ShowDetail" to the proper segues. The segue identifier is assigned in the Attributes Inspector on the right in Xcode.
If you are prepared to write an small extension to Optional, it can make the business of inserting the value of an optional variable less painful, and avoid having to write optionalVar ?? "" repeatedly:
Given:
extension Optional: CustomStringConvertible {
public var description: String {
switch self {
case .some(let wrappedValue):
return "\(wrappedValue)"
default:
return "<nil>"
}
}
}
Then you can write:
var optionalWithValue: String? = "Maybe"
var optionalWithoutValue: String?
print("optionalWithValue is \(optionalWithValue.description)")
print("optionalWithoutValue is \(optionalWithoutValue.description)")
which gives:
optionalWithValue is Maybe
optionalWithoutValue is <nil>
You can also write print("value is \(anOptionalVariable)") -- the .description is redundant since print() uses CustomStringConvertible.description anyway -- but although it works you still get the annoying compiler warning.
You can use the following to automatically produce "nil" (or any other String) for nil values and for non-nil values use the description provided by CustomStringConvertible
extension String.StringInterpolation {
mutating func appendInterpolation<T: CustomStringConvertible>(_ value: T?) {
appendInterpolation(value ?? "nil" as CustomStringConvertible)
}
}
For your own types you have to conform to CustomStringConvertible for this to work:
class MyClass: CustomStringConvertible {
var description: String {
return "Whatever you want to print when you use MyClass in a string"
}
}
With this set up, you can simply use your optionals the same way as any other type, without any compiler warnings.
var myClass: MyClass?
myClass = MyClass()
print("myClass is \(myClass)")
This code bothers me. Below, I'm trying to find the first instance of a specific type of ViewController in a NavigationController's stack. Simple. But when I've found it, I have to then cast it to the type I just looked for, which seems redundant to me.
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
guard let foundViewController = viewControllers.first(where: { $0 is T }) as? T else {
return nil
}
self.popToViewController(foundViewController, animated:animated)
return foundViewController
}
Only thing I can think of is this...
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
guard let foundViewController = viewControllers.flatMap({ $0 as? T }).first() else {
return nil
}
self.popToViewController(foundViewController, animated:animated)
return foundViewController
}
...but I've repeatedly found using flatMap like this tends to confuse people reading the code, and, as correctly pointed out in the comments below, iterates over the entire collection whereas first doesn't do that.
So is there another way to solve this issue?
You can use case patterns to select the viewControllers of the type you are interested in and pop and return the first one you find:
extension UINavigationController {
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
for case let vc as T in viewControllers {
self.popToViewController(vc, animated: animated)
return vc
}
return nil
}
}
Example:
Use a button in OrangeViewController to return to GreenViewController earlier in the stack:
#IBAction func popToGreen(_ sender: UIButton) {
let greenVC = self.navigationController?.popToFirstViewController(
ofType: GreenViewController.self,
animated: true
)
// Modify a property in GreenViewController that
// will be moved into a label in viewWillAppear
greenVC?.labelText = "Returned here from Orange"
}
popToLastViewController(ofType:animated:)
You might also want a function to pop to the most recent viewController of a type. That is easily achieved with a simple modification (adding .reversed()):
func popToLastViewController<T:UIViewController>(ofType type:T.Type, animated: Bool) -> T? {
for case let vc as T in viewControllers.reversed() {
self.popToViewController(vc, animated: animated)
return vc
}
return nil
}
I'm in favor of combining flatMap and lazy to get the behavior of conditionally casting to T, stripping out mismatches, and not enumerating the whole array:
func popToFirstViewController<T:UIViewController>(ofType type:T.Type, animated:Bool) -> T? {
guard let foundViewController = viewControllers.lazy.flatMap({ $0 as? T }).first {
return nil
}
self.popToViewController(foundViewController, animated:animated)
return foundViewController
}
As for "confusing people that read the code:" flatMap is fairly idiomatic Swift, and will be less ambiguous with the upcoming rename to compactMap. If readers in your environment really have trouble, you could always write a small helper (generic or not) that performs the same work under a clearer name.
I'm trying to deny access to a certain view controller if the userDefault is empty, but the code doesn't seem to work. To be a bit more clear, I'm saving a favorite-list to a userDefault. This is my code:
if UserDefaults.standard.array(forKey: "favorites") == nil {
navigationController?.popToRootViewController(animated: true)
return
}
The error is Index out of range, which means that the whole block is ignored (the code after this block runs and since the user default is empty it crashes when trying to retrieve information that isn't there).
The funny thing is, the code works the first time I try to enter the viewController (it denies me access). But if I favorite mark an object (save to userDefault), then un-favorite the same object (userDefault becomes empty), and enter the viewController, the program crashes.
I have tried:
if let favExist = UserDefaults.standard.array(forKey: "favorites") {
print("")
print("FAV EXISTS")
print("")
}else {
print("")
print("NOPE")
print("")
navigationController?.popToRootViewController(animated: true)
return
}
...and the same problem persists. In print() the log tells me FAV EXISTS after I favorite mark, then un-favorite mark, then try accessing the page (even though the userDefault now should be empty).
I have also tried code from other threads. The suggested code to solve my problem from the other thread was:
let defaults = UserDefaults.standard
if (!defaults.bool(forKey: "favorites")) {
defaults.set(true, forKey: "favorites")
}
I'm not really sure how to implement it though? Where do I use this? And what does it do?
Any idea what's wrong?
It´s enough to do this:
if let favorites = UserDefaults.standard.array(forKey: "favorites") {
// userDefault has a value
} else {
// userDefault is nil (empty)
}
Update:
You need to make a check within the if-statement if your arrat has any values too:
if let favorites = UserDefaults.standard.array(forKey: "favorites") {
print("Favorites exists")
if favorites.isEmpty {
print("Favorites is empty")
} else {
print("Favorites is not empty, it has \(favorites.count) items")
}
} else {
print("Favorites is nil")
}
When you set the UserDefaults Array also set a BOOL to UserDefaults. When you recover the Bool it won't crash even if it hasn't been set.
var favouritesset = UserDefaults.standard.bool(forKey: "favoritesset")
if favouritesset == true {
//Then Recover the Array
var array = UserDefaults.standard.array(forKey: "favorites")
}
OK, Rashwan L solved it for me. Thing was, my code (and suggested code by others) checked whether or not userDefault existed or not - it didn't bother whether there was a value stored or not. To work around the problem, I had to test if favExist.count == 0 was true. If true, user is blocked from the page and prevented from accessing the rest of the code. Se below:
if let favExist = UserDefaults.standard.array(forKey: "favorites") {
if(favExist.count == 0)
{
navigationController?.popToRootViewController(animated: true)
return
}
}else {
navigationController?.popToRootViewController(animated: true)
return
}
You do like this:
if UserDefaults.standard.array(forKey: "favs") != nil {
// userDefault has a value
} else {
// userDefault is nil (empty)
}
I'm new in the swift2 world and I currently struggle with a simple function :
// Get all moves for the first category
func getMuscles() -> BodyPart {
let bpart:BodyPart?
if let managedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext {
do{
let fetchRequest = NSFetchRequest(entityName: "BodyPart")
let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as! [BodyPart]
bpart = fetchResults[0]
} catch let error as NSError {
print(error)
bpart = nil
}
}
return bpart
}
How can I solve this issue ? And what are the 'best-practices' in swift2 for defining a function ?
Thank you
EDIT 1
I've tried to change the signature of the method, but error is still here :
The question you should be asking yourself is whether getMuscles() must always return an object or if it's fine for it to return a nil.
By changing the method signature to func getMuscles() -> BodyPart?,
you're basically stating that a nil might be returned from that method,
thus solving your immediate compile time issue.
In that particular context, because you're fetching objects from CoreData,
it might be wise to allow getMuscles() to return a nil.
The way you define your functions (if they return optionals ?, or not) entirely depends on the calling code.
Change your method signature to :
func getMuscles() -> BodyPart?
But be careful while unwrapping the return value when the this function is being called.
Just return:
func getMuscles() -> BodyPart? { }
Thats nothing to do with SWIFT2.. The return type is expecting some value BodyPart not an optional value BodyPart?...But you are returning a optional value bpart
func getMuscles() -> BodyPart {
let bpart:BodyPart?
....
return bpart
}
If you want to return bpart as it is you need to create the return type as optional
func getMuscles() -> BodyPart? {
let bpart:BodyPart?
....
return bpart
}
or if you want to just return the value try this
func getMuscles() -> BodyPart {
let bpart:BodyPart = ()//initialize here dont make optional
....
return bpart
}
I am trying to read textFields values which is coming trough alert controller but I am getting run time error on casting with Anyobject to NSArray
alertUserStatus.addAction(UIAlertAction(title: "Sign Up", style: UIAlertActionStyle.Default, handler: {
alertAction in
//let textField:NSArray=alertUserStatus.textFields as AnyObject! as NSArray
let textField=alertUserStatus.textFields! as AnyObject! as NSArray
let username:String=textField.objectAtIndex(0) as String
let password:String=textField.objectAtIndex(1) as String
var signUpUser=PFUser()
signUpUser.username=username
signUpUser.password=password
signUpUser.signUpInBackgroundWithBlock{
(success:Bool!,error:NSError!)->Void in
if success == true {
println("sign up successfully")
}
else{
println("fail to sign up")
}
}
}))
It always helps to look at the type you are getting from a function before trying to massage it into another type. In this case alertUserStatus.textFields returns [AnyObject]? – that is, an optional array of AnyObject. So there’s really no need to convert it to an AnyObject and then to an NSArray just so you can get the first two entries from it. Instead you want to unwrap it and convert it to an array of String.
It’s also best to avoid using !, even if you’re confident the optional can’t possibly be nil, because one of these days there’ll be an edge case that you miss that doesn’t hold and your code will crash with an inexplicable force-unwrap error message.
// nb you can skip the handler: argument name with trailing-closure syntax
alertUserStatus.addAction(UIAlertAction(title: "Sign Up", style: UIAlertActionStyle.Default) {
alertAction in
if let textFields = alertUserStatus.textFields as? [String] where textFields.count > 1 {
let username = textFields[0]
let password = textFields[1]
// rest of your code...
}
else {
// assert with intelligible message or log error or similar
}
})