Validate NSString for Hexadecimal value - cocoa

I'm working on a custom NSFormatter.
I need to verify user input step by step... i thought to check by isPartialStringValid: if the string contains only permitted chars "0123456789ABCDEF".
How can i verify this condition ? is there a way with NSString function to check if a string contain only some chars ?

Does this method work for you?
NSString *string = #"FF";
NSCharacterSet *chars = [[NSCharacterSet
characterSetWithCharactersInString:#"0123456789ABCDEF"] invertedSet];
BOOL isValid = (NSNotFound == [string rangeOfCharacterFromSet:chars].location);

Swift 5
extension String {
var isHexNumber: Bool {
filter(\.isHexDigit).count == count
}
}
print("text1".isHexNumber) // false
print("aa32".isHexNumber) // true
print("AD1".isHexNumber) // true

You can create a custom NSCharacterSet that contains the permitted characters (+ characterSetWithCharactersInString:) and then test the string against it with rangeOfCharacterFromSet:. If the returned range is equal to the entire range of the string, you have a match.
Another option would be matching with NSRegularExpression.
Sample code Swift:
func isValidHexNumber() -> Bool {
guard isEmpty == false else { return false }
let chars = CharacterSet(charactersIn: "0123456789ABCDEF").inverted
return uppercased().rangeOfCharacter(from: chars) == nil
}

In swift 2.1 you can extends String in this way:
extension String {
func isValidHexNumber() -> Bool {
let chars = NSCharacterSet(charactersInString: "0123456789ABCDEF").invertedSet
guard self.uppercaseString.rangeOfCharacterFromSet(chars) != nil else {
return false
}
return true
}
}

Swift one-liner without adding any extensions:
myString.allSatisfy(\.isHexDigit)
Using a keypath predicate was introduced in Swift 5.2 prior to that you could use:
myString.allSatisfy({ $0.isHexDigit })

Related

Get App Name in Swift

How do I get the application name in Swift?
Googling gave me this:
[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleName"];
I converted it to Swift; error - method doesn't exist:
NSBundle.mainBundle().infoDictionary.objectForKey("CFBundleName")
This should work:
NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String
infoDictionary is declared as a var infoDictionary: [NSObject : AnyObject]! so you have to unwrap it, access it as a Swift dictionary (rather than use objectForKey), and, as the result is an AnyObject, cast it.
Update Swift 3 (Xcode 8 beta 2)
Always better to use constants (and optionals) where possible, too:
Bundle.main.infoDictionary?[kCFBundleNameKey as String] as? String
I believe this solution is more elegant. What's more, using object(forInfoDictionaryKey:) is encouraged by Apple:
"Use of this method is preferred over other access methods because it returns the localized value of a key when one is available."
extension Bundle {
var displayName: String? {
return object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
}
}
Accessing bundle display name:
if let displayName = Bundle.main.displayName {
print(displayName)
}
I have created a simple extension to get the app name that is shown under the icon on the Home screen.
By default, apps only have CFBundleName set. Some apps, however, set CFBundleDisplayName (The user-visible name of the bundle) to change the title under the app icon. Adding spaces is often the case, e.g. bundle name "ExampleApp" could have bundle display name set to "Example App".
extension Bundle {
// Name of the app - title under the icon.
var displayName: String? {
return object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ??
object(forInfoDictionaryKey: "CFBundleName") as? String
}
}
Usage:
let appName = Bundle.main.displayName
Same answer in Swift 4.2
extension Bundle {
class var applicationName: String {
if let displayName: String = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String {
return displayName
} else if let name: String = Bundle.main.infoDictionary?["CFBundleName"] as? String {
return name
}
return "No Name Found"
}
}
you can use it like below
Bundle.applicationName
OR, An other way would be to avoid static or class method or property but to add to instance level.
extension Bundle {
var applicationName: String {
if let displayName: String = self.infoDictionary?["CFBundleDisplayName"] as? String {
return displayName
} else if let name: String = self.infoDictionary?["CFBundleName"] as? String {
return name
}
return "No Name Found"
}
}
and all it like following
Bundle.main.applicationName
Hope this helps :)
Swift 4
let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as! String
simple way:
let appName = NSBundle.mainBundle().infoDictionary?[kCFBundleNameKey as String] as? String
convenient way:
extension NSBundle {
class func mainInfoDictionary(key: CFString) -> String? {
return self.mainBundle().infoDictionary?[key as String] as? String
}
}
print(NSBundle.mainInfoDictionary(kCFBundleNameKey))
kCFBundleNameKey – Standard Info.plist key, see more in CFBundle
// Returns app's name
public static var appDisplayName: String? {
if let bundleDisplayName = Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String {
return bundleDisplayName
} else if let bundleName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String {
return bundleName
}
return nil
}
let appDisplayName = Bundle.main.infoDictionary?["CFBundleName"] as? String
It's optional, so put it in if let or guard statement.
All answers that just return CFBundleName will often not return the name the user expects, as if bundles have a CFBundleDisplayName, then this key is displayed by Finder, system frameworks, and most other apps.
Most answers just directly access the info dictionary but info dictionaries can be localized by string files and when accessing them directly, this localization is also ignored and thus again a wrong name may be returned, as Finder will display the localized name.
While CFBundleDisplayName is optional in Info.plist files, CFBundleName actually isn't, but if you forget to add it, nothing will break in your system, so you have a corrupt info dict, yet most users will probably never notice and in that case the code most answers may not return anything meaningful at all.
Here's my solution (Swift 3):
private
func stripFileExtension ( _ filename: String ) -> String {
var components = filename.components(separatedBy: ".")
guard components.count > 1 else { return filename }
components.removeLast()
return components.joined(separator: ".")
}
func nameOfApp ( ) -> String {
let bundle = Bundle.main
if let name = bundle.object(forInfoDictionaryKey: "CFBundleDisplayName")
?? bundle.object(forInfoDictionaryKey: kCFBundleNameKey as String),
let stringName = name as? String
{ return stringName }
let bundleURL = bundle.bundleURL
let filename = bundleURL.lastPathComponent
return stripFileExtension(filename)
}
How is this solution better?
It will check CFBundleDisplayName first and only fall back to CFBundleName if not present.
The object() method always operates on the localized version of the info dictionary, so if a localization exists, it will automatically be used.
If neither CFBundleDisplayName nor CFBundleName exist in the dictionary, the code falls back to just using the bundle filename on disk without the extension (so "My Cool App.app" will be "My Cool App"), this is a fallback so that this function will never return nil.
This one works for me in Swift 4.2
guard let dictionary = Bundle.main.infoDictionary else { return "" }
if let version: String = dictionary["CFBundleDisplayName"] as? String {
return version
} else {
return ""
}
This is what worked for me in Xcode 11.0 and Swift 5
let bundleID = Bundle.main.bundleIdentifier
let bundleInfoDict: NSDictionary = Bundle.main.infoDictionary! as NSDictionary
let appName = bundleInfoDict["CFBundleName"] as! String
print(bundleID!)
print(appName)
This should be more like what you are looking for:
let infoDictionary: NSDictionary = NSBundle.mainBundle().infoDictionary as NSDictionary!
let appName: NSString = infoDictionary.objectForKey("CFBundleName") as NSString
NSLog("Name \(appName)")
There may still be a better way to do this but it at least returns the app name correctly in my very limited testing...
Try this:
extension Bundle {
var displayName: String {
let name = object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
return name ?? object(forInfoDictionaryKey: kCFBundleNameKey as String) as! String
}
}
let bundleInfoDict: NSDictionary = NSBundle.mainBundle().infoDictionary!
let appName = bundleInfoDict["CFBundleName"] as String
This one works perfect for me
let appName = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleDisplayName") as! String
For swift 5, iOS 13*
As mentioned before, it‘s an optional, so put it in a guard statement. I do this using a struct:
struct Model {
struct ProgVariablen{
static var appBundleName:String {
get {guard Bundle.main.infoDictionary != nil else {return ""}
return Bundle.main.infoDictionary!["CFBundleName"] as! String
}//end get
}//end computed property
static var appBundleShortVersion:String {
get {guard Bundle.main.infoDictionary != nil else {return ""}
return Bundle.main.infoDictionary ["CFBundleShortVersionString"] as! String
}//end get
}//end computed property
static var appBundleBuild:String {
get {guard Bundle.main.infoDictionary != nil else {return ""}
return Bundle.main.infoDictionary["CFBundleVersion"] as! String
}//end get
}//end computed property
//initialsieren der Variablen
init(
appBundleName:String,
appBundleShortVersion:String,
appBundleBuild:String,
)
{
// do here nothing for 'let'
// do here nothing for 'computed properties'
// your other ‘var’ are here like:
// ProgVariablen.var1 = var1
}//end init
}//end struct ProgVariablen
}//end struct Model
Usage:
print("Model.ProgVariablen.appBundleName: '\(Model.ProgVariablen.appBundleName)'")
extension NSApplication {
static var name: String {
Bundle.main.object(forInfoDictionaryKey: kCFBundleNameKey as String) as? String ?? ProcessInfo.processInfo.processName
}
}
// Bundle+appName.swift
extension Bundle {
var appName: String {
object(forInfoDictionaryKey: "CFBundleDisplayName") as? String ??
object(forInfoDictionaryKey: "CFBundleName") as? String ??
""
}
}
// Usage (non optional)
let appName = Bundle.main.appName
Try this one,
let bundleID = NSBundle.mainBundle().bundleIdentifier

Swift Xcode 6: How do I use multiple functions in one statement?

I am a newcomer in xcode and swift, and I am having a problem with using two IBActions to allow a button to be enabled. I have 2 text fields, and I have a button that is disabled. I want the button to be enabled when both of the text fields are filled in. How can I do this? So far I have declared two functions with IBActions for the two text fields:
#IBAction func yourWeightEditingDidBegin(sender: AnyObject) {
}
#IBAction func calorieNumberEditingDidBegin(sender: AnyObject) {
}
Thanks!
One way is to use the UITextFieldDelegate function instead of IBOutlets:
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
if yourWeight.text != "" && calorieNumber.text != "" {
button.enabled = true
}
return true
}
I, too, implement UITextFieldDelegate, but I use shouldChangeCharactersInRange. This way, the status of that button changes as the user types:
If dealing with only two text fields, it looks like:
func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
// get the value this text field will have after the string is replaced
let value: NSString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
// get the value of the other text field
let otherValue = textField == yourWeight ? calorieNumber.text : yourWeight.text
// only enable done if these are both non-zero length
doneButton.enabled = (value != "" && otherValue != "")
return true
}
Clearly, for this to work, you must specify the delegate for both of those text fields (either in IB or programmatically). I also generally marry the above with the "auto-enable return key" option for the text fields, too.
You don't. Instead, try something like this
var weightIsFilled = false
var calorieNumberIsFilled = false
#IBAction func yourWeightEditingDidBegin(sender: AnyObject) {
if valueOfWeightTextFieldIsValid() {
self.weightIsFilled = true
}
if self.weightIsFilled && self.calorieNumberIsFilled {
self.enableButton()
}
}
#IBAction func calorieNumberEditingDidBegin(sender: AnyObject) {
if valueOfCalorieNumberTextFieldIsValid() {
self.calorieNumberIsFilled = true
}
if self.weightIsFilled && self.calorieNumberIsFilled {
self.enableButton()
}
}
You also may want to be using IBAction functions that are called when the textfield's value changes, not when editing begins on it.

