Sending an email with a loop as body in Swift - arrays

I would like to send an email with different steps and values depending on what the user has edited on an image. For that, I'm using UserDefaults to save values.
Then I have the next code into an UIAlertController:
alert.addAction(UIAlertAction(title: "Send", style: .default)
{ action -> Void in
//i is the last step register
let nombre = alert.textFields![0]
for n in 1...self.i {
print("Step \(n): \(self.filterUserDefaults.string(forKey: "Step_\(n)")!)")
}
let filters = [
"Brillo",
"Exposicion",
"Contraste",
"Saturacion",
"Saturacion_color",
"Temperatura",
"Blanco_Negro",
"HUE",
"Tintado_Rojo",
"Tintado_Rosa",
"Tintado_Naranja",
"Tintado_Amarillo",
"Tintado_Purpura",
"Tintado_Verde",
"Tintado_Azul",
"Tintado_Marron",
"Tintado_Gris"]
for filter in filters {
print("\(filter) = \(self.filterUserDefaults.float(forKey: filter).roundTo(places: 3))")
}
self.sendMail(filtro: nombre.text!, body: "XXXX")
})
present(alert, animated: true)
}
func sendMail(filtro: String, body: String) {
if MFMailComposeViewController.canSendMail() {
let mail = MFMailComposeViewController()
mail.mailComposeDelegate = self
mail.setToRecipients(["blablabla#gmail.com"])
mail.setSubject("Filtro \(filtro)")
mail.setMessageBody(body, isHTML: true)
present(mail, animated: true)
} else {
print("Error presenting email app.")
}
}
So printing is working as I expect but the problem is to put these prints into the body of sendMail function...
I tried:
var steps: [String]?
for n in 1...self.i {
steps = ["Step \(n): \(self.filterUserDefaults.string(forKey: "Step_\(n)")!)"]
}
.
.
.
self.sendMail(filtro: nombre.text!, body: steps!.joined(separator: "\n"))
But only the last step is written into the body's email and not the array...
Please, can anyone help me?
Thank you in advance!

u can try out following code
var steps: String = ""
for n in 1...self.i {
steps += "Step \(n): \(self.filterUserDefaults.string(forKey: "Step_\(n)")!)"
}

You are almost there, you need to initialise your array variable and use append
var steps = [String]()
for n in 1...self.i {
steps.append("Step \(n): \(self.filterUserDefaults.string(forKey: "Step_\(n)")!)")
}
...
self.sendMail(filtro: nombre.text!, body: steps.joined(separator: "\n"))

Related

How to set multiple field in Firestore document at once in swift

I'm trying to write two arrays of a custom object to Firestore document.
When I execute the program it only write the last one from the array.
The custom class is codable and encodable.
This is the function that execute the write function to Firestore:
func setShiftConstraints<ShiftConst: Codable>(employeeId: String, for encodeObject: ShiftConst, completion: #escaping (Bool)->()){
do{
let json = try encodeObject.toJson(excluding: [], photoString: nil)
let date = Date().toFormat(DateFormats.format3.rawValue)
self.db.batch().setData([date : json], forDocument: self.reference(to: .shifts).document(employeeId), merge: false).commit()
print("Shift uploaded!")
completion(true)
}catch let err{
print(err.localizedDescription)
completion(false)
}
}
And this is the function that get called when the user is submitting his arguments.
func sendShiftsDataToCloud(){
let group = DispatchGroup()
var returned = true
shiftConsts[0].shiftType = ShiftType(rawValue: self.shift1SegmentControl.selectedSegmentIndex)!
shiftConsts[1].shiftType = ShiftType(rawValue: self.shift2SegmentControl.selectedSegmentIndex)!
for shift in self.shiftConsts{
group.enter()
shift.date = Day().convertDateFormat(from: .format1, to: .format9, date: shift.date)
self.service.setShiftConstraints(employeeId: self.service.currentUid!, for: shift) { (finish) in
if !finish{
returned = false
}
}
group.leave()
}
group.notify(queue: .main) {
if returned{
print("finish")
Alert.showAlert(title: "Success!", text: "", type: .success)
}else{
print("error")
Alert.showAlert(title: "Error!", text: "Please try again.", type: .error)
}
}
}
the result is one array instead of two:

convert array of string into Double in swift

