I wrote this in Xcode 8 Beta 6 (8S201h):
guard let faceMembers = NSFontManager.shared().availableMembers(ofFontFamily: familyName ?? fontName) else { return nil }
And it worked just fine. Now that I've upgraded to Xcode 8 GM Seed (8A218a) Xcode 8 (8A218a), it crashes ( EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
).
Using the debugger to narrow it down, I found that something in NSFontManager.availableMembers(ofFontFamily:)
really hates this, since it crashes no matter what I put in there, even with common (definitely installed!) fonts like Helvetica Neue.
(lldb) po NSFontManager.shared()
<NSFontManager: 0x6100000a24c0>
(lldb) po familyName
▿ Optional<String>
- some : "Helvetica Neue"
(lldb) po fontName
"HelveticaNeue"
(lldb) po NSFontManager.shared().availableMembers(ofFontFamily: familyName ?? fontName)
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been returned to the state before expression evaluation.
(lldb) po NSFontManager.shared().availableMembers(ofFontFamily: familyName!)
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
The process has been returned to the state before expression evaluation.
(lldb) po NSFontManager.shared().availableMembers(ofFontFamily: "Not a real font?!")
nil
So when I pass it a valid font family name, it crashes... but when I pass it a fake one, it returns nil
.
Is this a problem I can solve, or just an issue with Xcode 8 GM Seed Xcode 8 which will be solved in an SDK update?
After looking through the crash log, I saw this suspiciousness:
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libswiftFoundation.dylib 0x0000000107cbb249 _TZFE10FoundationSa26_forceBridgeFromObjectiveCfTCSo7NSArray6resultRGSqGSax___T_ + 153
1 libswiftCore.dylib 0x00000001079031f3 swift_dynamicCast + 1635
2 libswiftCore.dylib 0x000000010790448b _dynamicCastFromExistential(swift::OpaqueValue*, swift::OpaqueValue*, swift::TargetExistentialTypeMetadata<swift::InProcess> const*, swift::TargetMetadata<swift::InProcess> const*, swift::DynamicCastFlags) + 91
3 libswiftCore.dylib 0x0000000107903919 swift_dynamicCast + 3465
4 libswiftFoundation.dylib 0x0000000107d6a348 _TPA__TFFs15_arrayForceCastu0_rFGSax_GSaq__U_FQ_Q0_ + 56
5 libswiftFoundation.dylib 0x0000000107cbbc45 _TFEsPs10Collection3mapurfzFzWx8Iterator7Element_qd__GSaqd___ + 885
6 libswiftFoundation.dylib 0x0000000107cbb4c3 _TFs15_arrayForceCastu0_rFGSax_GSaq__ + 227
7 libswiftFoundation.dylib 0x0000000107cbb7a5 _TZFE10FoundationSa36_unconditionallyBridgeFromObjectiveCfGSqCSo7NSArray_GSax_ + 197
So it seems like it's crashing within Swift-Foundation, in some function called _forceBridgeFromObjectiveC
... not sure if that helps anyone but it does confirm it's within the SDK/runtime.
The only way that I could figure out how to work around this in the meantime was to create a static method in an objective-c class. I then imported the header into my bridging header and called the static method from Swift 3, where it worked fine.
Hope this helps you get through these difficult times!
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
@interface WorkAround : NSObject
+ (NSArray *)typefacesForFontFamily:(NSString *)family;
@end
#import "WorkAround.h"
@implementation WorkAround
/// Returns an array of arrays, or nil, that contain information about
/// each typeface found for the specified font family.
+ (NSArray *)typefacesForFontFamily:(nonnull NSString *)family {
NSFontManager *fontManager = [NSFontManager sharedFontManager];
return [fontManager availableMembersOfFontFamily:family];
}
@end
Joseph E.'s answer is a good starting point. However to get it to work in Swift 3, Xcode 8 (8A218a) I had to approach differently...
Make sure to change the language to (Objective C). Its important that you do it this way, as it appears you cannot directly create an objective cm files? , even though there is an option to do so.
FontManager.h
#import <Cocoa/Cocoa.h>
@interface FontManager : NSFontManager
NS_ASSUME_NONNULL_BEGIN
+ (NSArray *)typefacesForFontFamily:(NSString *)family;
NS_ASSUME_NONNULL_END
@end
FontManager.m
#import "FontManager.h"
@implementation FontManager
NS_ASSUME_NONNULL_BEGIN
+ (NSArray *)typefacesForFontFamily:(nonnull NSString *)family {
NSFontManager *fontManager = [self sharedFontManager];
return [fontManager availableMembersOfFontFamily:family];
}
NS_ASSUME_NONNULL_END
@end
bridging-header.h
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "FontManager.h"
Usage in your Swift 3 project
if let fontMembers = FontManager.typefaces(forFontFamily: "Arial") as? [[Any]] { }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.