Cannot invoke '==' with an argument list of type '($T2, OSStatus)' in Xcode 6.1

When i was on Xcode version 6, the class i was using for accessing the Keychain was working but now in version 6.1 it is not working
Here is a part of the Keychain access class:
class func setData(value: NSData, forKey keyName: String) -> Bool {
var keychainQueryDictionary: NSMutableDictionary = self.setupKeychainQueryDictionaryForKey(keyName)
keychainQueryDictionary[kSecValueData as String] = value
// Protect the keychain entry so it's only valid when the device is unlocked
keychainQueryDictionary[kSecAttrAccessible as String] = kSecAttrAccessibleWhenUnlocked
let status: OSStatus = SecItemAdd(keychainQueryDictionary, nil)
if Int(status) == errSecSuccess { //I GET THE ERROR HERE
return true
} else if Int(status) == errSecDuplicateItem {
return self.updateData(value, forKey: keyName)
} else {
return false
}
}
It is not the only place where it is doing it here is another part of the code:
class func removeObjectForKey(keyName: String) -> Bool {
let keychainQueryDictionary: NSMutableDictionary = self.setupKeychainQueryDictionaryForKey(keyName)
//Delete
let status: OSStatus = SecItemDelete(keychainQueryDictionary);
if Int(status) == errSecSuccess { //GET ERROR HERE
return true
} else {
return false
}
}
It look likes the problem is with errSecSuccess can somebody help me please
OSStatus is an alias for Int32, so I think you solve that by removing the conversion to Int, like in:
if status == errSecSuccess {
Side note: your multiple if/elseif/else can be replaced by a switch:
switch (status) {
case errSecSuccess:
...
case errSecDuplicateItem:
...
default:
...
}
more readable in my opinion

Why does my extension method cause a type error?

I have the following Playground code:
// Playground - noun: a place where people can play
import Cocoa
extension Array {
func toHexString<CUnsignedChar>() -> String {
var returnString = NSMutableString(capacity: self.count * 2)
for i in self {
let val = i as Int // if someone would like to answer why casting this as a CUnsignedChar throws an error, I'd appreciate it -- but that's a separate question
returnString.appendFormat("%02X", val)
}
return returnString
}
}
var hashedString: String? {
get {
let x: CUnsignedChar[] = [0xA, 0xB, 0xC]
return x.toHexString()
}
}
println(hashedString)
This causes the error, "NSString is not a subtype of 'String'"
However, if I rewrite this code to be:
var hashedString: String? {
get {
let x: CUnsignedChar[] = [0xA, 0xB, 0xC]
var returnString = NSMutableString(capacity: x.count * 2)
for i in x {
returnString.appendFormat("%02X", i)
}
return returnString
}
}
println(hashedString)
I get no error.
Couple things I would recommend.
First, use UInt8 instead of CUnsignedChar for an array of bytes. Also, I would stay away from NSMutableString and use standard string concatenation and interpolation via Swift. I have not had much success trying to use CVarArgs inside Swift.
Here is my implementation:
extension Array
{
func toHexString() -> String
{
var hexString = ""
for value in self
{
if let integerValue = value as? UInt8
{
let stringValue = String(integerValue, radix: 16)
if integerValue < 0x10
{ hexString += "0\(stringValue)" }
else
{ hexString += stringValue }
}
}
return hexString;
}
}
let arrayOfBytes : Array<UInt8> = [ 0x0A, 0x13, 0x02, 0x2F, 0x22, 0x7A, 0xF1 ]
let hash = arrayOfBytes.toHexString()
let hashUppercase = hash.uppercaseString
It is unfortunate that you cannot create an extension solely for Array<UInt8> and must extend all Arrays, even if your method is only valid for one type.
I think it has to do with the fact that your extension does not return an optional, but your hashedString does.
However, the playground crashes like crazy when I try to mess around with the above code. =)

