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)
}
Related
I am trying to upload images to Real time Firebase Database by creating two folders using Compressor library and need to display image like messenger with username but i am unable to display image due to url issue
var filePath = mStorageRef!!.child("chat_profile_images")
.child(userId + ".jpg")
//Create another directory for thumbimages ( smaller, compressed images)
var thumbFilePath = mStorageRef!!.child("chat_profile_images")
.child("thumbs")
.child(userId + ".jpg")
filePath.putFile(resultUri)
.addOnCompleteListener{
task: Task<UploadTask.TaskSnapshot> ->
if (task.isSuccessful) {
//Let's get the pic url
var donwloadUrl = task.result?.storage?.downloadUrl.toString()
Log.d(TAG, "Profilepic link: $donwloadUrl")
//Upload Task
var uploadTask: UploadTask = thumbFilePath
.putBytes(thumbByteArray)
uploadTask.addOnCompleteListener{
task: Task<UploadTask.TaskSnapshot> ->
var thumbUrl = task.getResult()?.storage?.downloadUrl.toString()
Log.d(TAG, "Profilepic link: $thumbUrl")
i tried to change downloadUrl
filepath.downloadUrl.toString
thumbFilePath.downloadUrl.toString
but both these values getting "com.google.android.gms.tasks.zzu"
i also tried to change
task.result.sessionurl.downloadUrl.toString
for this one i am getting downloadUrl but not a complete solution for my problem as still i cannot display image i need to get thumbUrl downloadUrl
You have the exact same and very common misunderstanding as in this question, except it's in java. You should follow the documentation here to understand get getDownloadUrl works. As you can see from the linked API documentation, it's not a property getter, it's actually a method that returns a Task<Uri> that tracks the asynchronous fetch of the URL you want, just like the upload task:
filePath.downloadUrl
.addOnSuccessListener { urlTask ->
// download URL is available here
val url = urlTask.result.toString()
}.addOnFailureListener { e ->
// Handle any errors
}
This will only work after the upload is fully complete.
Correct way of getting download link after uploading
here
Just putting out there, I also encounter the same problem,
but both these values getting "com.google.android.gms.tasks.zzu"
but it wasn't the same mistake from the OP
used addOnCompleteListener instead of addOnSuccesslistener
My Error code:
imageRef.downloadUrl.addOnCompleteListener { url ->
val imageURL = url.toString()
println("imageURL: $imageURL , url: $url")
addUserToDatabase(imageURL)
}
I am attempting to generate a PDF file that contains object information and then attach it to another object that is stored in the database. The attachmentable plugin I am using is working now for user end attachments, but I need my system to be able to do it automatically.
I am using:
Grails 1.3.9
Attachmentable 0.3.0 http://grails.org/plugin/attachmentable
Rendering 0.4.3 http://grails.org/plugin/rendering
I have been able to generate and display the pdf, but do not know how to attach it using the attachmentable plugin. I need some way to take the generated pdf byte array and convert it to a MultipartFile for the attachmentable plugin function I call. The error I get shows that my argument types are invalid.
I save object1 and object2, then generate the pdf of object1 and try to attach it to object2.
Thanks in advance for you help!
Thing1 Controller Snippets:
ByteArrayOutputStream bytes = pdfRenderingService.render(template: "/thing1/pdf", model: [thing1: thing1])
attachmentableService.addAttachment("unknown", thing2.id, bytes)
AttachmentableService function I am attempting to call:
def addAttachment(def poster, def reference, CommonsMultipartFile file) {
addAttachment(CH.config, poster, reference, file)
}
def addAttachment(def config,
def poster,
def reference,
CommonsMultipartFile file) {
if (reference.ident() == null) {
throw new AttachmentableException(
"You must save the entity [${delegate}] before calling addAttachment.")
}
if (!file?.size) {
throw new EmptyFileException(file.name, file.originalFilename)
}
String delegateClassName = AttachmentableUtil.fixClassName(reference.class)
String posterClass = (poster instanceof String) ? poster : AttachmentableUtil.fixClassName(poster.class.name)
Long posterId = (poster instanceof String) ? 0L : poster.id
String filename = file.originalFilename
// link
def link = AttachmentLink.findByReferenceClassAndReferenceId(
delegateClassName, reference.ident())
if (!link) {
link = new AttachmentLink(
referenceClass: delegateClassName,
referenceId: reference.ident())
}
// attachment
Attachment attachment = new Attachment(
// file
name: FilenameUtils.getBaseName(filename),
ext: FilenameUtils.getExtension(filename),
length: 0L,
contentType: file.contentType,
// poster
posterClass: posterClass,
posterId: posterId,
// input
inputName: file.name)
link.addToAttachments attachment
if (!link.save(flush: true)) {
throw new AttachmentableException(
"Cannot create Attachment for arguments [$user, $file], they are invalid.")
}
// save file to disk
File diskFile = AttachmentableUtil.getFile(config, attachment, true)
file.transferTo(diskFile)
attachment.length = diskFile.length()
// interceptors
if(reference.respondsTo('onAddAttachment')) {
reference.onAddAttachment(attachment)
}
attachment.save(flush:true) // Force update so searchable can try to index it again.
return reference
}
Grails runtime error:
groovy.lang.MissingMethodException: No signature of method: com.macrobit.grails.plugins.attachmentable.services.AttachmentableService.addAttachment() is applicable for argument types: (java.lang.String, java.lang.Long, java.io.ByteArrayOutputStream) values: [unknown, 80536, %PDF-1.4 and a long string of unreadable data...]
Possible solutions: addAttachment(java.lang.Object, java.lang.Object, org.springframework.web.multipart.commons.CommonsMultipartFile), addAttachment(java.lang.Object, java.lang.Object, java.lang.Object, org.springframework.web.multipart.commons.CommonsMultipartFile)
Service Method I Added:
def customAddMethod(def poster, def reference, def pdfBytes) {
customAddMethod(CH.config, poster, reference, pdfBytes)
}
def customAddMethod(def config,
def poster,
def reference,
def pdfBytes) {
if (reference.ident() == null) {
throw new AttachmentableException(
"You must save the entity [${delegate}] before calling customAddMethod.")
}
String delegateClassName = AttachmentableUtil.fixClassName(reference.class)
String posterClass = (poster instanceof String) ? poster : AttachmentableUtil.fixClassName(poster.class.name)
Long posterId = (poster instanceof String) ? 0L : poster.id
String filename = "File Name"
// link
def link = AttachmentLink.findByReferenceClassAndReferenceId(
delegateClassName, reference.ident())
if (!link) {
link = new AttachmentLink(
referenceClass: delegateClassName,
referenceId: reference.ident())
}
// attachment
Attachment attachment = new Attachment(
// file
name: "File Name",
ext: "pdf",
length: 0L,
contentType: "application/pdf",
// poster
posterClass: posterClass,
posterId: posterId,
// input
inputName: "File Name")
link.addToAttachments attachment
if (!link.save(flush: true)) {
throw new AttachmentableException(
"Cannot create Attachment for arguments [$user, $file], they are invalid.")
}
// save file to disk
byte[] bytes = pdfBytes.toByteArray(); //convert ByteArrayOutputStream to ByteArray
File diskFile = AttachmentableUtil.getFile(config, attachment, true) //file path
FileOutputStream fos = new FileOutputStream(diskFile); //open file output stream to write to
fos.write(bytes); //write rendered pdf bytes to file
fos.flush();
fos.close();
attachment.length = diskFile.length()
// interceptors
if(reference.respondsTo('onAddAttachment')) {
reference.onAddAttachment(attachment)
}
attachment.save(flush:true) // Force update so searchable can try to index it again.
return reference
}
It looks like the AttachmentableService you referenced (from the Attachmentable plugin) assumes it's dealing with a file-upload scenario, such that you could easily grab the MultipartFile instance via request.getFile(). That's not the case for you - you're creating the file via the Rendering plugin, and you want that file attached to a domain object, right?
You could try constructing a CommonsMultipartFile instance manually by first writing the pdf bytes to disk, and then create a DiskFileItem via DiskFileItemFactory.
See this post for an example of what I'm thinking:
How to make CommonsMultipartFile from absolute file path?
Another, better, option might be to checkout that plugin's source and add a method that doesn't require you to go through those gyrations - perhaps a version of the addAttachment method that accepts a File or an OutputStream instead - and submit a PR to the plugin author.
(Looks like they're adding an 'addAttachment' method to qualifying domain objects, which also expects a CommonsMultipartFile).
Otherwise, you might just have to create your own service to basically provide the same end result, which apparently is to create an AttachmentLink and associated Attachment instance.
The plugin play-reactivemongo offers an easy way to upload a file:
def upload = Action(gridFSBodyParser(gridFS)) { request =>
val futureFile: Future[ReadFile[BSONValue]] = request.body.files.head.ref
futureFile.map { file =>
// do something
Ok
}.recover { case e: Throwable => InternalServerError(e.getMessage) }
}
Unfortunately this solution doesn't suit me because:
I would like only my DAO layer to depend on reactive-mongo.
I need to save the file only if a user is authenticated (with SecureSocial) and use some user's properties as checks and metadata.
If no user is authenticated the request body shouldn't be parsed at all (see also this question).
It would be something along the lines
def upload = SecuredAction { request =>
val user = request.user
val enumerator = an enumrator from the body parsing ???
myDAO.saveFile(user, enumerator)
object myDAO {
def saveFile(user:User, enumerator:Enumerator[Array[Byte]]) = {
...
val fileToSave = DefaultFileToSave(...)
gridfs.save(enumerator, fileToSave)
...
}
}
Unfortunately it seems there is no way to get an enumerator from the parsing of the request body. The only way seems to provide the Action with a parser and an Iteratee that will be fed with the the body being parsed.
I couldn't figure out how to achieve it in a reactive way (without using a temporary file or storing the body in memory). Is it at all possible?
Actually, you might consider not using girdFS built-in parser at all:
val gfs = new GridFS(db)
// the controller method, Authenticated here is custom object extending ActionBuilder
def upload = Authenticated.async(parse.multipartFormData) { request =>
...
request.body.file("photo") match {
// handle error cases
...
case Some(photo) =>
val fileToSave = DefaultFileToSave(photo.filename, photo.contentType)
// here some more operations, basically you don't need the and need only photo.ref.file
val enumerator = Enumerator(Image(photo.ref.file).fitToWidth(120).write)
gfs.save(enumerator, fileToSave) map {
//handle responses and stuff
...
}
}
}
}
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)
I'm looking into using a javascript logging framework in my app.
I quite like the look of log4javascript (http://log4javascript.org/) but I have one requirement which I'm not sure that it satisfies.
I need to be able to ask the framework for all messages which have been logged.
Perhaps I could use an invisible InPageAppender (http://log4javascript.org/docs/manual.html#appenders) to log to a DOM element, then scrape out the messages from that DOM element - but that seems pretty heavy.
Perhaps I need to write my own "InMemoryAppender"?
There's an ArrayAppender used in log4javascript's unit tests that stores all log messages it receives in an array accessible via its logMessages property. Hopefully it should show up in the main distribution in the next version. Here's a standalone implementation:
var ArrayAppender = function(layout) {
if (layout) {
this.setLayout(layout);
}
this.logMessages = [];
};
ArrayAppender.prototype = new log4javascript.Appender();
ArrayAppender.prototype.layout = new log4javascript.NullLayout();
ArrayAppender.prototype.append = function(loggingEvent) {
var formattedMessage = this.getLayout().format(loggingEvent);
if (this.getLayout().ignoresThrowable()) {
formattedMessage += loggingEvent.getThrowableStrRep();
}
this.logMessages.push(formattedMessage);
};
ArrayAppender.prototype.toString = function() {
return "[ArrayAppender]";
};
Example use:
var log = log4javascript.getLogger("main");
var appender = new ArrayAppender();
log.addAppender(appender);
log.debug("A message");
alert(appender.logMessages);