Swift - 'when' operator (is if repetitive?) - if-statement

In Swift, does if 'keep checking'. I.E. will its function be called when the variable is changed. I have the variable playBegin which gets changed halfway through my program. However, instead of directly implementing code in the change, the variable playBegin is set to 'true' and the code is carried out elsewhere. Essentially my question is: will if update and check to see the state of it's variable, or will I have to use something else?
To act like when in this statement:
when playBegin {
// blahdeblah
}
What would be a good substitute for when? playBegin is normally false, but becomes true halfway through the program.

drewag's answer is correct. Also if you want the code to always get executed at least once:
do {
// blahdeblah
} while playBegin == true

If you want this to repeat until playBegin is no longer true, you can just do:
while playBegin == true {
// blahdeblah
}
If you simply want it to execute once if playBegin is true, you would use an if statement:
if playBegin == true {
// blahdeblah
}

Actually Anna Dickinson had a good idea: use an observer.
To do this, you would set up a class:
class SomeObserverClass
{
var playBegin:Bool = false
{
willSet(playBegin) {
println("About to set playBegin to \(playBegin)")
}
didSet {
if playBegin == true {
// your code when playBegin == true
println("playBegin set to \(playBegin)")
}
}
}
}
In your AppDelegate file, above (outside) the AppDelegate class instantiate this class:
internal let someObserverClass:SomeObserverClass = SomeObserverClass()
Then in your code someplace if you say something like:
someObserverClass.playBegin = someBoolValue
the observer class code will be executed.
EDIT:
I'm not sure what you mean by "elaborate", but I'll try.
An observer property is a special kind of class property. For example, if you had a class:
class SomeClass
{
var someBool:Bool = false
func someFunction()
{
... some code ....
}
func someOtherFunction
{
... some other code ....
}
}
then someBool is a class property of SomeClass.
SomeClass's properties and methods could be accessible from any other class that has visibility of an instance of SomeClass. e.g. if you created an instance of SomeClass in your AppDelegate file outside of (above) the AppDelegate class (so that it's visible to any class in your project) with:
internal let someClass:SomeClass = SomeClass()
then the value of the variable someBool could be changed from anywhere in your project with something like:
someClass.someBool = true.
or someFunction could be executed with:
someClass.someFunction()
OK, that's basic stuff.
Now with an observer, it's the same thing except when we declare the class property someBool, we also define some code we want to be executed anytime the property (variable) is changed. In the code below, the only change to SomeClass is that I've added this code.
class SomeClass
{
var someBool:Bool = false
{
willSet
{
println("someBool is about to be changed from \(someBool)")
}
didSet
{
if someBool == true
{
// your code when someBool is true
println("someBool set to \(someBool)")
}else{
println("someBool set to \(someBool)")
}
}
}
func someFunction()
{
... some code ....
}
func someOtherFunction
{
... some other code ....
}
}
If then somewhere else in your project you write:
someClass.someBool = true
someClass.someBool = false
someClass.someBool = true
You'll get:
someBool is about to be changed from false
someBool set to true
someBool is about to be changed from true
someBool set to false
someBool is about to be changed from false
someBool set to true
A variation of this is:
class SomeClass
{
var someBool:Bool = false
{
willSet(someBool)
{
println("someBool is about to be changed to \(someBool)")
}
didSet
{
if someBool == true
{
// your code when someBool is true
println("someBool set to \(someBool)")
}else{
println("someBool set to \(someBool)")
}
}
}
func someFunction()
{
... some code ....
}
func someOtherFunction
{
... some other code ....
}
}
If then somewhere else in your project you write:
someClass.someBool = true
someClass.someBool = false
someClass.someBool = true
You'll get:
someBool is about to be changed to true
someBool set to true
someBool is about to be changed to false
someBool set to false
someBool is about to be changed to true
someBool set to true
Another Variation of this could be:
class SomeClass
{
var someBool:Bool = false
{
willSet(newBool)
{
println("someBool is about to be changed from \(someBool) to \(newBool)")
}
didSet
{
if someBool == true
{
// your code when someBool is true
println("someBool set to \(someBool)")
}else{
println("someBool set to \(someBool)")
}
}
}
func someFunction()
{
... some code ....
}
func someOtherFunction
{
... some other code ....
}
}
If then somewhere else in your project you write:
someClass.someBool = true
someClass.someBool = false
someClass.someBool = true
You would get:
someBool is about to be changed from false to true
someBool set to true
someBool is about to be changed from true to false
someBool set to false
someBool is about to be changed from false to true
someBool set to true
No need for if, do or while statements. The willSet and didSet code gets executed any time the class property (variable) is changed.
You don't need to implement both willSet and didSet. You can leave either of them out if you want.
Also, the willSet and didSet code does not get executed during initialization of the class.
I think this comes closer to the intent of your "when" statement than anything else.