Restrict NSTextField to only allow numbers

How do I restrict a NSTextField to allow only numbers/integers? I've found questions like this one, but they didn't help!
Try to make your own NSNumberFormatter subclass and check the input value in -isPartialStringValid:newEditingString:errorDescription: method.
#interface OnlyIntegerValueFormatter : NSNumberFormatter
#end
#implementation OnlyIntegerValueFormatter
- (BOOL)isPartialStringValid:(NSString*)partialString newEditingString:(NSString**)newString errorDescription:(NSString**)error
{
if([partialString length] == 0) {
return YES;
}
NSScanner* scanner = [NSScanner scannerWithString:partialString];
if(!([scanner scanInt:0] && [scanner isAtEnd])) {
NSBeep();
return NO;
}
return YES;
}
#end
And then set this formatter to your NSTextField:
OnlyIntegerValueFormatter *formatter = [[[OnlyIntegerValueFormatter alloc] init] autorelease];
[textField setFormatter:formatter];
Swift 3 Version
import Foundation
class OnlyIntegerValueFormatter: NumberFormatter {
override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
// Ability to reset your field (otherwise you can't delete the content)
// You can check if the field is empty later
if partialString.isEmpty {
return true
}
// Optional: limit input length
/*
if partialString.characters.count>3 {
return false
}
*/
// Actual check
return Int(partialString) != nil
}
}
Use:
let onlyIntFormatter = OnlyIntegerValueFormatter()
myNsTextField.formatter = onlyIntFormatter
Here's a solution with filtering. Give a delegate and an outlet to textfield and set controlTextDidChange method.
- (void)controlTextDidChange:(NSNotification *)aNotification {
NSTextField *textfield = [notification object];
NSCharacterSet *charSet = [NSCharacterSet characterSetWithCharactersInString:#"0123456789"];
char *stringResult = malloc([textfield.stringValue length]);
int cpt=0;
for (int i = 0; i < [textfield.stringValue length]; i++) {
unichar c = [textfield.stringValue characterAtIndex:i];
if ([charSet characterIsMember:c]) {
stringResult[cpt]=c;
cpt++;
}
}
stringResult[cpt]='\0';
textfield.stringValue = [NSString stringWithUTF8String:stringResult];
free(stringResult);
}
Try this -
NSNumberFormatter *formatter = [[[NSNumberFormatter alloc] init] autorelease];
[formatter setNumberStyle:NSNumberFormatterDecimalStyle];
[textField setFormatter:formatter];
Here is a Swift version:
override func isPartialStringValid(partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>) -> Bool {
if (count(partialString.utf16)) {
return true
}
if (partialString.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet) != nil) {
NSBeep()
return false
}
return true
}
In SWIFT, I do it this way
Convert the text value to Int with Int()
Check the converted value is not less than 0
If less than 0, display error message other accept the value
if ((Int(txtField.stringValue)) < 0){
// Display error message
}
[Works with Swift 3.0.1]
As others suggested, subclass NumberFormatter and override isPartialStringValid method. The easiest way is to drop a NumberFormatter object under your NSTextField in xib/storyboard and update it's Custom Class.
Next implementation allows only integers or blank value and plays a beep if string contains illegal characters.
class IntegerFormatter: NumberFormatter {
override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
// Allow blank value
if partialString.numberOfCharacters() == 0 {
return true
}
// Validate string if it's an int
if partialString.isInt() {
return true
} else {
NSBeep()
return false
}
}
}
String's numberOfCharacters() and isInt() are methods added in an extension.
extension String {
func isInt() -> Bool {
if let intValue = Int(self) {
if intValue >= 0 {
return true
}
}
return false
}
func numberOfCharacters() -> Int {
return self.characters.count
}
}
Here is the steps to create the same....
Just create the ANYCLASS(called SAMPLE) with sub classing the NSNumberFormatter ...
in .m file write the following code...
- (BOOL)isPartialStringValid:(NSString *)partialString newEditingString:(NSString **)newString errorDescription:(NSString **)error {
// Make sure we clear newString and error to ensure old values aren't being used
if (newString) { *newString = nil;}
if (error) {*error = nil;}
static NSCharacterSet *nonDecimalCharacters = nil;
if (nonDecimalCharacters == nil) {
nonDecimalCharacters = [[NSCharacterSet decimalDigitCharacterSet] invertedSet] ;
}
if ([partialString length] == 0) {
return YES; // The empty string is okay (the user might just be deleting everything and starting over)
} else if ([partialString rangeOfCharacterFromSet:nonDecimalCharacters].location != NSNotFound) {
return NO; // Non-decimal characters aren't cool!
}
return YES;
}
Now.. in your Actual Class set the formatter to your NSTextField object like below...
NSTextField *mySampleTxtFld;
for this set the Formatter...
SAMPLE* formatter=[[SAMPLE alloc]init];// create SAMPLE FORMATTER OBJECT
self.mySampleTxtFld.delegate=self;
[self.mySampleTxtFld setFormatter:formatter];
Your done!!!
Swift 2.0 custom formatter with 0 instead of empty space :
class OnlyIntegerValueFormatter: NSNumberFormatter {
override func isPartialStringValid(partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>) -> Bool {
if partialString.isEmpty {
newString.memory = "0"
return false
}
if Int(partialString) < 0 {
NSBeep()
return false
} else {
return true
}
}
}
// NSTextFieldNumberFormatter+Extension.swift
import Foundation
class TextFieldIntegerValueFormatter: NumberFormatter {
var maxLength: Int
init(maxLength: Int) {
self.maxLength = maxLength
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func isPartialStringValid(_ partialString: String, newEditingString newString: AutoreleasingUnsafeMutablePointer<NSString?>?, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool {
// Ability to reset your field (otherwise you can't delete the content)
// You can check if the field is empty later
if partialString.isEmpty {
return true
}
// Optional: limit input length
if partialString.count > maxLength {
return false
}
// Actual check
return Int(partialString) != nil
}
}
//Need to call like:
myNsTextField.formatter = TextFieldIntegerValueFormatter(maxLength: 6)

Resources