Add button in SwiftUI Stepper not hittable during XCTest - swiftui

My stepper is defined as follows (Standalone WatchOS app)
Stepper(value: $myCount) {
Text("\(myCount)").font(.footnote).accessibilityIdentifier("count_label")
}.accessibilityIdentifier("my_stepper")
It is fully functional on the real / simulator devices. During a test case, defined below, I am unable to invoke the increment button. (I get an error and the button itself is not hittable, ever)
XCTAssertTrue(app.steppers["my_stepper"].waitForExistence(timeout: 10))
XCTAssertFalse(app.steppers["my_stepper"].buttons["Remove"].isEnabled)
XCTAssertTrue(app.steppers["my_stepper"].buttons["Add"].isEnabled)
-> (Error) app.steppers["my_stepper"].buttons["Add"].tap()
Error kAXErrorCannotComplete performing AXAction
kAXScrollToVisibleAction on element AX element pid
I tried to forceTap (using coordinates) with no luck. Any idea how to invoke the increment action?

While the increment and decrement buttons exist in the view stack, they are not hittable. Likely a bug in SwiftUI that impacts either WatchOS or all platforms. Best way I have found to temporarily get past the issue is using the following tutorial :
app.steppers["my_stepper"].coordinate(withNormalizedOffset: CGVector(dx: 0.9, dy: 0.5)).tap()
This is a workaround that will probably fail on different devices. For me, it would only work with Ultra 49mm watchOS 9.0. Accepting this until a better answer is found.

Related

SwiftUI: Why does ForEach need an ID?

Im using a ForEach loop in my SwiftUI View and I am getting strange warnings.
It works fine like this:
ForEach(0..<7) { i in
// do something
}
Then I changed 7 to a constant:
let numberOfElements = 7
ForEach(0..<numberOfElements) { i in
// do something
}
And got the following warning:
Non-constant range: argument must be an integer literal
I googled an found the following solution which works:
let numberOfElements = 7
ForEach(0..<numberOfElements, id:\.self) { i in
// do something
}
However, I have no idea why it works. Why do I have to give an ID to the ForEach loop, and what is the ID for?
ForEach(0..<numberOfElements) { i in
// do something
}
The reason why using the above ForEach init pops the using literal value warning is because SwiftUI doesn't expect to re-render anything when using the Range<Int> init method. This is a documented requirement / feature. The exceptions are the init methods with id:.
A hashable id matters in SwiftUI as well as in many other view-tree based frameworks like React is because these UI frameworks needs to use these ids to track updates for views inside the ForEach or any other "Container Views", to help the framework achieve usable performance. If you want to dig deeper, take a look at this WWDC video: Demystify SwiftUI.

SwiftUI fails to build preview with compiling error

When I go to view my SwiftUI through the canvas preview in Xcode 11.3.1 I am getting the error
Compiling failed: 'Color' is only available in iOS 13.0 or newer
But the project itself builds successfully and the simulator loads without any issues. I have tried clearing the build folder, quitting Xcode and rebuilding but still no luck.
Any help would be great. Thanks in advance.
SwiftUI minimum deployment target is 13.0, so if you have project with support of older version, then all SwiftUI code (including preview providers) you have to prepend with availability modifier, like
#available(iOS 13.0, *) // << here !!
struct Demo: View {
var body: some View {
VStack {
Text("Hello")
}
}
}
You Should use Assets or Other option like Color Literal for Color.
Don't use the system Color option since your deployment target is 11.4.
Man, I've had a similar issue. The problem was that sometimes my preview worked sometimes it didn't... I reviewed the diagnostics and realized that there are some #_dynamicReplacement attributes mentioned (which are used, I guess, for hot reloading). It wasn't working when I've had a file with #available attributes opened in the (adjacent) editor. When I closed that editor everything worked back again.
Magic ✨
Also one more hint from my friend - when you have a file from another target (not the one hosting your Canvas-related code) in (adjacent) editor it behaves the same way.

Crash during UICollectionViewCell reordering after Swift 3.0 migration

I do have a strange issue in using the reorder feature in my app's UICollectionView. We have a custom layout which is implemented to show a decoration view. The collection view uses a flow based layout. When I move the first cell from its position to last cell position for reordering of the cells, the app crashes before it calls the collection view delegate's collectionView(moveItemAt: to) method.
Attached the stack trace of this issue. You can see that crash is happening in the bridging between NSIndexPath and IndexPath. I am not sure why it is happening inside UIKit. Searching for this issue found that it appears to be bug inside UIKIt which got introduced in Swift 3.0. I tested my old build which was built before swift 3.0 migration and it works without any crashes.
Can someone tell me how can I fix this issue?
Related bugs links
UICollectionView broken after Swift 3 migration?
https://bugs.swift.org/browse/SR-2103
https://bugs.swift.org/browse/SR-2417
func handleLongGesture(_ gesture: UILongPressGestureRecognizer) {
switch(gesture.state) {
case UIGestureRecognizerState.began:
if let movingPageIndexPath = collectionView.indexPathForItem(at: gesture.location(in: collectionView)) {
collectionView.beginInteractiveMovementForItem(at: movingPageIndexPath)
}
case UIGestureRecognizerState.changed:
collectionView.updateInteractiveMovementTargetPosition(gesture.location(in: collectionView))
case UIGestureRecognizerState.ended:
collectionView.endInteractiveMovement()
default:
collectionView.cancelInteractiveMovement()
}
}

Swift 3 - AWS iOS 'EXC_BAD_INSTRUCTION' Error

After converting over to Swift 3, my AWS iOS App now crashes when being run.
Has anyone seen anything similar after converting over to Swift 3?
Its crashing because you are doing a few things here:
trying to return a method call (which I don't think is possible - but I've never tried it)
application: didFinishLaunching should only ever return true because it's a boolean function as the name suggests. So the return value should be true. If you'd like to override application: didFinishLaunching you can declare that later in your AppDelegate
So here's what you can do... Try deleting the method and slowly type it back until Xcode auto-completes it for you. That will allow you to see what has been depreciated, or what other methods might be useful, also if you haven't yet hit Command + Shift + K to run a clean on your project. Xcode 8 and Swift 3 might have altered a few more things you aren't aware of.
launchOptions is an optional parameter. Doing a forced unwrap with the ! will crash if the value is nil, which it usually is.

How to make scrollspy effect in foundation 4

I have been playing with Foundation 4 for a while. I have to say it is very simple to use, and lots of its markups are very readable. The documentation is brief, but we can work around. Then I hit this really big problem. In Bootstrap, you will have a functionality called scrollspy. You fix a sidebar on left, and when you scroll through page, it tells your where you are. http://getbootstrap.com/javascript/#scrollspy
I notice Foundation 4 also has a js component called Magellan. Unfortunately, the documentation has been extremely vague about what it does and how to adjust it. I played it for a while and realize that I might not be able to achieve the same effect as Bootstrap's scrollspy, where I can have a fixed leftside panel. Magellan always pushes my panel to the top of my screen.
Does anyone have the experience working with magellan?
Take a loot at the scrollspy for jQuery page on github, read the info, might be helpful to use it separately on any project:
https://github.com/sxalexander/jquery-scrollspy
esp take a loot at where it says:
$(document).ready(function() {
$('#sticky-navigation').scrollspy({
min: $('#nav').offset().top,
onEnter: function(element, position) {
$("#nav").addClass('fixed');
},
onLeave: function(element, position) {
$("#nav").removeClass('fixed');
}
});
});