2
\$\begingroup\$

I have a plist file which has URLs in it and struct class is feeding from this plist. This struct class has static variable which I use to reach URLs in every part of the app. My question is that I'm defining this static instance with "!". I can also define it with optional ("?"). I'm not sure If I'm declaring singleton variable in right way.

Note: If something happens with reading Plist or creating struct class, app needs to display a message or something then close. So reading successfully is mandatory.

 struct URLs: Codable {
 let urlBaseURL: String
 let urlCheckCMS: String
 let urlJSON: String
 static var instance: URLs!
 enum CodingKeys: String, CodingKey {
 case urlBaseURL = "base_url"
 case urlCheckCMS = "check_cms_url"
 case urlJSON = "table_json_url"
 }
}
// MARK: Convenience initializers
extension URLs {
 init?(data: Data) {
 guard let me = try? PropertyListDecoder().decode(URLs.self, from: data) else { return nil }
 self = me
 URLs.instance = me
 }
 init?(_ plist: String, using encoding: String.Encoding = .utf8) {
 guard let data = plist.data(using: encoding) else { return nil }
 self.init(data: data)
 }
 init?(fromURL url: String) {
 guard let url = URL(string: url) else { return nil }
 guard let data = try? Data(contentsOf: url) else { return nil }
 self.init(data: data)
 }
 var plistData: Data? {
 return try? PropertyListEncoder().encode(self)
 }
 var plist: String? {
 guard let data = self.plistData else { return nil }
 return String(data: data, encoding: .utf8)
 }
}

If I try to declare singleton as usual way;

static let instance = URLs()

It gives below error;

Cannot invoke initializer for type 'URLs' with no arguments

So I get rid of this error by writing a default init method in which I read plist file and write it to instance. However, I'm not sure with this.

asked Jan 23, 2018 at 14:28
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Generally a singleton is declared as

 static let shared = URLs()

or for lazy instantiation

 static let shared = { URLs() }()

I would suggest naming the static property shared. That's a convention used throughout the standard library and Foundation.

In your case, if you change the value of instance, I'd use an optional. Your app init method (such as applicationDidFinishLaunching) should instantiate the instance of URLs with a guard and display the message if it fails.

If you are only setting the value once, I'd suggest something like

static var instance: URLs! { willSet { 
 guard instance == nil else { fatalError("value has already been set") }
} }

I normally only use unwrapped optionals when working with an Objective-C based class where I can't overload the initializer (such as view controllers and XCTestCase). In those cases, you know viewDidLoad(), awakeFromNib(), or setUp() will be called (or something is seriously wrong and it isn't safe to continue execution).

Over the last year I've switched to using dependency injection instead of referencing singletons. I even use DI for things like NSNotificationCenter and NSUserDefaults. That makes unit testing a lot easier.

answered Jan 24, 2018 at 19:08
\$\endgroup\$
2
  • \$\begingroup\$ I think I can't be clear fully. When I declare singleton as you tell (both two way) It says "cannot invoke initializer for type with no arguments" because at first url properties are not declared. They are fetched from a plist file. \$\endgroup\$ Commented Jan 25, 2018 at 6:13
  • \$\begingroup\$ I edit my question and add more detail, can you please check it. Thank you. \$\endgroup\$ Commented Jan 25, 2018 at 6:21

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.