Download font .ttf file from web and store on iPhone - objective-c

Is it possible to download .ttf file from web and store it on iPhone. Then use that for for labels and all other stuff ? Because my client want to control fonts from database and don't want to just drop fonts to xcode project right away.
So in future if he wants to change font, he will add new font to database, app will recognize new font on web (thats already done with images, not a problem), download it and use as font.
Thanks.

Actually it is possible to dynamically add fonts to the iOS runtime like this:
NSData *fontData = /* your font-file data */;
CFErrorRef error;
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)inData);
CGFontRef font = CGFontCreateWithDataProvider(provider);
if (! CTFontManagerRegisterGraphicsFont(font, &error)) {
CFStringRef errorDescription = CFErrorCopyDescription(error)
NSLog(#"Failed to load font: %#", errorDescription);
CFRelease(errorDescription);
}
CFRelease(font);
CFRelease(provider);
Source: This Blog Article of Marco Arment.

It is possible. I created an example swift project in github. You have to just add the few line below.
var uiFont : UIFont?
let fontData = data
let dataProvider = CGDataProviderCreateWithCFData(fontData)
let cgFont = CGFontCreateWithDataProvider(dataProvider)
var error: Unmanaged<CFError>?
if !CTFontManagerRegisterGraphicsFont(cgFont, &error)
{
print("Error loading Font!")
} else {
let fontName = CGFontCopyPostScriptName(cgFont)
uiFont = UIFont(name: String(fontName) , size: 30)
}
Github project link

The fonts have to be set in the plist of your app, and that file cannot be changed during runtime, so you need to compile your project with the fonts already added to it.
You'll have to think in other way of implementing it.

You could use FontLabel (https://github.com/vtns/FontLabel) or smth. similar to load ttfs from the file system. I don't think that you can use downloaded fonts with a UILabel. Because you need the plist entries for each font.

Swift 4 solution by extension:
extension UIFont {
/**
A convenient function to create a custom font with downloaded data.
- Parameter data: The local data from the font file.
- Parameter size: Desired size of the custom font.
- Returns: A custom font from the data. `nil` if failure.
*/
class func font(withData data: Data, size: CGFloat) -> UIFont? {
// Convert Data to NSData for convenient conversion.
let nsData = NSData(data: data)
// Convert to CFData and prepare data provider.
guard let cfData = CFDataCreate(kCFAllocatorDefault, nsData.bytes.assumingMemoryBound(to: UInt8.self), nsData.length),
let dataProvider = CGDataProvider(data: cfData),
let cgFont = CGFont(dataProvider) else {
print("Failed to convert data to CGFont.")
return nil
}
// Register the font and create UIFont.
var error: Unmanaged<CFError>?
CTFontManagerRegisterGraphicsFont(cgFont, &error)
if let fontName = cgFont.postScriptName,
let customFont = UIFont(name: String(fontName), size: size) {
return customFont
} else {
print("Error loading Font with error: \(String(describing: error))")
return nil
}
}
}
Usage:
let customFont = UIFont.font(withData: data, size: 15.0)

Related

Update Safari App Extension Content Blocker list

When creating a native content blocker from a Safari App Extension, how do I update the static JSON list after the plugin has loaded?
The only way I can see right now is to deploy a whole new version of the app which wouldn't update automatically for users.
Is it possible to update the JSON blocklist file for a content blocker from another URL without having to update the Safari App Extension through the Apple store?
YES its possible you can update the JSON blocklist
Step 1:
Create new JSON for content blocking rules
Step 2 :
Save the JSON file in Shared Container
fileprivate func saveRuleFile(ruleList:[Rule]) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(ruleList) {
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.xxx.xxxx.xxx")
print("sharedContainerURL = \(String(describing: sharedContainerURL))")
if let json = String(data: encoded, encoding: .utf8) {
print(json)
}
if let destinationURL = sharedContainerURL?.appendingPathComponent("Rules.json") {
do {
try encoded.write(to: destinationURL)
} catch {
print (error)
}
}
}
}
Step 3: Call this method to ask the Content blocker to reload the rules
SFContentBlockerManager.reloadContentBlocker(withIdentifier:"com.xxxx.xxx.xxxx", completionHandler: nil)
Step: 4
Read the JSON rules file from Shared container and pass the rules to content blocker extension
func beginRequest(with context: NSExtensionContext) {
let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.xxx.xxx.xxx")
let sourceURL = sharedContainerURL?.appendingPathComponent("Rules.json")
let ruleAttachment = NSItemProvider(contentsOf: sourceURL)
let item = NSExtensionItem()
item.attachments = ([ruleAttachment] as! [NSItemProvider])
context.completeRequest(returningItems: [item], completionHandler: nil)
}

NSFileWrapper's serializedRepresentation returns inappropriately large data

NSFileWrapper's serializedRepresentation returns inappropriately large data when the Cocoa framework, rather than only the Foundation framework is imported. How can I prevent serializedRepresentation from returning this huge data.
My Scenario:
I'm using packages, aka folders, to store data for an app on OS X.
The data returned by serializedRepresentation is a couple of orders of magnitude larger when Cocoa has been imported compared with when only Foundation has been imported.
When converting these files back into packages the resulting package appears to be identical.
This actual example package is 22KB:
folder: [
some.plist,
folder: [
1.png
]
]
With the Cocoa framework imported the file created from the data returned by serializedRepresentation is 3.2MB
With only Foundation imported the file created from the data returned by serializedRepresentation is 32KB
TL;DR:
Remove all the icons that the Cocoa implementation of NSFileWrapper adds.
Explanation and example:
The Cocoa framework adds some kind of icon to the files before returning the serializedRepresentation. This can make the resulting files very large.
To resolve this issue: manually remove the icon from each file.
Swift Example:
func removeIconsFromFileWrapper(wrapper: NSFileWrapper) {
wrapper.icon = nil
if wrapper.directory {
for directory in wrapper.fileWrappers!.values {
removeIconsFromFileWrapper(directory)
}
}
}
let thisFileURL = NSURL(fileURLWithPath: filePath)
if let fileWrapper = try?NSFileWrapper(URL: thisFileURL, options: NSFileWrapperReadingOptions.Immediate) {
removeIconsFromFileWrapper(fileWrapper)
let fileData = fileWrapper.serializedRepresentation
let newFileURL = thisFileURL.URLByAppendingPathExtension("extension")
do {
try fileData?.writeToURL(newFileURL, options: NSDataWritingOptions.AtomicWrite)
success = true
} catch {
// !!!: inform user that:
NSLog("the file \(newFileURL) could not be saved")
}
} else {
NSLog("the file could not be read")
}

Zip/Rar files with SWIFT

I am new to SWIFT and I just started implementing my first application for OS X. I created a simple project and decided Ill look into different frameworks for using zip/rar files. I started with ZipArchive as recommended but couldn't make it work in my project - didn't even compile (probably something wrong with my setup), I had similar experience with Objective-Zip and SSZipArchive. Finally I stumbled on the framework zip zap which attached to my project perfectly.
I looked into the examples:
ZZArchive* oldArchive = [ZZArchive archiveWithURL:[NSURL fileURLWithPath:#"/tmp/old.zip"] error:nil];
ZZArchiveEntry* firstArchiveEntry = oldArchive.entries[0];
NSLog(#"The first entry's uncompressed size is %lu bytes.", (unsigned long)firstArchiveEntry.uncompressedSize);
NSLog(#"The first entry's data is: %#.", [firstArchiveEntry newDataWithError:nil]);
but I couldn't make it work with SWIFT. The problem I faced is that I couldn't create a NSURL that worked with the ZZArchive.
let zip:ZZArchive = ZZArchive(NSURL(fileURLWithPath:"/Users/../tesData/test.zip"))
led to
fatal error: unexpectedly found nil while unwrapping an Optional value
and everything else I tried either didn't compile with a unwrapping error or compiled but on execution led to this error.
Can someone please either help me solve my unzipping problem, or lead me to a solution of how to zip/unzip/read zip/rar/cbr files with swift.
This should fix it
var path = NSURL.fileURLWithPath("/Users/../tesData/test.zip")
var archive: ZZArchive = ZZArchive(URL:(fileURLWithPath:path!), error: &err)
Here is an implementation of zzzip in swift
//Declare
var err: NSError? = NSError()
var path = NSURL.fileURLWithPath("/PathToZipFile/file.zip")
var URL3 = NSURL.fileURLWithPath("/pathanywhereinsystemtosaveunzipedfolder/")
var URL2 = NSURL.fileURLWithPath("/PathToUnzippedFile/file.xxx")
var fileManager = NSFileManager.defaultManager() //1
let archive: ZZArchive = ZZArchive(URL:(fileURLWithPath:path!), error: &err)//2-3
//Create Folder
fileManager.createDirectoryAtURL(URL3!, withIntermediateDirectories: true, attributes: nil, error: &err)
//Write First entry of archive to file
var k = archive.entries[0].newDataWithError(nil)
k.writeToURL(URL2!, atomically: false)
This works for small text files and images. it does have its limitations due to no errorchecking and only writing the first archive entery to a file. If any paths are wrong you will end with errors as soon as you try and compile.

NSUserDefaults Not Saving TextField Text (Swift)

I'm trying to create a game with Swift, and I want to add the ability to create a username, which will be saved in NSUserDefaults. This is my code:
println("Textfield Text: \(usernameTextfield.text)")
NSUserDefaults.standardUserDefaults().setObject(usernameTextfield.text, forKey:"Username")
NSUserDefaults.standardUserDefaults().synchronize()
println(NSUserDefaults.standardUserDefaults().objectForKey("Username") as? String)
The output is:
Textfield Text: MyUsername
nil
The only explanation I can see as to why it is printing nil is that either the saving or the loading of the username is failing. Is there any way this can be corrected or am I doing something wrong?
Any help is appreciated!
println("Textfield Text: \(usernameTextfield.text)")
var myValue:NSString = usernameTextfield.text
NSUserDefaults.standardUserDefaults().setObject(myValue, forKey:"Username")
NSUserDefaults.standardUserDefaults().synchronize()
var myOutput: AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey("Username")
println(myOutput)
In Swift 4.1
UserDefaults.standard.set(textfield.text, forKey: "yourKey") // saves text field text
UserDefaults.standard.synchronize()
// To Retrieve
textfield.text = UserDefaults.standard.value(forKey:"yourKey") as? String
I made a small modification to Roman's answer with Swift 2.0 and Xcode 6.4.
saving:
var myValue:NSString = usernameTF.text
NSUserDefaults.standardUserDefaults().setObject(myValue, forKey:"Username")
NSUserDefaults.standardUserDefaults().synchronize()
retrieving:
var myOutput = NSUserDefaults.standardUserDefaults().objectForKey("Username")
if (myOutput != nil)
{
self.title = "Welcome "+((myOutput) as! String)
}
In Swift 3.0
let userDefult = UserDefaults.standard //returns shared defaults object.
if let userName = usernameTextfield.text {
//storing string in UserDefaults
userDefult.set(userName, forKey: "userName") //Sets the value of the specified default key in the standard application domain.
}
print(userDefult.string(forKey: "userName")!)//Returns the string associated with the specified key.
For swift 3.0, You can create user default by,
UserDefaults.standard.set("yourValue", forKey: "YourString")
To Print the value in console :
print(UserDefaults.standard.string(forKey: "YourString")!)

Azure storage: Uploaded files with size zero bytes

When I upload an image file to a blob, the image is uploaded apparently successfully (no errors). When I go to cloud storage studio, the file is there, but with a size of 0 (zero) bytes.
The following is the code that I am using:
// These two methods belong to the ContentService class used to upload
// files in the storage.
public void SetContent(HttpPostedFileBase file, string filename, bool overwrite)
{
CloudBlobContainer blobContainer = GetContainer();
var blob = blobContainer.GetBlobReference(filename);
if (file != null)
{
blob.Properties.ContentType = file.ContentType;
blob.UploadFromStream(file.InputStream);
}
else
{
blob.Properties.ContentType = "application/octet-stream";
blob.UploadByteArray(new byte[1]);
}
}
public string UploadFile(HttpPostedFileBase file, string uploadPath)
{
if (file.ContentLength == 0)
{
return null;
}
string filename;
int indexBar = file.FileName.LastIndexOf('\\');
if (indexBar > -1)
{
filename = DateTime.UtcNow.Ticks + file.FileName.Substring(indexBar + 1);
}
else
{
filename = DateTime.UtcNow.Ticks + file.FileName;
}
ContentService.Instance.SetContent(file, Helper.CombinePath(uploadPath, filename), true);
return filename;
}
// The above code is called by this code.
HttpPostedFileBase newFile = Request.Files["newFile"] as HttpPostedFileBase;
ContentService service = new ContentService();
blog.Image = service.UploadFile(newFile, string.Format("{0}{1}", Constants.Paths.BlogImages, blog.RowKey));
Before the image file is uploaded to the storage, the Property InputStream from the HttpPostedFileBase appears to be fine (the size of the of image corresponds to what is expected! And no exceptions are thrown).
And the really strange thing is that this works perfectly in other cases (uploading Power Points or even other images from the Worker role). The code that calls the SetContent method seems to be exactly the same and file seems to be correct since a new file with zero bytes is created at the correct location.
Does any one have any suggestion please? I debugged this code dozens of times and I cannot see the problem. Any suggestions are welcome!
Thanks
The Position property of the InputStream of the HttpPostedFileBase had the same value as the Length property (probably because I had another file previous to this one - stupid I think!).
All I had to do was to set the Position property back to 0 (zero)!
I hope this helps somebody in the future.
Thanks Fabio for bringing this up and solving your own question. I just want to add code to whatever you have said. Your suggestion worked perfectly for me.
var memoryStream = new MemoryStream();
// "upload" is the object returned by fine uploader
upload.InputStream.CopyTo(memoryStream);
memoryStream.ToArray();
// After copying the contents to stream, initialize it's position
// back to zeroth location
memoryStream.Seek(0, SeekOrigin.Begin);
And now you are ready to upload memoryStream using:
blockBlob.UploadFromStream(memoryStream);