Search code examples
iosswiftfontsnsdatawritetofile

iOS - Download Dynamic Fonts


I am trying to download fonts for my app dynamically, as I will frequently want to add new ones on the fly and don't want to re-submit my app each time.

I have followed some other guides attempting to download the font file from a URL, save it to the app's Documents Directory, then load it using CGFontCreateWithDataProvider & CTFontManagerRegisterGraphicsFont.

However, the app keeps failing the 'writeToFile' function and spits out this line:

println("Failed to save font: \(fontFile)")

I have verified that the 'fontUrl' is valid (copy and pasted from debugger right into my desktop browser).

Also, here is what the value of 'fontPath' ends up being in the debugger and it looks right to me:

fontPath = "/Users/James/Library/Developer/CoreSimulator/Devices/E153E1F4-E3FD-4BCE-A40F-433EADF14591/data/Containers/Data/Application/969A248C-A7B3-4768-86E4-68A6C4E20ECF/Documents/Fonts/american_typewriter_medium_bt.ttf"

Code:

// Download and save font file
        let nsDocumentDirectory = NSSearchPathDirectory.DocumentDirectory
        let nsUserDomainMask = NSSearchPathDomainMask.UserDomainMask
        if let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory, nsUserDomainMask, true) {
            if paths.count > 0 {
                if let documentsDirectory = paths[0] as? String {
                    let fontPath = documentsDirectory.stringByAppendingPathComponent("Fonts/"+fontFile) as String
                    let fontUrl = NSURL(string: ApiUrlString + "fonts/" + fontFile)
                    if let fontData = NSData(contentsOfURL: fontUrl!)
                    {
                        if(!fontData.writeToFile(fontPath, atomically: true))
                        {
                            println("Failed to save font: \(fontFile)")
                            return
                        }
                    }
                    else
                    {
                        println("Failed to download font from URL: \(fontUrl)")
                        return
                    }

                    // Load Font
                    let data: NSData? = NSFileManager.defaultManager().contentsAtPath(fontPath)
                    if (data == nil)
                    {
                        println("Failed to load saved font: \(fontFile)")
                        return
                    }

                    var error: Unmanaged<CFError>?
                    let provider: CGDataProviderRef = CGDataProviderCreateWithCFData(data)
                    let font: CGFontRef = CGFontCreateWithDataProvider(provider)
                    if (!CTFontManagerRegisterGraphicsFont(font, &error))
                    {
                        println("Failed to register font, error: \(error)")
                        return
                    }

                    println("Successfully saved and registered font: \(fontFile)")
                }
            }
        }
        else
        {
            println("Failed to load documents directory: \(fontFile)")
        }

Solution

  • I'm not sure if this will help you, but it might. The site iOS Font List mentions the use of downloadable fonts. Maybe the sample code can lead you in the right direction.

    - (void)asynchronouslySetFontName:(NSString *)fontName toTextView:(UITextView *)textView {
        CGFloat size = 24.0f;
        UIFont *font = [UIFont fontWithName:fontName size:size];
    
        if (font && ([font.fontName compare:fontName] == NSOrderedSame || [font.familyName compare:fontName] == NSOrderedSame)) {
            textView.font = font;
            return;
        }
    
        NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithObject:fontName forKey:kCTFontNameAttribute];
        CTFontDescriptorRef desc = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)attrs);
    
        NSMutableArray *descs = [NSMutableArray array];
        [descs addObject:(__bridge id)desc];
        CFRelease(desc);
    
        __weak UITextView *weakTextView = textView;
    
        CTFontDescriptorMatchFontDescriptorsWithProgressHandler((__bridge CFArrayRef)descs, NULL,  ^(CTFontDescriptorMatchingState state, CFDictionaryRef progressParameter) {
            if (state == kCTFontDescriptorMatchingDidFinish) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    weakTextView.font = [UIFont fontWithName:fontName size:size];
                });
            }
    
            return YES;
        });
    }