While mutable push notifications process properly in a cousin Catalyst/iOS app, my macOS app does not execute methods in its Notification Service extension to mutate notification content before presentation (when app is active, in background, or terminated).
Console logs (below) show the Mac extension is found and launched, but then killed for "sluggish startup". It is the vanilla new file stub with zero dependencies. I'm doubtful this matters, but I am running Monterey RC1/Xcode 13 RC on an M1.
What am I configuring improperly?
Objective
Push a remote notification ~once a month to download a file, locally process user data, and then silence or display a modified alert accordingly.
I've Tried
Per macOS Notification Service Extension not being used
Ensured sandboxed
Enabled "Incoming Connection (Server)" (no difference)
Wipe DerivedData
Run on naive second Mac
Per Notification Service Extension Not working
Rebooting the Mac
Ensuring the deployment target of the app and extension are identical
Received Notification
From application(_ app: NSApplication, didReceiveRemoteNotification userInfo: [String : Any])
["customThing": value, "aps": {
alert = "Push Mutable";
"mutable-content" = 1;
sound = default;
}]
Signing/Capabilities
App Debug/Release
Sanboxed w/ outgoing connections
Hardened runtime
iCloud
Push (certificates)
Automatic signing w/ cert
Notifications Extension
Sandboxed
Hardened runtime
Automatic signing
Extension
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
self.contentHandler = contentHandler
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
if let bestAttemptContent = bestAttemptContent {
bestAttemptContent.title = "[modified]"
bestAttemptContent.body = "Changed"
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
}
Console
Perhaps relevant?
usernoted <IDENTIFIER> Extension will be killed due to sluggish startup error serviceExt com.apple.unc usernoted
LogFromBuildToNotificationPush.txt
Post
Replies
Boosts
Views
Activity
I guess this is more of a public question for Apple: can/will tide data be packaged into WeatherKit? (FB10383248)
Goal
With an MTKView, replicate the gravity of the AVCaptureVideoPreviewLayer or Apple's Camera app. Video device orientation does not change. The camera feed's edges do not budge a pixel, never revealing the screen background. Other on-screen VCs rotate normally.
Observed
Applying Tech QA 1890's transform during viewWillTransition, the MTKView does counter-rotate... BUT that rotation is still uncomfortably visible. The edges of the view come unpinned during the animation, masking some camera pixels and showing a white background set for the VC holding the MTKView.
Question
How can I make those edges stick to screen bounds like a scared clam?
I assume my error is in constraints, but I'm open to being wrong in other ways. :)
View Hierarchy
A tiny camera filter app has an overlay of camera controls (VC #1) atop an MTKView (in VC #2) pinned to the screen's edges.
UINavigationController
│
└─ CameraScreenVC
│
├── CameraControlsVC <- Please rotate subviews
│
└── MetalCameraFeedVC
└── MTKView <- Please no rotation edges
Code
Buildable demo repo
Relevant snippets below.
MetalCameraVC.swift
final class MetalCameraVC: UIViewController {
let mtkView = MTKView() // This VC's only view
/// Called in viewDidAppear
func setupMetal(){
metalDevice = MTLCreateSystemDefaultDevice()
mtkView.device = metalDevice
mtkView.isPaused = true
mtkView.enableSetNeedsDisplay = false
metalCommandQueue = metalDevice.makeCommandQueue()
mtkView.delegate = self
mtkView.framebufferOnly = false
ciContext = CIContext(
mtlDevice: metalDevice,
options: [.workingColorSpace: CGColorSpace(name: CGColorSpace.sRGB)!])
}
...
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
// blank
}
func draw(in mtkview: MTKView) {
image = image.transformed(by: scaleToScreenBounds)
image = image.cropped(to: mtkview.drawableSize.zeroOriginRect())
guard let buffer = metalCommandQueue.makeCommandBuffer(),
let currentDrawable = mtkview.currentDrawable
else { return }
ciContext.render(image,
to: currentDrawable.texture,
commandBuffer: buffer,
bounds: mtkview.drawableSize.zeroOriginRect(),
colorSpace: CGColorSpaceCreateDeviceRGB())
buffer.present(currentDrawable)
buffer.commit()
}
}
extension MetalCameraVC {
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mtkView)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
mtkView.frame = view.frame
if let orientation = AVCaptureVideoOrientation.fromCurrentDeviceOrientation() {
lastOrientation = orientation
}
}
}
+Rotation
/// Apple Technical QA 1890 Prevent View From Rotating
extension MetalCameraVC {
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
mtkView.center = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate { [self] context in
let delta = coordinator.targetTransform
let deltaAngle = atan2(delta.b, delta.a)
var currentAngle = mtkView.layer.value(forKeyPath: "transform.rotation.z") as? CGFloat ?? 0
currentAngle += -1 * deltaAngle + 0.1
mtkView.layer.setValue(currentAngle, forKeyPath: "transform.rotation.z")
} completion: { [self] context in
var rounded = mtkView.transform
rounded.a = round(rounded.a)
rounded.b = round(rounded.b)
rounded.c = round(rounded.c)
rounded.d = round(rounded.d)
mtkView.transform = rounded
}
}
}
That happened on launch this morning. Why? Never seen that before.
Upon trying to upload a new build to App Connect, which worked fine four weeks ago, validate gives an error that:
The product archive package's signature is invalid. Ensure that it is signed with your "3rd Party Mac Developer Installer" certificate.
Uploading gives the same error:
ERROR ITMS-90237: "The product archive package's signature is invalid. Ensure that it is signed with your "3rd Party Mac Developer Installer" certificate."
This is despite updating in my developer profile and downloading the new "3rd Party Mac Developer Installer" certificate, which shows up as valid in my keychain. So confused...
In a collapsible sidebar's toolbar area, I'd like to have icons available when it is open... similar to Xcode and many apps. However, every SwiftUI .toolbar positioning parameter I've tried only affects the main toolbar. Any tips?
Bonus: positioning the sidebar open/close button inside the sidebar toolbar when open and moving it to the main toolbar's leftmost side when not.
While function calls work as expected in an NSViewRepresentable, I'm unclear why the parameter passed below (to show a SwiftUI popup) does not update after a function call in the NSView. Any tips?
Some View Struct
@State var showMenu = false
var body: some View {
	 ZStack {
	 RightClickableSwiftUIView(onClick: $showMenu)
	 Image(name)
	 .popover(isPresented: $showMenu, ...)
	 }
}
RightClick.swift
import SwiftUI
struct RightClickableSwiftUIView: NSViewRepresentable {
		@Binding var onClick: Bool
		func updateNSView(_ nsView: RightClickableView, context: NSViewRepresentableContext<RightClickableSwiftUIView>) {
		}
		func makeNSView(context: Context) -> RightClickableView {
				RightClickableView(onClick: $onClick)
		}
		class RightClickableView : NSView {
				required init?(coder: NSCoder) {
						fatalError("init(coder:) has not been implemented")
				}
				init(onClick: Binding<Bool>) {
						_onClick = onClick
						super.init(frame: NSRect())
				}
				@Binding var onClick: Bool
				override func mouseDown(with theEvent: NSEvent) {
						print("left mouse")
				}
				override func rightMouseDown(with theEvent: NSEvent) {
						onClick = true
						print("right mouse")
				}
		}
}
This is related to another user's post:
https://developer.apple.com/forums/thread/127921
Images cannot be linked here, but the code below produces buttons that are inset differently than all auto-generated MacOS buttons. Keyboard modifiers are white, rather than gray.
Dividers also don't work properly, nor accept color changes, nor it is reliable to sort in a Rectangle. Spacer() is selectable, but at least an empty Text("") is not.
Why?
struct AppMenus: Commands {
		func some() {}
		@CommandsBuilder
		var body: some Commands {
				CommandMenu("Why") {
						Button("So") { some() }
								.keyboardShortcut(
KeyEquivalent("s"),
modifiers: .command)
						Text(" ")
Spacer()
Divider()
.foregroundColor(Color(.doesntCare))
						Button("Sad") { some() }
								.keyboardShortcut(
KeyEquivalent("s"),
modifiers: .command)
}
				CommandGroup(before: CommandGroupPlacement.sidebar) {
						Button("Why my button so sad?") {}
								.keyboardShortcut(KeyEquivalent("?"), modifiers: .command)
				}
		}
}
Storyboard menus connect to functions anywhere. SwiftUI menus can access functions in view models instantiated in the App struct, but not anything later because .commands only returns a Scene.
Is that a missing feature or am I missing something?
My ProblemHow do I return a cgImage from QLThumbnailGenerator.shared.generateRepresentations(for: ... )?My Hangups/Attempt (Code Below)My attempt has a function (line 13) that returns a CGImage for a ListView entry (line 39). I tried to use an empty CGImage (line 18), but its init parameters are confusing and seem excessive for a simple thumbnail. I'm likely going about this incorrectly.NotesCGImage is used so the same code can work for both iOS and MacOS Catalyst. (Saw this in WWDC 2019 session 719.)I tried but failed to glean an answer from:
https://developer.apple.com/documentation/quicklookthumbnailing/creating_quick_look_thumbnails_to_preview_files_in_your_apphttps://forums.developer.apple.com/message/375807App overview: This is a public service app that searches a specified library of PDFs for an array of terms for misidentified biomaterials, helping biologists screen for potentially false results in every paper they've read. It will be free.Apology/Gratitude:Thanks for your patience, as the answer is likely obvious and my vocabulary likely off. My dumb brain tunes out lectures without a little hands-on experience, so I started this first app from zero programming knowledge as a quarantine hobby. I'll return to the theory lectures after.Code: ResultsView.swiftimport SwiftUI
import MobileCoreServices
import Combine
import QuickLookThumbnailing
import CoreImage
import UIKit
struct ResultsView: View {
@EnvironmentObject var parsedScreeningData: ParsedScreeningData
@EnvironmentObject var search: Search
func generateThumbnail(ofThis: String) -> CGImage {
let url = self.search.libraryFolder.appendingPathComponent(ofThis)
let size: CGSize = CGSize(width: 68, height: 88)
let request = QLThumbnailGenerator.Request(fileAt: url, size: size, scale: (UIScreen.main.scale), representationTypes: .all)
let generator = QLThumbnailGenerator.shared
var image = CGImage()
generator.generateRepresentations(for: request) { (thumbnail, type, error) in
DispatchQueue.main.async {
if thumbnail == nil || error != nil {
assert(false, "Thumbnail failed to generate")
} else {
image = thumbnail!.cgImage
}
}
}
return image
}
var body: some View {
VStack{
List(search.searchResults) { datum in
HStack {
Image(self.generateThumbnail(ofThis: datum.PDFname), scale: (UIScreen.main.scale), label: Text("PDF"))
Text("File: \(datum.PDFname)")
Text("Cell line: \(self.parsedScreeningData.parsedScreeningData[datum.termFoundIndex].misidentifiedCellLine)")
.padding(.trailing, 10)
.padding(.leading, 10)
Spacer()
Image(systemName: "eyeglasses").foregroundColor(ColorManager.iconGreen)
// .onTapGesture {
// let url = URL.init(string: "\(datum.termFoundIndex)")
// guard let thisAddress = url, UIApplication.shared.canOpenURL(thisAddress) else { return }
// UIApplication.shared.open(thisAddress)
} // HStack
} // List
}// Vstack
.colorMultiply(ColorManager.beigeMedium)
.padding(.trailing, 0)
.padding(.leading, 0)
.listStyle(GroupedListStyle())
} // body
} // ResultsView struct
struct ResultsView_Previews: PreviewProvider {
static var previews: some View {
ResultsView().environmentObject(ParsedScreeningData()).environmentObject(Search())
}
}
The pushed view temporarily renders its contents with space for the hidden navigationBar immediately upon transition. How do I fix this behavior?Barebones demo:ContentView.swiftimport SwiftUI
struct ContentView: View {
@State var goToNextView = false
var body: some View {
NavigationView { ZStack {
Color.yellow.edgesIgnoringSafeArea(.all)
NavigationLink(destination: SecondView(), isActive: $goToNextView) {Text("")}
.navigationBarTitle("")
.navigationBarHidden(true)
.navigationBarBackButtonHidden(true)
VStack {
Button(action: {
print("Button clicked")
self.goToNextView = true
}) { Text("Go to second view") }
.padding()
Text("This is the first view.")
}
}
.foregroundColor(Color.blue)
}
}
}SecondView.swiftimport SwiftUI
struct SecondView: View {
var body: some View {
ZStack {
Color.purple
.edgesIgnoringSafeArea(.all)
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
Text("Pushed view")
}
.foregroundColor(Color.white)
}
}