How can i get "src" string from RSS in Swift? - swift

I have the RSS page with the html tag like this:
<description>
<![CDATA[
<a href='https://www.24h.com.vn/bong-da/psg-trao-than-dong-mbappe-sieu-luong-bong-chi-kem-messi-real-vo-mong-c48a1112120.html' title='PSG trao thần đồng Mbappe siĂªu lÆ°Æ¡ng bổng: Chỉ kĂ©m Messi, Real vỡ má»™ng'><img width='130' height='100' src='https://image.24h.com.vn/upload/4-2019/images/2019-12-27/1577463916-359-thumbnail.jpg' alt='PSG trao thần đồng Mbappe siĂªu lÆ°Æ¡ng bổng: Chỉ kĂ©m Messi, Real vỡ má»™ng' title='PSG trao thần đồng Mbappe siĂªu lÆ°Æ¡ng bổng: Chỉ kĂ©m Messi, Real vỡ má»™ng' /></a><br />PSG trong ná»— lá»±c giữ chĂ¢n “sĂ¡t thủ†Kylian Mbappe, sẵn sĂ ng tăng lÆ°Æ¡ng khổng lồ - má»™t Ä‘á»™ng thĂ¡i nhằm xua Ä‘uổi Real Madrid.
]]>
</description>
Please help me how can i get the value of src to show the image. I also try Getting img url from RSS feed swift but it doesn't work. Here is my code to get src (the code always run to image = "nil"):
let regex: NSRegularExpression = try! NSRegularExpression(pattern: "<img.*?src=\"([^\"]*)\"", options: .caseInsensitive)
let range = NSMakeRange(0, description.count)
if let textCheck = regex.firstMatch(in: description, options: .withoutAnchoringBounds, range: range) {
let text = (description as NSString).substring(with: textCheck.range(at: 1))
image = text
} else {
image = "nil"
}
Thank for your helping !

