Condition after variable binding in guard swift compilation issue - swift

I am using the nice guard statement from Swift 3.0 (in Xcode 8.0) and have the following function:
func parse(suffix s: String) throws -> Instruction {
guard let count = Int(s) where count >= 0 else {
throw InstructionParsingError(message: s + " should be a positive integer")
}
return CallInstruction(argCount: count)
}
My issue is that the swift compiler complains twice about the line containing my guard statement:
CallInstruction.swift:42:29: Boolean condition requires 'where' to separate it from variable binding
CallInstruction.swift:42:30: Expected ',' joining parts of a multi-clause condition
I tried
replacing the where with a , then the second error disappears but the first one is still there.
replacing the where with , where but then this line can't even be parsed
replacing the count in the where by Int(s) but have the same errors.
How should I change my code so that it compiles? (With a nice single guard statement I mean, of course I could have multiple guards, or ifs or switch but from what I read about the guard statement I should be able to have a clean readable line).

For solving of this issue i recommend you to use model Swift syntax in guard statement that replace where with ,.
func parse(suffix s: String) {
guard let count = Int(s), count >= 0 else {
return
}
}
Also it is possible to use if let statement :
func parseTwo(suffix s: String) {
if let count = Int(s), count >= 0 {
// done
print(count)
} else {
return
}
}

Related

Cannot get function to work without "Expression pattern of type 'Range<Double>' cannot match values of type 'Int'"

This is what I've tried and can't figure out where the error is coming from. Is there something missing? Syntax error? I tried doing similar with if-else in the function and also getting errors.
var steps = 0
func incrementSteps() -> Int {
steps += 1
print(steps)
return steps
}
incrementSteps()
let goal = 10000
func progressUpdate() -> Int {
let updated = progressUpdate()/goal
switch updated {
case (0.0..<0.1):
print("Great start")
case (0.11..<0.5):
print("Almost halfway")
case (0.51..<0.9):
print("Almost complete")
default:
print("Beat goal")
}
}
progressUpdate()
You need to specify updated as Double. And cast it back to Int when returning(if you require Int for your requirement).
Note: Also, you need to modify calling the progressUpdate function within progressUpdate definition which causes a recursion. If you want to do so you might want to give condition to break the loop.
func progressUpdate() -> Int {
let updated = Double(steps/goal)
switch updated {
case (0.0..<0.1):
print("Great start")
case (0.11..<0.5):
print("Almost halfway")
case (0.51..<0.9):
print("Almost complete")
default:
print("Beat goal")
}
return Int(updated)
}

return and exit from compactMap

