Check expression is valid or not - math

Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'Unable to parse the format
string "12+6+ == 1"'
I want to validate expression is valid or not. And I am trying this using following code :
let equationString = "12+6+"
do {
let expr = try NSExpression(format: equationString)
if let result = expr.expressionValue(with: nil, context: nil) as? NSNumber {
let x = result.doubleValue
print(x)
} else {
print("failed")
}
}
catch {
print("failed")
}
I have used try-catch statement, but still I am getting crash here. Is there any solution for this?
Any help would be appreciated.

And you can use it with try:
let equationString = "12+6+"//"12*/6+10-5/2"
do {
try TryCatch.try({
let expr = NSExpression(format: equationString)
if let result = expr.expressionValue(with: nil, context: nil) as? NSNumber {
let x = result.doubleValue
print(x)
} else {
print("failed")
}
})
} catch {
print("Into the catch.....")
// Handle error here
}
TryCatch.h:
+ (BOOL)tryBlock:(void(^)(void))tryBlock
error:(NSError **)error;
TryCatch.m:
#implementation TryCatch
+ (BOOL)tryBlock:(void(^)(void))tryBlock
error:(NSError **)error
{
#try {
tryBlock ? tryBlock() : nil;
}
#catch (NSException *exception) {
if (error) {
*error = [NSError errorWithDomain:#"com.something"
code:42
userInfo:#{NSLocalizedDescriptionKey: exception.name}];
}
return NO;
}
return YES;
}
#end

Related

Running async tcp connection on swiftUi chat app

For a school project, I have to create a swiftUi chat app.
I'm fairly new to swift but managed to use the Network framework to connect to my chat server using the following code :
import Foundation
import Network
class Client {
let connection: Connection
let host: NWEndpoint.Host
let port: NWEndpoint.Port
var queue: DispatchQueue = DispatchQueue.init(label: "keepAlive")
init(host: String, port: UInt16) {
self.host = NWEndpoint.Host(host)
self.port = NWEndpoint.Port(rawValue: port)!
let nwConnection = NWConnection(host: self.host, port: self.port, using: .tcp)
connection = BaburuConnection(nwConnection: nwConnection)
}
func start() {
print("Client started \(host) \(port)")
connection.didStopCallback = didStopCallback(error:)
connection.start()
}
func stop() {
connection.stop()
}
func sendMessage(content: String, channel: UUID) {
let msg = Data(Frame.Message(value: content, channel: channel).serialize())
connection.send(data: msg)
}
func sendCommand(command: CommandKind, args: [String]? = nil) {
let cmd = Data(Frame.Cmd(cmd: Command(kind: command, args: args)).serialize())
connection.send(data: cmd)
}
func isConnected() -> Bool {
return connection.running
}
func didStopCallback(error: Error?) {
if error == nil {
exit(EXIT_SUCCESS)
} else {
exit(EXIT_FAILURE)
}
}
}
import Foundation
import Network
var KeepAlive = 30
var maxReadLength = 4096
class Connection {
let nwConnection: NWConnection
let queue = DispatchQueue(label: "Client connection Q")
var running = false
private var serializedKA = Frame.KeepAlive().serialize()
private lazy var timer = Timer(timeInterval: TimeInterval(KeepAlive), repeats: true) { _ in
self.send(data: Data(Frame.KeepAlive().serialize()))
}
init(nwConnection: NWConnection) {
self.nwConnection = nwConnection
}
var didStopCallback: ((Error?) -> Void)? = nil
func start() {
print("connection will start")
nwConnection.stateUpdateHandler = stateDidChange(to:)
setupReceive()
nwConnection.start(queue: queue)
running = true
DispatchQueue.global(qos: .background).async {
let runLoop = RunLoop.current
runLoop.add(self.timer, forMode: .default)
runLoop.run()
}
}
private func stateDidChange(to state: NWConnection.State) {
switch state {
case .waiting(let error):
connectionDidFail(error: error)
case .ready:
print("Client connection ready")
case .failed(let error):
connectionDidFail(error: error)
default:
break
}
}
private func setupReceive() {
nwConnection.receive(minimumIncompleteLength: 1, maximumLength: maxReadLength) { (data, _, isComplete, error) in
if let data = data, !data.isEmpty {
let message = String(data: data, encoding: .utf8)
print("connection did receive, data: \(data as NSData) string: \(message ?? "-" )")
switch(Frame.parse(reader: BufferedString(str: message!))) {
case .success(let res):
if res is Frame.MessageWrapper {
print("message : ", res)
} else if res is Frame.Cmd {
print("command : ", res)
} else {
print("TODO")
}
case .failure(let err):
print(err)
}
}
if isComplete {
self.connectionDidEnd()
} else if let error = error {
self.connectionDidFail(error: error)
} else {
self.setupReceive()
}
}
}
func send(data: Data) {
nwConnection.send(content: data, completion: .contentProcessed( { error in
if let error = error {
self.connectionDidFail(error: error)
return
}
print("connection did send, data: \(data as NSData)")
}))
}
func stop() {
print("connection will stop")
self.timer.invalidate()
running = false
stop(error: nil)
}
private func connectionDidFail(error: Error) {
print("connection did fail, error: \(error)")
self.stop(error: error)
}
private func connectionDidEnd() {
print("connection did end")
self.stop(error: nil)
}
private func stop(error: Error?) {
self.nwConnection.stateUpdateHandler = nil
self.nwConnection.cancel()
if let didStopCallback = self.didStopCallback {
self.didStopCallback = nil
didStopCallback(error)
}
}
}
The code works fine when I use it with a blocking operation beneath it but I can't think of any good solution for implementing it in mw swiftUi app since I need to keep some sort of reference on the Client object in order to call it's sendMessage and sendCommand methods with a button and notify my ui when I receive a message.
Does anyone has any advises ?
Also, if there is a cleaner way than recursion for async reading, I would gladly change it.

