Hi @DTS Engineer!
If found this in the transcript of this video: https://developer.apple.com/videos/play/wwdc2023/10266
"When your app requests a connection to your XPC service, launchd spawns the XPC service and is the parent of that XPC service but your app is "responsible" for that XPC service.
If I am not mistaken, XPC service (with a lower "s") refers to an agent or daemon. Or is it just a spelling mistake and refers to a XPC Service?
Thanks again!
Marc
Post
Replies
Boosts
Views
Activity
Hi @DTS Engineer!
Thanks for your reply. I tried this and it works perfectly. But unfortunately it does not work in a sandboxed environment. Even if our app is not sandboxed at the moment, we plan to sandbox it later this year. So I would be interested in a solution that also works for a sandboxed app.
The only solution I found so far is using attributesOfItemAtPath: to get NSFileOwnerAccountName, NSFileGroupOwnerAccountName, and NSFilePosixPermissions and then check these values for the current user. Is there another way to do this from within a sandbox?
Thanks.
@DTS Engineer
I played around with responsible process constraints but it seems that this was completely wrong. Thanks for your support.
BTW: Could you please explain how to set launch-type to not being 3 ? Seems that there's no not operator.
Regards,
Marc
@DTS Engineer
Tried this, but then the app doesn't launch anymore:
<dict>
<key>launch-type</key>
<dict>
<key>$or</key>
<dict>
<key>$lt</key>
<integer>3</integer>
<key>$gt</key>
<integer>3</integer>
</dict>
</dict>
</dict>
Probably it's the wrong syntax because I get the following error if I launch the app:
AMFI: Launch Constraint Violation (enforcing), error info: c[4]p[1]m[2]e[6], ($or operator disallowed for active fact)
Unfortunately especially the operators are poorly documented. Any idea?
Thanks,
Marc
Thank you, @DTS Engineer. The correct syntax seems to be:
<dict>
<key>$or-array</key>
<array>
<array>
<string>$or</string>
<dict>
<key>launch-type</key>
<dict>
<key>$lt</key>
<integer>3</integer>
</dict>
</dict>
</array>
<array>
<string>$or</string>
<dict>
<key>launch-type</key>
<dict>
<key>$gt</key>
<integer>3</integer>
</dict>
</dict>
</array>
</array>
</dict>
Regards,
Marc
@DTS Engineer
I tried this with Xcode 16 beta 3.
@DTS Engineer
It fails in Xcode 16 beta 3.
Also tried your sample project from here: https://developer.apple.com/documentation/security/constraining_a_tool_s_launch_environment but as soon as I upload it for notarizing the constraints also disappear. So the issue seems not to be related to my project.
FB14511046
@DTS Engineer
I don't think that I misuse NSUserDefaults. In the app the user can save different configurations and they will stored in NSUserDefaults as an array of dictionaries. This works as expected. But now I got the request to allow the user to set one of the configurations as the default configuration. So I just wanted to add an additional key to one of the dictionaries in the array to mark it as the default.
I also tried "mutableArrayForKeyPath" but even if using this, the user defaults are not updated…
@Claude31 This is more or less what I am actually doing but the returned bounding rect seems to have some sort of spacing around the text.
This is how I try to get the font size at the moment but it seems the value is always a bit too small…
- (CGFloat)fontSizeToFitInRect:(NSRect)rect
minimumFontSize:(CGFloat)minFontSize
maximumFontSize:(CGFloat)maxFontSize
{
CGFloat fontSize = (maxFontSize > minFontSize) ? maxFontSize : NSHeight(rect);
CGFloat textHeight = CGFLOAT_MAX;
CGFloat textWidth = CGFLOAT_MAX;
while ((textHeight > NSHeight(rect) || textWidth > NSWidth(rect)) && fontSize >= minFontSize) {
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithAttributedString:self];
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[self font] toSize:--fontSize]
range:NSMakeRange(0, [[self string] length])
];
CGRect usedRect = [attrString boundingRectWithSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)
options:0
context:nil
];
textHeight = ceil(NSHeight(usedRect));
textWidth = ceil(NSWidth(usedRect));
}
return fontSize;
}
For me it looks like boundingRectWithSize:options:context: just uses the attributed string's font (and its attributes) to calculate a bounding rectangle that could hold any combination of characters with the specific font and font size. The height of the bounding rect is more or less the same, regardless of what combinations of characters I use in the string. So the question is how to get the real bounding rect… 🤷🏻♂️
@DTS Engineer
Thank you! This looks very promising. In the sample code it works as expected but if I try to create a NSTextLineFragment from an attributed string,typographicBounds always returns {{0, 0}, {0, 0}}…
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"test"];
[attrString addAttribute:NSFontAttributeName
value:[NSFont systemFontOfSize:90]
range:NSMakeRange(0, [[attrString string] length])
];
NSTextLineFragment *fragment = [[NSTextLineFragment alloc] initWithAttributedString:attrString
range:NSMakeRange(0, [[attrString string] length])
];
CGRect usedRect = [fragment typographicBounds];
@DTS Engineer
Thanks, again. I tried your code but it returns more or less the same rect as boundingRectWithSize:options:context:. So it returns the exact same height for a lower case "x" and an upper case "X":
x -> {{-66, 0}, {132, 303.63671875}}
X -> {{-82.88671875, 0}, {165.7734375, 303.63671875}}
Any idea? Thanks…
@DTS Engineer
Thanks a lot. Seems this is what I was looking for. This allows me to figure out the best font size for a text to fit in a given rect. The only issue still see is related to drawing the string. For whatever reason half of the string is missing:
Here's the code:
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:10] forKey:NSFontAttributeName];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"test" attributes:attributes];
// get the font size to make the string fit into the text view
CGFloat fontSize = [attrString fontSizeToFitInRect:[self bounds]
minimumFontSize:10
maximumFontSize:0
];
// change the attributed string's font size
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[attrString font] toSize:fontSize]
range:NSMakeRange(0, [attrString length])
];
// draw the string
[attrString drawInRect:[self frame]];
}
And here are the extensions I made to the NSAttributedString class to calculate the string's bounds:
- (CGFloat)fontSizeToFitInRect:(NSRect)rect
minimumFontSize:(CGFloat)minFontSize
maximumFontSize:(CGFloat)maxFontSize
{
CGFloat fontSize = (maxFontSize > minFontSize) ? maxFontSize : NSHeight(rect) * 2;
CGFloat textHeight = CGFLOAT_MAX;
CGFloat textWidth = CGFLOAT_MAX;
while ((textHeight > NSHeight(rect) || textWidth > NSWidth(rect)) && fontSize >= minFontSize) {
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithAttributedString:self];
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[self font] toSize:--fontSize]
range:NSMakeRange(0, [[self string] length])
];
CGRect usedRect = [attrString bounds];
textHeight = ceil(NSHeight(usedRect));
textWidth = ceil(NSWidth(usedRect));
}
return fontSize;
}
- (CGRect)bounds
{
CGRect usedRect = CGRectZero;
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self);
NSArray *ctRuns = (__bridge NSArray*)CTLineGetGlyphRuns(line);
for (id ctRun in ctRuns) {
NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((CTRunRef)ctRun);
id font = attributes[(NSString *)kCTFontAttributeName];
if (font) {
size_t runGlyphsCount = CTRunGetGlyphCount((CTRunRef)ctRun);
CGGlyph glyphs[runGlyphsCount];
CTRunGetGlyphs((CTRunRef)ctRun, CFRangeMake(0, 0), glyphs);
CGRect runRects[runGlyphsCount];
CTFontGetBoundingRectsForGlyphs((CTFontRef)font, kCTFontOrientationDefault, glyphs, runRects, runGlyphsCount);
for (size_t i = 0; i < runGlyphsCount; i++) {
CGRect rect = runRects[i];
CGFloat width = usedRect.size.width + rect.size.width;
CGFloat height = MAX(usedRect.size.height, rect.size.height);
CGFloat y = MIN(usedRect.origin.y, rect.origin.y);
usedRect = CGRectMake(usedRect.origin.x, y, width, height);
}
} else {
break;
}
}
return usedRect;
}
The string rect is a bit smaller than the rectangle of the view it should be drawn into. So I wonder why the string is cut off. With upper and lower case characters it looks like this:
Any idea? Thank you very much.
Regards,
Marc