You need to change your regex to be able to match single-quotes as well, not just double quotes, since the html string you're trying to parse contains single quotes, not double quotes like the one in the linked Q&A.
let regex: NSRegularExpression = try! NSRegularExpression(pattern: "<img.*?src=[\"\']([^\"\']*)[\"\']", options: .caseInsensitive)
If you are sure you only need to match single quotes, you can simplify the pattern by replacing [\"\'] with \'. Currently, the regex pattern will match both single and double quotes.

Related

Swift replace occurrence of string with condition

I have string like below
<p><strong>I am a strongPerson</strong></p>
I want to covert this string like this
<p><strong>I am a weakPerson</strong></p>
When I try below code
let old = "<p><strong>I am a strongPerson</strong></p>"
let new = old.replacingOccurrences(of: "strong", with: "weak")
print("\(new)")
I am getting output like
<p><weak>I am a weakPerson</weak></p>
But I need output like this
<p><strong>I am a weakPerson</strong></p>
My Condition here is
1.It has to replace only if word does not contain these HTML Tags like "<>".
Help me to get it. Thanks in advance.
You can use a regular expression to avoid the word being in a tag:
let old = "strong <p><strong>I am a strong person</strong></p> strong"
let new = old.replacingOccurrences(of: "strong(?!>)", with: "weak", options: .regularExpression, range: nil)
print(new)
I added some extra uses of the word "strong" to test edge cases.
The trick is the use of (?!>) which basically means to ignore any match that has a > at the end of it. Look at the documentation for NSRegularExpression and find the documentation for the "negative look-ahead assertion".
Output:
weak <p><strong>I am a weak person</strong></p> weak
Try the following:
let myString = "<p><strong>I am a strongPerson</strong></p>"
if let regex = try? NSRegularExpression(pattern: "strong(?!>)") {
let modString = regex.stringByReplacingMatches(in: myString, options: [], range: NSRange(location: 0, length: myString.count), withTemplate: "weak")
print(modString)
}

The regex.matches method is matching and counting wrongly

In a TextView the user can insert text and images, like a notes. To save the whole TextView content in the database (Realm), I replaced the image itself for a pattern "[image]imageName[/image]", so when I load back this data to the TextView, I want to replace back this pattern for the images. I made this function:
let attributedString = NSMutableAttributedString(string: txtNote.text)
let range = NSRange(location: 0, length: attributedString.string.utf16.count)
let regex = NSRegularExpression("[image](.*?)[/image]")
for match in regex.matches(in: attributedString.string, options: [], range: range) {
if let rangeForImageName = Range(match.range(at: 1), in: attributedString.string){
let imageName = String(attributedString.string[rangeForImageName])
if let image = loadImage(named: imageName) {
let attachment = NSTextAttachment()
attachment.image = image
let oldWidth = attachment.image!.size.width;
let scaleFactor = (oldWidth / (txtNote.frame.size.width - 10))
attachment.image = UIImage(cgImage: attachment.image!.cgImage!, scale: scaleFactor, orientation: .up)
let attString = NSAttributedString(attachment: attachment)
txtNote.textStorage.insert(attString, at: txtNote.selectedRange.location)
} else {
print("Image not found")
}
}
}
I also have this extension to avoid a try catch in the function above:
extension NSRegularExpression {
convenience init(_ pattern: String) {
do {
try self.init(pattern: pattern)
} catch {
preconditionFailure("Illegal regular expression: \(pattern).")
}
}
}
The example that I'm running, the content on attributedString:
Like Gorillaz :D
[image]4397ACA6-ADDC-4977-8D67-9FF44F10384A.jpeg[/image]
[image]9BE22CA8-9C6C-4FF9-B46F-D8AF33703061.jpeg[/image]

Etc.{
}
It should be 2 matches, and the image names should be: "4397ACA6-ADDC-4977-8D67-9FF44F10384A.jpeg" and "9BE22CA8-9C6C-4FF9-B46F-D8AF33703061.jpeg".
But my function is returning 14 matches and the image names like: "k", "ll", "", "]4397ACA6-ADDC-4977-8D67-9FF44F10384A.jp", "[", etc.
Any idea abou what I'm doing wrong? I've been research for some error like this unsuccessfully all day long.
The [image] and [/image] form character classes that match single chars, the former matching i, m, a, g or e and the latter also matching /.
If you want to treat a part of a regex as a literal substring, you may "quote" it with \Q...\E operators:
let regex = NSRegularExpression("\\Q[image]\\E(.*?)\\Q[/image]\\E")
If you are sure what you are doing, escape the brackets manually, "\\[image\\](.*?)\\[/image\\]".
See Regular Expression Metacharacters table:
\Q Quotes all following characters until \E.
\E Terminates a \Q ... \E quoted sequence.
"Quotes" means "adds backslashes before the special chars to make them match as literal chars" here.

Swift finding and changing range in attributed string

What would be the best way to change this string:
"gain quickness for 5 seconds. <c=#reminder>(Cooldown: 90s)</c> only after"
into an Attributed String while getting rid of the part in the <> and I want to change the font of (Cooldown: 90s). I know how to change and make NSMutableAttributedStrings but I am stuck on how to locate and change just the (Cooldown: 90s) in this case. The text in between the <c=#reminder> & </c> will change so I need to use those to find what I need.
These seem to be indicators meant to be used for this purpose I just don't know ho.
First things first, you'll need a regular expression to find and replace all tagged strings.
Looking at the string, one possible regex could be <c=#([a-zA-Z-9]+)>([^<]*)</c>. Note that will will work only if the string between the tags doesn't contain the < character.
Now that we have the regex, we only need to apply it on the input string:
let str = "gain quickness for 5 seconds. <c=#reminder>(Cooldown: 90s)</c> only after"
let attrStr = NSMutableAttributedString(string: str)
let regex = try! NSRegularExpression(pattern: "<c=#([a-zA-Z-9]+)>([^<]*)</c>", options: [])
while let match = regex.matches(in: attrStr.string, options: [], range: NSRange(location: 0, length: attrStr.string.utf16.count)).first {
let indicator = str[Range(match.range(at: 1), in: str)!]
let substr = str[Range(match.range(at: 2), in: str)!]
let replacement = NSMutableAttributedString(string: String(substr))
// now based on the indicator variable you might want to apply some transformations in the `substr` attributed string
attrStr.replaceCharacters(in: match.range, with: replacement)
}

How can we remove every characters other than numbers, dot and colon in swift?

I am stuck at getting a string from html body
<html><head>
<title>Uaeexchange Mobile Application</title></head><body>
<div id='ourMessage'>
49.40:51.41:50.41
</div></body></html>
I Would like to get the string containing 49.40:51.41:50.41 . I don't want to do it by string advance or index. Can I get this string by specifying I need only numbers,dot(.) and colon(:) in swift. I mean some numbers and some special characters?
I tried
let stringArray = response.componentsSeparatedByCharactersInSet(
NSCharacterSet.decimalDigitCharacterSet().invertedSet)
let newString = stringArray.joinWithSeparator("")
print("Trimmed\(newString)and count\(newString.characters.count)")
but this obviously trims away dot and colon too. any suggestions friends?
The simple answer to your question is that you need to include "." & ":" in the set that you want to keep.
let response: String = "<html><head><title>Uaeexchange Mobile Application</title></head><body><div id='ourMessage'>49.40:51.41:50.41</div></body></html>"
var s: CharacterSet = CharacterSet.decimalDigits
s.insert(charactersIn: ".:")
let stringArray: [String] = response.components(separatedBy: s.inverted)
let newString: String = stringArray.joined(separator: "")
print("Trimmed '\(newString)' and count=\(newString.characters.count)")
// "Trimmed '49.40:51.41:50.41' and count=17\n"
Without more information on what else your response might be, I can't really give a better answer, but fundamentally this is not a good solution. What if the response had been
<html><head><title>Uaeexchange Mobile Application</title></head><body>
<div id='2'>Some other stuff: like this</div>
<div id='ourMessage'>49.40:51.41:50.41</div>
</body></html>
Using a replace/remove solution to this is a hack, not an algorithm - it will work until it doesn't.
I think you should probably be looking for the <div id='ourMessage'> and reading from there to the next <, but again, we'd need more information on the specification of the format of the response.
I'd recommend to use an HTML parser, nevertheless this is a simple solution with regular expression:
let extractedString = response.replacingOccurrences(of: "[^\\d:.]+", with: "", options: .regularExpression)
Or the positive regex search which is more code but also more reliable:
let pattern = ">\\s?([\\d:.]+)\\s?<"
let regex = try! NSRegularExpression(pattern: pattern)
if let match = regex.firstMatch(in: response, range: NSMakeRange(0, response.utf8.count)) {
let range = match.rangeAt(1)
let startIndex = response.index(response.startIndex, offsetBy: range.location)
let endIndex = response.index(startIndex, offsetBy: range.length)
let extractedString = response.substring(with: startIndex..<endIndex)
print(extractedString)
}
While the simple (negative) regex search removes all characters which don't match digits, dots and colons the positive search considers also the closing (>) and opening tags (<) around the desired result so an accidental digit, dot or colon doesn't match the pattern.
You can also use the String.replacingOccurrences() method in other ways, without regex, as follows:
import Foundation
var response: String = "<html><head><title>Uaeexchange Mobile Application</title></head><body><div id='ourMessage'>49.40:51.41:50.41</div></body></html>"
let charsNotToBeTrimmed = (0...9).map{String($0)} + ["." ,":"] // you can add any character you want here, that's the advantage
for i in response.characters{
if !charsNotToBeTrimmed.contains(String(i)){
response = response.replacingOccurrences(of: String(i), with: "")
}
}
print(response)
Basically, this creates an array of characters which should not be trimmed and if a character is not out there, it gets removed in the for-loop
But you have to be warned that what you're trying to do isn't quite right...

Regex find/replace list of keys (Swift 3)

I have a HTML file embedded into my xCode project which has tags within it such as:
{DESCRIPTION}
{LOCATION}
{TIME_SUBMITTED}
I load the contents of the file into a String with:
let url = Bundle.main.url(forResource: "emailTemplate", withExtension: "html")
var messageBody:NSString!
do { messageBody = try String(contentsOf: url!) as NSString! }
catch { messageBody = "" }
Now I have populated "messageBody" I need to find and replace the tags based on my UI, for example:
1) find "{DESCRIPTION}" and replace it with lblDescription.text
2) find "{LOCATION}" and replace it with lblLocation.text
I am trying to use code similar to:
messageBody.enumerateSubstrings(in: NSMakeRange(0, messageBody.length), options: .byWords) { (substring, substringRange, enclosingRange, _) -> () in
print(substring!)
}
However, I am completely useless with regex and could do with some assistance to find and replace if the substring equals a tag. Any ideas please?
You don't need regex for this. Repeated calls to replacingOccurrences will do:
import Foundation
let emailTemplate = "Hello {USER}\n" +
"{DESCRIPTION}\n" +
"\n" +
"Regards."
let email = emailTemplate
.replacingOccurrences(of: "{USER}", with: "John Smith")
.replacingOccurrences(of: "{DESCRIPTION}", with: "Have a nice day")
print(email)