How do I load data using ObservableObject in SwiftUI?

I am trying to transition an app from UIKit to SwiftUI which depends on basic DynamoDB resources but I have hit a snag in forcing the view to refresh as data is added to the list. I have been at this set of code for hours trying different things and I thought I might see if anyone might know why the 'SessionsData' seems to be thrown away and will not accumulate the 'Sessions' objects.
Does anyone have any quick thoughts???
class SessionsData: ObservableObject {
let didChange = PassthroughSubject<SessionsData, Never>()
#Published var data: [Sessions] = [] {
didSet {
didChange.send(self)
}
}
init() {
load()
}
func load() {
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
let scanExpression = AWSDynamoDBScanExpression()
scanExpression.limit = 20
var temp : [Sessions] = []
dynamoDBObjectMapper.scan(Sessions.self, expression: scanExpression).continueWith(block: { (task:AWSTask<AWSDynamoDBPaginatedOutput>!) -> Any? in
if let error = task.error as NSError? {
print("The request failed. Error: \(error)")
} else if let paginatedOutput = task.result {
for session in paginatedOutput.items as! [Sessions] {
print("Item Found")
temp.append(session)
}
DispatchQueue.main.async {
self.data = temp
self.didChange.send(self)
}
}
print(self.data.count)
return true
})
}
}
struct Events: View {
#ObservedObject var sessionsData = SessionsData()
var body: some View {...}
}
Looks like you've over-complicated the code. The PassthroughSubject is unnecessary. Whenever you change a #Published property, it should trigger an update.
class SessionsData: ObservableObject {
#Published var data: [Sessions] = []
init() {
load()
}
func load() {
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
let scanExpression = AWSDynamoDBScanExpression()
scanExpression.limit = 20
var temp : [Sessions] = []
dynamoDBObjectMapper.scan(Sessions.self, expression: scanExpression).continueWith(block: { (task:AWSTask<AWSDynamoDBPaginatedOutput>!) -> Any? in
if let error = task.error as NSError? {
print("The request failed. Error: \(error)")
} else if let paginatedOutput = task.result {
for session in paginatedOutput.items as! [Sessions] {
print("Item Found")
temp.append(session)
}
DispatchQueue.main.async {
self.data = temp
}
}
print(self.data.count)
return true
})
}
}
I don't have experience with DynamoDB, but here are a few things from SwiftUI / Combine perspective. In ObseravbleObjects have change a significant bit and and are now declared with objectWillChange and then sending newValue in willSet:
class SessionsData: ObservableObject {
public let objectWillChange = PassthroughSubject<[Sessions], Never>()
public private(set) var items: [Sessions] = [] {
willSet {
objectWillChange.send(newValue)
}
}
init() {
self.items = []
}
public func load() {
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
let scanExpression = AWSDynamoDBScanExpression()
scanExpression.limit = 20
var temp: [Sessions] = []
dynamoDBObjectMapper
.scan(Sessions.self,
expression: scanExpression)
.continueWith(block: { (task:AWSTask<AWSDynamoDBPaginatedOutput>!) -> Any? in
if let error = task.error as NSError? {
print("The request failed. Error: \(error)")
} else if let paginatedOutput = task.result,
let sessions = paginatedOutput.items as? [Sessions] {
temp.append(contentsOf: sessions)
}
DispatchQueue.main.async {
self.items = temp
}
}
return true
})
}
}
For the UI part you have to just call your load() method defined above in .onApear() and everything else should happen magically:
struct Events: View {
#ObservedObject var sessionsData: SessionsData
var body: some View {
List {
ForEach(self.sessionsData.items) { session in
Text(session.name) // or something of that kind
}
} .onAppear(perform: { self.sessionsData.load() })
}
}

