How to insert signature image into PDF file in Swift? - html

I'm adding signature through a SwiftSignatureView and it give us UIImage. How can I add it in to PDF file? Here is my code,
func convertToPdfFileAndShare(){
let imageData = sigImage!.pngData() ?? nil
let base64String = imageData?.base64EncodedString() ?? ""
let fmt = UIMarkupTextPrintFormatter(markupText: " \(ShowPDFVC.condition)</h1><h1>Signature:</h1><p> </p><br></div><div><br><html><body><p></p><p><b><img src='data:image/png;base64,\(String(describing: base64String))'></b></p></body></html><br><br>")
"sigImage" is signature image.

you could try this approach to write your signature to a pdf file:
if let theImage = sigImage, let pdfPage = PDFPage(image: theImage) {
let doc = PDFDocument()
pdfPage.setBounds(CGRect(x: 0, y: 0, width: theImage.size.width, height: theImage.size.height), for: .mediaBox)
doc.insert(pdfPage, at: 0)
// write the pdf document to file
writeToFile(doc, file: "test")
}
func writeToFile(_ doc: PDFDocument, file: String) {
do {
let dir = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = dir.appendingPathComponent(file).appendingPathExtension("pdf")
doc.write(to: fileURL)
} catch {
print("\(error)")
}
}

Related

SwiftUI local JSON save string value

I created a local json. I change the value of the name key in the json and when I close and open the application, it says "Test" again. How can I save the change I made on the Json file?
Why can't I save the string value? I shared all the codes with you. If you want I can share the project.
Local JSON File
{
"person": {
"name": "Test"
}
}
Model
struct PersonContainer: Codable {
var person: Person?
}
struct Person: Codable {
var name: String?
}
JSON Provider
class JSONProvider: ObservableObject {
#Published var personContainer: PersonContainer = PersonContainer()
var fm = FileManager.default
var fresult: Bool = false
#Published var subUrl: URL? = URL(string: "")
var mainUrl: URL? = Bundle.main.url(forResource: "test", withExtension: "json")
func getData() {
do {
let documentDirectory = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
subUrl = documentDirectory.appendingPathComponent("test.json")
loadFile(mainPath: mainUrl!, subPath: subUrl!)
} catch {
print(error)
}
}
func loadFile(mainPath: URL, subPath: URL){
if fm.fileExists(atPath: subPath.path){
decodeData(pathName: subPath)
if ((personContainer.person) != nil) {
decodeData(pathName: mainPath)
}
}else{
decodeData(pathName: mainPath)
}
}
func decodeData(pathName: URL){
do{
let jsonData = try Data(contentsOf: pathName)
let decoder = JSONDecoder()
let personContainer = try decoder.decode(PersonContainer.self, from: jsonData)
self.personContainer = personContainer
} catch {}
}
func writeToFile(location: URL) {
do{
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let JsonData = try encoder.encode(personContainer)
try JsonData.write(to: location)
} catch {
}
}
}
ContentView
struct ContentView: View {
#State var text: String = ""
#ObservedObject var jsonProvider: JSONProvider = JSONProvider()
var body: some View {
VStack {
TextField("Placeholder", text: $text)
.padding()
.background(Color(UIColor.secondarySystemBackground))
.cornerRadius(15)
.padding(.horizontal)
Text("Hello, world! \(jsonProvider.personContainer.person?.name ?? "")")
.padding()
Button(action: {
jsonProvider.personContainer.person?.name = text
jsonProvider.writeToFile(location: jsonProvider.subUrl!)
}) {
Text("Button")
}
}
.onAppear {
jsonProvider.getData()
}
}
}
Looks like you were on the right track, but there were a few things missing.
Since the original main bundle's test.json should only be loaded if the file in the documents directory doesn't exist, a lot of the logic can be simplified. For example, you can remove the #Published subUrl, since it never gets changed and isn't observed by the View.
Make sure that you call the writeToFile when the button is pressed.
Also, it's always a good idea to do something (like printing the error) inside the catch blocks in case something has gone wrong.
class JSONProvider: ObservableObject {
#Published var personContainer: PersonContainer = PersonContainer()
private var fm = FileManager.default
private let mainUrl: URL = Bundle.main.url(forResource: "test", withExtension: "json")!
func getData() {
if fm.fileExists(atPath: documentDirectoryJSONURL().path) {
decodeData(fromURL: documentDirectoryJSONURL())
} else {
decodeData(fromURL: mainUrl)
}
}
func documentDirectoryJSONURL() -> URL {
do {
let documentDirectory = try fm.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
return documentDirectory.appendingPathComponent("test.json")
} catch {
fatalError("Couldn't create URL")
}
}
func decodeData(fromURL url: URL){
do{
let jsonData = try Data(contentsOf: url)
let decoder = JSONDecoder()
let personContainer = try decoder.decode(PersonContainer.self, from: jsonData)
self.personContainer = personContainer
} catch {
print(error)
assertionFailure("Error decoding JSON")
}
}
func writeToFile() {
do{
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(personContainer)
try jsonData.write(to: documentDirectoryJSONURL())
} catch {
print(error)
}
}
}
struct ContentView: View {
#State var text: String = ""
#ObservedObject var jsonProvider: JSONProvider = JSONProvider()
var body: some View {
VStack {
TextField("Placeholder", text: $text)
.padding()
.background(Color(UIColor.secondarySystemBackground))
.cornerRadius(15)
.padding(.horizontal)
Text("Hello, world! \(jsonProvider.personContainer.person?.name ?? "")")
.padding()
Button(action: {
jsonProvider.personContainer.person?.name = text
jsonProvider.writeToFile()
}) {
Text("Write")
}
}
.onAppear {
jsonProvider.getData()
}
}
}

How do I correctly write this data to a JSON file without overwriting the file?

I am writing a JSON file to documents directory, I would like to keep it in one file and read it later. The struct looks like this:
struct SymptomD:Codable
{
var symptom:String
var severity:String
var comment:String
var timestamp:String
}
Then I write to documents like so:
var completeData = SymptomD(symptom: "", severity: "", comment: "", timestamp: "")
func writeTrackedSymptomValues(symptom: String, comment: String, time: String, timestamp: String) {
completeData.symptom = symptom
completeData.severity = self.severity
completeData.comment = comment
completeData.timestamp = timestamp
createJSON()
}
var logFile: URL? {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil }
let fileName = "symptom_data.json"
return documentsDirectory.appendingPathComponent(fileName)
}
func createJSON() {
guard let logFile = logFile else {
return
}
let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(completeData) //This does not work, I am not sure how to add data without overwriting the previous file.
fileHandle.closeFile()
}
} else {
do {
try JSONEncoder().encode(completeData)
.write(to: logFile)
} catch {
print(error)
}
}
}
With this I can only add the data once, I am not sure how I should go about adding another 'row' basically to the JSON file, so that I can read these and decode them with my struct for use in a tableView later. The JSON file made looks like this:
What is a way I can call the createJSON function again, without overwriting the whole file, and how should I go about organising this so that when I read the JSON I can decode it simply and access the info.
Update:
Using this I am able to add more lines to the JSON,
let jsonData = try! JSONEncoder().encode(completeData)
let jsonString = String(data: jsonData, encoding: .utf8)!
print(jsonString)
if FileManager.default.fileExists(atPath: logFile.path) {
if let fileHandle = try? FileHandle(forWritingTo: logFile) {
fileHandle.seekToEndOfFile()
fileHandle.write(jsonData)
fileHandle.closeFile()
}
Giving me this:
{"timestamp":"1592341465","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592342433","comment":"","severity":"Moderate","symptom":"Anxiety"}{"timestamp":"1592342458","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592343853","comment":"","severity":"Mild","symptom":"Anxiety"}{"timestamp":"1592329440","comment":"","severity":"Mild","symptom":"Fatigue"}{"timestamp":"1592344328","comment":"","severity":"Mild","symptom":"Mood Swings"}{"timestamp":"1592257920","comment":"test","severity":"Mild","symptom":"Anxiety"}
But when trying to parse this, it crashes with an error:
Code=3840 "Garbage at end."
What am I doing wrong?
The issue looks pretty clear to me. You are appending another dictionary to an existing dictionary but you should have created an array of dictionaries to be able to append a dictionary to it.
struct SymptomD: Codable {
var symptom, severity, comment, timestamp: String
init(symptom: String = "", severity: String = "", comment: String = "", timestamp: String = "") {
self.symptom = symptom
self.severity = severity
self.comment = comment
self.timestamp = timestamp
}
}
If you would like to manually append the text to your json string you will need to seek to the position before the end of your file, add a comma before the next json object and a closed bracket after it:
extension SymptomD {
func write(to url: URL) throws {
if FileManager.default.fileExists(atPath: url.path) {
let fileHandle = try FileHandle(forWritingTo: url)
try fileHandle.seek(toOffset: fileHandle.seekToEndOfFile()-1)
let data = try JSONEncoder().encode(self)
fileHandle.write(Data(",".utf8) + data + Data("]".utf8))
fileHandle.closeFile()
} else {
try JSONEncoder().encode([self]).write(to: url)
}
}
}
Playground testing:
var logFile: URL? {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("symptom_data.json")
}
var symptomD = SymptomD()
symptomD.symptom = "Anxiety"
symptomD.severity = "Mild"
symptomD.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD.write(to: logFile)
}
} catch {
print(error)
}
var symptomD2 = SymptomD()
symptomD2.symptom = "Depression"
symptomD2.severity = "Moderate"
symptomD2.timestamp = .init(Date().timeIntervalSince1970)
do {
if let logFile = logFile {
try symptomD2.write(to: logFile)
}
} catch {
print(error)
}
do {
if let logFile = logFile {
let symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: logFile))
print(symptoms)
}
} catch {
print(error)
}
This will print:
[__lldb_expr_532.SymptomD(symptom: "Anxiety", severity: "Mild",
comment: "", timestamp: "1592356106.9662929"),
__lldb_expr_532.SymptomD(symptom: "Depression", severity: "Moderate", comment: "", timestamp: "1592356106.978864")]
edit/update:
If you need to update a single "row" of your JSON, you will need to make your struc conform to equatable, read your collection and find its index:
extension SymptomD: Equatable {
static func ==(lhs: SymptomD, rhs: SymptomD) {
(lhs.symptom, lhs.severity, lhs.comment ,lhs.timestamp) ==
(rhs.symptom, rhs.severity, rhs.comment ,rhs.timestamp)
}
#discardableResult
mutating func updateAndWrite(symptom: String? = nil, severity: String? = nil, comment: String? = nil, timestamp: String? = nil, at url: URL) throws -> [SymptomD]? {
var symptoms = try JSONDecoder().decode([SymptomD].self, from: .init(contentsOf: url))
if let index = symptoms.firstIndex(of: self) {
self.symptom = symptom ?? self.symptom
self.severity = severity ?? self.severity
self.comment = comment ?? self.comment
self.timestamp = timestamp ?? self.timestamp
symptoms[index] = self
try JSONEncoder().encode(symptoms).write(to: url, options: .atomic)
return symptoms
}
return nil
}
}

