objective-cswiftcocoanstextfieldnsformatter

macOS - Trying to use a custom NSFormatter on a NSTextField fails miserably


I am trying to add a NSFormatter object to a NSTextField, so I can validate if the textfield carries on just alphanumeric strings.

So I do this:

  1. I create a new Swift macOS app.
  2. I add a NSTextField to the view controller
  3. I add a custom Formatter to the view controller
  4. I connect the text field's formatter outlet to the Formatter object, using interface builder.

I create this class and assign to the Formatter object.

FormatterTextNumbers.h

#import <Foundation/Foundation.h>
@import AppKit;


NS_ASSUME_NONNULL_BEGIN

@interface FormatterTextNumbers : NSFormatter

@end

NS_ASSUME_NONNULL_END

FormatterTextNumbers.m

#import "FormatterTextNumbers.h"

@implementation FormatterTextNumbers

- (BOOL)isAlphaNumeric:(NSString *)partialString
{
  static NSCharacterSet *nonAlphanumeric = nil;
  if (nonAlphanumeric == nil) {
  nonAlphanumeric = [NSCharacterSet characterSetWithCharactersInString:@"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'. -"];
  nonAlphanumeric = [nonAlphanumeric invertedSet];
  }

  NSRange range = [partialString rangeOfCharacterFromSet:nonAlphanumeric];
    if (range.location != NSNotFound) {
      return NO;
    } else {
      return YES;
    }
}

- (BOOL)isPartialStringValid:(NSString *)partialString
            newEditingString:(NSString * _Nullable __autoreleasing *)newString
            errorDescription:(NSString * _Nullable __autoreleasing *)error {

  if ([partialString length] == 0) {
      return YES; // The empty string is okay (the user might just be deleting everything and starting over)
  } else if ([self isAlphaNumeric:partialString]) {
    *newString = partialString;
    return YES;
  }
  NSBeep();
  return NO;
}

You ask, why do I have these classes in Objective-C if my project uses Swift? Simple: if I create a subclass of the Formatter class using Swift, Xcode will not let me assign that subclass to the Formatter object. I need to create an Objective-C subclass of NSFormatter instead.

Said that, when I run the project, the text field disappears and I get this message, whatever this means:

Failure1[3071:136161] Failed to set (contentViewController) user defined inspected property on (NSWindow): *** -stringForObjectValue: only defined for abstract class. Define -[FormatterTextNumbers stringForObjectValue:]!

I remove the connection between the text field and the Formatter object and the app runs fine.


Solution

  • You have to define that method

    From Apple documentation for NSFormatter (which is actually semi-abstract)

    Summary
    
    The default implementation of this method raises an exception.
    Declaration
    
    - (NSString *)stringForObjectValue:(id)obj;
    

    Actually the same is for

    - (BOOL)getObjectValue:(out id _Nullable * _Nullable)obj forString:(NSString *)string errorDescription:(out NSString * _Nullable * _Nullable)error;