Michael Voccola Michael Voccola - 1 month ago 7
Swift Question

How to create a settings object in Swift

I'm attempting to create an object in Swift-3 that will hold a variety of basic user-settings which can be easily accessed throughout the application. I currently have this setup as a struct called

PTSettings
. It is implemented like so:

struct PTSettings {
static var aUserSettings: String()
}


Which can be accessed easily around the application like so:
PTSettings.aUserSetting = "Foo"


What I am struggling with here is that I'd like this struct to observe the UIScreen notifications from
NotificationCenter
. When a screen connects
PTSettings
initializes the external screen, assigns a view to it, shows a banner to let the user know etc...

I am familiar with doing all of these tasks on UIViewController; however I am not proficient in utilizing structs. My hope is that when the application loads, the struct will be initialized and in that init will being observing
NotificationCenter
while also checking to see if there was a screen connected before the application was loaded.

Below is what I currently have.

/// Struct containing various user-generate data such as color, messages and other settings.
struct PTSettings {


// External UI
//
static var externalScreen: UIScreen!
//
static var externalWindow: UIWindow!
//
static var extDisplay: PTExternalDisplayVC?


init () {
// Receive notifications if a screen is connected or disconnected
//
let center = NotificationCenter.default
center.addObserver(self, selector: #selector(PTSettings.handleScreenDidConnectNotification(notification:)), name: NSNotification.Name.UIScreenDidConnect, object: nil)
center.addObserver(self, selector: #selector(PTSettings.handleScreenDidDisconnectNotification(notification:)), name: NSNotification.Name.UIScreenDidDisconnect, object: nil)
center.addObserver(self, selector: #selector(PTSettings.handleScreenModeDidChangeNotification), name: NSNotification.Name.UIScreenModeDidChange, object: nil)
}




// MARK: External Displays
//
static func initializeExternalScreen(externalScreen:UIScreen) {
self.externalScreen = externalScreen

externalScreen.overscanCompensation = UIScreenOverscanCompensation(rawValue: 3)!

// Create a new window sized to the external screen's bounds
self.externalWindow = UIWindow(frame: self.externalScreen.bounds)

// Assign screen object to screen property of the new window
self.externalWindow.screen = externalScreen

// Load the clock view
let viewForExternalScreen = self.storyboard?.instantiateViewController(withIdentifier: "ExternalDisplay") as! PTExternalDisplayVC
viewForExternalScreen.view.frame = self.externalWindow.frame

// Add the view to the window
self.externalWindow.addSubview(viewForExternalScreen.view)

// Create a reference to the viewForExternalScreen
self.extDisplay = viewForExternalScreen

// Make the window visible
self.externalWindow.makeKeyAndVisible()
}
//
static func handleScreenDidConnectNotification (notification:Notification) {
if let screen = notification.object as? UIScreen {
initializeExternalScreen(externalScreen: screen)
}
}
//
static func handleScreenDidDisconnectNotification (notification:Notification) {
if externalWindow != nil {
externalWindow.isHidden = true
externalWindow = nil
displayConnectedLabel.text = "No Display"
displayConnectedLabel.textColor = CustomColors.Red.color
JSSAlertView().warning(self, title: "External Display Disconnected.")
}
}
//
static func handleScreenModeDidChangeNotification () {
let screen = UIScreen.screens[1]
initializeExternalScreen(externalScreen: screen)
}

}


The compiler is complaining for each addition of observers in the
init()
method something like this:

Argument of '#selector' refers to static method 'handleScreenDidConnectNotification(notification:)' that is not exposed to Objective-C

However when adding
@objc
before the methods, it then complains:

@objc can only be used with members of classes, @objc protocols and concrete extensions of classes.

How can I go about achieving the desired result, and am I just totally off in this approach?

Answer

am I just totally off in this approach

You are indeed totally off. Distinguish Objective-C (and Cocoa) from Swift. Swift structs are a purely Swift feature. You are trying to use a Cocoa feature, namely calling into a method via a selector, with a Swift feature, a struct. You can't do that. A Swift struct lacks the Objective-C introspection features that allow selectors to work. That is why you can make a selector only for a method of an Objective-C class, i.e. a class that inherits from NSObject (as you have done in the past).

However, there's another form of adding a notification center observer, where you use a trailing closure / function, not a selector. You might try that instead (though I don't guarantee anything).

Comments