How to modify the video format after the download in swift

I have a problem in modifying the video format after downloading from videoplayback to mp4 and save to camera.
This is my download code, but I downloaded some video with a different format example : "videoplayback". I can't save to camera because I want to change format video to mp4.
func SessionDownload(URLSession : String) {
MBProgressHUD.hideAllHUDs(for: view, animated: true)
let hud = MBProgressHUD.showAdded(to: self.view, animated: true)
// Set the bar determinate mode to show task progress.
progress = 0.0
hud?.mode = MBProgressHUDMode.determinateHorizontalBar
hud?.isUserInteractionEnabled = true;
hud?.labelText = NSLocalizedString("Downloading...", comment: "HUD loading title")
DispatchQueue.global(qos: .default).async(execute: {() -> Void in
// Do something useful in the background and update the HUD periodically.
self.doSomeWorkWithProgress()
DispatchQueue.main.async(execute: {() -> Void in
//hud?.hide(true)
hud?.labelText = NSLocalizedString("Just Wait...", comment: "HUD loading title")
})
})
let videoPath = URLSession
print(videoPath)
let s = videoPath
let url = NSURL(string:s)!
let req = NSMutableURLRequest(url:url as URL)
let config = URLSessionConfiguration.default
let task = self.session.downloadTask(with: req as URLRequest)
self.task = task
task.resume()
}
//MARK:- share video
func doSomeWorkWithProgress() {
// This just increases the progress indicator in a loop.
while progress < 1.0 {
DispatchQueue.main.async(execute: {() -> Void in
print(self.progress)
MBProgressHUD(for: self.view).progress = self.progress
})
usleep(50000)
}
}
//MARK:- URL Session delegat
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
print("downloaded \(100*totalBytesWritten/totalBytesExpectedToWrite)")
taskTotalBytesWritten = Int(totalBytesWritten)
taskTotalBytesExpectedToWrite = Int(totalBytesExpectedToWrite)
percentageWritten = Float(taskTotalBytesWritten) / Float(taskTotalBytesExpectedToWrite)
print(percentageWritten)
let x = String(format:"%.2f", percentageWritten)
print(x)
self.progress = Float(x)!
print(progress)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
// unused in this example
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
print("completed: error: \(error)")
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Finished downloading!")
let fileManager = FileManager()
// this can be a class variable
let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
print(directoryURL)
let docDirectoryURL = NSURL(fileURLWithPath: "\(directoryURL)")
print(docDirectoryURL)
//Save To Photos
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL:directoryURL)
}) { saved, error in
if saved {
let alertController = UIAlertController(title: "Your video was successfully saved", message: nil, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
let destinationFilename = downloadTask.originalRequest?.url?.lastPathComponent
print(destinationFilename!)
// append that to your base directory
let destinationURL = docDirectoryURL.appendingPathComponent("\(destinationFilename!)")
print(destinationURL!)
/* check if the file exists, if so remove it. */
if let path = destinationURL?.path {
if fileManager.fileExists(atPath: path) {
do {
try fileManager.removeItem(at: destinationURL!)
} catch let error as NSError {
print(error.debugDescription)
}
}
}
do
{
try fileManager.copyItem(at: location, to: destinationURL!)
}
catch {
print("Error while copy file")
}
DispatchQueue.main.async(execute: {() -> Void in
MBProgressHUD.hide(for: self.view, animated: true)
})
// let videoLink = NSURL(fileURLWithPath: filePath)
let objectsToShare = [destinationURL!] //comment!, imageData!, myWebsite!]
let activityVC = UIActivityViewController(activityItems: objectsToShare , applicationActivities: nil)
activityVC.setValue("Video", forKey: "subject")
//New Excluded Activities Code
if #available(iOS 9.0, *) {
activityVC.excludedActivityTypes = [UIActivity.ActivityType.airDrop, UIActivity.ActivityType.addToReadingList, UIActivity.ActivityType.assignToContact, UIActivity.ActivityType.copyToPasteboard, UIActivity.ActivityType.mail, UIActivity.ActivityType.message, UIActivity.ActivityType.openInIBooks, UIActivity.ActivityType.postToTencentWeibo, UIActivity.ActivityType.postToVimeo, UIActivity.ActivityType.postToWeibo, UIActivity.ActivityType.print]
} else {
// Fallback on earlier versions
activityVC.excludedActivityTypes = [UIActivity.ActivityType.airDrop, UIActivity.ActivityType.addToReadingList, UIActivity.ActivityType.assignToContact, UIActivity.ActivityType.copyToPasteboard, UIActivity.ActivityType.mail, UIActivity.ActivityType.message, UIActivity.ActivityType.postToTencentWeibo, UIActivity.ActivityType.postToVimeo, UIActivity.ActivityType.postToWeibo, UIActivity.ActivityType.print ]
}
if let popoverController = activityVC.popoverPresentationController {
popoverController.sourceView = self.BtnDownloadVideo
popoverController.sourceRect = self.BtnDownloadVideo.bounds
}
self.present(activityVC, animated: true, completion: nil)
}
I'm assuming when you download a file from the internet, you are sure you are downloading a video in this circumstance? And what you are really wanting is just to change the format, i.e. PathExtension such as .mp4, .png, jpeg, etc.
Iff (if and only if) this is the case, then you can add a file extension on to the path component.
let destinationURL = docDirectoryURL.appendingPathComponent("\(destinationFilename!)").appendingPathExtension("mp4")
Now, when you check your saved files, it will include the ".mp4"
Again, I'm assuming you are 110% confident you are downloading a ".mp4" from the interwebs.

Swift: how to display image from html source

How can I display image from html source in some of sites in swift 2.2?
Actually I don't have any JSON or XML.
The important thing is that I have to use regex.
I tried this:
if htmlContent != nil
{
let htmlContent = (item?.htmlContent)! as NSString
var imageSource = ""
let rangeOfString = NSMakeRange(0, htmlContent.length)
let regex = try! NSRegularExpression(pattern: "(<img.*?src=\")(.*?)(\".*?>)", options: [.CaseInsensitive])
if htmlContent.length > 0
{
let match = regex.firstMatchInString(htmlContent as String, options: [.WithTransparentBounds], range: rangeOfString)
if match != nil
{
let imageURL = htmlContent.substringWithRange(match!.rangeAtIndex(2)) as NSString
print(imageURL)
if NSString(string: imageURL.lowercaseString).rangeOfString("feedBurner").location == NSNotFound
{
imageSource = imageURL as String
}
}
}
if imageSource != ""
{
imgHTMLLoader.setImageWithURL(NSURL(fileURLWithPath: imageSource), placeholderImage: UIImage(named: "placeholder"))
print("placeholderImage is not! nil")
}
else
{
imgHTMLLoader.image = UIImage(named: "placeholder")
print("placeholderImage is nil")
}
}
in this sample(library)... htmlContent always is nil.
this sample , use "Helper library" but it dosn't work...
thanks
Using SwiftSoup third party library and Swift 3.0
let doc = try SwiftSoup.parse("<div id=div1><p>Hello</p><p>Another <b>element</b></p><div id=div2><img src=foo.png></div></div>");
for element in try doc.select("img").array(){
try print(element.attr("src"))
}
//foo.png

Swift - Write Image from URL to Local File

I've been learning swift rather quickly, and I'm trying to develop an OS X application that downloads images.
I've been able to parse the JSON I'm looking for into an array of URLs as follows:
func didReceiveAPIResults(results: NSArray) {
println(results)
for link in results {
let stringLink = link as String
//Check to make sure that the string is actually pointing to a file
if stringLink.lowercaseString.rangeOfString(".jpg") != nil {2
//Convert string to url
var imgURL: NSURL = NSURL(string: stringLink)!
//Download an NSData representation of the image from URL
var request: NSURLRequest = NSURLRequest(URL: imgURL)
var urlConnection: NSURLConnection = NSURLConnection(request: request, delegate: self)!
//Make request to download URL
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
if !(error? != nil) {
//set image to requested resource
var image = NSImage(data: data)
} else {
//If request fails...
println("error: \(error.localizedDescription)")
}
})
}
}
}
So at this point I have my images defined as "image", but what I'm failing to grasp here is how to save these files to my local directory.
Any help on this matter would be greatly appreciated!
Thanks,
tvick47
In Swift 3:
Write
do {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsURL.appendingPathComponent("\(fileName).png")
if let pngImageData = UIImagePNGRepresentation(image) {
try pngImageData.write(to: fileURL, options: .atomic)
}
} catch { }
Read
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let filePath = documentsURL.appendingPathComponent("\(fileName).png").path
if FileManager.default.fileExists(atPath: filePath) {
return UIImage(contentsOfFile: filePath)
}
The following code would write a UIImage in the Application Documents directory under the filename 'filename.jpg'
var image = .... // However you create/get a UIImage
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let destinationPath = documentsPath.stringByAppendingPathComponent("filename.jpg")
UIImageJPEGRepresentation(image,1.0).writeToFile(destinationPath, atomically: true)
In swift 2.0, stringByAppendingPathComponent is unavailable, so the answer changes a bit. Here is what I've done to write a UIImage out to disk.
documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
if let image = UIImage(data: someNSDataRepresentingAnImage) {
let fileURL = documentsURL.URLByAppendingPathComponent(fileName+".png")
if let pngImageData = UIImagePNGRepresentation(image) {
pngImageData.writeToURL(fileURL, atomically: false)
}
}
UIImagePNGRepresentaton() function had been deprecated. try image.pngData()
#IBAction func savePhoto(_ sender: Any) {
let imageData = UIImagePNGRepresentation(myImg.image!)
let compresedImage = UIImage(data: imageData!)
UIImageWriteToSavedPhotosAlbum(compresedImage!, nil, nil, nil)
let alert = UIAlertController(title: "Saved", message: "Your image has been saved", preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok", style: .default)
alert.addAction(okAction)
self.present(alert, animated: true)
}
}
Update for swift 5
just change filename.png to something else
func writeImageToDocs(image:UIImage){
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let destinationPath = URL(fileURLWithPath: documentsPath).appendingPathComponent("filename.png")
debugPrint("destination path is",destinationPath)
do {
try image.pngData()?.write(to: destinationPath)
} catch {
debugPrint("writing file error", error)
}
}
func readImageFromDocs()->UIImage?{
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let filePath = URL(fileURLWithPath: documentsPath).appendingPathComponent("filename.png").path
if FileManager.default.fileExists(atPath: filePath) {
return UIImage(contentsOfFile: filePath)
} else {
return nil
}
}