I am checking some dynamic texts with Regex Rules, using switch for each index of regex pattern array. everything is working fine but in the last case i want the map to stop mapping and do an early return! but seems like i can't return inside a map function! any better approach or solution ?
fileprivate func regexMatch(pattern regex: [String], in text: String) -> Dictionary<String, Any>
{
do
{
for (index, string) in regex.enumerated()
{
let regex = try NSRegularExpression(pattern: string, options: .caseInsensitive)
let results: [NSTextCheckingResult] = regex.matches(in: text, range: NSRange(text.startIndex..., in: text))
_ = results.compactMap
{
/* If i put a guard check to see if result is filled
* then return, works fine but iterates again for the next index and keeps returning till it’s over!
*/
switch index
{
case 0:
// Does something
case 1:
// Does something else
case 2:
// Does the last thing
// If succeed finding the match! Just return or exit the map!
let carNames = String(Range($0.range, in: text).map { String(text[$0]) }!).lowercased()
for case let car as Car in carsList!
{
if spottedCarNamesInReceipt.contains(car.name!.lowercased())
{
result["car"] = car
return // This does work though, but the map starts iterating again over the next index!
}
}
default:
break;
}
}
}
return result
} catch let error
{
print("Invalid regex: \(error.localizedDescription)")
return [:]
}
You don't need to use compactMap if you don't use results. And there is no way to exit from compactMap. Use for in cycle.
Using return statement forEach loop or any kind of Maps, exits only for the current call in the closure while for...in loop exits all the next subsequent calls as well. so for...in solves the problem if an early exit is needed.

Separating multiple if conditions with commas in Swift

We already know multiple optional bindings can be used in a single if/guard statement by separating them with commas, but not with && e.g.
// Works as expected
if let a = someOpt, b = someOtherOpt {
}
// Crashes
if let a = someOpt && b = someOtherOpt {
}
Playing around with playgrounds, the comma-style format also seems to work for boolean conditions though I can't find this mentioned anywhere. e.g.
if 1 == 1, 2 == 2 {
}
// Seems to be the same as
if 1 == 1 && 2 == 2 {
}
Is this an accepted method for evaluating multiple boolean conditions, and is the behaviour of , identical to that of && or are they technically different?
Actually the result is not the same. Say that you have 2 statements in an if and && between them. If in the first one you create a let using optional binding, you won't be able to see it in the second statement. Instead, using a comma, you will.
Comma example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)), cell.isSelected {
//Everything ok
}
&& Example:
if let cell = tableView.cellForRow(at: IndexPath(row: n, section: 0)) && cell.isSelected {
//ERROR: Use of unresolved identifier 'cell'
}
Hope this helps.
In Swift 3, the where keyword in condition clauses were replaced by a comma instead.
So a statement like if 1 == 1, 2 == 2 {} is saying "if 1 equals 1 where 2 equals 2..."
It's probably easiest to read a conditional statement with an && instead of a ,, but the results are the same.
You can read more about the details of the change in Swift 3 in the Swift Evolution proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0099-conditionclauses.md
When it comes to evaluating boolean comma-separated conditions, the easies way to think of a comma is a pair or brackets.
So, if you have
if true, false || true {}
It gets transformed into
if true && (false || true) {}
Here is a case where they are sufficiently different as to require the ,. The following code will yield
'self' captured by a closure before all members were initialized
class User: NSObject, NSCoding {
public var profiles: [Profile] = []
private var selectedProfileIndex : Int = 0
public required init?(coder aDecoder: NSCoder) {
// self.profiles initialized here
let selectedIndex : Int = 100
if 0 <= selectedIndex && selectedIndex < self.profiles.count { <--- error here
self.selectedProfileIndex = selectedIndex
}
super.init()
}
...
}
This is due to the definition of && on Bool:
static func && (lhs: Bool, rhs: #autoclosure () throws -> Bool) rethrows -> Bool
The selectedIndex < self.profiles.count on the RHS is caught in a closure.
Changing the && to , will get rid of the error. Unfortunately, I'm not sure how , is defined, but I thought that this was interesting.
https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_condition-list
The Swift grammar says that the if statement condition-list can be composed by multiple condition separated by commas ,
A simple condition can be a boolean expression, a optional-binding-condition or other things:
So, using the comma to separate multiple expressions is perfectly fine.
When pattern matching a associated value in a switch, it matters when using a , or &&. One compiles, the other won't (Swift 5.1). This compiles (&&):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10 && integer < 10:
print("hi")
default:
fatalError()
}
}
This won't (,):
enum SomeEnum {
case integer(Int)
}
func process(someEnum: SomeEnum) {
switch someEnum {
// Compiles
case .integer(let integer) where integer > 10, integer < 10:
print("hi")
default:
fatalError()
}
}

SQLite trace for logging

