Post

Replies

Boosts

Views

Activity

Reply to Attachment is displayed incorrectly
I've tested various options for custom attachment drawing, and the only one I can get to work consistently on macOS 15.0 Release Candidate is setting the image property of the text attachment. I'm using NSImage.init(size:flipped:drawingHandler:) to do custom drawing, similar to what I was previously doing in NSTextAttachmentCell.draw(withFrame:in:characterIndex:). I also set the bounds of the text attachment to adjust the position of the attachment, which I was previously doing with NSTextAttachmentCell.cellBaselineOffset(). The one thing I haven't been able to accomplish with this approach is changing the appearance of the attachment when it's selected. I also experimented with view-based text attachments, using NSTextAttachmentViewProvider. This worked well in an NSTextView, but in an NSTextField or NSSearchField, the custom view would only show when the text field was first responder. I can't find any clear documentation on how view-based text attachments are meant to be used, so I'm not sure if there's any way to work around that problem. I've filed a bug report on this issue, FB15109146.
Sep ’24
Reply to Use of the Expanded/Condensed SF Font Families in iOS 16
Thanks to the tip from @hacknicity, here's an example in Swift for UIKit: var descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .largeTitle) let traits: [UIFontDescriptor.TraitKey:Any] = [.width: -1.0] descriptor = descriptor.addingAttributes([.traits: traits]) let uiFont = UIFont(descriptor: descriptor, size: descriptor.pointSize) This creates a narrow version of the "large title" text style. According to the header file, the width value should be a float between -1.0 to 1.0. -1.0 will give you a narrow width, 0.0 is a normal width, and 1.0 is wide. There are options between those values—for example 0.25 gave me a width that's just a bit more narrow than normal. It seems there's not a direct equivalent of UIFontDescriptor in SwiftUI. However, you can create a SwiftUI Font from a UIFont. Just add one more line to the example above: let font = Font(uiFont as CTFont) I imagine this will become easier to use in a later beta.
Jun ’22
Reply to When validating a receipt, what is the correct way to find the most recent transaction for an app with a single subscription?
I should have mentioned that this is happening after using the "Restore Purchases" button in my app. This calls the restoreCompletedTransactions() method on the default SKPaymentQueue. I then wait for paymentQueueRestoreCompletedTransactionsFinished() and send the receipt to my server for validation. Afterward I go through the items in transactions where the transactionState is restored or purchased and call finishTransaction() for each one. I did some testing, and it seems this is an important step in what's happening. If I validate the same receipt without restoring anything, I get consistent results. The transaction in latest_receipt_info is still not the most recent transaction—but it is the same transaction each time. When I discovered this, I thought I might be able to solve the problem by sorting the transactions by date before calling finishTransaction(). This doesn't seem to make a difference. It's possible I'm overlooking something in my code but it seems like simply calling restoreCompletedTransactions() jumbles the transactions and results in a random one being considered the "latest" according to the verifyReceipt endpoint. This post on Stack Overflow - https://stackoverflow.com/q/42312927/813247 is the closest I've found to anyone discussing similar behavior. I hadn't noticed it previously but I'm seeing the same thing—restoreCompletedTransactions() does change the transaction IDs. Seems surprising to me, but it's not a problem. Then I noticed that original_purchase_date is also changing—it's the current date and time! The purchase_date is the actual date of the purchase. That's backwards from what I would expect. But this at least makes sense now: according to this date it is the latest transaction. It seems like it's re-running all the transactions in an effectively random order and updating the purchase date as it goes. This still doesn't make sense to me, but I at least feel like I understand what's happening. The sole answer to that Stack Overflow post suggests using SKReceiptRefreshRequest instead of restoreCompletedTransactions(). My understanding was that these do not necessarily do the same thing, and restoreCompletedTransactions() should be done to make sure transactions appear on a new device, or after deleting and reinstalling the app. SKReceiptRefreshRequest is what you should use if the receipt file is invalid or missing. These seem like pretty distinct use cases that are backed up by the current documentation. There's also a reply here from someone at Apple - https://developer.apple.com/forums/thread/127923 suggesting the app won't pass app review if I rely on SKReceiptRefreshRequest for this. I think at this point I'm going to: Assume latest_receipt_info may contain more than one transaction in non-chronological order. I'll loop through each one and look for the latest expires_date_ms. This addresses my first two questions and seems like the safest approach. Assume that latest_receipt_info may not include the most recent purchase and check receipt.in_app as well. It's extra work, but due to the way purchases are restored it seems necessary to show my customers when their subscription expired. For my bonus questions I'm just going to assume I'm looking in the right place for the grace period and that pending_renewal_info should only contain a single item in my case. It shouldn't be hard to adjust these later as necessary. I feel pretty comfortable with this now, but if anyone has relevant knowledge I'd love to hear it.
Aug ’20
Reply to How can I use conditional code for a new OS version while building with the current SDK?
I think @dimitri's answer is correct, and if at all possible it's definitely better to use a compiler check or preprocessor flag - https://stackoverflow.com/questions/38813906/swift-how-to-use-preprocessor-flags-like-if-debug-to-implement-api-keys/47395485#47395485 as a way to prepare in advance for a new SDK. This is safer because APIs can change during betas, and an approach like this will allow the compiler to make sure your code is correct when you build with the newer SDK. In my case I want to improve how a change in functionality is handled in the current version of my app, building with the current SDK. I was leaning toward using NSClassFromString() or responds(to:), both of which are available in Swift. But after thinking it over I don't think it makes sense to use those unless that's exactly what I need to check for—and in my case, it's not. I eventually found an NSHipster article titled "Swift System Version Checking" (which the forum won't allow me to link to for some reason). One of several options it suggests is isOperatingSystemAtLeast(_:) which seems like a good choice for handling a change in behavior rather than a change in API: if ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 14, minorVersion: 0, patchVersion: 0)) { 		print("Seems to be iOS 14!") }
Jul ’20
Reply to Where is the private DTK forum?
For me the tag doesn't show up in the prominent "View all tags >" page linked at the top of every page. I had to click on my avatar in the top-right corner (just below the search icon). Then a popup appears, with a "Developer Forums" section, and a link to "Universal App Quick Start". Of course as @designatednerd said, you'll also need access to the DTK for this to appear. In my case I made sure I'm signed into the same account I used to apply for the program, and to purchase the kit.
Jul ’20
Reply to Universal Purchase Questions (Mac and iOS)
I doubt this will apply to many people, but one update on the App ID Prefix situation: I'm so sure I tried code signing previously and didn't have any issues, but when I tried to code sign last night I got an error that my App ID Prefix is not a valid Team ID for my account. This is technically true since it's not a Team ID at all. I've contacted Apple and I'm waiting for a response. While it's possible there will be some other solution to this, I now strongly suspect I'm going to be forced to change the App ID Prefix to use universal purchase. Here's a document from Apple explaining how that works along with a note that Apple can change it for you. (Of if you're using a wildcard-based App ID, you can do it yourself.)
Mar ’20
Reply to Universal Purchase Questions (Mac and iOS)
This is an assumption based on my own understanding of the situation, but I'm confident you will effectively have a new Mac app that has the same receipt information as your iOS app. Any information from the previous version of your Mac app will be missing from that receipt. If you want to merge anything from your older Mac app I think you'll be entirely on your own. You could potentially accomplish this by getting the receipt from the old app (a bit challenging in a sandboxed app, but possible) and then using your licensing server to tie the two receipts together.
Mar ’20
Reply to What is the best way to coordinate a price change with an app update?
That's a good point, thank you. (And those folks could always ask Apple for a refund, which they would hopefully be granted.)I also have concerns about the opposite problem though. When an app update is released, I know it can take an unpredictable amount of time to propagate. For example, say I release the update, then change the price, and the price change goes into effect before the update propagates everywhere. If a bunch of people download it for free during that time, how does that affect the "original_application_version" in the receipt? I don't know if that propagation time has any connection to the receipt version or not.If a few people get lucky and have a bunch of features unlocked for free, that's fine, no big deal. But without knowing how this works I worry that I'll run into the occasional issue where propagation takes several hours, and potentially hundreds of people download the old version for free. I'd love to know beforehand whether that's a valid concern and if there's anything I can do to minimize it!
Feb ’20