Swift - Detecting an unspecified Interface style - swift

I want to be able to read if the current app appearance is set to unspecified. When clicking on the desire table view cell, the appearance is properly set where changing the appearance of device mimics with the app. However I cannot find a way to read the value of appearance to be: light/dark/unspecified. Only light/dark. Upon reading some more I read this below.
The trait collection contains a complete set of trait values
describing the current environment, and does not include unspecified
or unknown values.
I need to check if the appearance is unspecified in other parts and I was curious if anyone had ideas on how to check. Here is a snippet of what I have, below is a helper function
let appearanceMap = [Appearance.device, Appearance.light, Appearance.dark]
switch appearanceMap[indexPath.row] {
case .light:
UIApplication.shared.changeInterfaceStyle(.light)
case .dark:
UIApplication.shared.changeInterfaceStyle(.dark)
case .device:
UIApplication.shared.changeInterfaceStyle(.unspecified)
}
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let keyWindow = appDelegate.window else { return }
let currentAppearance = keyWindow.traitCollection.userInterfaceStyle
print(currentAppearance.stringValue)
extension UIApplication {
func changeInterfaceStyle(_ mode: UIUserInterfaceStyle) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let keyWindow = appDelegate.window else { return }
keyWindow.overrideUserInterfaceStyle = mode
}
}

Maybe try to use this instead
if UIApplication.shared.windows.first?.overrideUserInterfaceStyle == .unspecified {
print("unspecified")
}

Related

MVVM project. How to connect Model View to the View

I am trying to make an app that will show the weather in my city. I am using MVVM architecture and I have my Model, ModelView and View as follows. I have a variable inside the WeatherModelView class that I want to use in my view controller:
label.text = "(weatherViewModel.res?.main.temp ?? -123)"
but it does not work.
(https://i.stack.imgur.com/8BTEJ.png)
View Controller](https://i.stack.imgur.com/qW54n.png)
It does not give an error, it simply prints -123.0 on the label, which is the nil case after unwrapping. I would like it to print the actual weather. I don't think there are problems with the URL or the JSON decoding.
This is what is wrongfully shown when I run it: simulator
In "viewdidload" "fetchWeather" is not complete.
You need set "label.text" after it completed
change res in your view model
var res = WeatherModel? {
didSet {
resHasData?()
}
}
var resHasData?: (() -> Void)?
add my code in the last line "viewDidLoad"
weatherViewModel.resHasData = { [weak sekf] in
guard let self = self else { return }
self.label.text = "\(self.weatherViewModel.res?.main.temp ?? -123)"
}
good luck.

is this firestore closure causing a memory leak?

So my goal is to fix this condition issue when it comes to instantiating the right viewController. I have a function that I basically use to navigate a user to the right viewController depending on the type of user and if they're logged in or not.
Here is this function :
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
self.window = window
let auth = Auth.auth()
let actualuser = Auth.auth().currentUser
auth.addStateDidChangeListener { (_, user) in
if user != nil {
db.document("student_users/\(actualuser?.uid)").getDocument { (docSnapshot, error) in
if error != nil {
print("\(error)")
} else {
let docSnap = docSnapshot?.exists
guard docSnap! else {
let alreadyLoggedInAsASchoolViewController = self.storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.SchoolEventDashboardStoryboardID) as! SchoolTableViewController
let navigationizedSchoolVC = UINavigationController(rootViewController: alreadyLoggedInAsASchoolViewController)
self.window!.rootViewController = navigationizedSchoolVC
self.window!.makeKeyAndVisible()
return
}
let alreadyLoggedInAsAStudentViewController = self.storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.StudentEventDashboardStoryboardID) as! StudentSegmentedTableViewController
let navigationizedVC = UINavigationController(rootViewController: alreadyLoggedInAsAStudentViewController)
self.window!.rootViewController = navigationizedVC
self.window!.makeKeyAndVisible()
}
}
} else {
let notLoggedInAtAll = self.storyboard.instantiateViewController(withIdentifier: Constants.StoryboardIDs.GothereMainMenuStoryboardID) as! GothereMainMenuViewController
let navMainMenu = UINavigationController(rootViewController: notLoggedInAtAll)
self.window!.rootViewController = navMainMenu
self.window!.makeKeyAndVisible()
}
}
}
I also have the exact block of code like this for the sceneDidEnterForeground for push notification purposes. Now the issue is when I run the simulator and launch the app for the first time, the correct viewController will show up, but when I logout as a school user and login as a school user in that same simulator session, the wrong viewController (aka the viewController of the other type of user) shows up.
Not that it would be a real situation in production where a student user would just have access to a school user's account and log in like that in the same scene session, but still, better to be safe than sorry. So this leads to me ask, is this a memory leak or a completely different issue?
I also get this error :
Your query is based on the variable actualuser, which looks like it is only set once, when the scene is first set up. Inside the state change callback, it's never updated.
So, when you log out, then log back in as a different user, that initial value of actualuser will be used, explaining why you see the wrong view controller. Then, when you run the app again and the scene is set up, actualuser gets set to the auth().currentUser again, showing you the correct view controller.
The solution here is to base your query on the current (and current) user.
Something like:
db.document("student_users/\(user.uid)")
(Instead of checking user != nil, do an optional binding with let user = user and then you can avoid the ? unwrapping)
This is not, by the way, a memory leak, which is a different type of issue: https://en.wikipedia.org/wiki/Memory_leak

