How to edit and update xml file with Swift - swift

I'm trying to merge two xml files to a new xml file. Also adding one line <xxxxxxxxxxXML Seperatorxxxxxxxxxx> in between two xml.
I'm using SWXMLHash pod to parser my xml file. I already have two var of xml that I need to merge. Thanks!
var oldXML: XMLIndexer?
var newXML: XMLIndexer?
var combinedXML: XMLIndexer?
func mergeEHR(oldXML: XMLIndexer, newXML: XMLIndexer) -> XMLIndexer {
do {
/// Don't know how to merge it.
} catch {
print(error)
}
}
I want it to be
var combinedXML =
<oldXML>
...
</oldXML>
xxxxxxxxxxXML Seperatorxxxxxxxxxx
<newXML>
...
</newXML>

Since combined XML will be not a valid XML (even if it was, doesn't matter), treat them as a raw String:
let oldXMLString = try! String(contentsOfFile: <#PathToOldXML#>) // If it is on the file, else assign it the way it should
let separator = "<xxxxxxxxxxXML Seperatorxxxxxxxxxx>"
let newXMLString = try! String(contentsOfFile: <#PathToNewXML#>) // If it is on the file, else assign it the way it should
let combinedXMLString = [oldXMLString, separator, newXMLString].joined(separator: "\n")
try! combinedXMLString.write(toFile: <#PathToDestinationOfTheCombinedXML#>, atomically: <#Bool#>, encoding: <#String.Encoding#>)
Then you can treat the result as an invalid XML or etc. again.

Related

Swift 5: How to save results of NSFetchRequest to File

I am new to programming in general and have started with Swift. I have a feeling what I'm attempting to do is a bit outside of my scope, but I've come so far so here's the ask:
I am adding a tracker to a program for macOS X I've already created. The end user inputs a number and hits "Add to tracker" which then takes that number, the timestamp from the button click and writes that to the appropriate entity in Core Data. Everything works perfectly, my NSTable displays the data and I my batch delete works, but I cannot for the life of me work out the best way to take the results from the NSFetchRequest and print them to a text file.
Here is the code for my fetch request that occurs when the "print" button is hit:
#IBAction func printTracker(_ sender: Any) {
fetchRequest.propertiesToFetch = ["caseDate","caseNumber"]
fetchRequest.returnsDistinctResults = true
fetchRequest.resultType = NSFetchRequestResultType.dictionaryResultType
do {
let results = try context.fetch(fetchRequest)
let resultsDict = results as! [[String:String]]
} catch let err as NSError {
print(err.debugDescription)
}
}
After the "resultsDict" declaration is where I just can't seem to come to a workable solution for getting it to string, then to txt file.
If I add a print command to the console as is, I can see that resultsDict pulls correctly with the following format:
[["caseNumber": "12345", "caseDate": "3/22/21, 5:48:18 PM"]]
Ideally I need it in plaintext more like
"3/22/21, 5:48:18 PM : 12345"
Any advice or help on the conversion would be greatly appreciated.
A simple way if there is not a huge amount of data returned is to create a string from the fetched data and then write that string to disk
First create the string by getting the values from the dictionary and adding them in the right order into a string and joining the strings with a new line character
let output = results.reduce(into: []) { $0.append("\($1["caseDate", default: ""]) : \($1["caseNumber", default: ""])") }
.joined(separator: "\n")
Then we can write them to file, here I use the Document directory as the folder to save the file in
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let path = paths[0].appendingPathComponent("results.txt")
do {
try String(output).write(to: path, atomically: true, encoding: .utf8)
} catch {
print("Failed to write to file, error: \(error)")
}

How can I write to a file, line by line in Swift 4

I need help figuring out how to write repeatedly to the same, open, output file.
I am using Swift 4.2. My searches and tests have turned up only code that writes a single text string to a file and then closes the file. The next opening overwrites the last one. An example is shown below.
The problem is that I need to be able to write large numbers of records (say, 1.5 million) and perform calculations on each record just before it is written to a file. That’s not feasible when the code will only write once before closing. I'm calling this "writing line by line", much like the opposite, to "read line by line."
I tried to find an option in various Swift write statements and SO posts, but everything seems to be geared toward writing once then closing the file. I tried an open for append, but that did not work and anyway it seems inefficient to open, close, reopen-append each time I want to write to a file. I tried some C code in Swift, using open(… and freopen(… but could not get something that the compiler wouldn't complain about. Hopefully, there is a way to do this all in Swift. The following code works nicely for one write.
let file0 = “test_file.txt”
let s0 = ("This is a test line of text")
do {
try s0.write(to: NSURL(fileURLWithPath: file0) as URL, atomically: false, encoding: String.Encoding.utf8)
} catch {
print("Problem writing to file0")
}
How can I adapt this code snippet to write a string, and then another and another etc, and before closing the file when it’s all done? If not with this, is there Swift code that will do the job?
Following are the essential code components needed to write to a file, line-by-line in Swift. First is some file management code to create a file if it does not exist, then there is code to print a series of example statements, followed by code to print to the file in a loop, and finally close the file. This code worked correctly in Swift 4.2. The difference between this and the method in the question is that the write statements in this code use a method of fileHandle! and the question shows a method of a Swift string.
print("Swift_Write_to_File_Test_1")
var outFilename: NSString = "test_file.txt"
// Begin file manager segment
// Check for file presence and create it if it does not exist
let filemgr = FileManager.default
let path = filemgr.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last?.appendingPathComponent(outFilename as String)
if !filemgr.fileExists(atPath: (path?.absoluteString)!) {
filemgr.createFile(atPath: String(outFilename), contents:Data(" ".utf8), attributes: nil)
}
// End file manager Segment
// Open outFilename for writing – this does not create a file
let fileHandle = FileHandle(forWritingAtPath: outFilename as String)
if(fileHandle == nil)
{
print("Open of outFilename forWritingAtPath: failed. \nCheck whether the file already exists. \nIt should already exist.\n");
exit(0)
}
var str: NSString = "2. Test string from NSString.\n";
var str0: String = "3. Test string from a Swift String.\n"
var str1: NSString = "4. Test string from NSString.\n";
fileHandle!.write("1. Text String in-line with code statement.\n".data(using: .utf8)!)
fileHandle!.write(String(str).data(using: .utf8)!)
fileHandle!.write(str0.data(using: .utf8)!)
fileHandle!.write(String(str1).data(using: .utf8)!)
fileHandle!.write("5. Text String in-line with code statement.\n".data(using: .utf8)!)
fileHandle!.write("6. Text in a loop follows: \n".data(using: .utf8)!)
for i in 0...5
{
//Assemble a string then write it to the file.
var s0: String = ""
s0 = String(i)
//s0.append(" ... some text here.\n") // See improvement below
s0 += " ... some text here.\n" // This is a better than .append
fileHandle!.write(s0.data(using: .utf8)!)
}
// End of file-writing segment
fileHandle!.closeFile()
This worked for me in Swift 5:
func writeFile() -> Bool
{
let outFilename: String = "test_file.txt"
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let outFilePath = documentsURL!.appendingPathComponent(outFilename).path
let fileManager = FileManager.default
// If file exists, remove it
if fileManager.fileExists(atPath: outFilePath)
{
do { try fileManager.removeItem(atPath: outFilePath) }
catch { return false }
}
// Create file and open it for writing
fileManager.createFile(atPath: outFilePath, contents:Data(" ".utf8), attributes: nil)
let fileHandle = FileHandle(forWritingAtPath: outFilePath)
if fileHandle == nil
{
return false
}
else
{
// Write data
fileHandle!.write("Test line 1\n".data(using: .utf8)!)
fileHandle!.write("Test line 2\n".data(using: .utf8)!)
fileHandle!.write("Test line 3\n".data(using: .utf8)!)
// Close file
fileHandle!.closeFile()
return true
}
}

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.

Content Blocker extension with a String instead of a file

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.

Any Method to remove comment characters from JSON response in Xcode?

I am new to JSON. Are there any methods in JSON parser to remove the comment characters from a response.
Eg. //{"response":"success".......
its SBJson for iPhone.
from http://code.google.com/p/json-framework
The JSON grammar doesn't allow comments. That doesn't answer your question obviously, but I suspect you'll have to do some string manipulation and replace all those comment characters with empty strings and parse it with the JSON library only after doing so.
Nowadays, very easy to do this:
Here's how you get and parse actual json
// normal json no comments
func getStuff() {
guard let url = URL(string: "http://you.com/x.json") else { return print("?") }
let configuration = URLSessionConfiguration.ephemeral
aSession = URLSession(configuration: configuration)
aSession.dataTask(with: url) { [weak self] data, response, error in
guard let _ = self else { return print("woe") }
guard let data = data else { return print("woe") }
do {
let result = try JSONDecoder().decode(YourStructure.self, from: data)
localBlah = Dictionary(uniqueKeysWithValues: result.whatever)
} catch let error {
print(error)
}
}.resume()
}
Here's how you get and parse "json" which has simple comment lines:
During development, remove #comment lines from "json":
Notice the line of code which decodes the data :
let result = try JSONDecoder().decode(YourStructure.self, from: data)
Simply paste in these three lines of code, before, that line:
let s = String(decoding: data, as: UTF8.self)
let fixed = s.replacingOccurrences(
of: "(?m)^#.*",
with: "",
options: .regularExpression)
guard let data2: Data = fixed.data(using: .utf8) else { return print("woe") }
let result = try JSONDecoder().decode(YourStructure.self, from: data2)
So during development in your "json" on your server, you can have things like ..
"measures": [{
"screen": "options",
"topMargin": 25,
#don 't change topmargin anyone
"leftMargin": 12,
#Note,
Steve prefers 13. But everyone
else prefers 12. "rightMargin": 20,
},
It's that simple.
Important note on using regex:
Regex is a sophisticated process. The example regex used in this post simply means
"Delete any full lines, which, start with a '#'
So, it only understands simple "full-line" comments.
How to write regex is beyond the scope of this QA.
When you pre-munge the text at let fixed =, use whatever regex or other technique you wish.
The JSON parsers are very finicky about what is at the start of a JSON block to parse - they DO NOT like characters other than "{" at the start (at least that's what I found with TouchJSON, and it sounds like your case with SBJson is similar).
So just take your string and eliminate any characters before the opening "{", then you can parse:
NSRange startJSONRange = [myJSONString rangeOfString:#"{"];
startJSONRange.length = myJSONString.length - startJSONRange.location;
NSString *correctJSONString = [myJSONString substringWithRange:startJSONRange];
// parse correctJSONString
That will work, but the REAL fix is to tell whoever is sending you JSON to cut out the nonsense and send real JSON.