Post

Replies

Boosts

Views

Activity

Reply to How to find freeze that doesn't happen to me?
This might be a bug in iOS 16/17 (all the users reporting the problem were on iOS 16.x). When I was running iOS 16 on my sim and using the iOS 16-compatible version of Xcode, the freeze was never happening to me. iOS 17 got released along with a new version of Xcode, so I updated my Xcode to the new version (I was using the latest one before this as well). Once I did that, I started getting the UI to freeze up for me in the simulator! Woo! Now that it was happening to me, I was able to run Instruments and track the stacktrace where the UI was locking up more precisely. I got a slightly different stacktrace which referenced a UICalendarView before the UICollectionView stack frames. (I forgot to capture a copy to paste here.) I wasn't familiar with UICalendarView but it appears to be a new replacement for UIDatePicker. I finally found that viewWillLayoutSubviews was being called over and over, forever. I wasn't changing the size of anything in my UI during those calls... but deep in the iOS code, it was. Something in the UICalendarView code was making some kind of layout change every time layoutSubviews was called, so the system would call viewWillLayoutSubviews again and again, forever because something kept changing. My UIDatePicker would start off-screen in my storyboard layout and then when it was needed, I would change the constraints in my layout to be smaller and that would cause the UIDatePicker to slide onto the screen. The only workaround that I found to avoid this weird iOS bug, was to remove the UIDatePicker from my storyboard and then create one in code instead. And instead of doing it during initial creation of the view, I deferred the creation until just before I was going to show the UIDatePicker. And then whatever tickled the weird bug in UICalendarView just didn't happen.
Sep โ€™23
Reply to How to find freeze that doesn't happen to me?
The other threads, for reference: Fatal error: ๐Ÿ‘ฎ Main thread was blocked for 2.00s ๐Ÿ‘ฎ Crashed: WatchDog 0 libswiftCore.dylib 0x373e4 _assertionFailure(_:_:file:line:flags:) + 264 1 Watchdog 0x8328 $s8WatchdogAAC9threshold10strictModeABSd_SbtcfcyycfU_TA + 204 2 Watchdog 0x8348 $sIeg_IeyB_TR + 28 3 Watchdog 0x8c1c $s8Watchdog10PingThread33_F0B684E1C455B9DE2999D4562E567E94LLC4mainyyF + 816 4 Watchdog 0x8c3c $s8Watchdog10PingThread33_F0B684E1C455B9DE2999D4562E567E94LLC4mainyyFTo + 28 5 Foundation 0x5b524 __NSThread__start__ + 716 6 libsystem_pthread.dylib 0x16b8 _pthread_start + 148 7 libsystem_pthread.dylib 0xb88 thread_start + 8 com.apple.uikit.eventfetch-thread 0 libsystem_kernel.dylib 0xca4 mach_msg2_trap + 8 1 libsystem_kernel.dylib 0x13b74 mach_msg2_internal + 80 2 libsystem_kernel.dylib 0x13e4c mach_msg_overwrite + 540 3 libsystem_kernel.dylib 0x11e8 mach_msg + 24 4 CoreFoundation 0x79024 __CFRunLoopServiceMachPort + 160 5 CoreFoundation 0x7a250 __CFRunLoopRun + 1208 6 CoreFoundation 0x7f3ec CFRunLoopRunSpecific + 612 7 Foundation 0x41fb4 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] + 212 8 Foundation 0x41e9c -[NSRunLoop(NSRunLoop) runUntilDate:] + 64 9 UIKitCore 0x4cfcc8 -[UIEventFetcher threadMain] + 416 10 Foundation 0x5b524 __NSThread__start__ + 716 11 libsystem_pthread.dylib 0x16b8 _pthread_start + 148 12 libsystem_pthread.dylib 0xb88 thread_start + 8 com.google.firebase.crashlytics.MachExceptionServer 0 HerdBoss 0x1f5b8c FIRCLSProcessRecordAllThreads + 393 (FIRCLSProcess.c:393) 1 HerdBoss 0x1f5f6c FIRCLSProcessRecordAllThreads + 424 (FIRCLSProcess.c:424) 2 HerdBoss 0x1ed440 FIRCLSHandler + 34 (FIRCLSHandler.m:34) 3 HerdBoss 0x1efc18 FIRCLSMachExceptionServer + 521 (FIRCLSMachException.c:521) 4 libsystem_pthread.dylib 0x16b8 _pthread_start + 148 5 libsystem_pthread.dylib 0xb88 thread_start + 8 com.apple.NSURLConnectionLoader 0 libsystem_kernel.dylib 0xca4 mach_msg2_trap + 8 1 libsystem_kernel.dylib 0x13b74 mach_msg2_internal + 80 2 libsystem_kernel.dylib 0x13e4c mach_msg_overwrite + 540 3 libsystem_kernel.dylib 0x11e8 mach_msg + 24 4 CoreFoundation 0x79024 __CFRunLoopServiceMachPort + 160 5 CoreFoundation 0x7a250 __CFRunLoopRun + 1208 6 CoreFoundation 0x7f3ec CFRunLoopRunSpecific + 612 7 CFNetwork 0x2586f0 _CFURLStorageSessionDisableCache + 60900 8 Foundation 0x5b524 __NSThread__start__ + 716 9 libsystem_pthread.dylib 0x16b8 _pthread_start + 148 10 libsystem_pthread.dylib 0xb88 thread_start + 8 Thread #1 0 libsystem_kernel.dylib 0x11ac __workq_kernreturn + 8 1 libsystem_pthread.dylib 0xe28 _pthread_wqthread + 364 2 libsystem_pthread.dylib 0xb7c start_wqthread + 8 Thread #2 0 libsystem_kernel.dylib 0x11ac __workq_kernreturn + 8 1 libsystem_pthread.dylib 0xe28 _pthread_wqthread + 364 2 libsystem_pthread.dylib 0xb7c start_wqthread + 8 Thread #3 0 libsystem_kernel.dylib 0x11ac __workq_kernreturn + 8 1 libsystem_pthread.dylib 0xe28 _pthread_wqthread + 364 2 libsystem_pthread.dylib 0xb7c start_wqthread + 8 Thread #4 0 libsystem_kernel.dylib 0x11ac __workq_kernreturn + 8 1 libsystem_pthread.dylib 0xe28 _pthread_wqthread + 364 2 libsystem_pthread.dylib 0xb7c start_wqthread + 8 Thread #5 0 libsystem_kernel.dylib 0x11ac __workq_kernreturn + 8 1 libsystem_pthread.dylib 0xe28 _pthread_wqthread + 364 2 libsystem_pthread.dylib 0xb7c start_wqthread + 8 Thread #6 0 libsystem_kernel.dylib 0x11ac __workq_kernreturn + 8 1 libsystem_pthread.dylib 0xe28 _pthread_wqthread + 364 2 libsystem_pthread.dylib 0xb7c start_wqthread + 8
Sep โ€™23
Reply to How to find freeze that doesn't happen to me?
Thanks @daniil_berlin, that's a great tool! I used it for this and I'll definitely be using it in the future. The sad part is that when I got the Firebase crash report for the Watchdog, literally every thread is running in Apple code, not my code. I can't see anything that would be causing my freeze/deadlock. The main thread seems to be doing some UICollectionView code but I don't even -have- a UICollectionView in my app. Any idea where this could be or what is happening? Here is what it shows: com.apple.main-thread 0 libobjc.A.dylib 0xa6d0 objc_alloc_init + 36 1 UIKitCore 0x14e7f8 +[UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:] + 24 2 UIKitCore 0x14e0a0 -[_UICollectionCompositionalLayoutSolver _createAndCacheLayoutAttributesForItemAtIndexPath:frame:zIndex:] + 152 3 UIKitCore 0x13a04 __85-[_UICollectionCompositionalLayoutSolver _layoutAttributesForElementsInRect:handler:]_block_invoke + 944 4 UIKitCore 0x6b799c __85-[_UICollectionCompositionalLayoutSolver _layoutAttributesForElementsInRect:handler:]_block_invoke_2 + 40 5 UIKitCore 0x484278 _UIRTreeContainerNode::enumerateIndexesIntersecting(CGRect const&, bool*, void (unsigned long, bool*) block_pointer) const + 160 6 UIKitCore 0x4842b4 _UIRTreeContainerNode::enumerateIndexesIntersecting(CGRect const&, bool*, void (unsigned long, bool*) block_pointer) const + 220 7 UIKitCore 0x40adb0 -[_UIRTree enumerateIndexesForFramesIntersectingFrame:withBlock:] + 52 8 UIKitCore 0x11b14 -[_UICollectionCompositionalLayoutSolver _layoutAttributesForElementsInRect:handler:] + 1092 9 UIKitCore 0x4483a4 -[_UICollectionCompositionalLayoutSolver layoutAttributesForElementsInRect:] + 172 10 UIKitCore 0x1321c -[UICollectionViewCompositionalLayout layoutAttributesForElementsInRect:] + 112 11 UIKitCore 0x12840 __45-[UICollectionViewData validateLayoutInRect:]_block_invoke + 136 12 UIKitCore 0x43ce3c -[UICollectionViewData validateLayoutInRect:] + 1252 13 UIKitCore 0x2878c -[UICollectionView layoutSubviews] + 220 14 UIKitCore 0x4420 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1992 15 QuartzCore 0x9f30 CA::Layer::layout_if_needed(CA::Transaction*) + 500 16 QuartzCore 0x1d4ac CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 148 17 QuartzCore 0x2e8d8 CA::Context::commit_transaction(CA::Transaction*, double, double*) + 444 18 QuartzCore 0x5de80 CA::Transaction::commit() + 648 19 UIKitCore 0x4fd4f0 _UIApplicationFlushCATransaction + 84 20 UIKitCore 0x64dd94 _UIUpdateSequenceRun + 84 21 UIKitCore 0xcb2894 schedulerStepScheduledMainSection + 144 22 UIKitCore 0xcb1df0 runloopSourceCallback + 92 23 CoreFoundation 0xd3128 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28 24 CoreFoundation 0xdf7b4 __CFRunLoopDoSource0 + 176 25 CoreFoundation 0x645e8 __CFRunLoopDoSources0 + 244 26 CoreFoundation 0x7a0d4 __CFRunLoopRun + 828 27 CoreFoundation 0x7f3ec CFRunLoopRunSpecific + 612 28 GraphicsServices 0x135c GSEventRunModal + 164 29 UIKitCore 0x39cf58 -[UIApplication _run] + 888 30 UIKitCore 0x39cbbc UIApplicationMain + 340 31 HerdBoss 0xced0 main + 16 (main.m:16) 32 ??? 0x1dd1b0dec (Missing)
Sep โ€™23
Reply to Auto-Renew of Subscription stops after 12 renews, how to allow more autorenew in TestFlight?
It will allow you to subscribe again, it just takes a while. It calculates how many periods you have in the last so many hours, and if that value is 12 or greater, it will disable auto-renewal for you How many hours? If it limits me to 12 renews in "the last so many hours", how many is that? Because yesterday it stayed broken for the rest of my work day and that means that I get to test for one hour during my work day. It seems like this should be seen as a previous obvious design flaw in the system. Developers don't work for one hour and then stop. First, Sandbox and TestFlight use the same server environment... The differences between the responsiveness in TestFlight and in the Sandbox should be plainly obvious to anyone who uses it, because it can't just be me. :( The AppStore server events that get delivered to my server get delivered from TestFlight in about 5 seconds or so. In the Sandbox, they get delivered about 60 seconds later or sometimes not at all. It's hard for me to believe that TestFlight and Sandbox are really be the same with that kind of difference. And let me tell you that I've tried filing several bugs in the past through the Feedback Assistant and they usually get ignored or X months later I'll get a follow-up question about it that I can't possibly answer because my development environment has X months worth of changes between now and then. So, I'm hesitant to spend that time because I worry that it isn't worth it. But you're one of the Commerce Engineers, so, please tell me what info I need to provide in my post to the Feedback Assistant so that this problem can actually be looked at and you'll know what you need to know and avoid the X-months-later request for info that I can't possibly give anymore. One example of problems we see in the Sandbox environment is that if I have a paid auto-renew subscription and then I turn off auto-renew and then turn auto-renew back on during the same billing period (i.e. no other transactions happen in between), then the info I get back from StoreKit2 will still say their auto-renew is turned off, even though the attempt to turn it back on was successful. It -will- auto-renew at the correct time, but until then, the StoreKit2 response says that it will not. If you can let me know what info I need to put into the ticket to inspect that problem, then I'll file a bug in the FA.
Apr โ€™23
Reply to Deleting transactions for Sandbox purchases?
Looks like they've added the ability to clear purchases for Sandbox Testers (woo!) which is great for me, the developer... but the next step is testing with my testers which I do with TestFlight. TestFlight isn't using Sandbox Tester info, it's using the real Apple Id account info (but not charging them). Is there any way to clear out TestFlight purchase history so that my testers can start from the beginning again and test a first time purchase/subscription?
Nov โ€™22
Reply to How to disable Temporary Tabs in Xcode?
Hi Jason, I don't click files to open them. I use Cmd-Shift-O to open quickly, type the first couple letters and then hit enter. Having to scroll through hundreds of source files in my project is not an efficient way to open files especially when I know the name and can just type a couple of letters of the name and it jumps right to it. I can open them quickly with a few keystrokes and don't have to move my hands off of the keyboard. The other way I navigate to different files is to to Ctrl-Cmd click a function/property name to navigate to that file. Doing a Ctrl-Cmd-Click is already a pain, please don't tell me that I need to contort my hand to do a Ctrl-Option-Cmd-Click in order to return to the standard functionality for tabs. :( How do I want it to work? I'd like it to work the way that editor tabs have worked for as long as editor tabs were a thing in IDEs. When I open a tab, leave the tab open. I'll close it when I'm done. If I navigate to a different file, open that different file in a different tab and don't close my previous tab. I don't see a way to a way to add even more keys to the Open Quickly behavior and I'm not looking forward to having to make a weird claw with my hand in order to hold down even more keys to navigate to a function. It's especially vexing when the file that I'm dealing with is a storyboard, because the storyboard(s) in our apps can get exceedingly large and it takes Xcode a little bit of time to load, parse, and render it. So, it causes Xcode to pause a bit while processing. Then if I try to open a UITableViewCell subclass after that, and it replaces my storyboard tab with the new class... I have to wait for Xcode to load, parse, and render the storyboard again when I need to look at it again 4 seconds later. What I want is to remove the entire concept of Temporary Tabs from Xcode, but I'll settle for the ability to disable them for me. Just leave my tabs alone. You want Xcode to keep replacing your tab? Cool. You do you. But give me the ability to just use normal tabs that I've been using for almost 25+ years. This change to the editor tabs is the main reason I've moved to doing almost all of my development with JetBrains's AppCode instead of using Xcode. But I'm doing something right now that I use to use Xcode for and this problem has returned to my life, so I'm trying to find a way to get Xcode to behave in a way that isn't maddening. You can also select all the files you plan to work on and use File > Open in tab, which opens all of those files into their own tabs. This kind of thing sounds like something you could do when working on a beginner's project or a 1st year CS class project. This is not the kind of thing you can do with even a reasonably complex professional level app that has hundreds and hundreds of .m and/or .swift files. Kenny FB11750631
Nov โ€™22
Reply to How to verify x5c JWT signatures with Firebase's php-jwt, how to decode?
Figured it out with some help on Stack Overflow. The JWT returned by Apple isn't signed with my private key, it is signed with Apple's public key. That public key is embedded in the certificate in the header['x5c'] array. I had to convert that base64-encoded-DER certificate data into PEM format (which just means wrapping it in some text). Then I could extract the public key from that PEM certificate. THEN I could use that to do the verify/decode. list($headerb64, $bodyb64, $cryptob64) = explode('.', $jwt); $headertext = JWT::urlsafeB64Decode($headerb64); $header = JWT::jsonDecode($headertext); $dercertificateb64 = $header->x5c[0]; $wrappedcertificatetext = trim(chunk_split($dercertificateb64, 64)); $certificate = <<<EOD -----BEGIN CERTIFICATE----- $wrappedcertificatetext -----END CERTIFICATE----- EOD; $publickey = openssl_pkey_get_public($certificate); $decoded = JWT::decode($jwt, new Key($publickey, $header->alg)); This isn't the final step though, this just verifies the internal consistency of the first element in the JWT, but doesn't test whether it really comes from Apple. The other elements in the header['x5c'] array are the certificate chain needed to complete the verification of the chain. I haven't figured out how to do that part yet.
Oct โ€™22
Reply to How to format a number(text) in UITextField when user is typing?
I tried out Claude's code and it did mostly a great job, but it had a few serious problems. It works well if the user is only appending characters and only deleting from the end, but if they move the cursor and add/delete from the middle, or select several characters and add/delete replacing a multi-character range, then the code doesn't behave properly and jumps the cursor to the end of the text every time. This code is based on Claude's and is a little more convoluted, but it seems to handle all of the scenarios that I can come up with and it should maintain the user's cursor in the same place that it was before we changed the text in the textfield. My code also limits the fractional portion to a max of 3 digits, but you can customize that if you want. func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { let oldText = textField.text ?? "" let oldTextNS = oldText as NSString let newText = oldTextNS.replacingCharacters(in: range, with: string) formatter.usesGroupingSeparator = true formatter.numberStyle = NumberFormatter.Style.decimal formatter.maximumFractionDigits = 6 var wasBackspace = false if let char = string.cString(using: String.Encoding.utf8) { let compareToBackspace = strcmp(char, "\\b") if (compareToBackspace == -92) { wasBackspace = true } } let strippedNewText = newText.replacingOccurrences(of: formatter.groupingSeparator, with: "") if strippedNewText == "-" { // Accept leading minus return true } if strippedNewText.lengthOfBytes(using: .utf8) == 0 { return true } else { // limit entry of fractional digits to max 3 places. But in order to do that, we need to let it format // to more than 3 digits and then check if it is generating more than 3 digits so that we can tell // the textfield to reject the change. guard let value = Double(strippedNewText) else { // couldn't parse a valid double, so reject the insert. return false } let formattedNumber = formatter.string(from: NSNumber(value: value)) ?? "" let formattedNumberWithoutThousandsGroups = formattedNumber.replacingOccurrences(of: formatter.groupingSeparator, with: "") guard let decimal = Decimal(string: formattedNumberWithoutThousandsGroups) else { // couldn't parse a valid decimal, so reject the insert. return false } // significantFractionalDecimalDigits is a custom extension: // // extension Decimal { // var significantFractionalDecimalDigits: Int { // return max(-exponent, 0) // } // } if decimal.significantFractionalDecimalDigits > 3 { // too many fractional digits, don't let them insert another return false } textField.text = formattedNumber // check the original text, count up the number of grouping characters // we had in the substring before the changed range var numGroupersBeforeChange = 0 var numNumberCharsBeforeChange = 0 var i = 0 for index in oldText.indices { if i == range.location { break } i += 1 let char = oldText[index] if String(char) == formatter.groupingSeparator { numGroupersBeforeChange += 1 } else { numNumberCharsBeforeChange += 1 } } // check the newly formatted text, count up the number of grouping characters // we have spanning the range of the string covered that is analogous to the old text // up to the changed ranged. // for example, if the old text was "123,456,789" and we insert a 0 between the 5 and 6... // the string before the change would be "123,45" and numgroupersbeforechange=1 // the analogous string after the change would be "1,234,5" and numgroupersafterchange=2 var numGroupersAfterChange = 0 var numNumberCharsAfterChange = 0 i = 0 for index in formattedNumber.indices { let char = formattedNumber[index] if String(char) == formatter.groupingSeparator { numGroupersAfterChange += 1 } else { numNumberCharsAfterChange += 1 } if numNumberCharsBeforeChange == numNumberCharsAfterChange { break } i += 1 } let numGroupersAdded = numGroupersAfterChange - numGroupersBeforeChange var cursorOffset = range.location + numGroupersAdded let numCharsAddedByUserChange = wasBackspace ? 0 : string.count cursorOffset += numCharsAddedByUserChange if let newPosition = textField.position(from: textField.beginningOfDocument, offset: cursorOffset) { let newSelectedRange = textField.textRange(from: newPosition, to: newPosition) textField.selectedTextRange = newSelectedRange } // No need to insert the typed char: we've done just above, unless we just typed separator return string == formatter.decimalSeparator } }
Jul โ€™22