Unable to make Graph + iCloud works

what I have to write here?
db = Graph(cloud: "iCloud.com.devname.appname", completion: { (done, error) in
if let errore = error {
debugPrint("Error iCloud: \(errore.localizedDescription)")
return
}
})
or
db = Graph(cloud: "fantasyString", completion: { (done, error) in
if let errore = error {
debugPrint("Errore iCloud: \(errore.localizedDescription)")
return
}
})
I tried everything but I'm unable to make iCloud works
Thank you for your help, Daniel
EDIT:
the way I read data form db:
var customers : [Entity] {
let search = Search<Entity>(graph: db).for(types: "Customers")
return search.sync(completion: nil).sorted { ($0["name"] as! String) < ($1["name"] as! String)}
}
the way I save the record:
func newCustomer(name:String, phone:String, mail:String, closure: #escaping ()->()) {
let cliente = Entity(type: "Customers")
cliente["name"] = name
cliente["phone"] = phone
cliente["mail"] = mail
db.sync { (done, error) in
if let errore = error {
debugPrint("Errore addCustomer: \(errore.localizedDescription)")
return
}
if done { closure() }
}
}
EDIT 2: the GraphDelegate implementation:
extension DataManager: GraphDelegate {
func graphWillPrepareCloudStorage(graph: Graph, transition: GraphCloudStorageTransition) {
debugPrint("graphWillPrepareCloudStorage")
if transition == .initialImportCompleted {
debugPrint("iCloud initialImportCompleted ok")
self.clientiCont?.tableView.reloadData()
}
}
func graphDidPrepareCloudStorage(graph: Graph) {
debugPrint("graphDidPrepareCloudStorage")
self.clientiCont?.tableView.reloadData()
}
func graphWillUpdateFromCloudStorage(graph: Graph) {
debugPrint("graphWillUpdateFromCloudStorage")
self.clientiCont?.tableView.reloadData()
}
func graphDidUpdateFromCloudStorage(graph: Graph) {
debugPrint("graphDidUpdateFromCloudStorage")
// refresh clienti
self.clientiCont?.tableView.reloadData()
// refresh lista ordini
self.gestCliCont?.tableOrder.reloadData()
// refresh oridine
self.gestOrdCont?.showOrder()
self.gestOrdCont?.tableProdotti.reloadData()
}
}
EDIT: the iCloud config
Thanks to one of my students I found the bug:
if you make a record this way everything works fine:
let record = Entity(type: "Names", graph: self.db)
but if you use this init it doesn't: let record = Entity(type: "Names")
so the solution is: make a record this way
let record = Entity(type: "Names", graph: self.db)

