Post

Replies

Boosts

Views

Activity

App Intents Extension doesn't work when changing bundle identifier
Recently I realized that even though I was able to add my app's intents in the Shortcuts app, selecting any of the parameters didn't show the popup list of suggestions (the ones declared by DynamicOptionsProvider in the AppIntent subclass) and running it showed an error. In the image you can see the two suggestions for the working app version ("asdf" and "bla") that would not be there in the non-working version. I knew that when I had added this functionality, it worked, so I found the app version that caused the App Intents Extension to stop working. Apparently, the problem was that I had removed the Swift files declared in the app extension from my main app's target. Probably when I first added the App Intents Extension I had noticed that adding the extension's source files to the main target made it work, but later thought that it shouldn't be necessary and didn't test if it still worked. Today I created an empty project with a new App Intents Extension and confirmed that the Shortcuts app was correctly showing the parameter popup suggestions, even without including the extension's source files in the main target. Then during the course of almost an entire day I gradually reduced my original Xcode project to this new sample project to find what else would make the extension work, other than including the extension's source files in the main target. My very last resource was changing the bundle identifier, which solved the issue. My original project's targets have identifiers like org.domain.OriginalApp and org.domain.OriginalApp.AppIntent, while the sample project's targets have identifiers like org.domain.SampleApp and org.domain.SampleApp.AppIntent. How could including the App Intents Extension's source files in the main target or changing the bundle identifiers cause the Shortcuts app to correctly show the parameter popup suggestions?
0
0
939
Feb ’24
Time modified is reset to midnight for some files on FTP server
Every now and then I notice that the date modified of some files on my FTP server has slightly changed by setting the time to midnight. I notice this because I regularly sync the files from the FTP server to a folder on my Mac by comparing the date modified, and it happens every now and then that files that weren’t modified are listed for syncing. For many months after their creation, the time is correct and they are only synced once, but at some point the time is displayed as midnight for some reason and they are suddenly marked for syncing. The wrong time is reported both programmatically as well as in the Finder. The same files are displayed with what seems to be the correct time modified in the app Cyberduck. In this case it seems to be exactly 6 months old files. I didn't check this for the other files that this issue happened with in the past, but it could be about the same timeframe. Is this an issue with macOS? Is there a workaround? I already filed feedback FB13671336.
0
0
484
Mar ’24
URL.checkResourceIsReachable() throws error if file is on FTP server and name contains special characters
I have a file named ä.txt (with German umlaut) on my FTP server. I select it like this: let openPanel = NSOpenPanel() openPanel.runModal() let source = openPanel.urls[0] Running this code unexpectedly throws an error: do { print(try source.checkResourceIsReachable()) } catch { print(error) // Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory” } Manipulating the URL also seems to change the underlying characters: print(source) // file:///Volumes/abc.com/httpdocs/%C3%A4.txt print(URL(fileURLWithPath: source.path)) // file:///Volumes/abc.com/httpdocs/a%CC%88.txt Note that both variants of the URL above also throw the same error when running URL.checkResourceIsReachable(). If I download the file to my Mac, then both variants print file:///Users/me/Downloads/a%CC%88.txt and neither of them throws an error when running URL.checkResourceIsReachable(). What is the problem? How can I correctly access this file on the FTP server?
6
0
1k
Apr ’24
Select directory in SwiftUI file importer
I want to show a file importer that allows to select both regular files as well as directories. When running the following code on iOS, I can tap a PDF file and the file importer closes as expected, but when tapping a directory, the file importer shows its contents. How can I instead select that directory and close the file importer? The navigation bar shows a Cancel button, but no Open button. struct FileView: View { @State private var showFileImporter = false var body: some View { ScrollView { VStack(alignment: .leading) { VStack(alignment: .center) { Button("Open") { showFileImporter = true } } } } .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [.pdf, .directory], onCompletion: { result in // TODO }) } }
3
0
2.7k
Apr ’24
App Store Connect API randomly returns error "An unexpected error occurred on the server side" or "The request timed out"
I use the App Store Connect API to run many parallel requests to update different parts of a single app. I am randomly getting errors such as An unexpected error occurred on the server side. or The request timed out. Usually when these errors happen, I can simply run the unsuccessful requests one or two more times and then they succeed. Is there an explanation for this? Is this possibly caused by too many parallel requests? What is the maximum suggested number of parallel requests?
0
0
696
May ’24
SwiftUI Image(uiImage:) converts colored image to black and white
I have a data object that dynamically changes the UIImage assigned to one of its instance variables, but when showing this image in SwiftUI, it's always black and white. The following sample code shows the difference between the same image, but using first the native constructor Image(systemName:) and then Image(uiImage:). When using AppKit and Image(nsImage:) this issue doesn't happen. import SwiftUI import UIKit struct ContentView: View { @State var object = MyObject() var body: some View { Image(systemName: "exclamationmark.triangle.fill") .symbolRenderingMode(.palette) .foregroundStyle(.white, .yellow) Image(uiImage: object.image) } } class MyObject { var image = UIImage(systemName: "exclamationmark.triangle.fill")! .applyingSymbolConfiguration(.init(paletteColors: [.white, .systemYellow]))! } #Preview { ContentView() }
0
0
649
May ’24
Xcode UI test cannot tap menu button in form
Apparently UI tests are unable to tap menu buttons but can tap regular buttons inside forms. Earlier today I was able to see in the Simulator that the UI test tries to tap the button by tapping the center of the containing form row, which works for regular buttons, but not for menu buttons. In fact, when trying in the SwiftUI preview in Xcode it seems that menu buttons have to be tapped exactly on top of them, while regular buttons can be tapped anywhere in the form row. (Now I’m not able to see touches performed by the UI test anymore in the Simulator for an unknown reason, even though I have “Show single touches” enabled in the Simulator settings.) How can I open a menu button in a UI test? The UI code: struct ContentView: View { @State private var label1 = "Menu 1" @State private var label2 = "Menu 2" var body: some View { NavigationStack { Form { LabeledContent("Menu 1") { Button(label1) { label1 = "Menu 1 tapped" } .accessibilityIdentifier("menu1") } LabeledContent("Menu 2") { Menu(label2) { Button("Button") { } .accessibilityIdentifier("button") } .accessibilityIdentifier("menu2") } } } } } #Preview { ContentView() } And the test: final class problemUITests: XCTestCase { func testExample() throws { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch() app.collectionViews.element(boundBy: 0).buttons["menu1"].tap() app.collectionViews.element(boundBy: 0).buttons["menu2"].tap() app.collectionViews.element(boundBy: 0).buttons["button"].tap() } }
2
0
727
Oct ’24
Simulate hardware keyboard button press in Xcode UI test for iPad
XCUIElement has two methods named typeKey(_:modifierFlags:): the first one takes a String as an argument, the second one a XCUIKeyboardKey, which has constants like .downArrow. Now, even if the documentation says that both methods are "Available in macOS and in iPadOS 15 and later", when compiling the UI tests for iOS, the following line app.typeKey(.downArrow, modifierFlags: []) produces a compiler error Type 'String' has no member 'downArrow' Am I missing something or is there a workaround?
0
0
473
Jul ’24
Xcode downloaded client crash report at libsecinit_appsandbox.c
Among the newest crash reports downloaded by Xcode for my App Store app there is one that contains a single thread, crashed at libsecinit_appsandbox.c, without any trace of my app name. What could possibly cause such a crash? Is this something related with app signing? The app has been on the App Store for a long time and this never happened to me. Thread 0 Crashed: 0 libsystem_secinit.dylib 0x00000001941dcfcc _libsecinit_appsandbox.cold.12 + 60 (libsecinit_appsandbox.c:454) 1 libsystem_secinit.dylib 0x00000001941dbf94 _libsecinit_appsandbox + 1764 (libsecinit_appsandbox.c:454) 2 libsystem_trace.dylib 0x00000001872be598 _os_activity_initiate_impl + 64 (activity.c:131) 3 libsystem_secinit.dylib 0x00000001941db85c _libsecinit_initializer + 80 (libsecinit_initializer.c:17) 4 libSystem.B.dylib 0x00000001941f2664 libSystem_initializer + 280 (init.c:338) 5 dyld 0x00000001871fe608 invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168 (Loader.cpp:2198) 6 dyld 0x000000018723cd6c invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 340 (MachOAnalyzer.cpp:2481) 7 dyld 0x0000000187230400 invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 496 (MachOFile.cpp:1669) 8 dyld 0x00000001871df2fc dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 300 (MachOFile.cpp:1185) 9 dyld 0x000000018722f394 dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 192 (MachOFile.cpp:1628) 10 dyld 0x000000018723c880 dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 516 (MachOAnalyzer.cpp:2448) 11 dyld 0x00000001871fa53c dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 564 (Loader.cpp:2189) 12 dyld 0x00000001872042d4 dyld4::PrebuiltLoader::runInitializers(dyld4::RuntimeState&) const + 44 (PrebuiltLoader.cpp:818) 13 dyld 0x000000018721dcc0 dyld4::APIs::runAllInitializersForMain() + 84 (DyldAPIs.cpp:3889) 14 dyld 0x00000001871e4010 dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3156 (dyldMain.cpp:891) 15 dyld 0x00000001871e2ef4 start + 1868 (dyldMain.cpp:1265) Crash report
Topic: Code Signing SubTopic: General
2
0
629
Sep ’24
New iPhone 6.9" screenshots with App Store Connect API
The App Store Connect API documentation still doesn't list the new 6.9" iPhone display type: https://developer.apple.com/documentation/appstoreconnectapi/screenshotdisplaytype I already opened a similar topic about the new iPad Pro 13" screenshot type in May (4 months ago), but nobody replied and that screenshot type is still not listed in the specification. I also created a bug report in Feedback Assistant back then, without any reply. Why does it take so long to update the API specification?
0
0
999
Sep ’24
SKLabelNode keeps jumping back and forth when displaying different numbers with equal number of digits
I'm trying to display a right-aligned timecode in my game. I had expected that digits would all have the same width, but this doesn't seem to be the case in SpriteKit, even though it seems to be the case in AppKit. In SpriteKit, with the default font there is a noticeable difference in width between the digit 1 and the rest (1 is thinner), so whenever displaying a number with the least significant digit 1 all preceding digits shift slightly to the right. This happens even when setting a NSAttributedString with a font that has a fixedAdvance attribute. class GameScene: SKScene { override func didMove(to view: SKView) { let label = SKLabelNode(text: "") view.scene!.addChild(label) // label.horizontalAlignmentMode = .left label.horizontalAlignmentMode = .right var i = 11 Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in label.text = "\(i)" // let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight", .fixedAdvance: 20]), size: 30)! // let paragraphStyle = NSMutableParagraphStyle() // paragraphStyle.alignment = .right // label.attributedText = NSAttributedString(string: "\(i)", attributes: [.font: font, .foregroundColor: SKColor.labelColor, .paragraphStyle: paragraphStyle]) i += 5 } } } With AppKit, when using SpriteKit's default font HelveticaNeue-UltraLight, this issue doesn't exist, regardless whether I set the fixedAdvance font attribute. class ViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight"]), size: 30)! // let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-Light", .fixedAdvance: 20]), size: 30)! let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .right let textField = NSTextField(labelWithString: "") textField.font = font textField.alignment = .right // textField.alignment = .left textField.frame = CGRect(x: 100, y: 100, width: 100, height: 100) view.addSubview(textField) var i = 11 Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in textField.stringValue = "\(i)" // textField.attributedStringValue = NSAttributedString(string: "\(i)", attributes: [.font: font, .paragraphStyle: paragraphStyle]) i += 5 } } } Is there a solution to this problem? I filed FB15553700.
0
0
659
Oct ’24
Xcode UI test always fails to tap close button in font picker navigation bar
I'm trying to close the UIFontPickerViewController in a UI test by tapping the close button in the navigation bar. In a default iOS app, I embed the default storyboard view controller in a navigation view controller, then in code I open the font picker like this: class ViewController: UIViewController, UIFontPickerViewControllerDelegate { override func viewDidAppear(_ animated: Bool) { let picker = UIFontPickerViewController(configuration: .init()) picker.delegate = self self.present(picker, animated: true) } func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) { } } And the UI test looks like this: final class problemUITests: XCTestCase { @MainActor func testExample() throws { let app = XCUIApplication() app.launch() sleep(2) let button = app.navigationBars.element(boundBy: 1).buttons.element(boundBy: 0) print(button.debugDescription) XCTAssert(button.waitForExistence(timeout: 2)) button.tap() } } When running the UI test, the XCTAssert always fails and the button.tap() also fails with an error message Failed to tap "chiudi" Button: No matches found for Element at index 1 from input {( NavigationBar )} "chiudi" means "close" in Italian, my macOS system language. It sounds to me like I correctly get the close button, but the messages Xcode prints make no sense to me. It's particularly confusing given that the output of the print statement shows that the button is there, but somehow fails to be tapped: Attributes: Button, 0x104f44660, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Element subtree: →Button, 0x104f44660, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Image, 0x104f44780, {{709.0, 327.5}, {30.0, 30.0}}, identifier: 'UICloseButtonBackground' Path to element: →Application, 0x104f35940, pid: 29803, label: 'problem' ↳Window (Main), 0x104f376a0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f42e10, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43100, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43220, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43340, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43460, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43580, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f436a0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f437c0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f438e0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43a00, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43b20, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43c40, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f43e80, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f43fa0, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f440c0, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f441e0, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f44300, {{276.0, 314.0}, {480.0, 748.0}} ↳NavigationBar, 0x104f44420, {{276.0, 314.0}, {480.0, 108.0}}, identifier: 'Scegli font' ↳Button, 0x104f44660, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Query chain: →Find: Target Application 'org.desairem.problem' Output: { Application, 0x104f781b0, pid: 29803, label: 'problem' } ↪︎Find: Descendants matching type NavigationBar Output: { NavigationBar, 0x10607c0d0, {{0.0, 24.0}, {1032.0, 50.0}}, identifier: 'UIFontPickerView' NavigationBar, 0x10607dab0, {{276.0, 314.0}, {480.0, 108.0}}, identifier: 'Scegli font' } ↪︎Find: Element at index 1 Output: { NavigationBar, 0x1064693a0, {{276.0, 314.0}, {480.0, 108.0}}, identifier: 'Scegli font' } ↪︎Find: Descendants matching type Button Output: { Button, 0x104f714a0, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Button, 0x104f71800, {{711.0, 378.0}, {17.0, 22.0}}, identifier: 'Dictate', label: 'Avvia dettatura' } ↪︎Find: Element at index 0 Output: { Button, 0x104f5d5e0, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' }
0
0
462
Oct ’24
How to correctly resolve URL bookmarks for use in Simulator UI tests?
To make UI testing easier and faster, I usually create URL bookmarks during normal app usage in the Simulator so that they can be instantly resolved on app launch during UI tests. For example, one of my apps allows browsing selected folders and stores bookmarks so they can be quickly opened again on following app launches, and instead of selecting the test folder each time at the beginning of the UI test, I select it once during normal app usage so that it's available immediately during the UI test. This usually works fine, but every now and then the UI tests fail because the tested app isn't able to resolve the stored bookmark. I don't know why this happens, but usually opening and closing the app again in the Simulator and re-running the UI tests solves the issue. The problem now is that I've just tried to setup some new UI tests for Apple Vision Pro Simulator and I'm never able to resolve bookmarks. So I created a sample project that reproduces the issue, and curiously enough the bookmarks don't even resolve when using an iPad Simulator (which usually works fine with my regular UI tests). What am I doing wrong? This can be reproduced with a default iOS project, embedding the default storyboard view controller in a navigation view controller, and this code: import UIKit class ViewController: UIViewController, UIDocumentPickerDelegate { override func viewDidLoad() { navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .add, primaryAction: UIAction(handler: { _ in let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder]) picker.delegate = self self.present(picker, animated: true) })) if let bookmark = UserDefaults.standard.data(forKey: "bookmark") { readBookmark(bookmark) } } func readBookmark(_ bookmark: Data) { do { let label = UILabel(frame: CGRect(x: 100, y: 100, width: 600, height: 100)) label.numberOfLines = 0 var stale = false let url = try URL(resolvingBookmarkData: bookmark, bookmarkDataIsStale: &stale) if !url.startAccessingSecurityScopedResource() { fatalError() } label.text = url.path view.addSubview(label) } catch { fatalError(error.localizedDescription) } } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { do { let url = urls[0] if !url.startAccessingSecurityScopedResource() { fatalError() } let bookmark = try url.bookmarkData() UserDefaults.standard.set(bookmark, forKey: "bookmark") readBookmark(bookmark) } catch { fatalError(error.localizedDescription) } } } And a default UI test, which always crashes because of the fatalError() in the catch clause of readBookmark(_:): final class problemUITests: XCTestCase { @MainActor func testExample() throws { let app = XCUIApplication() app.launch() } }
0
0
462
Oct ’24
How to get GKMatch instance after accepting GKInvite?
In my SceneKit game I'm able to connect two players with GKMatchmakerViewController. Now I want to support the scenario where one of them disconnects and wants to reconnect. I tried to do this with this code: nonisolated public func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) { Task { @MainActor in switch state { case .connected: break case .disconnected, .unknown: let matchRequest = GKMatchRequest() matchRequest.recipients = [player] do { try await GKMatchmaker.shared().addPlayers(to: match, matchRequest: matchRequest) } catch { } @unknown default: break } } } nonisolated public func player(_ player: GKPlayer, didAccept invite: GKInvite) { guard let viewController = GKMatchmakerViewController(invite: invite) else { return } viewController.matchmakerDelegate = self present(viewController) } But after presenting the view controller with GKMatchmakerViewController(invite:), nothing else happens. I would expect matchmakerViewController(_:didFind:) to be called, or how would I get an instance of GKMatch? Here is the code I use to reproduce the issue, and below the reproduction steps. Code Run the attached project on an iPad and a Mac simultaneously. On both devices, tap the ship to connect to GameCenter. Create an automatched match by tapping the rightmost icon on both devices. When the two devices are matched, on iPad close the dialog and tap on the ship to disconnect from GameCenter. Wait some time until the Mac detects the disconnect and automatically sends an invitation to join again. When the notification arrives on the iPad, tap it, then tap the ship to connect to GameCenter again. The iPad receives the call player(_:didAccept:), but nothing else, so there’s no way to get a GKMatch instance again.
6
0
915
Apr ’25
SKNode.zPosition causes nodes to flicker by reordering them for 1 frame
When running the sample code below, every 3 seconds the middle sprite is replaced by a new one. When this happens, most of the time a flicker is noticeable. When recording the screen and stepping through the recording frame by frame, I noticed that the flicker is caused by a temporary reordering of the nodes’. Below you find two screenshots of two consecutive frames where the reordering is clearly visible. This only happens for a SpriteKit scene used as an overlay for a SceneKit scene. Commenting out buttons.zPosition = 1 or avoiding the fade in/out animations solves the issue. I have created FB15945016. import SceneKit import SpriteKit class GameViewController: NSViewController { let overlay = SKScene() var buttons: SKNode! var previousButton: SKSpriteNode! var nextButton: SKSpriteNode! var pageContainer: SKNode! var pageViews = [SKNode]() var page = 0 override func viewDidLoad() { super.viewDidLoad() let scene = SCNScene(named: "art.scnassets/ship.scn")! let scnView = self.view as! SCNView scnView.scene = scene overlay.anchorPoint = CGPoint(x: 0.5, y: 0.5) scnView.overlaySKScene = overlay buttons = SKNode() buttons.zPosition = 1 overlay.addChild(buttons) previousButton = SKSpriteNode(systemImage: "arrow.uturn.backward.circle") previousButton.position = CGPoint(x: -100, y: 0) buttons.addChild(previousButton) nextButton = SKSpriteNode(systemImage: "arrow.uturn.forward.circle") nextButton.position = CGPoint(x: 100, y: 0) buttons.addChild(nextButton) pageContainer = SKNode() pageViews = [SKSpriteNode(systemImage: "square.and.arrow.up"), SKSpriteNode(systemImage: "eraser")] overlay.addChild(pageContainer) setPage(0) Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { [self] _ in setPage((page + 1) % 2) } } func setPage(_ page: Int) { pageViews[self.page].run(.sequence([ .fadeOut(withDuration: 0.2), .removeFromParent() ]), withKey: "fade") self.page = page let pageView = pageViews[page] pageView.alpha = 0 pageView.run(.fadeIn(withDuration: 0.2), withKey: "fade") pageContainer.addChild(pageView) } override func viewDidLayout() { overlay.size = view.frame.size } } extension SKSpriteNode { public convenience init(systemImage: String) { self.init() let width = 100.0 let image = NSImage(systemSymbolName: systemImage, accessibilityDescription: nil)!.withSymbolConfiguration(.init(hierarchicalColor: NSColor.black))! let scale = NSScreen.main!.backingScaleFactor image.size = CGSize(width: width * scale, height: width / image.size.width * image.size.height * scale) texture = SKTexture(image: image) size = CGSize(width: width, height: width / image.size.width * image.size.height) } }
1
0
725
Nov ’24
App Intents Extension doesn't work when changing bundle identifier
Recently I realized that even though I was able to add my app's intents in the Shortcuts app, selecting any of the parameters didn't show the popup list of suggestions (the ones declared by DynamicOptionsProvider in the AppIntent subclass) and running it showed an error. In the image you can see the two suggestions for the working app version ("asdf" and "bla") that would not be there in the non-working version. I knew that when I had added this functionality, it worked, so I found the app version that caused the App Intents Extension to stop working. Apparently, the problem was that I had removed the Swift files declared in the app extension from my main app's target. Probably when I first added the App Intents Extension I had noticed that adding the extension's source files to the main target made it work, but later thought that it shouldn't be necessary and didn't test if it still worked. Today I created an empty project with a new App Intents Extension and confirmed that the Shortcuts app was correctly showing the parameter popup suggestions, even without including the extension's source files in the main target. Then during the course of almost an entire day I gradually reduced my original Xcode project to this new sample project to find what else would make the extension work, other than including the extension's source files in the main target. My very last resource was changing the bundle identifier, which solved the issue. My original project's targets have identifiers like org.domain.OriginalApp and org.domain.OriginalApp.AppIntent, while the sample project's targets have identifiers like org.domain.SampleApp and org.domain.SampleApp.AppIntent. How could including the App Intents Extension's source files in the main target or changing the bundle identifiers cause the Shortcuts app to correctly show the parameter popup suggestions?
Replies
0
Boosts
0
Views
939
Activity
Feb ’24
Time modified is reset to midnight for some files on FTP server
Every now and then I notice that the date modified of some files on my FTP server has slightly changed by setting the time to midnight. I notice this because I regularly sync the files from the FTP server to a folder on my Mac by comparing the date modified, and it happens every now and then that files that weren’t modified are listed for syncing. For many months after their creation, the time is correct and they are only synced once, but at some point the time is displayed as midnight for some reason and they are suddenly marked for syncing. The wrong time is reported both programmatically as well as in the Finder. The same files are displayed with what seems to be the correct time modified in the app Cyberduck. In this case it seems to be exactly 6 months old files. I didn't check this for the other files that this issue happened with in the past, but it could be about the same timeframe. Is this an issue with macOS? Is there a workaround? I already filed feedback FB13671336.
Replies
0
Boosts
0
Views
484
Activity
Mar ’24
URL.checkResourceIsReachable() throws error if file is on FTP server and name contains special characters
I have a file named ä.txt (with German umlaut) on my FTP server. I select it like this: let openPanel = NSOpenPanel() openPanel.runModal() let source = openPanel.urls[0] Running this code unexpectedly throws an error: do { print(try source.checkResourceIsReachable()) } catch { print(error) // Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory” } Manipulating the URL also seems to change the underlying characters: print(source) // file:///Volumes/abc.com/httpdocs/%C3%A4.txt print(URL(fileURLWithPath: source.path)) // file:///Volumes/abc.com/httpdocs/a%CC%88.txt Note that both variants of the URL above also throw the same error when running URL.checkResourceIsReachable(). If I download the file to my Mac, then both variants print file:///Users/me/Downloads/a%CC%88.txt and neither of them throws an error when running URL.checkResourceIsReachable(). What is the problem? How can I correctly access this file on the FTP server?
Replies
6
Boosts
0
Views
1k
Activity
Apr ’24
Select directory in SwiftUI file importer
I want to show a file importer that allows to select both regular files as well as directories. When running the following code on iOS, I can tap a PDF file and the file importer closes as expected, but when tapping a directory, the file importer shows its contents. How can I instead select that directory and close the file importer? The navigation bar shows a Cancel button, but no Open button. struct FileView: View { @State private var showFileImporter = false var body: some View { ScrollView { VStack(alignment: .leading) { VStack(alignment: .center) { Button("Open") { showFileImporter = true } } } } .fileImporter(isPresented: $showFileImporter, allowedContentTypes: [.pdf, .directory], onCompletion: { result in // TODO }) } }
Replies
3
Boosts
0
Views
2.7k
Activity
Apr ’24
App Store Connect API randomly returns error "An unexpected error occurred on the server side" or "The request timed out"
I use the App Store Connect API to run many parallel requests to update different parts of a single app. I am randomly getting errors such as An unexpected error occurred on the server side. or The request timed out. Usually when these errors happen, I can simply run the unsuccessful requests one or two more times and then they succeed. Is there an explanation for this? Is this possibly caused by too many parallel requests? What is the maximum suggested number of parallel requests?
Replies
0
Boosts
0
Views
696
Activity
May ’24
SwiftUI Image(uiImage:) converts colored image to black and white
I have a data object that dynamically changes the UIImage assigned to one of its instance variables, but when showing this image in SwiftUI, it's always black and white. The following sample code shows the difference between the same image, but using first the native constructor Image(systemName:) and then Image(uiImage:). When using AppKit and Image(nsImage:) this issue doesn't happen. import SwiftUI import UIKit struct ContentView: View { @State var object = MyObject() var body: some View { Image(systemName: "exclamationmark.triangle.fill") .symbolRenderingMode(.palette) .foregroundStyle(.white, .yellow) Image(uiImage: object.image) } } class MyObject { var image = UIImage(systemName: "exclamationmark.triangle.fill")! .applyingSymbolConfiguration(.init(paletteColors: [.white, .systemYellow]))! } #Preview { ContentView() }
Replies
0
Boosts
0
Views
649
Activity
May ’24
Xcode UI test cannot tap menu button in form
Apparently UI tests are unable to tap menu buttons but can tap regular buttons inside forms. Earlier today I was able to see in the Simulator that the UI test tries to tap the button by tapping the center of the containing form row, which works for regular buttons, but not for menu buttons. In fact, when trying in the SwiftUI preview in Xcode it seems that menu buttons have to be tapped exactly on top of them, while regular buttons can be tapped anywhere in the form row. (Now I’m not able to see touches performed by the UI test anymore in the Simulator for an unknown reason, even though I have “Show single touches” enabled in the Simulator settings.) How can I open a menu button in a UI test? The UI code: struct ContentView: View { @State private var label1 = "Menu 1" @State private var label2 = "Menu 2" var body: some View { NavigationStack { Form { LabeledContent("Menu 1") { Button(label1) { label1 = "Menu 1 tapped" } .accessibilityIdentifier("menu1") } LabeledContent("Menu 2") { Menu(label2) { Button("Button") { } .accessibilityIdentifier("button") } .accessibilityIdentifier("menu2") } } } } } #Preview { ContentView() } And the test: final class problemUITests: XCTestCase { func testExample() throws { // UI tests must launch the application that they test. let app = XCUIApplication() app.launch() app.collectionViews.element(boundBy: 0).buttons["menu1"].tap() app.collectionViews.element(boundBy: 0).buttons["menu2"].tap() app.collectionViews.element(boundBy: 0).buttons["button"].tap() } }
Replies
2
Boosts
0
Views
727
Activity
Oct ’24
Simulate hardware keyboard button press in Xcode UI test for iPad
XCUIElement has two methods named typeKey(_:modifierFlags:): the first one takes a String as an argument, the second one a XCUIKeyboardKey, which has constants like .downArrow. Now, even if the documentation says that both methods are "Available in macOS and in iPadOS 15 and later", when compiling the UI tests for iOS, the following line app.typeKey(.downArrow, modifierFlags: []) produces a compiler error Type 'String' has no member 'downArrow' Am I missing something or is there a workaround?
Replies
0
Boosts
0
Views
473
Activity
Jul ’24
Xcode downloaded client crash report at libsecinit_appsandbox.c
Among the newest crash reports downloaded by Xcode for my App Store app there is one that contains a single thread, crashed at libsecinit_appsandbox.c, without any trace of my app name. What could possibly cause such a crash? Is this something related with app signing? The app has been on the App Store for a long time and this never happened to me. Thread 0 Crashed: 0 libsystem_secinit.dylib 0x00000001941dcfcc _libsecinit_appsandbox.cold.12 + 60 (libsecinit_appsandbox.c:454) 1 libsystem_secinit.dylib 0x00000001941dbf94 _libsecinit_appsandbox + 1764 (libsecinit_appsandbox.c:454) 2 libsystem_trace.dylib 0x00000001872be598 _os_activity_initiate_impl + 64 (activity.c:131) 3 libsystem_secinit.dylib 0x00000001941db85c _libsecinit_initializer + 80 (libsecinit_initializer.c:17) 4 libSystem.B.dylib 0x00000001941f2664 libSystem_initializer + 280 (init.c:338) 5 dyld 0x00000001871fe608 invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168 (Loader.cpp:2198) 6 dyld 0x000000018723cd6c invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 340 (MachOAnalyzer.cpp:2481) 7 dyld 0x0000000187230400 invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 496 (MachOFile.cpp:1669) 8 dyld 0x00000001871df2fc dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 300 (MachOFile.cpp:1185) 9 dyld 0x000000018722f394 dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 192 (MachOFile.cpp:1628) 10 dyld 0x000000018723c880 dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 516 (MachOAnalyzer.cpp:2448) 11 dyld 0x00000001871fa53c dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 564 (Loader.cpp:2189) 12 dyld 0x00000001872042d4 dyld4::PrebuiltLoader::runInitializers(dyld4::RuntimeState&) const + 44 (PrebuiltLoader.cpp:818) 13 dyld 0x000000018721dcc0 dyld4::APIs::runAllInitializersForMain() + 84 (DyldAPIs.cpp:3889) 14 dyld 0x00000001871e4010 dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3156 (dyldMain.cpp:891) 15 dyld 0x00000001871e2ef4 start + 1868 (dyldMain.cpp:1265) Crash report
Topic: Code Signing SubTopic: General
Replies
2
Boosts
0
Views
629
Activity
Sep ’24
New iPhone 6.9" screenshots with App Store Connect API
The App Store Connect API documentation still doesn't list the new 6.9" iPhone display type: https://developer.apple.com/documentation/appstoreconnectapi/screenshotdisplaytype I already opened a similar topic about the new iPad Pro 13" screenshot type in May (4 months ago), but nobody replied and that screenshot type is still not listed in the specification. I also created a bug report in Feedback Assistant back then, without any reply. Why does it take so long to update the API specification?
Replies
0
Boosts
0
Views
999
Activity
Sep ’24
SKLabelNode keeps jumping back and forth when displaying different numbers with equal number of digits
I'm trying to display a right-aligned timecode in my game. I had expected that digits would all have the same width, but this doesn't seem to be the case in SpriteKit, even though it seems to be the case in AppKit. In SpriteKit, with the default font there is a noticeable difference in width between the digit 1 and the rest (1 is thinner), so whenever displaying a number with the least significant digit 1 all preceding digits shift slightly to the right. This happens even when setting a NSAttributedString with a font that has a fixedAdvance attribute. class GameScene: SKScene { override func didMove(to view: SKView) { let label = SKLabelNode(text: "") view.scene!.addChild(label) // label.horizontalAlignmentMode = .left label.horizontalAlignmentMode = .right var i = 11 Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in label.text = "\(i)" // let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight", .fixedAdvance: 20]), size: 30)! // let paragraphStyle = NSMutableParagraphStyle() // paragraphStyle.alignment = .right // label.attributedText = NSAttributedString(string: "\(i)", attributes: [.font: font, .foregroundColor: SKColor.labelColor, .paragraphStyle: paragraphStyle]) i += 5 } } } With AppKit, when using SpriteKit's default font HelveticaNeue-UltraLight, this issue doesn't exist, regardless whether I set the fixedAdvance font attribute. class ViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-UltraLight"]), size: 30)! // let font = NSFont(descriptor: NSFontDescriptor(fontAttributes: [.name: "HelveticaNeue-Light", .fixedAdvance: 20]), size: 30)! let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.alignment = .right let textField = NSTextField(labelWithString: "") textField.font = font textField.alignment = .right // textField.alignment = .left textField.frame = CGRect(x: 100, y: 100, width: 100, height: 100) view.addSubview(textField) var i = 11 Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in textField.stringValue = "\(i)" // textField.attributedStringValue = NSAttributedString(string: "\(i)", attributes: [.font: font, .paragraphStyle: paragraphStyle]) i += 5 } } } Is there a solution to this problem? I filed FB15553700.
Replies
0
Boosts
0
Views
659
Activity
Oct ’24
Xcode UI test always fails to tap close button in font picker navigation bar
I'm trying to close the UIFontPickerViewController in a UI test by tapping the close button in the navigation bar. In a default iOS app, I embed the default storyboard view controller in a navigation view controller, then in code I open the font picker like this: class ViewController: UIViewController, UIFontPickerViewControllerDelegate { override func viewDidAppear(_ animated: Bool) { let picker = UIFontPickerViewController(configuration: .init()) picker.delegate = self self.present(picker, animated: true) } func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) { } } And the UI test looks like this: final class problemUITests: XCTestCase { @MainActor func testExample() throws { let app = XCUIApplication() app.launch() sleep(2) let button = app.navigationBars.element(boundBy: 1).buttons.element(boundBy: 0) print(button.debugDescription) XCTAssert(button.waitForExistence(timeout: 2)) button.tap() } } When running the UI test, the XCTAssert always fails and the button.tap() also fails with an error message Failed to tap "chiudi" Button: No matches found for Element at index 1 from input {( NavigationBar )} "chiudi" means "close" in Italian, my macOS system language. It sounds to me like I correctly get the close button, but the messages Xcode prints make no sense to me. It's particularly confusing given that the output of the print statement shows that the button is there, but somehow fails to be tapped: Attributes: Button, 0x104f44660, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Element subtree: →Button, 0x104f44660, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Image, 0x104f44780, {{709.0, 327.5}, {30.0, 30.0}}, identifier: 'UICloseButtonBackground' Path to element: →Application, 0x104f35940, pid: 29803, label: 'problem' ↳Window (Main), 0x104f376a0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f42e10, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43100, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43220, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43340, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43460, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43580, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f436a0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f437c0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f438e0, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43a00, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43b20, {{0.0, 0.0}, {1032.0, 1376.0}} ↳Other, 0x104f43c40, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f43e80, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f43fa0, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f440c0, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f441e0, {{276.0, 314.0}, {480.0, 748.0}} ↳Other, 0x104f44300, {{276.0, 314.0}, {480.0, 748.0}} ↳NavigationBar, 0x104f44420, {{276.0, 314.0}, {480.0, 108.0}}, identifier: 'Scegli font' ↳Button, 0x104f44660, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Query chain: →Find: Target Application 'org.desairem.problem' Output: { Application, 0x104f781b0, pid: 29803, label: 'problem' } ↪︎Find: Descendants matching type NavigationBar Output: { NavigationBar, 0x10607c0d0, {{0.0, 24.0}, {1032.0, 50.0}}, identifier: 'UIFontPickerView' NavigationBar, 0x10607dab0, {{276.0, 314.0}, {480.0, 108.0}}, identifier: 'Scegli font' } ↪︎Find: Element at index 1 Output: { NavigationBar, 0x1064693a0, {{276.0, 314.0}, {480.0, 108.0}}, identifier: 'Scegli font' } ↪︎Find: Descendants matching type Button Output: { Button, 0x104f714a0, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' Button, 0x104f71800, {{711.0, 378.0}, {17.0, 22.0}}, identifier: 'Dictate', label: 'Avvia dettatura' } ↪︎Find: Element at index 0 Output: { Button, 0x104f5d5e0, {{701.0, 320.0}, {43.0, 44.0}}, label: 'chiudi' }
Replies
0
Boosts
0
Views
462
Activity
Oct ’24
How to correctly resolve URL bookmarks for use in Simulator UI tests?
To make UI testing easier and faster, I usually create URL bookmarks during normal app usage in the Simulator so that they can be instantly resolved on app launch during UI tests. For example, one of my apps allows browsing selected folders and stores bookmarks so they can be quickly opened again on following app launches, and instead of selecting the test folder each time at the beginning of the UI test, I select it once during normal app usage so that it's available immediately during the UI test. This usually works fine, but every now and then the UI tests fail because the tested app isn't able to resolve the stored bookmark. I don't know why this happens, but usually opening and closing the app again in the Simulator and re-running the UI tests solves the issue. The problem now is that I've just tried to setup some new UI tests for Apple Vision Pro Simulator and I'm never able to resolve bookmarks. So I created a sample project that reproduces the issue, and curiously enough the bookmarks don't even resolve when using an iPad Simulator (which usually works fine with my regular UI tests). What am I doing wrong? This can be reproduced with a default iOS project, embedding the default storyboard view controller in a navigation view controller, and this code: import UIKit class ViewController: UIViewController, UIDocumentPickerDelegate { override func viewDidLoad() { navigationItem.rightBarButtonItem = UIBarButtonItem(systemItem: .add, primaryAction: UIAction(handler: { _ in let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder]) picker.delegate = self self.present(picker, animated: true) })) if let bookmark = UserDefaults.standard.data(forKey: "bookmark") { readBookmark(bookmark) } } func readBookmark(_ bookmark: Data) { do { let label = UILabel(frame: CGRect(x: 100, y: 100, width: 600, height: 100)) label.numberOfLines = 0 var stale = false let url = try URL(resolvingBookmarkData: bookmark, bookmarkDataIsStale: &stale) if !url.startAccessingSecurityScopedResource() { fatalError() } label.text = url.path view.addSubview(label) } catch { fatalError(error.localizedDescription) } } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { do { let url = urls[0] if !url.startAccessingSecurityScopedResource() { fatalError() } let bookmark = try url.bookmarkData() UserDefaults.standard.set(bookmark, forKey: "bookmark") readBookmark(bookmark) } catch { fatalError(error.localizedDescription) } } } And a default UI test, which always crashes because of the fatalError() in the catch clause of readBookmark(_:): final class problemUITests: XCTestCase { @MainActor func testExample() throws { let app = XCUIApplication() app.launch() } }
Replies
0
Boosts
0
Views
462
Activity
Oct ’24
How to get GKMatch instance after accepting GKInvite?
In my SceneKit game I'm able to connect two players with GKMatchmakerViewController. Now I want to support the scenario where one of them disconnects and wants to reconnect. I tried to do this with this code: nonisolated public func match(_ match: GKMatch, player: GKPlayer, didChange state: GKPlayerConnectionState) { Task { @MainActor in switch state { case .connected: break case .disconnected, .unknown: let matchRequest = GKMatchRequest() matchRequest.recipients = [player] do { try await GKMatchmaker.shared().addPlayers(to: match, matchRequest: matchRequest) } catch { } @unknown default: break } } } nonisolated public func player(_ player: GKPlayer, didAccept invite: GKInvite) { guard let viewController = GKMatchmakerViewController(invite: invite) else { return } viewController.matchmakerDelegate = self present(viewController) } But after presenting the view controller with GKMatchmakerViewController(invite:), nothing else happens. I would expect matchmakerViewController(_:didFind:) to be called, or how would I get an instance of GKMatch? Here is the code I use to reproduce the issue, and below the reproduction steps. Code Run the attached project on an iPad and a Mac simultaneously. On both devices, tap the ship to connect to GameCenter. Create an automatched match by tapping the rightmost icon on both devices. When the two devices are matched, on iPad close the dialog and tap on the ship to disconnect from GameCenter. Wait some time until the Mac detects the disconnect and automatically sends an invitation to join again. When the notification arrives on the iPad, tap it, then tap the ship to connect to GameCenter again. The iPad receives the call player(_:didAccept:), but nothing else, so there’s no way to get a GKMatch instance again.
Replies
6
Boosts
0
Views
915
Activity
Apr ’25
SKNode.zPosition causes nodes to flicker by reordering them for 1 frame
When running the sample code below, every 3 seconds the middle sprite is replaced by a new one. When this happens, most of the time a flicker is noticeable. When recording the screen and stepping through the recording frame by frame, I noticed that the flicker is caused by a temporary reordering of the nodes’. Below you find two screenshots of two consecutive frames where the reordering is clearly visible. This only happens for a SpriteKit scene used as an overlay for a SceneKit scene. Commenting out buttons.zPosition = 1 or avoiding the fade in/out animations solves the issue. I have created FB15945016. import SceneKit import SpriteKit class GameViewController: NSViewController { let overlay = SKScene() var buttons: SKNode! var previousButton: SKSpriteNode! var nextButton: SKSpriteNode! var pageContainer: SKNode! var pageViews = [SKNode]() var page = 0 override func viewDidLoad() { super.viewDidLoad() let scene = SCNScene(named: "art.scnassets/ship.scn")! let scnView = self.view as! SCNView scnView.scene = scene overlay.anchorPoint = CGPoint(x: 0.5, y: 0.5) scnView.overlaySKScene = overlay buttons = SKNode() buttons.zPosition = 1 overlay.addChild(buttons) previousButton = SKSpriteNode(systemImage: "arrow.uturn.backward.circle") previousButton.position = CGPoint(x: -100, y: 0) buttons.addChild(previousButton) nextButton = SKSpriteNode(systemImage: "arrow.uturn.forward.circle") nextButton.position = CGPoint(x: 100, y: 0) buttons.addChild(nextButton) pageContainer = SKNode() pageViews = [SKSpriteNode(systemImage: "square.and.arrow.up"), SKSpriteNode(systemImage: "eraser")] overlay.addChild(pageContainer) setPage(0) Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { [self] _ in setPage((page + 1) % 2) } } func setPage(_ page: Int) { pageViews[self.page].run(.sequence([ .fadeOut(withDuration: 0.2), .removeFromParent() ]), withKey: "fade") self.page = page let pageView = pageViews[page] pageView.alpha = 0 pageView.run(.fadeIn(withDuration: 0.2), withKey: "fade") pageContainer.addChild(pageView) } override func viewDidLayout() { overlay.size = view.frame.size } } extension SKSpriteNode { public convenience init(systemImage: String) { self.init() let width = 100.0 let image = NSImage(systemSymbolName: systemImage, accessibilityDescription: nil)!.withSymbolConfiguration(.init(hierarchicalColor: NSColor.black))! let scale = NSScreen.main!.backingScaleFactor image.size = CGSize(width: width * scale, height: width / image.size.width * image.size.height * scale) texture = SKTexture(image: image) size = CGSize(width: width, height: width / image.size.width * image.size.height) } }
Replies
1
Boosts
0
Views
725
Activity
Nov ’24