Content Blocker extension with a String instead of a file - swift

I'm using the function NSItemProvider(contentsOfURL: NSBundle.mainBundle().URLForResource("blockerList", withExtension: "json") in a content blocker extension.
The thing is that all my rules are stored in a few dictionaries and, when I'm using this function, it's always because the rules have changed. I'm currently creating a String from these dictionaries that looks like "[{\"trigger\": {\"url-filter\": \"webiste.com\"},\"action\": {"\type\": \"css-display-none\",\"selector\":\".testContentBlocker\"}}]"and I have to transform it in a JSON file to finally be able to use it in the function written previously described.
Instead of having to put the String in a JSON file to be able to use it, could I do something simpler to use NSItemProvider()?

By loading the extension up in the debugger (and by using Hopper), you can see that NSItemProvider(contentsOfURL:) is simply registering to provide data from the file's contents with type public.json.
(lldb) po attachment
<NSItemProvider: 0x7fd4c250f2a0> {types = (
"public.file-url",
"public.json"
)}
It's roughly equivalent to this:
// possible implementation of NSItemProvider.init(contentsOfURL:)
convenience init?(contentsOfURL fileURL: NSURL!)
{
self.init(item: fileURL, typeIdentifier: (fileURL.fileURL ? kUTTypeFileURL : kUTTypeURL) as String)
let type = UTTypeCreatePreferredIdentifierForTag(
kUTTagClassFilenameExtension, fileURL.pathExtension!, nil)?.takeRetainedValue() as! String
registerItemForTypeIdentifier(type) { completionHandler, expectedValueClass, options in
let data = try! NSData(contentsOfURL: fileURL, options: .DataReadingMappedAlways)
completionHandler(data, nil)
}
}
So you can do this yourself, in memory:
// get the data
let data = NSData(contentsOfURL: NSBundle.mainBundle().URLForResource("blockerList", withExtension: "json")!)
// put the data in an item provider
let attachment = NSItemProvider(item: data, typeIdentifier: kUTTypeJSON as String)
// send the item to Safari
let item = NSExtensionItem()
item.attachments = [attachment]
context.completeRequestReturningItems([item], completionHandler: nil);
If you want to provide content dynamically, you can use NSJSONSerialization to transform a dictionary into NSData at runtime.

Related

Issue with UserDefaults (converting data to array and back)

What I want to do:
I want to get an array from UserDefaults that I saved beforehand and append a custom object to it. Afterwards I want to encode it as a Data-type again and set this as the UserDefaults Key again.
My problem:
The encoding part is what is not working as intended for me.
It says: -[__SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x60000011a540
But I do not know how to fix this.
Below is my code for more context:
do {
let decoded = defaults.object(forKey: "ExArray") as! Data
var exo = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decoded) as! [Exerc]
exo.append(datas[indexPath.row])
let enco = try NSKeyedArchiver.archivedData(withRootObject: exo, requiringSecureCoding: false) <- Here is the error
defaults.set(enco, forKey: "ExArray")
} catch {
print("Error encoding custom object NOSEARCHO")
}
This is how Exerc looks:
struct Exerc: Codable {
var title: String
var exID: String
}
Seems like you are not using the archiver features, so why don't you just use the codable?
do {
let key = "ExArray"
let decoded = defaults.data(forKey: key)!
var exo = try JSONDecoder().decode([Exerc].self, from: decoded)
exo.append(datas[indexPath.row])
let enco = try JSONEncoder().encode(exo)
defaults.set(enco, forKey: key)
} catch {
print("Error encoding/decoding custom object NOSEARCHO", error)
}
It just a simple refactored MVP of the original code, but you can even work a bit on this and make it human readable right in the plist file!

How to read variables from txt file?

For example i've got a build archive and i want it to read some variables from txt file. So the question is there any way to upload a txt file to iPhone device and how to read that variables?
In my project i've got this constants:
baseURL: String = "someUrl"
client_id: String
client_secret: String
authorize_uri: String
token_uri: String
scope: String
redirect_uris: [String]
secret_in_body: Bool
and for example i created a txt file with this strings and somehow uploaded it to my device
i want the builded project to read this file
You could create a new plist file in your project, then parse it into a dictionary at startup. Once there you can just refer to each value by its key
If you create an extension to Bundle()
extension Bundle {
func parsePlist(ofName name: String) -> [String: String]? {
// check if plist data available
guard let plistURL = Bundle.main.url(forResource: name, withExtension: "plist"),
let data = try? Data(contentsOf: plistURL)
else {
return nil
}
// parse plist into [String: Anyobject]
guard let plistDictionary = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [String: String] else {
return nil
}
return plistDictionary
}
}
then you can load it into a dictionary like this
var dict = Bundle().parsePlist(ofName: "plistName")!
Xcode does a nice job of allowing you to edit the plist, and it'll be automatically included in your build

Send Core Data object to post api Alamofire multipartFormData