I'm trying to convert a string into a double in swift. I managed to extract the string from a website (www.x-rates.com) into an array but I cannot convert it after in a double in order to make some work around this number. Can anyone tell me what I'm supposed to do or what I did wrong? I know that my label don't update now but I will do it later, the first thing that I'm trying to do is the conversion.
thx a lot!
Here is the code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var resultLabel: UILabel!
#IBOutlet weak var moneyTextField: UITextField!
#IBAction func convert(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var message = ""
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
message = newContentArray[0]
var message = Float(newContentArray[0])! + 10
}
}
}
}
}
DispatchQueue.main.sync(execute: {
self.resultLabel.text = "the value of the dollar is " + message
}
)}
task.resume()
func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I will talk about convert an Array of String to Array of Double.
In swift Array has a method called map, this is responsable to map the value from array, example, in map function you will receive an object referent to your array, this will convert this object to your new array ex.
let arrOfStrings = ["0.3", "0.4", "0.6"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double in
return Double(value)!
}
The result will be
UPDATE:
#LeoDabus comments an important tip, this example is considering an perfect datasource, but if you have a dynamic source you can put ? on return and it will work, but this will return an array with nil
like that
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.map { (value) -> Double? in
return Double(value)
}
Look this, the return array has a nil element
If you use the tips from #LeoDabus you will protect this case, but you need understand what do you need in your problem to choose the better option between map or compactMap
example with compactMap
let arrOfStrings = ["0.3", "0.4", "0.6", "a"];
let arrOfDoubles = arrOfStrings.compactMap { (value) -> Double? in
return Double(value)
}
look the result
UPDATE:
After talk with the author (#davidandersson) of issue, this solution with map ou contactMap isn't his problem, I did a modification in his code and work nice.
first I replaced var message = "" per var rateValue:Double = 0.0 and replacedFloattoDouble`
look the final code
let url = URL(string: "https://www.x-rates.com/calculator/?from=EUR&to=USD&amount=1")!
let request = NSMutableURLRequest(url : url)
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
var rateValue:Double = 0.0;
if let error = error {
print(error)
} else {
if let unwrappedData = data {
let dataString = NSString(data: unwrappedData, encoding: String.Encoding.utf8.rawValue)
var stringSeperator = "<span class=\"ccOutputRslt\">"
if let contentArray = dataString?.components(separatedBy: stringSeperator){
if contentArray.count > 0 {
stringSeperator = "<span"
let newContentArray = contentArray[1].components(separatedBy: stringSeperator)
if newContentArray.count > 0 {
rateValue = Double(newContentArray[0])! + 10
}
}
}
}
}
//
print("Rate is \(rateValue)"); //Rate is 11.167
}
task.resume()
Hope to help you
The reason your code doesn’t work in my opinion is that you have two variables with the same name that are defined in different scopes and you use the wrong one at the end.
At the beginning you define
var message = ""
And then when converting to a number further down
var message = Float(newContentArray[0])! + 10
So change the last line to something like
var number = Float(newContentArray[0])! + 10
And use number in your calculations. Although I think
var number = Double(message)
should work equally fine since you have assigned newContentArray[0] to message already and Double is more commonly used than Float (I don’t understand + 10)

CloudKit nil optional error

I am trying to load, then modify and resave an array.
Here is code, modify func is the top one:
func modifyUserGroupsRequestee(){
print("step2")
acceptedUsersArray.append(groupNameLbl.text!)
//error
userGroupRecordToUpdate.setObject(acceptedUsersArray as CKRecordValue?, forKey: "userGroups")
database.save(recordToUpdate) { (savedRecord, error) in
if error != nil{
print(error.debugDescription)
}else{
print("SAVED RECORD")
}
}
}
func resaveUserGroups(){
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
//operation.resultsLimit = CKQueryOperationMaximumResults
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil{
self.userGroupRecordToUpdate = record
// self.acceptedUsersArray = (record.object(forKey: "userGroups") as! Array)
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
// self.feedTableView.reloadData()
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
// }
//self.feedTableView.reloadData()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
//self.tableView.reloadData()
// print(leaderboardInfo.count)
}
}
The function prints step1 but never gets to step2. In the bottom function, I have an if let statement I tried to create to solve my nil issue (I commented my previous code above that line- self.acceptedUsersArray... Anyway, I believe I am implementing the if let statement incorrectly, because no data is loaded, even though there is data in cloud kit.
And I do have my personal user cloudKit records set up, here's a pic:
You should try to keep your code always indented consistently.
(In Xcode editor, Cmd+A (Select All), then Ctrl+I (Re-Indent).)
With confusing comments removed, your resaveUserGroups shows as:
func resaveUserGroups() {
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
self.userGroupRecordToUpdate = record
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
}
}
Omitting some parts to clarify:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
database.add(operation)
}
}
The line database.add(operation) exists inside the recordFetchedBlock.
You may need to fix some more parts (that's another story), but at least, you need to move the line out of the closure to execute the operation you have created:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
//database.add(operation)
} //↓
database.add(operation)
}
I just solved it. Apparently there always needs to be some kind of value inside the array even if it was just created. I was trying to query an array that existed in the recordType, but not yet under the specific record.

Alamofire : Updating array's values with JSON's data

I have a problem when trying to update an array of struct with the JSON's data. Here's my code:
This is how the struct is defined:
Struct
struct Shot {
var title: String
var desc: String
var img: String
init(title: String, desc: String, img: String) {
self.title = title
self.desc = desc
self.img = img
}
}
Here I try to update the array, but it doesn't work:
Code
func authDribbble() {
let endURL = "https://api.dribbble.com/v1/shots/"
let token = "***"
Alamofire.request(.GET, endURL, parameters: ["access_token" : token])
.responseJSON { response in
if let JSON = response.result.value {
for i in 0..<JSON.count {
let titleA: String = String(JSON[i]["title"])
let descA: String = String(JSON[i]["description"])
let imgA: String = String(JSON[i]["images"])
self.data += [Shot(title: titleA, desc: descA, img: imgA)]
}
}
}
}
The problem is that after the function is performed, the array stays void. What's wrong?
Thank you.
Working under the assumption that you've called this method as follows:
authDribbble()
print("my data: \(self.data)") // Prints empty array.
The issue is that Alamofire is performing the request asynchronously. That is, the work isn't done yet when your code proceeds to the next line (the print statement). You need to implement a callback mechanism. One way to do this is to pass a function as a parameter of your authDribbble() function. That would change the function to something like:
func authDribbble(completion: () -> Void) {
// create the url
// create parameters
Alamofire.request(.GET, url!, parameters: params)
.responseJSON { response in
guard let JSON = response.result.value as? [[String : AnyObject]] else {
// Wrong type in JSON response.
print(response.result.error)
}
self.data = JSON.map { dict in
guard let title = dict["title"] as? String,
desc = dict["description"] as? String,
img = dict["images"] as? String else {
// Handle error creating Shot from JSON
}
return Shot(title: title, desc: desc, img: img)
}
completion()
}
}
}
You would then replace the call to authDribbble() with:
authDribbble() {
print("data: \(self.data)") // Prints populated array.
}

Swift array doesn't hold values

I ran into the following problem while trying to build something with Swift. Apparently, the values that I added into an array are not saved pass some point. They are sent just fine with the protocol while the task is running, but after it completes, if I try to see the values in the array, it returns empty.
What am i doing wrong? My guess is that it get deallocated after task finishes. If that is so, is there a way to make it strong? Is there something I should know about this task thingie? Can you please explain to me how this works and what I should do?
Here is the code:
var exchangeArray : ExchangeValues[] = [];
func fetchResult(){
var currenciesOrder = ["EUR", "USD", "GBP", "CHF", "NOK", "SEK", "DKK", "CZK","TRY", "BGN", "MDL", "PLN", "XDR", "XAU", "UAH", "RUB", "RSD","CAD", "AUD", "JPY", "EGP", "BRL","HUF", "MXN","KRW", "CNY","NZD","INR","AED", "ZAR"];
let dateFormat = NSDateFormatter();
dateFormat.dateFormat = "yyyy-MM-dd";
for days in 0..2 {
let daysToSubstract = Double(60*60*24*days);
let date : String = dateFormat.stringFromDate(NSDate().dateByAddingTimeInterval(-daysToSubstract));
var url: NSURL = NSURL(string: "http://openapi.ro/api/exchange/all.json?date=" + date);
var session = NSURLSession.sharedSession();
var task = session.dataTaskWithURL(url, completionHandler: {
(data, response, error) -> Void in
if (response != nil){
var err: NSError?;
if(err?) {
println("request Error \(err!.localizedDescription)");
}
//send the result to protocol
let results = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary;
let temp : NSDictionary = results["rate"] as NSDictionary;
for key in 0..currenciesOrder.count{
for (currencyKey : AnyObject, currencyValue : AnyObject) in temp {
if currenciesOrder[key] as String == currencyKey as String {
let tempExchange = ExchangeValues(currency: currencyKey as? String, value: currencyValue.doubleValue, date:date );
self.exchangeArray.append(tempExchange);
}
}
}
self.delegate?.didReceiveResults(self.exchangeArray);
} else {
println("error: \(error.localizedDescription)");
}
})
task.resume();
}
println("\(exchangeArray.count)");
}
I kind of figured out what the problem is:
The task block returns void, so I think it empties the array after it finishes. The result is to create another function that gets called from the task, where the array works just fine (it gets passed the values while they exist) and any further processing can be done there.
I hope this helps someone. The code is as easy as this:
func sendResults(array : ExchangeValues[]) -> Void{
println("\(exchangeArray.count)"); }
Of course, you can have the function return something if you need to.

Resources