I'm trying to debug some SQLite queries in my code using tracing to just log to the console everything that happens, but there seems to be almost no information on it - a Google search for "sqlite3_trace_v2 swift" only returns two pages of results, none of which were helpful except the above link. Using the following code, I was able to get it to the point where it at least runs the trace callback:
func traceSQL (database: OpaquePointer?) {
var pointer: OpaquePointer?
func traceCallback (mask: UInt32, pointer: UnsafeMutableRawPointer?, query: UnsafeMutableRawPointer?, result: UnsafeMutableRawPointer?) -> Int32 {
print("SQLite Trace:")
if let query = query?.load(as: UnsafePointer<Int8>.self) {
print(String(cString: query))
} else {
print("Could not load query.")
}
if let result = result?.load(as: UnsafePointer<Int8>.self) {
print(String(cString: result))
} else {
print("Could not load result.")
}
return 0
}
sqlite3_trace_v2(database, 15, traceCallback as #convention(c) (UInt32, UnsafeMutableRawPointer?, UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Int32, &pointer)
}
but I can't figure out what to do with the output of the function - currently, it just prints out a string of unreadable characters, and my previous attempts didn't even manage that. I suspect at least part of the problem is that I don't really know how to work with UnsafeMutableRawPointer in Swift (something else that seems to be lacking in available information).
tl;dr: How do I log tracing results from SQLite?
The main error in your code is that you derefence the raw pointers
passed to the callback instead of reinterpreting (casting) them.
Also the meaning of those pointers is different for the different
events.
Here is an example how to trace the various events and how to convert
the raw pointers to the "correct" types, using a literal closure
as callback. The comments explaining the meaning of the p and x
argument are taken from SQL Trace Event Codes.
let traceMask = SQLITE_TRACE_STMT|SQLITE_TRACE_PROFILE|SQLITE_TRACE_ROW|SQLITE_TRACE_CLOSE
sqlite3_trace_v2(database, UInt32(traceMask), { (reason, context, p, x) -> Int32 in
switch Int32(reason) {
case SQLITE_TRACE_STMT:
// The P argument is a pointer to the prepared statement.
// The X argument is a pointer to a string which is the unexpanded SQL text
guard
let pStmt = OpaquePointer(p),
let cSql = x?.assumingMemoryBound(to: CChar.self)
else {
return 0
}
let sql = String(cString: cSql) // The unexpanded SQL text
let expandedSql = String(cString: sqlite3_expanded_sql(pStmt)) // The expanded SQL text
print("SQLITE_TRACE_STMT:", expandedSql)
case SQLITE_TRACE_PROFILE:
// The P argument is a pointer to the prepared statement and the X argument points
// to a 64-bit integer which is the estimated of the number of nanosecond that the
// prepared statement took to run.
guard
let pStmt = OpaquePointer(p),
let duration = x?.load(as: UInt64.self)
else {
return 0
}
let milliSeconds = Double(duration)/Double(NSEC_PER_MSEC)
let sql = String(cString: sqlite3_sql(pStmt)) // The unexpanded SQL text
print("SQLITE_TRACE_PROFILE:", milliSeconds, "ms for statement:", sql)
case SQLITE_TRACE_ROW:
// The P argument is a pointer to the prepared statement and the X argument is unused.
guard
let pStmt = OpaquePointer(p)
else {
return 0
}
print("SQLITE_TRACE_ROW")
case SQLITE_TRACE_CLOSE:
// The P argument is a pointer to the database connection object and the X argument is unused.
guard
let database = OpaquePointer(p)
else {
return 0
}
print("SQLITE_TRACE_CLOSE")
default:
break
}
return 0
}, nil)
Of course you can restrict the trace mode to the events that you
are interesting in, e.g.
let traceMask = SQLITE_TRACE_STMT
to trace only prepared statements.

Syntax changes from Swift to Swift 2, cannot invoke enumerate with a string and extra NSErrorPointer argument

I am working on Swift 1.2 with Xcode 6 but now I've installed Xcode 7 with Swift 2.1. There are many errors in Swift 2.1 and many syntax changes, even though my code works well with Swift 1.2. The first problem is with this method:
func getSubstringUpToIndex(index: Int, fromString str: String) -> String
{
var substring = ""
for (i, letter) in enumerate(str) {
substring.append(letter)
if i == index - 1 {
break
}
}
return substring
}
Another problem occurs on this line, "extra argument 'error' in call":
let jsonResult: Dictionary = NSJSONSerialization.JSONObjectWithData(self.mutableData, options: NSJSONReadingOptions.MutableContainers, error: &error) as! Dictionary<String, AnyObject>
In both cases it's Swift 1.2 code, not 2.0.
In the first snippet of code, the enumerate method does not exist anymore, you can update it this way:
func getSubstringUpToIndex(index: Int,
fromString str: String) -> String
{
var substring = ""
for (i, letter) in str.characters.enumerate() {
substring.append(letter)
if i == index - 1 {
break
}
}
return substring
}
Or this way, using substringRithRange :
func getSubstringUpToIndex2(index: Int,
fromString str: String) -> String {
let range = str.startIndex.advancedBy(0)..<str.startIndex.advancedBy(index)
return str.substringWithRange(range)
}
Try them out on swiftstub.
The second line you pasted does not work anymore because swift 2 now handles errors with try/do/catch, the last parameter NSErrorPointer does not exist anymore, learn more about swift2 error handling here.