회사 제품이 원격 관련 제품이라 macOS에서 앱의 [화면 기록] 권한이 필요하다.
업무를 하다가 발생한 이슈인데, 좀 얼탱이 없어서 정리겸 작성한다.
14.3에서도 정상적으로 문제없이 돌아가던 권한 체크 방식이 macOS 14.4 beta 2에서 갑자기 말썽이라는 것이다.
위의 사진과 같이 앱의 화면 기록 권한 (macOS 14부터는 화면 및 시스템 오디오 녹음으로 변경)이 있음에도 불구하고
시스템 OS에서 발생하는 팝업에서 허용을 눌러주지 않으면 제어가 불가능하다는 것이다.
우선 기존 화면 권한을 확인하는 로직은 대부분의 macOS개발자들이 아마 아래와 같이 사용했을것이다.
#import <AVFoundation/AVFoundation.h>
BOOL isScreenRecordingPermissionEnabled() {
NSDictionary *options = @{(__bridge NSString *)kCGDisplayStreamShowCursor: @NO};
CGDisplayStreamRef displayStream = CGDisplayStreamCreate(CGMainDisplayID(), 1, 1, kCVPixelFormatType_32BGRA, (__bridge CFDictionaryRef)options);
CGDisplayStreamStart(displayStream);
CGDisplayStreamStop(displayStream);
BOOL isPermissionEnabled = (CGDisplayStreamIsActive(displayStream) == true);
CGDisplayStreamRelease(displayStream);
return isPermissionEnabled;
}
// 사용 예시
BOOL isPermissionEnabled = [self isScreenRecordingPermissionEnabled];
if (isPermissionEnabled) {
NSLog(@"화면 기록 권한이 활성화되어 있습니다.");
} else {
NSLog(@"화면 기록 권한이 비활성화되어 있습니다.");
}
(뤼튼에게 물어봐)
CGDisplayStream 계열의 API 를 사용하고 있다.
14.0 부터 deprecated 된대서 1차 싸함을 느꼈다.
후 .. 14.4 beta2 Release Note에 해당 api 관련해서 뭐 바뀐것도 없다고 생각했었는데 14.0 release note 보니까
Screen Sharing 관련하여 CGDisplayStream APIs 사용시 추가적인 알림을 표시한다고 Release Note에 명시되어있더라
그럼 14부터 안되야지 왜 14.3까지는 되고 14.4 beta2 에서만 안되는지 이해가 안갔다.
https://developer.apple.com/documentation/macos-release-notes/macos-14-release-notes#ScreenSharing
해당 이슈로 골치아픈게 나뿐만 아니라 분명 다른 나라에도 있을거라고 생각하고 해당 이슈 관련해서 좀 찾아봤는데
진짜로 있다요
https://support.jumpdesktop.com/hc/en-us/community/posts/23796182773133
https://forums.macrumors.com/threads/macos-14-4-beta-1-bug-fixes-changes-and-more.2417788/
일단 나부터 급하니 화면 권한 얻는 방법에대해서 엄청나게 찾아봤다.
https://developer.apple.com/videos/play/wwdc2019/701/?time=1007
WWDC19 에서도 나왔던 얘긴데, 간략히 설명하자면 사용자가 화면 녹화용 앱을 사전 승인하지 않는 한 window name과 공유 상태를 알수 없다.
따라서 아래와 같이 권한을 확인하는 방법도 있다는걸 적어놓도록 하겠슴다
- (BOOL)ScreeningRecordPermissionCheck {
if (@available(macOS 10.15, *)) {
// CGWindowListCopyWindowInfo 함수를 사용하여 현재 화면에 표시되는 창에 대한 정보를 가져와서
// kCGWindowListOptionOnScreenOnly 옵션을 사용하여 화면에만 표시되는 창에 대한 정보만 가져오도록 설정하고, kCGNullWindowID를 사용하여 모든 창을 대상
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSUInteger numberOfWindows = CFArrayGetCount(windowList);
NSUInteger numberOfWindowsWithInfoGet = 0;
for (int idx = 0; idx < numberOfWindows; idx++) {
NSDictionary *windowInfo = (NSDictionary *)CFArrayGetValueAtIndex(windowList, idx);
NSString *windowName = windowInfo[(id)kCGWindowName];
NSNumber* sharingType = windowInfo[(id)kCGWindowSharingState];
// 현재 창의 이름(windowName)이 존재하거나 공유 상태(sharingType)가 kCGWindowSharingNone이 아닌 경우, numberOfWindowsWithInfoGet 변수를 증가
if (windowName || kCGWindowSharingNone != sharingType.intValue) {
numberOfWindowsWithInfoGet++;
} else {
NSNumber* pid = windowInfo[(id)kCGWindowOwnerPID];
NSString* appName = windowInfo[(id)kCGWindowOwnerName];
}
}
CFRelease(windowList);
// 즉 모든 창 정보를 성공적으로 받은 경우에는 YES를 반환
if (numberOfWindows == numberOfWindowsWithInfoGet) {
return YES;
} else {
return NO;
}
}
return YES;
}
추가적으로 CGRequestScreenCaptureAccess() 함수도 필요할 수 있으니 알아서 잘 조합해서 사용하면 될 거 같다.
*화면 권한 사용하는 프로그램 몇개 다운받아서 테스트해봤는데
Google Chrome Remote Desktop은 호스트 단에서 허용 안해주면 화면 안나오고 TeamViewer는 잘되네요. (2024/02/07)