Sqlite.swift no such table error

I have created a function for mapping json data to a sqlite store using sqlite.swift. I am receiving strange behavior though since the first time I try to run an insert I get a "No such table" error". But the inserts work perfectly after that initial throw. My code works by making a connection to the database onload using SQLData.instance.connect(). Then every time a call to insert is placed it initializes the table to make sure it is set up with all the columns within the data.
//attached to button
let userJSON = JSON(["user_id" : id, "username" : email])
SQLData.instance.insert(table: "users", data: userJSON) { result in
if let result = result{
print(result)
}
}
//sql class
class SQLData {
static let instance = SQLData()
var db: Connection? = nil
func connect() {
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
do {
self.db = try Connection("\(path)/contacts.sqlite3")
if let db = self.db {
try db.run(Table("users").drop(ifExists: true))
}
print("SQLite connection established.")
} catch {
print("Couldn't create SQLite connection.")
}
}
func initTable(table: String, data: JSON, completionHandler: (Table?) -> Void){
if let db = db {
let t = Table(table)
do {
try db.run(t.create(temporary: false, ifNotExists: true) { r in
r.column(Expression<Int>("id"), primaryKey: true)
r.column(Expression<Bool>("synced"), defaultValue: false)
for (key,val) in data {
if val.bool != nil{
r.column(Expression<Bool>(key), defaultValue: false)
} else if val.int != nil {
r.column(Expression<Int>(key), defaultValue: 0)
} else if val.double != nil {
r.column(Expression<Double>(key), defaultValue: 0.0)
} else {
r.column(Expression<String?>(key), defaultValue: "")
}
}
completionHandler(t)
})
} catch let err {
print(err)
completionHandler(nil)
}
} else {
completionHandler(nil)
}
}
func insert(table: String, data: JSON, completionHandler: #escaping (Int64?) -> Void){
self.initTable(table: table, data: data) { t in
if let db = db, let t = t {
var query: [Setter] = []
for (key, val) in data {
if val.bool != nil{
query.append(Expression<Bool>(key) <- val.boolValue)
} else if val.int != nil {
query.append(Expression<Int>(key) <- val.intValue)
} else if val.double != nil {
query.append(Expression<Double>(key) <- val.doubleValue)
} else {
query.append(Expression<String?>(key) <- val.stringValue)
}
}
do {
try db.transaction {
let insert = t.insert(query)
let rowid = try db.run(insert)
completionHandler(rowid)
}
} catch let err {
print(err)
completionHandler(nil)
}
} else {
completionHandler(nil)
}
}
}
}
Change your connection function as below
func connect(){
do {
let databaseFilePath = Bundle.main.path(forResource: "contacts", ofType: "sqlite3")!
db = try Connection(databaseFilePath)
} catch {
print(error)
}
}
and bu sure your .sqlite3 (or .db) file in to your project hierarchy and copy bundle resources like images

Recursive function if statement mismatched types in Rust

