Label not being updated - swift

I am trying to update a label to indicate status, but it is not being refreshed/shown as expected
func setStatusText(status: String) {
statusText.stringValue = status;
}
func doSomething() {
setStatusText(status: "Activating license"); // This is never shown
var rc = PerformAction()
if (rc == 0) {
setStatusText(status: "\(action) succeeded") // Correctly displayed
} else {
setStatusText(status: "\(action) failed") // Correctly displayed
}
The status update before the action is never being shown, but the status set right after the action is correctly displayed. What do I do to be sure the first status is shown?
Edited: I tried something else to try to isolate it. I added a button press, which will set text, sleep, and then set text again. I never see "first" in the status.
#IBAction func TestSettingText(_ sender: Any) {
self.statusText.stringValue = "first";
sleep(15)
self.statusText.stringValue = "second";
}

I don't know what exactly PerformAction does but it seems like it finishes quickly so you just can't see the Activating license status. It is there but only for a very brief moment and instantly changed to the next status

Related

how to validate document paths in firestore?

So, in my previous question, I ended up figuring out my own issue, (I would recommend taking a look at that before reading this one), but the 20 seconds of glory was cut short when I realized that the outcome was similar across all users on the app, which is what I didn't want and totally forgot about.
With the function down below, I can purchase the event and the buttons will show up for that event and go away if I cancel, and it's unique for each event, which I adore. Now, the problem with the function down below is that if I make a purchase on user1 account and the buttons show up and stay there how they're supposed to, when I log into user2 account and perhaps want to purchase that same event, the buttons are already showing up even though user2 hasn't done anything.
getSchoolDocumentID { (schoolDocID) in
if let schID = schoolDocID {
self.db.document("school_users/\(schID)/events/\(self.selectedEventID!)").getDocument { (documentSnapshot, error) in
if let error = error {
print("There was an error fetching the document: \(error)")
} else {
guard let docSnap = documentSnapshot!.get("purchased") else {
return
}
if docSnap as! Bool == true {
self.viewPurchaseButton.isHidden = false
self.cancelPurchaseButton.isHidden = false
self.creditCard.isHidden = true
self.purchaseTicketButton.isHidden = true
} else {
self.creditCard.isHidden = false
self.purchaseTicketButton.isHidden = false
}
}
}
}
}
So i tried to solve the problem on my own but ran into a roadblock. I tried to make a subcollection of events_bought when users purchase an event and have the details stored in fields that I can call later on in a query. This was something I thought I could use to make the purchases unique amongst all users.
The function below looks through events_bought subcollection and pulls up a field and matches it with a piece of data on the displayedVC, the issue is if the event hasn't been purchased and I go on it with that user, it crashes and says how the document reference path has the wrong number of segments which I don't get because it's the same as the function above, so I realized that the path wouldn't exist and tried to figure out ways to validate the path and came up with the function down below.
getEventsBoughtEventID { (eventBought) in
if let idOfEventBought = eventBought {
let docPath = self.db.document("student_users/\(self.user?.uid)/events_bought/\(idOfEventBought)")
if docPath.path.isEmpty {
self.creditCard.isHidden = false
self.purchaseTicketButton.isHidden = false
} else {
self.db.document("student_users/\(self.user?.uid)/events_bought/\(idOfEventBought)").getDocument { (documentSnapshot, error) in
if let error = error {
print("There was an error trying to fetch this document: \(error)")
} else {
guard let docSnapEventName = documentSnapshot!.get("event_name") else {
return
}
if docSnapEventName as! String == self.selectedEventName! {
self.viewPurchaseButton.isHidden = false
self.cancelPurchaseButton.isHidden = false
self.creditCard.isHidden = true
self.purchaseTicketButton.isHidden = true
}
}
}
}
}
}
I wasn't really sure if it would work or not so I tried my luck, but I still end up getting the same document reference errors. If anyone can figure out how I can validate a document path and use logic to make certain things happen, that would be great. Thanks.
So i finally figured out how to come about doing this. It was a 4 hour grind and struggle but i got it, with a few bugs of course. So i found out the reason my app crashed was not just because of the path segments, but cause of the fact that the idOfEventBought didn't exist for some events because those events weren't purchased yet and that there was no subcollection called events_bought even created yet.
Firstly, I added a test document in a subcollection called events_bought when a user signs up, which makes sense because it would have to be made eventually anyways.
db.document("student_users/\(result?.user.uid)/events_bought/test_document").setData(["test": "test"])
This line of code allowed me to come up with my next method, that can verify if an event was bought or not.
func checkIfUserMadePurchase(shouldBeginQuery: Bool) -> Bool {
if shouldBeginQuery == true {
getEventsBoughtEventID { (eventBought) in
if let idOfEventBought = eventBought {
self.docListener = self.db.document("student_users/\(self.user?.uid)/events_bought/\(idOfEventBought)").addSnapshotListener(includeMetadataChanges: true) { (documentSnapshot, error) in
if let documentSnapshot = documentSnapshot {
if documentSnapshot.exists {
self.creditCard.isHidden = true
self.purchaseTicketButton.isHidden = true
self.viewPurchaseButton.isHidden = false
self.cancelPurchaseButton.isHidden = false
}
}
}
}
}
return true
} else {
creditCard.isHidden = false
purchaseTicketButton.isHidden = false
viewPurchaseButton.isHidden = true
cancelPurchaseButton.isHidden = true
return false
}
}
I used this method to verify if the event has been purchased yet, and if it hasn't show the right buttons.
I then call it in the process of when the purchase button in my UIAlertController is pressed.
self.checkIfUserMadePurchase(shouldBeginQuery: true)
Lastly, I create a function that uses logic to verify is the event has been purchased, and if it has been purchased, do something specific. I then call this function in the viewDidLoad() , viewWillAppear(), and viewWillDisappear().
func purchasedStatusVerification() {
db.collection("student_users/\(user?.uid)/events_bought").whereField("event_name", isEqualTo: self.selectedEventName!).getDocuments { (querySnapshot, error) in
if let querySnapshot = querySnapshot {
if querySnapshot.isEmpty {
self.checkIfUserMadePurchase(shouldBeginQuery: false)
} else {
self.checkIfUserMadePurchase(shouldBeginQuery: true)
}
}
}
}
With all this in place, my app runs how i want to, I can successfully purchase an event and it won't show up in another users account. There are a few bugs like when a new event is created, the wrong and the right buttons are all displayed, but the wrong buttons go away after logging in and out. Also, the isHidden() method moves pretty slow, when i load the vc and the event has a status of purchased, the purchaseTicketButton is there for a split second, then disappears, which is quite annoying. All in all, I figured it out, and will try to improve it near production time.
In your document path "student_users/\(self.user?.uid)/events_bought/\(idOfEventBought)" you use self.user?.
? will produce
student_users/Optional(uid)/events_bought string,
but not
student_users/uid/events_bought string.
Use self.user! or if let user = self.user {

UICollectionView.reloadData() Design Error

When i did cv.reloadData my cells is broken.
First
Last
When i write answer and click send button run this func.
func checkPlay(playedWord : String) {
if WordCheck(word: playedWord) {
DispatchQueue.main.async {
self.collectionView.reloadData()
}
if isGameOver() {
//alert end save user default if score is max? and do proccess
}
}
}
If i get to comment cv.reloadData() func. My design is be safe but not writing answer.
I'm so sory for my bad English

Assert that code gets ran at some point in unit test?

I have this unit test:
func testState() {
searchController.filter.query = "Missouri"
searchController.resultsSignal.subscribePast(with: self) { sections in
if sections.count < 1 { return }
// Want to test that code at least gets to here at some point
...
}
}
And I want to make sure that I at least get past the line if sections.count < 1 { return } at some point.
Since it is getting ran when a signal is fired, I don't care if there is a different signal that gets fired at some point, but I do want to make sure that sections.count > 0 at some point in the test.
Is there a way to do this? I was thinking of using a boolean and initializing it to false then setting it to true if sections.count was ever more than one, and asserting that the value was true, but this doesn't work unless I do a delay since I am using signals. Thanks.
You can use an XCTestExpectation and call .fulfill after sections.count to notify the test that an asynchronous test has succeeded.
func testState() {
let expectation = XCTestExpectation(description: "Should execute")
searchController.filter.query = "Missouri"
searchController.resultsSignal.subscribePast(with: self) { sections in
if sections.count < 1 { return }
// Want to test that code at least gets to here at some point
expectation.fulfill()
...
}
wait(for: [expectation], timeout: 10) // Will fail if .fulfill does not get called within ten seconds
}

runTransactionBlock causing delays in main thread

I have an app where when a user clicks on a UIButton,
1. I update a child ( say tapCount) in Firebase
Load a ViewController with an image.
I am using a transactionBlock to increment this count as multiple users could be incrementing this count at the same time. What I notice is because there is a slight delay in completing this Firebase transaction, the app waits to complete it and then loads the View Controller when an image is clicked. It almost makes the app a little sluggish. So what's the recommended way to do this without causing any UI issues ? Is there a way to run this in a background thread ? Thanks
func incrementTapCount() {
_tapCount += 1
// update the link
ref.runTransactionBlock { (currentData:MutableData) -> TransactionResult in
currentData.childData(byAppendingPath: "tapCount").value = self._tapCount
return TransactionResult.success(withValue: currentData)
}
}
func moreCommentAction(_ sender: AnyObject) {
let postCategory = self.globalPost.postCat
if Auth.auth().currentUser?.uid != nil {
//user is logged in
// this increments the Like count when a button is tapped.
self.globalPost.incrementTapCount()
// let likeCount = self.globalPost.favoriteBoost + self.globalPost.favoriteDict.count + self.globalPost.tapCount
// likeButton.setTitle(" \(likeCount)", for: UIControlState())
}
else {
//user is not logged in, so skip incrementing the tapCount.
}
let moreCommentVC = sender.storyboard?!.instantiateViewController(withIdentifier: "MoreCommentViewController") as! MoreCommentViewController
moreCommentVC.globalPost = self.globalPost
sender.present(moreCommentVC, animated: true, completion: nil)
}
I can see the same issue with obj-c. runTransactionBlock() returns immediately but before the andCompletionBlock() is called, the main thread is blocked for 1-2 secs.
Did not happen before, started to see it after update to newer firebase version. In my case I managed to make this lag shorter by decreasing amount of data to update. It seems firebase is doing something like local cache update on the main thread (just a theory).

Stopping Mac from sleeping while app is running

I want to stop a the user's Macbook from automatically sleeping while one the app is running. How would I do that? I have seen this
https://developer.apple.com/library/content/qa/qa1340/_index.html
and this:
Disable sleep mode in OS X with swift
and this:
Using Swift to disable sleep/screen saver for OSX
But there is no simple line of code that I can put or a delegate method that makes this simple like there is in iOS? I just want to have the entire app work properly and stop the computer from automatically going to sleep. If someone puts in it to sleep manually, then obviously that is fine. I don't want to prevent the user from putting the computer to sleep. But as long as my app is running I don't want the computer to sleep. Like when you are watching a movie, the app does not go to sleep. How do I do that?
I feel like this is the code that I need but do I just run this function in the viewdidload and it will work? Is it that simple?
var assertionID: IOPMAssertionID = 0
var success: IOReturn?
func disableScreenSleep(reason: String = "Unknown reason") -> Bool? {
guard success != nil else { return nil }
success = IOPMAssertionCreateWithName( kIOPMAssertionTypeNoDisplaySleep as CFString,
IOPMAssertionLevel(kIOPMAssertionLevelOn),
reason as CFString,
&assertionID )
return success == kIOReturnSuccess
}
As I already mentioned in comments you need to remove the guard otherwise it will simply return nil and will never disable the screen sleep. You can also simplify your method, return void and change your success property to a Bool property like disabled to monitor the screen sleep state:
var assertionID: IOPMAssertionID = 0
var sleepDisabled = false
func disableScreenSleep(reason: String = "Disabling Screen Sleep") {
if !sleepDisabled {
sleepDisabled = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep as CFString, IOPMAssertionLevel(kIOPMAssertionLevelOn), reason as CFString, &assertionID) == kIOReturnSuccess
}
}
func enableScreenSleep() {
if sleepDisabled {
IOPMAssertionRelease(assertionID)
sleepDisabled = false
}
}
To disable the screen simply just call the method inside viewDidLoad of your first view controller:
override func viewDidLoad() {
super.viewDidLoad()
disableScreenSleep()
print("sleep Disabled:", sleepDisabled)
}