Document based app using one single window

I have a document-based macOS application, which is a basic text editor. The default behavior is to create a new window for every opened document. But I want to only have one window displayed at a time and when opening a document or creating a new one, this should happen in the same window and thus replace the old document.
I've tried to do some hacking in the makeWindowControllers method from NSDocument by not instantiating a new window controller but reusing an old one. But after some problems with this approach, I figured this is not the right way to go. I was wondering if there is a common approach to this problem.
This is the code I've tried
class Document: NSDocument {
static var sharedWindow: NSWindowController?
override func makeWindowControllers() {
// Instaniate window controller there is none
if Document.sharedWindow == nil {
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
Document.sharedWindow = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as? NSWindowController
}
guard let sharedWindow = Document.sharedWindow else { return }
if let previousDocument = sharedWindow.document as? NSDocument {
previousDocument.close()
}
self.addWindowController(sharedWindow)
sharedWindow.contentViewController?.representedObject = content
(sharedWindow.contentViewController as? ViewController)?.handleOpenDocumentOperation()
}
...
}

Swift: Passing a class method as a function parameter (instead of a generic function)

I'm trying to call a function on a root view controller from a popover view controller. The function below works but it seems there should be a better way:
func doSomethingInRoot() {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let currentRoot = appDelegate.window!.rootViewController as? RootViewController {
currentRoot.somefunction()
}
}
In the above code, doSomethingInRoot would be contained in the popover, and someFunction would be a function of RootViewController.
What I'm looking for is a way to pass someFunction to another function that contains the if let above, however, the problem is currentRoot is only assigned as a RootViewController within the above (i.e., doSomethingInRoot doesn't know that it's being passed a method of RootViewController instead of a generic function).
In writing this question, I realized that I could do the following, which feels pretty clean, but I'm still curious whether I can pass a method to a specific class as parameter.
func doSomethingInRoot() {
if let root = getRoot() {
root.someFunction()
}
}
func getRoot() -> RootViewController? {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let currentRoot = appDelegate.window!.rootViewController as? RootViewController {
return currentRoot
} else {
return nil
}
}
In your popover view controller, you could add a property like this to hold the reference to someFunction
var rootsSomeFunction: (()->Void)!
Then assign the reference when creating the popover VC whether it's in prepareForSegue, or after you instantiate if from storyboard...etc. I'd also move getRoot() to the App Delegate.
popoverVC.rootsSomeFunction = appDelegate.getRoot().someFunction().self
Finally, in your popover view controller, you can call it by the property like this:
self.rootsSomeFunction()

How Save UILocalNotifications in CoreData

Answer is below, image is here:
I was searching how to do this for a couple of days and was only able to find people who stored UILocalNotificaations in NSUserDefaults. Saving these in NSUserDefaults seemed wrong to me because it is supposed to be used for small flags. I just now finally figured out how to store notifications in CoreData. This is Using Xcode 7.3.1 and Swift 2.2
First off you need to create a new entity in your CoreDataModel
and then add a single attribute to it. the attribute should be of type Binary Data I named my table/entity "ManagedFiredNotifications" and my attribute "notification". it should look like this:
Image linked in Question above.
Next you need to add an extension to UILocalNotification it should go like this:
extension UILocalNotification {
func save() -> Bool {
let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate
let firedNotificationEntity = NSEntityDescription.insertNewObjectForEntityForName("ManagedFiredNotifications", inManagedObjectContext: appDelegate!.managedObjectContext)
guard appDelegate != nil else {
return false
}
let data = NSKeyedArchiver.archivedDataWithRootObject(self)
firedNotificationEntity.setValue(data, forKey: "notification")
do {
try appDelegate!.managedObjectContext.save()
return true
} catch {
return false
}
}
}
Now for saving a notification all you need to do is call
UILocalNotification.save()
On the notification you would like to save. my notifications were named 'notification' so I would call notification.save()
To retrieve a notification you need a method like this
func getLocalFiredNotifications() -> [UILocalNotification]? {
let managedObjectContext = (UIApplication.sharedApplication().delegate as? AppDelegate)!.managedObjectContext
let firedNotificationFetchRequest = NSFetchRequest(entityName: "ManagedFiredNotifications")
firedNotificationFetchRequest.includesPendingChanges = false
do {
let fetchedFiredNotifications = try managedObjectContext.executeFetchRequest(firedNotificationFetchRequest)
guard fetchedFiredNotifications.count > 0 else {
return nil
}
var firedNotificationsToReturn = [UILocalNotification]()
for managedFiredNotification in fetchedFiredNotifications {
let notificationData = managedFiredNotification.valueForKey("notification") as! NSData
let notificationToAdd = NSKeyedUnarchiver.unarchiveObjectWithData(notificationData) as! UILocalNotification
firedNotificationsToReturn.append(notificationToAdd)
}
return firedNotificationsToReturn
} catch {
return nil
}
}
Note that this returns an array of UILocalNotifications.
When retrieving these if you plan on removing a few of them and then storing the list again you should remove them when you get them something like this works:
func loadFiredNotifications() {
let notifications = StudyHelper().getLocalFiredNotifications()
if notifications != nil {
firedNotifications = notifications!
} else {
// throw an error or log it
}
classThatRemoveMethodIsIn().removeFiredLocalNotifications()
}
I hope this helps someone who had the same problems that I did trying to implement this.