Related

Unexpected SwiftUI #State behavior

The setup:
My app has a View ToolBar (all shortened):
struct ToolBar: View {
#State private var ownerShare: CKShare?
#State private var show_ownerModifyShare = false
// …
The toolbar has some buttons that are created by functions. One of them is
func sharingButton(imageName: String, enabled: Bool) -> SharingButton {
return SharingButton(systemImageName: imageName, enabled: enabled) {
Task {
do {
(_, participantShare, ownerShare) = try await dataSource.getSharingInfo()
// …
if ownerShare != nil { show_ownerModifyShare = true }
} catch (let error) {
//...
}
}
}
}
This is the body:
var body: some View {
HStack {
// …
sharingButton(imageName: "square.and.arrow.up", enabled: currentMode == .displayingItems)
.fullScreenCover(isPresented: $show_ownerModifyShare) {
// A CKShare record exists in the iCloud private database. Present a controller that allows to modify it.
CloudSharingView(container: CKContainer(identifier: kICloudContainerID), shareRecord: ownerShare!, dataSource: dataSource)
}
}
}
}
The problem:
When the sharingButton is tapped, ownerShare is set in Task {…}, since the iCloud database is shared as an owner.
Accordingly, show_ownerModifyShare = true is executed, and thus the body of struct ToolBar is newly rendered.
However, CloudSharingView(container: CKContainer(identifier: kICloudContainerID), shareRecord: ownerShare!, dataSource: dataSource) crashes, because ownerShare is nil, although it is only set true after ownerShare has been set != nil.
My question:
What could be the reason, and how to correct the code?
EDIT: (due to the comment of jnpdx):
I replaced .fullScreenCover(isPresented: by
.fullScreenCover(item: $ownerShare) { _ in
CloudSharingView(container: CKContainer(identifier: kICloudContainerID), shareRecord: ownerShare!, dataSource: dataSource)
}
but the code still crashes with Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value when ownerShare! is used.
You should use the item form of fullScreenCover, which allows you to send a dynamically-changed parameter to the inner closure:
.fullScreenCover(item: $ownerShare) { share in
CloudSharingView(container: CKContainer(identifier: kICloudContainerID), shareRecord: share, dataSource: dataSource)
}
This is a common issue with sheet and the related functions in SwiftUI, which calculate their closures when first rendered and not at presentation time. See related:
SwiftUI: Switch .sheet on enum, does not work

SecTrustEvaluateWithError leads to UI unresponsiveness

In my code I am using this function
func checkIsError(someDate: Date) -> Bool {
var someError: SomeError?
guard SecTrustEvaluateWithError(trust, &someError),
someError == nil else {
return false
}
return true
}
When I run application and open file in which is that function I am getting
This method should not be called on the main thread as it may lead to UI unresponsiveness.
I tried using DispatchQueue.global.async() but when I am using that I can't return value from Void function (in my case false). Is there any option I don't run SecTrustEvaluateWithError on main thread without and return false when it fail?
You can call the method itself on the background thread. Try this:
DispatchQueue.global(qos: .background).async {
if !self.checkValidation(yourDate) {
DispatchQueue.main.async {
self.showPopupOrSmth()
}
}
}

asynchronous initialisation with swiftui

Basically - I run up against this a lot - I don't understand how you correctly do asynchronous initialisation in swift with callbacks. (with combine - I can do it). In particular - I have this code:
struct MyView : View {
#State var initialised : Bool = false
init()
{
var initialisedBinding = $initialised
Photos.PHPhotoLibrary.RequestAuthorization {
status in
if (status == Photos.PHAuthorizationStatus.authorized) {
print("here I am")
initialisedBinding.wrappedValue = true
initialisedBinding.update()
}
}
}
var body : some View {
VStack {
if (initialised) {
Text("yep")
} else {
Text("nope")
}
}
}
And when I run it - I get the print out - but the text never changes - it always remains "nope". What am I doing wrong, and how do I do it right? (Without using combine - I can do it with like a currentValueSubject and a .onreceive - but it's extra overhead, and I really want to know why the above code doesn't work - obviously I'm understanding something bad)
State is not ready in init yet, so you bound to nowhere. Moreover such activity in init is not good, because view can be created many times during rendering. The more appropriate place is .onAppear
struct MyView : View {
#State var initialised : Bool = false
var body : some View {
VStack {
if (initialised) {
Text("yep")
} else {
Text("nope")
}
}.onAppear {
Photos.PHPhotoLibrary.RequestAuthorization {
status in
if (status == Photos.PHAuthorizationStatus.authorized) {
print("here I am")
self.initialised = true
}
}
}
}
}

Delegate not being called in Swift

I can't figure out why my Delegate is not being called.
Here is where I define the protocol and call the delegate:
protocol CommentRatingViewControllerDelegate: class {
func didCommentOrRatePost(updatedRating: Bool, addedComment:Bool)
}
class CommentRatingViewController: UIViewController, UITextViewDelegate {
weak var delegate:CommentRatingViewControllerDelegate?
...
#IBAction func saveRatingComment(_ sender: Any) {
var updatedRating = false
var addedComment = false
rating = ratingView.rating
if rating != 0.0 {
saveRating(articleID: post.articleID, userID: post.userID)
updatedRating = true
}
if commentsTextView.text != "" {
saveComment(articleID: post.articleID, userID: post.userID, comment: commentsTextView.text!)
addedComment = true
}
self.delegate?.didCommentOrRatePost(updatedRating: updatedRating, addedComment: addedComment)
close()
}
....
And here is where conform to the delegate protocol:
extension PostDetailViewController: CommentRatingViewControllerDelegate {
func didCommentOrRatePost(updatedRating: Bool, addedComment: Bool) {
if updatedRating == true || addedComment == true {
networkingState = .searching
if updatedRating {
getRating(articleID: post.articleID)
}
if addedComment {
post.numberOfComments += post.numberOfComments
}
networkingState = .finishedSearching
tableView.reloadData()
}
}
}
When you conform to a protocol, if you want to call delegate methods, it is not enough to make your class conform to the protocol, you also need to set the delegate to self inside the class.
class CommentRatingViewController: UIViewController, UITextViewDelegate {
override func viewDidLoad() {
self.delegate = self
}
}

Chart JS Zoom Pan

I am using the Chart JS Zoom/Pan Plugin (https://github.com/chartjs/chartjs-plugin-zoom) and was wondering if there is a way to disable it when there is no data present. The current basic options settings are
pan: {
enabled: true,
mode: 'x'
},
zoom: {
enabled: true,
mode: 'x'
}
Yes, you can change this by setting "enabled" to false and/or use chart.resetZoom() link
Try this:
// since myChart.update() is not working. You need to do this.
var myChartOptions = myChart.options; // need to store in variable.
// check data present
if (data.length > 0) {
myChartOptions.pan.enabled = true;
myChartOptions.zoom.enabled = true;
myChart.options = myChartOptions; // update myChart.options.
}
else {
myChartOptions.pan.enabled = false;
myChartOptions.zoom.enabled = false;
myChart.options = myChartOptions; // update myChart.options.
}