I have a screen on my app that I get some fields and save on my object Order. This object is my Core Data Object. After saving it, I need to send it to my backend through Alamofire POST multipartFormData.
The problem is that this is a Core Data Object (not Codable) and I need to send Data type on multipartFormData. How can I convert my object to Data? Is there another way of doing it?
What I've done:
let order = Order(context: DatabaseController.getContext())
order.orderItem = orderItem
order.product = product
order.value = value
order.date = date
Alamofire part:
Alamofire.upload (
multipartFormData: { multipartFormData in
multipartFormData.append(order, withName: "order")
},
to: url,
headers: headers,
encodingCompletion: { encodingResult in
The problem is how to put my object Order inside multipartFormData?
Could anyone help me please?
Updated:
Ok, sending the whole object didn't work, my api didn't accept, so I made a specific json only with the fields needed and turned it a Data type:
(PS: files are Data type from images user choose, even from camera or gallery)
var files = Dictionary<Data, String>()
var jsonFiles = [[String: String]]()
var jsonOrder = [String: Any]()
for file in files {
let dict : [String: String] = [ "orderImageIdLocal": uuidOrderImageIdLocal,
"orderItemAnalysisIdLocal": uuidAnalysisIdLocal,
"urlImageLocal": "\(imageId).jpg"]
jsonFiles.append(dict)
}
jsonOrder = [ "reason": "\(textViewReason)",
"orderImagess": jsonFiles,
"orderAnalysisId": "",
"orderIdLocal": "\(uuidAnaliseIdLocal)",
"orderId": "\(orderId ?? "")",
"typeSolicitation": "\(typeSolicitation)"]
Then I convert it to Data type like you said and send to Alamofire like above:
let orderData = try? JSONSerialization.data(withJSONObject: jsonOrder, options: .prettyPrinted) {
My problem now is that my api expect a zip file with those images user took from camera or gallery. So I am trying to use ZIPFoundation. I still don't know how to zip it and send. Should I zip each picture as Data type? Then transform zip file into Data type so I can send through multipartFormData.append?
I have tried: here and here
Here the code as an extension of NSManagedObject which creates dictionary from the attributes name.
extension NSManagedObject {
func toData() -> Data? {
let keys = Array(self.entity.attributesByName.keys)
let dict = self.dictionaryWithValues(forKeys: keys)
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
return jsonData
}
catch{}
return nil
}
}
Usage:
let jsonData = order.toData()
multipartFormData.append(jsonData, withName: "order")

Converting Docx Files To Text In Swift

I have a .docx file in my temporary storage:
let location: NSURL = NSURL.fileURLWithPath(NSTemporaryDirectory())
let file_Name = location.URLByAppendingPathComponent("5 November 2016.docx")
What I now want to do is extract the text inside this document. But I cannot seem to find any converters or methods of doing this.
I have tried this:
let file_Content = try? NSString(contentsOfFile: String(file_Name), encoding: NSUTF8StringEncoding)
print(file_Content)
However it prints nil.
So how do I read the text in a docx file?
Swift 4, Xcode 9.1, OSX targets from 10.10 to 10.13
I have found that the following code extracts text handily from a Word .doc file, which then easily goes into a string. (The attributed string contains formatting information that might be parsed to good effect.) The main info that I wanted to convey was the bit about using .docFormat to specify the document type.
let openPanel = NSOpenPanel()
var fileString = String("")
var fileData = NSData()
let fileURL = openPanel.url
do {
fileData = try NSData(contentsOf: fileURL!)
if let tryForString = try? NSAttributedString(data: fileData as Data, options: [
.documentType: NSAttributedString.DocumentType.docFormat,
.characterEncoding: String.Encoding.utf8.rawValue
], documentAttributes: nil) {
fileString = tryForString.string
} else {
fileString = "Data conversion error."
}
fileString = fileString.trimmingCharacters(in: .whitespacesAndNewlines)
} catch {
print("Word Document File Not Found")
}
Your initial issue is with how you get the string from the URL. String(File_Name) is not the correct way to convert a file URL into a file path. The proper way is to use the path function.
let location = NSURL.fileURLWithPath(NSTemporaryDirectory())
let fileURL = location.URLByAppendingPathComponent("My File.docx")
let fileContent = try? NSString(contentsOfFile: fileURL.path, encoding: NSUTF8StringEncoding)
Note the many changes. Use proper naming conventions. Name variables more clearly.
Now here's the thing. This still won't work because a docx file is a zipped up collection of XML and other files. You can't load a docx file into an NSString. You would need to use NSData to load the zip contents. Then you would need to unzip it. Then you would need to go through all of the files and find the desired text. It's far from trivial and it is far beyond the scope of a single stack overflow post.

How do I convert a file into a JSON Object in SwiftyJson

I am trying to import Json data from a user uploaded txt file into a standard object that I can use via SwiftyJson framework
Here is the contents of the text fie:
{
"String": "answer",
"String2": "answer2"
}
I have successfully read it and turned it into a String file using:
let openPanel = NSOpenPanel()
let arrayOfExtensions = ["txt"]
openPanel.allowedFileTypes = arrayOfExtensions
let result = openPanel.runModal()
if result == NSFileHandlingPanelCancelButton {
return
}
let fileUrl = openPanel.URL
do {
let stringResult = try String(contentsOfURL: fileUrl!, encoding: NSUTF8StringEncoding)
print (stringResult)
completionhandler(retrievedData: stringResult, error: nil)
I am trying to convert this into a JSON object using:
let jsonFile = JSON(contentsOfFile)
The problem is that the resulting JSON object created appears to be blank for all the fields except rawvalue.
Here is the screenshot from the debug console.
How to I sucessfully read in the string from the file and then make it populate via SwiftJson correctly?
The problem above was that I was using the wrong method to parse it into JSON.
SwiftyJSON seems to be badly documented hence others had a similar problem.
The correct way to do this is to use :
let jsonFile = JSON.parse(string:contentsOfFile)