I am trying to convert a project from Swift 2.3 to Swift 3.
Here is some issue with contains(_:) from Collection:
extension Collection {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
The error is Missing argument label 'where:' in call
I added where:, but now, another error appears:
Cannot convert value of type 'Self.Index' to expected argument type '(_) throws -> Bool'
From Swift 3.0 language guide it seems that it should work without errors:
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
In Swift 3, the indices property of Collection is not a Collection, but just an IndexableBase & Sequence. Which has no contains(_:) method, but only contains(where:) method.
(From the generated header.)
associatedtype Indices : IndexableBase, Sequence = DefaultIndices<Self>
public var indices: Self.Indices { get }
You may need to write something like this:
extension Collection {
subscript (safe index: Index) -> Iterator.Element? {
return (startIndex..<endIndex).contains(index) ? self[index] : nil
}
}
Or else you can invoke contains(_:) method for Sequence where Iterator.Element : Equatable, with adding some constraints:
extension Collection
where Indices.Iterator.Element: Equatable, Index == Indices.Iterator.Element
{
subscript (safe index: Indices.Iterator.Element) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
Both work for simple Arrays:
let arr = [1,2,3]
print(arr[safe: 3]) //->nil
print(arr[safe: 2]) //->Optional(3)
But I'm not sure which is the safer generally.
Related
I am trying to fill an array with data from Firebase. But after filling the array I can't call its' members from their index. I am filling the array by this function
func loadSounds(){
Firestore.firestore().collection("data").getDocuments{ (snapshot, error) in
if error == nil{
for document in snapshot!.documents{
let name = document.data()["name"] as? String ?? "error"
let sounds = document.data()["sounds"] as? [String : [String : Any]]
var soundsArray = [dataSound]()
if let sounds = sounds{
for sound in sounds {
let soundName = sound.value["name"] as? String ?? "error"
let soundImage = sound.value["image"] as? String ?? "error"
soundsArray.append(dataSound(name: soundName , image: soundImage ))
}
}
categoriesArray.append(Category(category: name , sounds: soundsArray))
}
print(categoriesArray[0].category)
} else {
print(error)
}
} }
When I try to access it from View, It gives index out of bounds error.
struct ContentView: View {
init(){
loadSounds()
}
var body: some View {
Text(categoriesArray[0].category)}}
If I try to access it via ForEach, it works, also when I try to print it from loadSounds function it works, but I need to access them from their index in View. Thanks for any help.
Never access items of an array by index in the rendering area of a SwiftUI View, in almost all cases the array is empty when the view is rendered the first time.
In your case use .first and handle the optional
var body: some View {
Text(categoriesArray.first?.category ?? "No value")}} // or empty string
Trying to query a document and then update it in a function in my ViewModel. Trying something like the below, but it doesn't work. Any advice would be appreciated!
func addToFruits(name: String) {
db.collection("fruits").whereField("name", isEqualTo: name)
.getDocument()
.limit(to: 1)
.updateData(["fruits": FieldValue.arrayUnion([name])])
}
func addToRoutine(routine: String, habit: String) {
db.collection("routines").whereField("name", isEqualTo: routine).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
document.updateData(["habits": FieldValue.arrayUnion([habit])])
}
}
}
}
In the first one, error I get is "Value of type 'Query' has no member 'getDocument'" and not sure how to resolve this. Second one, error I get is "Value of type 'QueryDocumentSnapshot' has no member 'updateData'"
It's not exactly clear what you're attempting to update but here's some quick example code that will read in a user named 'Steve' and update his age to 50. Keep in mind this will read in the FIRST user named 'Steve' and update their age - then when it's run again, will read the NEXT Steve etc etc - that may be what your attempting to do.
func readUsersAndUpdateTheirAgeTo50() {
let users = self.db.collection("users") //self.db points to *my* firestore
users.whereField("name", isEqualTo: "Steve").limit(to: 1).getDocuments(completion: { querySnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
guard let docs = querySnapshot?.documents else { return }
for doc in docs {
let docId = doc.documentID
let name = doc.get("name")
print(docId, name)
let ref = doc.reference
ref.updateData(["age": 20])
}
})
}
If I just wanted to update all Steve's age to 50, remove the limit
.limit(to: 1)
Note this code is kind of sloppy as since there is a limit of 1, we wouldn't need the loop. Also note that not every Steve is 50 so there should be additional parameters to narrow down which Steve it is - like a uid for example.
A simple attempt to add print and remove elements from a vector but I'm stuck with this error and couldn't find a solution online, so posting it here.
When I try to remove the item without the type definition it gives me an error to add the type, when I do add the type , i get an error regarding RangeBounds , don't know what it means so might need an explanation on this.
Version 1
use structopt::StructOpt;
#[derive(StructOpt,Debug)]
struct CliArgs {
action: String,
id: String,
}
#[derive(PartialEq,Eq,Debug,Clone)]
struct Item{
id:String,
}
fn main() -> std::result::Result<(),Box<dyn std::error::Error>>{
let args = CliArgs::from_args();
let mut items = vec![];
match args.action.as_str() {
"add" => {
items.push(Item {id:args.id});
println!("Added!");
println!("{:?}",items);
},
"print" => {
println!("{:?}",items);
},
"delete" => {
items.drain(|x| x.id == args.id);
println!("{:?}", items);
}
_ => {
return Err("Invalid Action, actions supported are: add and delete".into());
}
};
Ok(())
}
Version 2:
use structopt::StructOpt;
#[derive(StructOpt,Debug)]
struct CliArgs {
action: String,
id: String,
}
#[derive(PartialEq,Eq,Debug,Clone)]
struct Item{
id:String,
}
fn main() -> std::result::Result<(),Box<dyn std::error::Error>>{
let args = CliArgs::from_args();
let mut items = vec![];
match args.action.as_str() {
"add" => {
items.push(Item {id:args.id});
println!("Added!");
println!("{:?}",items);
},
"print" => {
println!("{:?}",items);
},
"delete" => {
items.drain(|x:Item| x.id == args.id);
println!("{:?}", items);
}
_ => {
return Err("Invalid Action, actions supported are: add and delete".into());
}
};
Ok(())
}
The Errors I get
Version 1:
❯ cargo run
Compiling workit v0.1.0 (/Users/reaper/code/workit)
error[E0282]: type annotations needed
--> src/main.rs:32:26
|
32 | items.drain(|x| x.id == args.id);
| ^ consider giving this closure parameter a type
|
= note: type must be known at this point
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
error: could not compile `workit`.
To learn more, run the command again with --verbose.
Version 2:
❯ cargo run
Compiling workit v0.1.0 (/Users/reaper/code/workit)
error[E0277]: the trait bound `[closure#src/main.rs:32:25: 32:49 args:_]: std::ops::RangeBounds<usize>` is not satisfied
--> src/main.rs:32:19
|
32 | items.drain(|x:Item| x.id == args.id);
| ^^^^^ the trait `std::ops::RangeBounds<usize>` is not implemented for `[closure#src/main.rs:32:25: 32:49 args:_]`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `workit`.
To learn more, run the command again with --verbose.
Using warp.rs 0.2.2, let's consider a basic web service with one route for GET /:
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let getRoot = warp::get().and(warp::path::end()).and_then(routes::getRoot);
warp::serve(getRoot).run(([0, 0, 0, 0], 3030)).await;
Ok(())
}
My goal is to use ? for error handling in the route handlers, so let's write one that can error and return early in crate::routes:
use crate::errors::ServiceError;
use url::Url;
pub async fn getRoot() -> Result<impl warp::Reply, warp::Rejection> {
let _parsed_url = Url::parse(&"https://whydoesn.it/work?").map_err(ServiceError::from)?;
Ok("Hello world !")
}
This version works.
Here the error that's returned by Url::parse() is a url::ParseError
To convert between error types, from url::ParseError to ServiceError, then from ServiceError to warp::Rejection, I've written some error helpers in crate::errors:
#[derive(thiserror::Error, Debug)]
pub enum ServiceError {
#[error(transparent)]
Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
}
impl warp::reject::Reject for ServiceError {}
impl From<ServiceError> for warp::reject::Rejection {
fn from(e: ServiceError) -> Self {
warp::reject::custom(e)
}
}
impl From<url::ParseError> for ServiceError {
fn from(e: url::ParseError) -> Self {
ServiceError::Other(e.into())
}
}
Now, the above works, and I'm trying to shorten the second code block to use ? for error handling directly, and convert automatically from the underlying error (here url::ParseError) to a warp::Rejection.
Here's what I've tried:
use crate::errors::ServiceError;
use url::Url;
pub async fn getRoot() -> Result<impl warp::Reply, ServiceError> {
let _parsed_url = Url::parse(&"https://whydoesn.it/work?")?;
Ok("Hello world !")
}
The url::ParseError returned by Url::Parse will convert fine into a ServiceError to return, but returning a ServiceError from my handler doesn't work.
The first compilation error I get is:
error[E0277]: the trait bound `errors::ServiceError: warp::reject::sealed::CombineRejection<warp::reject::Rejection>` is not satisfied
--> src/main.rs:102:54
|
102 | let getRoot = warp::get().and(warp::path::end()).and_then(routes::getRoot);
| ^^^^^^^^ the trait `warp::reject::sealed::CombineRejection<warp::reject::Rejection>` is not implemented for `errors::ServiceError`
Is there a way I can keep the short error handling using ? only and either:
make ServiceError implement warp::reject::sealed::CombineRejection<warp::reject::Rejection> ?
work around that ?
You can implement From to convert your error type into warp::Rejection using reject::custom. Rejection encapsulates custom types which you can later choose to inspect inside of a recover handler.
This example uses a plain error struct, but if you have an error enum you can match on the variants inside the recovery handler and perform different logic as needed.
use serde::Deserialize;
use snafu::{ensure, Snafu};
use std::convert::Infallible;
use warp::{
filters::{any, query, BoxedFilter},
http::StatusCode,
reject::Reject,
Filter, Rejection, Reply,
};
// A normal error type, created by SNAFU
#[derive(Debug, Snafu)]
#[snafu(display("Expected a value less than 10, but it was {}", value))]
struct LessThanTenError {
value: i32,
}
// A function that might fail
fn validate(value: i32) -> Result<i32, LessThanTenError> {
ensure!(value < 10, LessThanTenContext { value });
Ok(value)
}
// We need a custom type to later extract from the `Rejection`. In
// this case, we can reuse the error type itself.
impl Reject for LessThanTenError {}
// To allow using `?`, we implement a conversion from our error to
// `Rejection`
impl From<LessThanTenError> for Rejection {
fn from(other: LessThanTenError) -> Self {
warp::reject::custom(other)
}
}
#[tokio::main]
async fn main() {
let api = simple_math().recover(report_invalid);
let p: std::net::SocketAddr = "0.0.0.0:8888".parse().unwrap();
warp::serve(api).run(p).await;
}
#[derive(Debug, Deserialize)]
struct QueryParams {
a: i32,
b: i32,
}
fn simple_math() -> BoxedFilter<(impl Reply,)> {
any::any()
.and(query::query())
.and_then(|args: QueryParams| async move {
// Look at us using those question marks!
let a = validate(args.a)?;
let b = validate(args.b)?;
let sum = validate(a + b)?;
// We specify that we are returning an error type of
// `Rejection`, which allows the compiler to know what
// type to convert to when using `?` here.
Ok::<_, Rejection>(format!("The sum is {}", sum))
})
.boxed()
}
async fn report_invalid(r: Rejection) -> Result<impl Reply, Infallible> {
if let Some(e) = r.find::<LessThanTenError>() {
// It was our specific error type, do whatever we want. We
// will just print out the error text.
Ok(warp::reply::with_status(
e.to_string(),
StatusCode::BAD_REQUEST,
))
} else {
// Do prettier error reporting for the default error here.
Ok(warp::reply::with_status(
String::from("Something bad happened"),
StatusCode::INTERNAL_SERVER_ERROR,
))
}
}
[dependencies]
serde = { version = "1.0.118", features = ["derive"] }
snafu = "0.6.10"
tokio = { version = "0.2.23", features = ["full"] }
warp = "0.2.5"
% curl 'http://127.0.0.1:8888'
< HTTP/1.1 500 Internal Server Error
Something bad happened
% curl -v 'http://127.0.0.1:8888?a=1&b=2'
< HTTP/1.1 200 OK
The sum is 3
% curl -v 'http://127.0.0.1:8888?a=6&b=5'
< HTTP/1.1 400 Bad Request
Expected a value less than 10, but it was 11
See also:
Is there a way to do validation as part of a filter in Warp?
When should I implement std::convert::From vs std::convert::Into?
How do you define custom `Error` types in Rust?
From my findings, there are two solutions.
Abandon ? in favor of your own macro that constructs and returns a response if there is an error.
Use PR #458 by cjbassi instead of the mainline release by:
Implementing warp::reply::Reply on your error type so that it converts into the correct user facing error message.
Replace warp = "0.2" with warp = { git = "https://github.com/cjbassi/warp.git", branch = "error"} in your Cargo.toml file
use .map_async instead of .and_then for handlers
I am trying to save a nested dictionary into UserDefaults but whenever I try to save I get a crash:
Why is this crashing? In swift 2 it was working just fine
libc++abi.dylib: terminating with uncaught exception of type
NSException
this is my function:
var arrRes = [[String:AnyObject]]() //Array of dictionary
GetNewsFeed.getAllNews { (result) in
if let resData = result.arrayObject {
self.arrRes = resData as! [[String : AnyObject]]
self.defaults.set(self.arrRes, forKey: self.ARRAY_CACHE) // ERROR
self.defaults.synchronize()
print(self.defaults.dictionary(forKey: self.ARRAY_CACHE))
}
}
Very difficult to answer with just that snippet. I think the following line returns a nil value and causing the crash in the later line:
self.arrRes = resData as! [[String : AnyObject]]
Use following optional chaining technique and see if that solves :
if let data = resData as? [[String : AnyObject]] {
self.defaults.set(data, forKey: self.ARRAY_CACHE)
} else {
debugPrint("invalid data")
}