fn recursive_binary_search<T: Ord>(list: &mut [T], target: T) -> bool {
if list.len() < 1 {
return false;
}
let guess = list.len() / 2;
if target == list[guess] {
return true;
} else if list[guess] > target {
return recursive_binary_search(&mut list[0..guess], target);
} else if list[guess] < target {
return recursive_binary_search(&mut list[guess..list.len()], target);
}
}
the compiler throws an error on if target == list[guess] saying
src/main.rs:33:5: 39:6 error: mismatched types [E0308]
src/main.rs:33 if target == list[guess] {
^
src/main.rs:33:5: 39:6 help: run `rustc --explain E0308` to see a detailed explanation
src/main.rs:33:5: 39:6 note: expected type `bool`
src/main.rs:33:5: 39:6 note: found type `()`
error: aborting due to previous error
I can't figure out how to rewrite this function to satisfy the type checker. I assume it is because I have the return type set to bool and there is a return function call?
dikaiosune's answer explains the problem: the resulting type of your if is (), which is being returned instead of a bool.
Here's a few ways of writing the code a bit more idiomatically:
I'd start by writing it with implicit returns:
fn recursive_binary_search<T: Ord + Eq>(list: &[T], target: T) -> bool {
if list.len() < 1 {
return false;
}
let guess = list.len() / 2;
if target == list[guess] {
true
} else if list[guess] > target {
recursive_binary_search(&list[0..guess], target)
} else {
recursive_binary_search(&list[guess..list.len()], target)
}
}
Then I'd perform the compare just once, instead of potentially twice. Could save a bit of time if comparisons are expensive, but it also looks nice with the match:
use std::cmp::Ordering;
fn recursive_binary_search<T: Ord + Eq>(list: &[T], target: T) -> bool {
if list.is_empty() {
return false;
}
let guess = list.len() / 2;
match target.cmp(&list[guess]) {
Ordering::Less => recursive_binary_search(&list[..guess], target),
Ordering::Greater => recursive_binary_search(&list[guess..], target),
Ordering::Equal => true,
}
}
You can also drop the beginning and end parts of the ranges, and use is_empty for the guard clause.
Then there's the problem of the stack overflow if you search for a value larger than the biggest value... you need to ignore the pivot when recurring:
use std::cmp::Ordering;
fn recursive_binary_search<T: Ord>(list: &[T], target: T) -> bool {
if list.is_empty() {
return false;
}
let guess = list.len() / 2;
match target.cmp(&list[guess]) {
Ordering::Less => recursive_binary_search(&list[..guess], target),
Ordering::Greater => recursive_binary_search(&list[guess+1..], target),
Ordering::Equal => true,
}
}
fn main() {
assert!(!recursive_binary_search(&[1,2,3,4,5], 0));
assert!(recursive_binary_search(&[1,2,3,4,5], 1));
assert!(recursive_binary_search(&[1,2,3,4,5], 2));
assert!(recursive_binary_search(&[1,2,3,4,5], 3));
assert!(recursive_binary_search(&[1,2,3,4,5], 4));
assert!(recursive_binary_search(&[1,2,3,4,5], 5));
assert!(!recursive_binary_search(&[1,2,3,4,5], 6));
}
If you aren't implementing this for learning purposes, use the built-in binary_search.
The issue here is that Rust evaluates the if/else if/else if block as the return value because it lacks an else clause, and statements which don't evaluate to any value have the type (). Incidentally, the code you've presented does exhaustively cover all possibilities (the item at the current index of the slice is either equal to, less than, or greater than the target), but the compiler doesn't know that unless you give it an else clause at the end:
fn recursive_binary_search<T: Ord + Eq>(list: &[T], target: T) -> bool {
if list.len() < 1 {
return false;
}
let guess = list.len() / 2;
if target == list[guess] {
return true;
} else if list[guess] > target {
return recursive_binary_search(&list[0..guess], target);
} else {
return recursive_binary_search(&list[guess..list.len()], target);
}
}
PS: This function doesn't require mutable references, so I'd recommend using regular references as in my code above.
EDIT: For posterity, here's the same code w/o explicit returns:
fn recursive_binary_search<T: Ord>(list: &[T], target: T) -> bool {
if list.len() < 1 {
return false;
}
let guess = list.len() / 2;
if target == list[guess] {
true
} else if list[guess] > target {
recursive_binary_search(&list[0..guess], target)
} else {
recursive_binary_search(&list[guess..list.len()], target)
}
}

Resources