Skip to main content
2 of 5
added 526 characters in body
rmaddy
  • 195
  • 7

Translating Objective-C use of static and +(void)initialize to Swift

I am converting an old Objective-C class into Swift. My actual question is at the very end after all of the code.

Here is a cut-down version of the Objective-C class:

DateInfo.h:

#import <Foundation/Foundation.h>

@interface RMYearInfo : NSObject

// There are also some instance properties but those aren't relevant to the question

+ (NSInteger)numberOfMonths;
+ (NSArray *)shortMonthNames;
+ (NSArray *)longMonthNames;
+ (NSInteger)firstWeekday;
// several other class methods

@end

DateInfo.m:

#import "DateInfo.h"

static NSArray *shortMonthNames = nil;
static NSArray *longMonthNames = nil;
static NSInteger firstWeekday;
// there are several other statics as well

@implementation DateInfo

+ (void)reinitialize {
    NSCalendar *cal = [NSCalendar currentCalendar];
    firstWeekday = [cal firstWeekday] - 1;
    
    NSDateFormatter *yearFormatter = [[NSDateFormatter alloc] init];

    NSArray *shortMonths = [yearFormatter shortStandaloneMonthSymbols];
    NSArray *longMonths = [yearFormatter standaloneMonthSymbols];
    NSMutableArray *upperShortMonths = [[NSMutableArray alloc] initWithCapacity:shortMonths.count];
    NSMutableArray *upperLongMonths = [[NSMutableArray alloc] initWithCapacity:shortMonths.count];
    for (NSString *name in shortMonths) {
        [upperShortMonths addObject:[name uppercaseString]];
    }
    for (NSString *name in longMonths) {
        [upperLongMonths addObject:[name uppercaseString]];
    }
    shortMonthNames = [upperShortMonths copy];
    longMonthNames = [upperLongMonths copy];

    NSLocale *locale = [NSLocale current];
    // lots of other processing for other statics
    // based on yearFormatter, locale, and cal
}

+ (void)initialize {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reinitialize) name:NSCurrentLocaleDidChangeNotification object:nil];
    
    [self reinitialize];
}

+ (NSInteger)numberOfMonths {
    return shortMonthNames.count;
}

+ (NSArray *)shortMonthNames {
    return shortMonthNames;
}

+ (NSArray *)longMonthNames {
    return longMonthNames;
}

+ (NSInteger)firstWeekday {
    return firstWeekday;
}

// Lots of other instance and class methods

As you can see, the initialize method sets up a notification handler so all of the static variables can be reinitialized if the locale is changed while the app is running.

Here is my Swift code. Since there is no initialize in Swift (any more), my solution is to use private backing variables for the public static variables.

import Foundation

public struct DateInfo {
    // some normal instance properties irrelevant to the question

    private static var _formatter: DateFormatter!
    private static var formatter: DateFormatter {
        if _formatter == nil {
            _formatter = DateFormatter()
            NotificationCenter.default.addObserver(forName: NSLocale.currentLocaleDidChangeNotification, object: nil, queue: nil) { (notification) in
                _formatter = DateFormatter()
                _shortMonthNames = nil
                _longMonthNames = nil
                _firstWeekday = nil
                // reset all of the other statics as well
            }
        }

        return _formatter
    }

    private static var _shortMonthNames: [String]!
    public static var shortMonthNames: [String] {
        if _shortMonthNames == nil {
            // Processing is actually more complex than this simple assignment
            _shortMonthNames = formatter.shortStandaloneMonthSymbols
        }

        return _shortMonthNames
    }

    private static var _longMonthNames: [String]!
    public static var longMonthNames: [String] {
        if _longMonthNames == nil {
            // Processing is actually more complex than this simple assignment
            _longMonthNames = formatter.standaloneMonthSymbols
        }

        return _longMonthNames
    }

    public static var numberOfMonths: Int {
        return shortMonthNames.count
    }

    private static var _firstWeekday: Int!
    public static var firstWeekday: Int {
        if _firstWeekday == nil {
            _firstWeekday = Calendar.current.firstWeekday - 1
        }

        return _firstWeekday
    }

    // lots of other similar private/public pairs of statics
}

Is this an appropriate way to translate the functionality given the need to be able to reinitialize the statics? I don't like having a private static backing each public static property.

rmaddy
  • 195
  • 7