Iam working on a simple Apple mailkit extension but cant get something readable out of my mails.
func allowMessageSendForSession(_ session: MEComposeSession, completion: #escaping (Error?) -> Void) {
let mailMessage = session.mailMessage;
let subject = mailMessage.subject
let sender = mailMessage.fromAddress.addressString ?? "undefined";
let data = String(data: mailMessage.rawData!, encoding: .utf8)
In data is the header and the mail body. But its filled with so many 'quoted-printable' strings.
Something like this Viele Gr=C3=BC=C3=\n=9Fe =F0=9F=A4=9D. It should be Viele Grüße 🤝.
I already tried the code in this answer https://stackoverflow.com/a/32827598/1407823 but it seems to only work with single words. I cannot get it to work with a whole text.
Is there no built in way to parse text like this?
There is no built-in way to decode the message, you need a RFC822 parser for example MimeParser on GitHub, available as Swift Package.
This is an example how to decode the body as plain text, messageData represents the raw data of the message
import MimeParser
do {
let messageString = String(data: messageData, encoding: .utf8)!
let parser = MimeParser()
let mime = try parser.parse(messageString)
switch mime.content {
case .body(let body): print(body.raw)
case .alternative(let mimes), .mixed(let mimes):
if let plainTextMime = mimes.first(where: {$0.header.contentType?.subtype == "plain"}),
let decodedBody = try plainTextMime.decodedContentString() {
print(decodedBody)
}
}
} catch {
print(error)
}
With subtype == "html" you get the HTML text, if available
Related
I want to create a RAW-Email inside my iOS-App.
Looking to the documentation,
I need to encode my message to MIME-Standard, but I'm not so familiar with this topic. In the documentation there is also example-code for python and java.
How can I achieve this in SWIFT?
func sendRawMail(){
let sender = "sender#mail.com"
let recipient = "recipient#mail.com"
let rawMessage = AWSSESRawMessage()
// rawMessage?.data = "I guess HERE I have to put the MIME- Data?!"
let rawRequest = AWSSESSendRawEmailRequest()
rawRequest?.destinations = [recipient]
rawRequest?.source = sender
rawRequest?.rawMessage = rawMessage
AWSSES.default().sendRawEmail(rawRequest!) { (response, error) in
if let response = response{
print(response)
}
if let error = error{
print(error)
}
}
}
Sending an empty mail with my code works so far.
After a lot of reading, I finally get it working.
You need to write a big string, based on MIME-Standards.
This big string you have to encode to BASE64.
Attachments needed also to transform from data to an BASE64-String.
I will post my code, which can send Mails with an .png image. I will test other fileTypes as well, but it have to be the same principle.
First I created a enum for my fileTypes.
enum DataTypes{
case png
case jpg
case pdf
}
Now I created a function to get the string-value for the specific DataType
func getMIMEDataType(dataType:DataTypes) -> String{
var MIMEData = String()
switch dataType {
case .png:
MIMEData = "image/png"
case .jpg:
MIMEData = "image/jpg"
case .pdf:
MIMEData = "application/pdf"
}
return MIMEData
}
Finally the function to send the raw-mail. There are already variables in the message-string so you can use this function flexible.
func sendRawMail(sender:String,reciepients:[String],subject:String,message:String,attachment:Data?,dataType:DataTypes?,attachmentName:String?,completion: #escaping (_ messageCode:String?,_ error:Error?) -> ()){
let attachmentString = attachment!.base64EncodedString(options: .lineLength64Characters)
let MIMEDataType = getMIMEDataType(dataType: dataType!)
let message:String = """
Subject: \(subject)
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="XXXXboundary text"
This is a multipart message in MIME format.
--XXXXboundary text
Content-Type: text/plain
\(message)
--XXXXboundary text
Content-Type: \(MIMEDataType);
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="\(attachmentName!).\(MIMEDataType.components(separatedBy: "/")[1])"
\(attachmentString)
--XXXXboundary text--
"""
let data = message.data(using: .utf8)
let rawMessage = AWSSESRawMessage()
rawMessage?.data = data
let rawRequest = AWSSESSendRawEmailRequest()
rawRequest?.destinations = reciepients
rawRequest?.source = sender
rawRequest?.rawMessage = rawMessage
AWSSES.default().sendRawEmail(rawRequest!) { (response, error) in
if let response = response{
completion(response.messageId,nil)
}
if let error = error{
completion(nil,error)
}
}
}
You can use it like this and can handle the result in the completionHandler.
sendRawMail(sender: "sender.mail#mail.com", reciepients: ["recipient.mail#mail.com"], subject: "This is a test", message: "TestMessage", attachment: attachment.data, dataType: .png, attachmentName: "testpicture") { (messageID, error) in
if let messageID = messageID{
print(messageID)
}
if let error = error{
print(error)
}
I hope it will help somebody in future :)
I know how to get data response from url. But the data response contains html source. Although I can handle it to get what I need but will be better if I know how to get only text. I use:
let task = URLSession.shared.dataTask(with: request)
{
data, response, error in guard
let data = data, error == nil else
{
// check for fundamental networking error
print(error!)
return
}
result = String(data: data, encoding: .utf8) ?? ""
}
task.resume()
You could do it like this.
let text = String(decoding: data, as: UTF8.self) // Convert data to string
.components(separatedBy: "\n") // Split string into multiple line
.first // Get the first line
Unless the endpoint has an option (like a query parameter) to return only the text, then you will get whatever the server wants to send and you will need to sort it out client side.
can you help me,
I'm facing an issue if the JSON came with multilines like this
"{\"groupId\":\"58\",\"chat\":\"send 2lines\nsecondline\"}"
I'm taking the response from server and convert it with this function
let dataDic = self.convertToDictionary(text: (remoteMessage.appData["message"]! as AnyObject) as! String)
print(dataDic!)
and this is my function
func convertToDictionary(text: String) -> [String: AnyObject]? {
if let data = text.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject]
return json
} catch {
print(error.localizedDescription)
}
}
return nil
}
but the problem came if the code have multilines because it's put \n in the return and
it gives me
The data couldn’t be read because it isn’t in the correct format
Error Domain=NSCocoaErrorDomain Code=3840 "Unescaped control character around character 145." UserInfo={NSDebugDescription=Unescaped control character around character 145.}
You should put an extra "\" before "\n", before parsing your JSON. Try using "replacingOccurencesOf" function.
That way your JSON is formatted before parsing.
I have registration page in my iOS app that I'm trying to write in Swift. The first thing I'm testing out is sending a POST with the email address, this is the way I'm doing so:
var bodyData = ("userEmail=%#\" \" &userPassword=%#\" \"&userDevice=%#\" \"", emailAddress.text, password.text, deviceModel)
let dataToSend = (bodyData as NSString).dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPMethod = "POST"
request.HTTPBody = dataToSend
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error=\(error)")
return
}
// print("response = \(response)")
let responseString = NSString(data: data!, encoding: NSUTF8StringEncoding)
print("responseString = \(responseString)")
}
task.resume()
However, I'm getting a message back stating that it was an empty post. This is the exact response from the output above: responseString = Optional({"complete":"false","message":"Empty Post"})
I've researched different ways to send a simple POST in Swift, and this appears to be correct. I can't see anything wrong with it or why it would output a message saying that the post was empty... Except for maybe the format of the string?
The database is expecting multiple things for the "new user" service, and I'm only sending one part due to it being a test. Could this be the issue? The new-user service is expecting:
Service URL : https://test.com/services/new-user/
Required Post Fields:
For New User:
'userEmail'
'userPassword'
'userDevice'
(From the documentation).
I haven't worked with web services much. After brainstorming more I think these may be the culprits: I may be getting the response back because I'm not sending all the data at once. I also may be sending it incorrectly. Can I send it as text or do I need to send it as JSON?
A couple of issues:
You have a line that says:
var bodyData = ("userEmail=%#\" \" &userPassword=%#\" \"&userDevice=%#\" \"", emailAddress.text, password.text, deviceModel)
That does not do what you intended. It's creating a tuple with four items that consists of a format string and three values, not a single formatted string. Print the bodyData and you'll see what I mean.
You either want to use String(format: ...), or even easier, use string interpolation. (See code snippet below.)
Assuming that emailAddress and password are UITextField objects, note that the text property is optional, so you have to unwrap those optionals before you use them. Look at the bodyData string and you'll see what I mean.
I don't know if deviceModel was optional as well, but if so, unwrap that, too.
You have a space right before the userPassword parameter of the request. That will make it not well formed. Remove that space. You can probably simplify that format string by getting rid of a bunch of those \" references, too.
You probably should be specifying the Content-Type of the request. It's often not necessary, but it's good practice.
You're clearly getting a JSON response, so you might want to parse it.
Thus, you might do something like:
guard emailAddress.text != nil && password.text != nil else {
print("please fill in both email address and password")
return
}
// use
//
// let bodyString = String(format: "userEmail=%#&userPassword=%#&userDevice=%#", emailAddress.text!, password.text!, deviceModel)
//
// or use string interpolation, like below:
let bodyString = "userEmail=\(emailAddress.text!)&userPassword=\(password.text!)&userDevice=\(deviceModel)"
let bodyData = bodyString.dataUsingEncoding(NSUTF8StringEncoding)
request.HTTPMethod = "POST"
request.HTTPBody = bodyData
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in
guard error == nil && data != nil else {
print("error=\(error)")
return
}
do {
let responseObject = try NSJSONSerialization.JSONObjectWithData(data!, options: [])
print(responseObject)
} catch let parseError as NSError {
print(parseError)
}
}
task.resume()
Note, you really should be percent-escaping the values you are adding to the body, too (notably, if the values might have spaces, +, &, or other reserved characters in them). See https://stackoverflow.com/a/28027627/1271826.
If you don't want to get into the weeds of this sort of stuff, consider using a framework like Alamofire, which takes care of this stuff for you. For example:
guard emailAddress.text != nil && password.text != nil else {
print("please fill in both email address and password")
return
}
let parameters = [
"userEmail" : emailAddress.text!,
"userPassword" : password.text!,
"userDevice" : deviceModel
]
Alamofire.request(.POST, urlString, parameters: parameters)
.responseJSON { response in
switch response.result {
case .Failure(let error):
print(error)
case .Success(let value):
print(value)
}
}
I am new to JSON. Are there any methods in JSON parser to remove the comment characters from a response.
Eg. //{"response":"success".......
its SBJson for iPhone.
from http://code.google.com/p/json-framework
The JSON grammar doesn't allow comments. That doesn't answer your question obviously, but I suspect you'll have to do some string manipulation and replace all those comment characters with empty strings and parse it with the JSON library only after doing so.
Nowadays, very easy to do this:
Here's how you get and parse actual json
// normal json no comments
func getStuff() {
guard let url = URL(string: "http://you.com/x.json") else { return print("?") }
let configuration = URLSessionConfiguration.ephemeral
aSession = URLSession(configuration: configuration)
aSession.dataTask(with: url) { [weak self] data, response, error in
guard let _ = self else { return print("woe") }
guard let data = data else { return print("woe") }
do {
let result = try JSONDecoder().decode(YourStructure.self, from: data)
localBlah = Dictionary(uniqueKeysWithValues: result.whatever)
} catch let error {
print(error)
}
}.resume()
}
Here's how you get and parse "json" which has simple comment lines:
During development, remove #comment lines from "json":
Notice the line of code which decodes the data :
let result = try JSONDecoder().decode(YourStructure.self, from: data)
Simply paste in these three lines of code, before, that line:
let s = String(decoding: data, as: UTF8.self)
let fixed = s.replacingOccurrences(
of: "(?m)^#.*",
with: "",
options: .regularExpression)
guard let data2: Data = fixed.data(using: .utf8) else { return print("woe") }
let result = try JSONDecoder().decode(YourStructure.self, from: data2)
So during development in your "json" on your server, you can have things like ..
"measures": [{
"screen": "options",
"topMargin": 25,
#don 't change topmargin anyone
"leftMargin": 12,
#Note,
Steve prefers 13. But everyone
else prefers 12. "rightMargin": 20,
},
It's that simple.
Important note on using regex:
Regex is a sophisticated process. The example regex used in this post simply means
"Delete any full lines, which, start with a '#'
So, it only understands simple "full-line" comments.
How to write regex is beyond the scope of this QA.
When you pre-munge the text at let fixed =, use whatever regex or other technique you wish.
The JSON parsers are very finicky about what is at the start of a JSON block to parse - they DO NOT like characters other than "{" at the start (at least that's what I found with TouchJSON, and it sounds like your case with SBJson is similar).
So just take your string and eliminate any characters before the opening "{", then you can parse:
NSRange startJSONRange = [myJSONString rangeOfString:#"{"];
startJSONRange.length = myJSONString.length - startJSONRange.location;
NSString *correctJSONString = [myJSONString substringWithRange:startJSONRange];
// parse correctJSONString
That will work, but the REAL fix is to tell whoever is sending you JSON to cut out the nonsense and send real JSON.