i've got this error after parsing.
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFString stringValue]: unrecognized selector sent to instance 0x4b68480'
Code is
-(void)parser: (NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if([elementName isEqualToString:#"gallery"]){
}
else if ([elementName isEqualToString:#"asset"]){
NSString *str ;
str = [[attributeDict objectForKey:#"type"] stringValue]; <- HERE
NSLog(#"type = %#",str);
str = [[attributeDict objectForKey:#"thumbnail"] stringValue]; <- HERE
NSLog(#"thumbnail = %#",str);
str = [[attributeDict objectForKey:#"large"] stringValue]; <- HERE
NSLog(#"large = %#",str);
}
NSLog(#"Processing Element: %#",elementName);
}
XML tree looks like
"<"gallery ...">"
"<"asset type="image" thumbnail="/..." large="/..." ">"
...
Thank you for help!
The error tells obviously [attributeDict objectForKey:...] is returning NSString already. So, you do not have to call stringValue to store into str. i.e., instead of
str = [[attributeDict objectForKey:#"type"] stringValue];
you can just do
str = [attributeDict objectForKey:#"type"];
Related
I'm simply trying to access a record in core data by a property I've named "id" that is of String type. The following keeps complaining about 'Unable to parse the format string "id == 8DF3F2C6741B47C8864D1052C36E2C4D"'. How can I solve this issue?
private func getEntity(id: String) -> NSManagedObject? {
var myEntity: NSManagedObject?
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "MyEntity")
fetchRequest.predicate = NSPredicate(format: "id = \(id)")
do {
var tempArray = try getCurrentManagedContext().fetch(fetchRequest)
myEntity = tempArray.count > 0 ? tempArray[0] : nil
} catch let error as NSError {
print("get failed ... \(error) ... \(error.userInfo)")
}
return myEntity
}
It’s a format string. Instead of
NSPredicate(format: "id = \(id)")
Write
NSPredicate(format: "id == %#", id)
public convenience init(nsurl:NSURL) {
var enc:NSStringEncoding = NSUTF8StringEncoding
var err:NSError?
let str:String? =
NSString(
contentsOfURL:nsurl, usedEncoding:&enc, error:&err
)
if err != nil { self.init(err!) }
else { self.init(string:str!) }
}
Swift is version 1.2, error message is:
NSString? is not convertible to string
With Swift 1.2 automatic bridging between String and NSString has been removed.
So you have to explicitly do the cast :
let str = NSString(contentsOfURL:nsurl, usedEncoding:&enc, error:&err) as? String
Swift's String accepts this same initializer as NSString, so you don't even have to use NSString nor to typecast:
let str = String(contentsOfURL: nsurl, encoding: enc, error: &err)
Update for Swift 2.0
do {
let str = try String(contentsOfURL: nsurl, encoding: enc)
print(str)
} catch {
print(error)
}
You can also use an Optional with try? if you want, in this case no need for do catch:
if let str = try? String(contentsOfURL: nsurl, encoding: enc) {
print(str)
} else {
// didn't succeed
}
In Swift 3 I am using the following
do{
let str = try String(contentsOf:personUrl!)
print(str)
}catch let error{
print(error)
}
I need to know if a string contains an Int to be sure that a name the user entered is a valid full name,
for that I need to either make the user type only chars, or valid that there are no ints in the string the user entered.
Thanks for all the help.
You can use Foundation methods with Swift strings, and that's what you should do here. NSString has built in methods that use NSCharacterSet to check if certain types of characters are present. This translates nicely to Swift:
var str = "Hello, playground1"
let decimalCharacters = CharacterSet.decimalDigits
let decimalRange = str.rangeOfCharacter(from: decimalCharacters)
if decimalRange != nil {
print("Numbers found")
}
If you're interested in restricting what can be typed, you should implement UITextFieldDelegate and the method textField(_:shouldChangeCharactersIn:replacementString:) to prevent people from typing those characters in the first place.
Simple Swift 4 version using rangeOfCharacter method from String class:
let numbersRange = stringValue.rangeOfCharacter(from: .decimalDigits)
let hasNumbers = (numbersRange != nil)
This method is what i use now for checking if a string contains a number
func doStringContainsNumber( _string : String) -> Bool{
let numberRegEx = ".*[0-9]+.*"
let testCase = NSPredicate(format:"SELF MATCHES %#", numberRegEx)
let containsNumber = testCase.evaluateWithObject(_string)
return containsNumber
}
If your string Contains a number it will return true else false. Hope it helps
//Swift 3.0 to check if String contains numbers (decimal digits):
let someString = "string 1"
let numberCharacters = NSCharacterSet.decimalDigits
if someString.rangeOfCharacter(from: numberCharacters) != nil
{ print("String contains numbers")}
else if someString.rangeOfCharacter(from: numberCharacters) == nil
{ print("String doesn't contains numbers")}
//A function that checks if a string has any numbers
func stringHasNumber(_ string:String) -> Bool {
for character in string{
if character.isNumber{
return true
}
}
return false
}
/// Check stringHasNumber function
stringHasNumber("mhhhldiddld")
stringHasNumber("kjkdjd99900")
if (ContainsNumbers(str).count > 0)
{
// Your string contains at least one number 0-9
}
func ContainsNumbers(s: String) -> [Character]
{
return s.characters.filter { ("0"..."9").contains($0)}
}
Swift 2.3. version working.
extension String
{
func containsNumbers() -> Bool
{
let numberRegEx = ".*[0-9]+.*"
let testCase = NSPredicate(format:"SELF MATCHES %#", numberRegEx)
return testCase.evaluateWithObject(self)
}
}
Usage:
//guard let firstname = textField.text else { return }
let testStr1 = "lalalala"
let testStr2 = "1lalalala"
let testStr3 = "lal2lsd2l"
print("Test 1 = \(testStr1.containsNumbers())\nTest 2 = \(testStr2.containsNumbers())\nTest 3 = \(testStr3.containsNumbers())\n")
You need to trick Swift into using Regex by wrapping up its nsRegularExpression
class Regex {
let internalExpression: NSRegularExpression
let pattern: String
init(_ pattern: String) {
self.pattern = pattern
var error: NSError?
self.internalExpression = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error)
}
func test(input: String) -> Bool {
let matches = self.internalExpression.matchesInString(input, options: nil, range:NSMakeRange(0, countElements(input)))
return matches.count > 0
}
}
if Regex("\\d/").test("John 2 Smith") {
println("has a number in the name")
}
I got these from http://benscheirman.com/2014/06/regex-in-swift/
let numericCharSet = CharacterSet.init(charactersIn: "1234567890")
let newCharSet = CharacterSet.init(charactersIn: "~`##$%^&*(){}[]<>?")
let sentence = "Tes#ting4 #Charact2er1Seqt"
if sentence.rangeOfCharacter(from: numericCharSet) != nil {
print("Yes It,Have a Numeric")
let removedSpl = sentence.components(separatedBy: newCharSet).joined()
print(sentence.components(separatedBy: newCharSet).joined())
print(removedSpl.components(separatedBy: numericCharSet).joined())
}
else {
print("No")
}
I am trying to get the string for "content" but when I execute the code tha application crash... when I try with self.content.text = "\(theText)" it works but I want to encode to UTF8 because I use cyrilic and I get back kind of these \U0421\U044a\U0434\U044a\U0440\U0436\U0430\U043d\U0438\U0435 \U043d\U0430 \U043f\U0438\U0449\U043e\U0432\U0430 Anyone who can fix the issue?
Here is the code:
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
let theReq = NSFetchRequest(entityName: "Entity1")
theReq.returnsObjectsAsFaults = false
let myPredicate = NSPredicate(format: "objectId == \"\(contentID)\"")
theReq.predicate = myPredicate
let executed:AnyObject = context.executeFetchRequest(theReq, error: nil) as Array
let theText : AnyObject = executed.valueForKey("content")
self.content.text = theText
That has nothing to do with encoding problems.
executeFetchRequest() returns an array of managed objects.
Applying valueForKey() to that array returns an array of values.
Printing the description of an array uses \Unnnn escape sequences for all
non-ASCII characters.
So the solution should be simple: Select a single element of the fetched array:
let executed = context.executeFetchRequest(theReq, error: nil)[0] as NSManagedObject
let theText = executed.valueForKey("content") as String
self.content.text = theText
Of course should also check if the fetch was successful or failed, and if it returned
any object. A more detailed version would look like this:
var error : NSError?
let result = context.executeFetchRequest(theReq, error: &error)
if !result {
println("fetch failed: \(error)")
} else if result.count == 0 {
println("nothing found")
} else {
let obj = result[0] as NSManagedObject
let theText = obj.valueForKey("timeStamp") as String
self.content.text = theText
}
I want to replace a substring (e.g. #"replace") of an NSAttributedString with another NSAttributedString.
I am looking for an equivalent method to NSString's stringByReplacingOccurrencesOfString:withString: for NSAttributedString.
Convert your attributed string into an instance of NSMutableAttributedString.
The mutable attributed string has a mutableString property. According to the documentation:
"The receiver tracks changes to this string and keeps its attribute mappings up to date."
So you can use the resulting mutable string to execute the replacement with replaceOccurrencesOfString:withString:options:range:.
Here is how you can change the string of NSMutableAttributedString, while preserving its attributes:
Swift:
// first we create a mutable copy of attributed text
let originalAttributedText = nameLabel.attributedText?.mutableCopy() as! NSMutableAttributedString
// then we replace text so easily
let newAttributedText = originalAttributedText.mutableString.setString("new text to replace")
Objective-C:
NSMutableAttributedString *newAttrStr = [attribtedTxt.mutableString setString:#"new string"];
In my case, the following way was the only (tested on iOS9):
NSAttributedString *attributedString = ...;
NSAttributedString *anotherAttributedString = ...; //the string which will replace
while ([attributedString.mutableString containsString:#"replace"]) {
NSRange range = [attributedString.mutableString rangeOfString:#"replace"];
[attributedString replaceCharactersInRange:range withAttributedString:anotherAttributedString];
}
Of course it will be nice to find another better way.
Swift 4:
Updated sunkas excellent solution to Swift 4 and wrapped in "extension". Just clip this into your ViewController (outside the class) and use it.
extension NSAttributedString {
func stringWithString(stringToReplace: String, replacedWithString newStringPart: String) -> NSMutableAttributedString
{
let mutableAttributedString = mutableCopy() as! NSMutableAttributedString
let mutableString = mutableAttributedString.mutableString
while mutableString.contains(stringToReplace) {
let rangeOfStringToBeReplaced = mutableString.range(of: stringToReplace)
mutableAttributedString.replaceCharacters(in: rangeOfStringToBeReplaced, with: newStringPart)
}
return mutableAttributedString
}
}
With Swift 4 and iOS 11, you can use one of the 2 following ways in order to solve your problem.
#1. Using NSMutableAttributedString replaceCharacters(in:with:) method
NSMutableAttributedString has a method called replaceCharacters(in:with:). replaceCharacters(in:with:) has the following declaration:
Replaces the characters and attributes in a given range with the characters and attributes of the given attributed string.
func replaceCharacters(in range: NSRange, with attrString: NSAttributedString)
The Playground code below shows how to use replaceCharacters(in:with:) in order to replace a substring of an NSMutableAttributedString instance with a new NSMutableAttributedString instance:
import UIKit
// Set initial attributed string
let initialString = "This is the initial string"
let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red]
let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes)
// Set new attributed string
let newString = "new"
let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue]
let newAttributedString = NSMutableAttributedString(string: newString, attributes: newAttributes)
// Get range of text to replace
guard let range = mutableAttributedString.string.range(of: "initial") else { exit(0) }
let nsRange = NSRange(range, in: mutableAttributedString.string)
// Replace content in range with the new content
mutableAttributedString.replaceCharacters(in: nsRange, with: newAttributedString)
#2. Using NSMutableString replaceOccurrences(of:with:options:range:) method
NSMutableString has a method called replaceOccurrences(of:with:options:range:). replaceOccurrences(of:with:options:range:) has the following declaration:
Replaces all occurrences of a given string in a given range with another given string, returning the number of replacements.
func replaceOccurrences(of target: String, with replacement: String, options: NSString.CompareOptions = [], range searchRange: NSRange) -> Int
The Playground code below shows how to use replaceOccurrences(of:with:options:range:) in order to replace a substring of an NSMutableAttributedString instance with a new NSMutableAttributedString instance:
import UIKit
// Set initial attributed string
let initialString = "This is the initial string"
let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red]
let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes)
// Set new string
let newString = "new"
// Replace replaceable content in mutableAttributedString with new content
let totalRange = NSRange(location: 0, length: mutableAttributedString.string.count)
_ = mutableAttributedString.mutableString.replaceOccurrences(of: "initial", with: newString, options: [], range: totalRange)
// Get range of text that requires new attributes
guard let range = mutableAttributedString.string.range(of: newString) else { exit(0) }
let nsRange = NSRange(range, in: mutableAttributedString.string)
// Apply new attributes to the text matching the range
let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue]
mutableAttributedString.setAttributes(newAttributes, range: nsRange)
I had to bold text in <b> tags, here what I've done:
- (NSAttributedString *)boldString:(NSString *)string {
UIFont *boldFont = [UIFont boldSystemFontOfSize:14];
NSMutableAttributedString *attributedDescription = [[NSMutableAttributedString alloc] initWithString:string];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#".*?<b>(.*?)<\\/b>.*?" options:NSRegularExpressionCaseInsensitive error:NULL];
NSArray *myArray = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)] ;
for (NSTextCheckingResult *match in myArray) {
NSRange matchRange = [match rangeAtIndex:1];
[attributedDescription addAttribute:NSFontAttributeName value:boldFont range:matchRange];
}
while ([attributedDescription.string containsString:#"<b>"] || [attributedDescription.string containsString:#"</b>"]) {
NSRange rangeOfTag = [attributedDescription.string rangeOfString:#"<b>"];
[attributedDescription replaceCharactersInRange:rangeOfTag withString:#""];
rangeOfTag = [attributedDescription.string rangeOfString:#"</b>"];
[attributedDescription replaceCharactersInRange:rangeOfTag withString:#""];
}
return attributedDescription;
}
NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithString:#"I am a boy."];
[result addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, [result length])];
NSMutableAttributedString *replace = [[NSMutableAttributedString alloc] initWithString:#"a"];
[replace addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [replace length])];
[result replaceCharactersInRange:NSMakeRange(5, [replace length]) withAttributedString:replace];
I find that all of the other answers does not work. Here is how I replaced content of a NSAttributed string in a category extension:
func stringWithString(stringToReplace:String, replacedWithString newStringPart:String) -> NSMutableAttributedString
{
let mutableAttributedString = mutableCopy() as! NSMutableAttributedString
let mutableString = mutableAttributedString.mutableString
while mutableString.containsString(stringToReplace) {
let rangeOfStringToBeReplaced = mutableString.rangeOfString(stringToReplace)
mutableAttributedString.replaceCharactersInRange(rangeOfStringToBeReplaced, withString: newStringPart)
}
return mutableAttributedString
}
I have a specific requirement and fixed like below. This might help someone.
Requirement: In the storyboard, rich text directly added to UITextView's attribute which contains a word "App Version: 1.0". Now I have to dynamise the version number by reading it from info plist.
Solution:
Deleted version number 1.0 from the storyboard, just kept "App Version:" and added below code.
NSAttributedString *attribute = self.firsttextView.attributedText;
NSMutableAttributedString *mutableAttri = [[NSMutableAttributedString alloc] initWithAttributedString:attribute];
NSString *appVersionText = #"App Version:";
if ([[mutableAttri mutableString] containsString:appVersionText]) {
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
NSString* version = [infoDict objectForKey:#"CFBundleShortVersionString"];
NSString *newappversion = [NSString stringWithFormat:#"%# %#",appVersionText,version] ;
[[mutableAttri mutableString] replaceOccurrencesOfString:appVersionText withString:newappversion options:NSCaseInsensitiveSearch range:NSMakeRange(0, mutableAttri.length)];
self.firsttextView.attributedText = mutableAttri;
}
Done!! Updated/modified attributedText.
i created a Swift 5 extension for that
extension NSMutableAttributedString {
func replace(_ findString: String, with replacement: String, attributes: [NSAttributedString.Key : Any]) {
let ms = mutableString
var range = ms.range(of: findString)
while range.location != NSNotFound {
addAttributes(attributes, range: range)
ms.replaceCharacters(in: range, with: replacement)
range = ms.range(of: findString)
}
}
}
use case
attributedString.replace("%EMAIL%", with: email, attributes: [.font:boldFont])