I have a fair amount of code that looks something like this...
let wantsIgnored = UserDefaults.standard.bool(forKey: "ViewShowIgnoredElements") ?? true
The idea was that if the defaults didn't include that key, it would still get a reasonable default value. This worked fine in Swift 2, but in Swift 3 it warns...
Left side of nil coalescing operator '??' has non-optional type 'Double', so the right side is never used
So it seems that UserDefaults.standard.bool will always return a value, which I guess makes sense if it's a Bool. Ok fine, but what is the best way to solve this problem? I could check every key with objectForKey, but that makes the code much messier. Or I could not do this check at all, and make sure that every key is in the initial set of defaults, by hand I guess.
I'm sure I'm not the only one to have faced this, any canonical solutions?
I'm afraid you would think this as messy, but you can write something like this:
let wantsIgnored = UserDefaults.standard.object(forKey: "ViewShowIgnoredElements") as? Bool ?? true
Please try.
Shouldn't it be:
let wantsIgnored = UserDefaults.standard.set(true, forKey: "ViewShowIgnoredElements")
Then to change it:
UserDefaults.standard.set(false, forKey: "ViewShowIgnoredElements")
And to call it:
let xxxx = UserDefaults.standard.bool(forKey: "ViewShowIgnoredElements")
or:
func boolTest (_myBool: Bool){
if _myBool{
//do something
}
}
//and call that function
boolTest(_myBool: UserDefault.standard.bool(forKey: "ViewShowIgnoredElements"))
Related
I'm trying to setup some kind of global counter in my app.
What the best way of doing this?
I have the following code that works but it's damn ugly, I'd like to increment when the var is accessed so I don't have to call the function.
I tried to use get and set but it's not possible with property wrappers.
class GlobalDisplayInfo: ObservableObject {
#Published var nextAvailableInt : Int = 0
func getNextAvailableInt() -> Int {
nextAvailableInt += 1
return self.nextAvailableInt
}
}
What you want to achieve is not possible as it is against how Published works.
Let's say you access the published var in two places:
When the first one accesses the var, it would increment the var which would require the second place where it uses this var to refresh (access to this var again) which would require the var to increment again which would require the other place which uses this var to access and it just goes on - I think you got the point.
Your problem basically sounds rather like a design problem and I suggest you review it. Also, I think it'd be better if you just tell us what you want to achieve (without using any coding language, just a plain explanation of what you want) then an answer might come up.
It is not clear the usage, but looks like you need the following
class GlobalDisplayInfo: ObservableObject {
private var _nextAvailableInt = 0
var nextAvailableInt : Int {
_nextAvailableInt += 1
return _nextAvailableInt
}
}
I am getting an error that I don't understand. I am not sure if it is a compiler error or if I am doing something wrong?
Inside a swiftUI View I have a list showing elements from core data (Figure 1). In the example below I replaced the t.name with "yo" for some undefined reason 😅.
Anyway, the tasks is a fetch request from Core Data:
#FetchRequest(entity: Task.entity(), sortDescriptors: []) var tasks: FetchedResults<Task>
FIGURE 1: Works fine to build and run the app.
FIGURE 2: Does not work to build and run the app.
Please help me understand what I am doing wrong or is this a compiler bug? Why can't I add the if block inside the ForEach? I can provide more information if needed. Thanks!
You can use if inside ForEach, but you should remember that ForEach is not language operator foreach but a struct type with ViewBuilder in constructor with generics in declaration, so it needs to determine type, which in your case it cannot determine.
The possible solution is to tell explicitly which type you return, as below (tested with Xcode 11.2 / iOS 13.2)
ForEach(tasks, id: \.id) { name -> Text in
if (true) {
return Text("name")
}
}
You have to return nil in case of a false condition. So you need to declare parameter as Optional and return nil in case of a false condition (XCode - 11.3.1).
ForEach(tasks, id: \.id) { t -> Text? in
return condition ? Text("text") : nil
}
}
For some reason, in the ForEach.init you're using, the view building closure isn't annotated with #ViewBuilder. This means that the if/else you're using is Swift's own if statement, not the SwiftUI construct which returns a _ConditionalContent.
I don't know if it's considered a bug by Apple, but I would definitely consider it to be one.
The easiest workaround is just to wrap the if/else in a Group - the Group.init is a #ViewBuilder, so will handle the if/else correctly.
What also worked for me was using a Group inside the ForEach like this:
ForEach(self.items, id: \.self { item in
Group {
if item.id == 0 {
Text(String(item.id))
}
}
}
Edit: Please note the question below discusses using delegation between
2 viewcontrollers that are also implemented in a UITabBarController.
I've done a fair bit of searching here and on YouTube, but haven't seen my issue replicated elsewhere. I'll keep it to the point.
I have 2 view controllers that I coded myself -not generated by XCode-; TabOneController, and TabTwoController
below are the coding for both...
import UIKit
class TabOneController: UIViewController{
private let instanceOfTabOneView = TabOneView()
var vc1Delegate: fromOneToTwo!
override func loadView() {
super.loadView()
view.addSubview(instanceOfTabOneView.buildTheVu())
view.backgroundColor = UIColor.white
runThisOnce()
}
func runThisOnce(){
vc1Delegate.passTheValue(heroNameIs: "pass this to TabTwoController")
}
}
protocol fromOneToTwo{
func passTheValue(heroNameIs: String)
}
as for tab 2...
import UIKit
class TabTwoController: UIViewController, fromOneToTwo{
private let instanceOfTabTwoView = TabTwoView()
override func loadView() {
super.loadView()
view.addSubview(instanceOfTabTwoView.buildTheVu())
assignDelegateToSelf()
}
func assignDelegateToSelf(){
let instanceTabOne = TabOneController()
instanceTabOne.vc1Delegate = self
}
func passTheValue(heroNameIs:String){
instanceOfTabTwoView.txtFld.text = heroNameIs
}
}
I'm getting the following error at runtime -the app builds successfully-...
fatal error: unexpectedly found nil while unwrapping an Optional value
on the following line...
vc1Delegate.passTheValue(heroNameIs: "pass this to TabTwoController")
When I comment out the above line, the app builds and runs successfully, but of course the app doesn't execute the delegation.
I kinda understand what the compiler is trying to tell me, that the
vc1Delegate
hasn't been instantiated -I guess-. But I searched under every rock, and can't seem to find how to get around this.
I'd appreciate any help or guidance. Sorry if my code seems immature, I'm new to Swift and programming in general. Thank you.
In a UITabBarController, the first tab is instantiating by default. The view controller initialization executes the loadView and finds a nil because the second tab did not initialize yet; this is normal behavior. My suggestion is making the delegate weak optional with the ? suffix and run the delegate code elsewhere. Also, always capitalize the first letter in class and protocol names.
weak var vc1Delegate: FromOneToTwo?
If this structure is mandatory, try with a custom notification observer instead.
First thing first, your error happens in line var vc1Delegate: fromOneToTwo! while you declared this delegate variable as not null but then calling passTheValue on it. A correct practice will be
var vc1Delegate: fromOneToTwo?
func runThisOnce(){
if let delegate = vc1Delegate {
vc1Delegate.passTheValue(heroNameIs: "pass this to TabTwoController")
}
}
Secondly, you are not using delegate correctly. In the assignDelegateToSelf() function, you are creating a new instance of TabOneController
and then assign delegate. Instead, you need to find out the existing TabOneController instance and assign delegate.
I try this and worked add delegate = self in table cellforRowAt
like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "ChapterPracticeTableViewCell") as! ChapterPracticeTableViewCell
cell.deligate = self
return cell
}
I have set a pan gesture to an imageView, though it's action method never being called in swift 3.
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.panGestureHandler(panGesture:)))
panGesture.minimumNumberOfTouches = 1
imageview.addGestureRecognizer(panGesture)
the action method:
#objc func panGestureHandler(panGesture recognizer: UIPanGestureRecognizer) {
}
Am I missing anything?
Ok, seems dumb, but after debugging I've noticed that the imageView's userInteractionEnabled is set to false.
After adding this line imageview.isUserInteractionEnabled = true everything seems to be working properly.
You may try to use a function handler for you rUIPanGestureRecognizer like this.
func panGestureHandler(recognizer: UIPanGestureRecognizer) {
}
I'm in the process of migrating one of my projects to Swift 3 and I'm hung up on converting a NSURLRequest to NSURLMutableRequest. In Swift 2 I could simply:
let mreq = req.mutableCopy() as! NSMutableURLRequest
But now mutableCopy is no longer a thing in Swift 3. I tried various permutations of constructors and looked in the docs for info to no avail. I must be missing something. There has to be a way to make a mutable copy of an object.
I just figured it out. Dang it was too obvious.
let mreq = req.mutableCopy() as! NSMutableURLRequest
becomes
var mreq = req