@DTS Engineer hanks for your thorough answer. I'm testing the approach in my project, and while it mostly works, I've run into one issue.
Occasionally, after scrolling the text view, viewportLayoutController.viewportRange ends up being nil, which causes the app to crash since the property is force unwrapped. I know I could use guard let or if let to prevent the crash, but that would prevent me from continuing the layout process.
Do you know what might cause viewportRange of NSTextViewportLayoutController to be nil, and how I can ensure it always has a value?
EDIT: I think I've solved this. It seems to be simply due to a pending layout. Calling viewportLayoutController.layoutViewport() prior to accessing viewportRange cause it to have a value. Now, I'm unsure why there's a pending layout since alignViewportToDocumentEndLocationIfNeeded() is called from textViewportLayoutControllerDidLayout(_:) at which point a layout was just completed.
Post
Replies
Boosts
Views
Activity
@hassan313 Thank you for providing an alternative approach. However, based on my testing, this approach is not performant and does not guarantee scrolling to the target location.
The approach presented by hassan313 is similar to one I'm experimenting with using the code below. Here's a brief overview of what the code does:
Calls ensureLayout(_:) but with an empty text range containing only the target location, i.e., the end of the document. This is near instant.
Determines the frame of the target text line fragment.
Scrolls the NSScrollView to make the frame of the target text line fragment visible.
Recursively performs the same steps from step 1 until the frame of the target text line fragment remains unchanged, at which point we're no longer getting an estimated frame.
The code can be generalized to scroll to any location in the document, but for the purpose of this example, it scrolls to the end of the document only.
While this works, it feels hacky to keep scrolling until the frame of the text line fragment stabilizes. I'm thinking there must be a better approach, and I would appreciate if someone from Apple can shed some light on how this is done in NSTextView.
override func moveToEndOfDocument(_ sender: Any?) {
moveToEndOfDocument(previousTextLineFragmentFrame: .null)
}
private func moveToEndOfDocument(previousTextLineFragmentFrame: CGRect) {
let targetLocation = textLayoutManager.documentRange.endLocation
let beforeTargetLocation = textLayoutManager.location(targetLocation, offsetBy: -1)!
let textRange = NSTextRange(location: targetLocation, end: targetLocation)!
textLayoutManager.ensureLayout(for: textRange)
textLayoutManager.textViewportLayoutController.layoutViewport()
guard let textLayoutFragment = textLayoutManager.textLayoutFragment(for: beforeTargetLocation) else {
return
}
guard let textLineFragment = textLayoutFragment.textLineFragment(for: targetLocation, isUpstreamAffinity: true) else {
return
}
let textLayoutFragmentFrame = textLayoutFragment.layoutFragmentFrame
let textLineFragmentFrame = textLineFragment.typographicBounds.offsetBy(dx: 0, dy: textLayoutFragmentFrame.minY)
guard textLineFragmentFrame != previousTextLineFragmentFrame else {
// Scrolling did not affect the layout of the text line fragment, so we do not need to scroll again.
return
}
scrollToVisible(textLineFragmentFrame)
moveToEndOfDocument(previousTextLineFragmentFrame: textLineFragmentFrame)
}
From reading Quinn's guide on the difference when adding app groups to an iOS app and a macOS app, I found that the app group identifier I used in my macOS app wasn't correct. I was using the same app group identifier on macOS as I did my iOS app, i.e. group.com.example.MyApp but on macOS it had to be prefixed with the team ID, like 123XY4Z5X6.group.com.example.MyApp.
After changing the app group identifier in my macOS build, the app is signed and exported correctly when using xcodebuild in my CI/CD pipeline. I'm still unsure why it worked when signing through Xcode and not when signing through xcodebuild when using an app group identifier without the team ID prefix, though.
Could it be that app groups aren't available for Developer ID-signed apps? I suppose that would make sense 🤔
It wouldn't really explain why exporting and signing work in Xcode and not in xcodebuild, but maybe Xcode has some magic to remove the app group entitlement before signing and xcodebuild lacks this logic.
Surprisingly to me, the "Mac Team Provisioning Profile: com.example.MyApp" and "Mac Team Direct Provisioning Profile: com.example.MyApp" provisioning profiles that Xcode creates when signing and exporting the app, does not include the com.apple.security.application-groups entitlement that my app uses.
As a test, I extracted the generated provisioning profiles from ~/Library/MobileDevice/Provisioning Profiles and installed them during my CI/CD pipeline to see if that was sufficient and while xcodebuild did pick up the provisioning profiles, the signing failed with the following error because the provisioning profiles does not contain the entitlement.
Error: error: exportArchive: Cloud signing permission error
Error: error: exportArchive: Provisioning profile "Mac Team Direct Provisioning Profile: com.example.MyApp" doesn't include the com.apple.security.application-groups entitlement.
I got the same issue when uploading a build today with Xcode beta 5. The font have been in the app for over a year.