Convert PDF To Base64 in SwiftUI - ios

I am fetching PDF data. It provides me URL, and when I am converting it to base64. This is what I am getting:
The file “file.pdf” couldn’t be opened because you don’t have permission to view it.
I am not getting why is it giving this. This is how I am getting the pdf file url:
#State var openPDF = false
#State var fileName = ""
#State var pdfFile : URL?
var body: some View {
Button {
self.openPDF.toggle()
}, label {
Text("Add Attachment")
}.fileImporter(isPresented: $vm.openPDF, allowedContentTypes: [.pdf]) { result in
do {
let fileURL = try result.get()
print(fileURL)
self.fileName = fileURL.lastPathComponent
self.pdfFile = fileURL
} catch {
print("error getting documents")
print(error.localizedDescription)
}
}
}
An this is how I am converting it to base64:
do {
let filePath = pdfFile
let fileData = try Data.init(contentsOf: filePath!)
let fileStream:String = fileData.base64EncodedString()
print(fileStream)
} catch {
print("error")
print(error.localizedDescription)
}
In this do-catch, it is giving the error that "this file could not be opened because you don't have permission.

Do this to convert PDF to Base64 String:
fileapp.startAccessingSecurityScopedResource() {
defer {
DispatchQueue.main.async {
fileapp.stopAccessingSecurityScopedResource()
}
}
do {
let fileData = try Data.init(contentsOf: fileapp)
let fileStream:String = fileData.base64EncodedString()
print(fileStream)
base64PDF = fileStream
} catch {
print("error")
print(error.localizedDescription)
}
}

Related

Record screen and save video to server in Swift

I have a code that uses ReplayKit to record the screen, and saves it in the documents Directory of the app. The problem comes when I am trying to access it, it does not find the URL of the video.
import SwiftUI
import ReplayKit
import AVKit
struct ContentView: View {
#State var screenRecording = ScreenRecording.singleton
#State var buttonMessage: String = "Start recording"
#State var showVideo: Bool = false
var body: some View {
Button(buttonMessage) {
if screenRecording.recording {
screenRecording.stopRecording()
buttonMessage = "Start recording"
} else {
screenRecording.startRecording()
buttonMessage = "Stop recording"
}
screenRecording.recording = !screenRecording.recording
}
Button("next screen") {
showVideo = true
}
if showVideo {
VideoPlayer(player: AVPlayer(url: screenRecording.getDocument()))
.frame(height: 400)
.onAppear {
print("URL: \(screenRecording.getDocument().path)")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class ScreenRecording: ObservableObject {
var recording: Bool = false
private let screenRecorder = RPScreenRecorder.shared()
static let singleton = ScreenRecording()
private init() {}
func startRecording() {
if !screenRecorder.isRecording && RPScreenRecorder.shared().isAvailable {
screenRecorder.isMicrophoneEnabled = true
screenRecorder.isCameraEnabled = true
screenRecorder.startRecording { err in
guard err == nil else {
print("Failed to start recording...")
return
}
print("Recording starts!")
}
} else {
print("Trying to start recording when a recording is already happening!")
}
}
func stopRecording() {
if screenRecorder.isRecording {
screenRecorder.stopRecording(withOutput: getDocumentDirectoryURL()) { err in
guard err == nil else {
print("An error has ocurred: \(String(describing: err))")
return
}
}
print("Recording stops!")
} else {
print("Trying to stop recording when NO recording is initiated!")
}
}
func getDocumentDirectoryURL() -> URL {
let documentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
print("Filepath is: \(documentDirURL.path)")
return documentDirURL
}
func getDocument() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
print("This re the paths: \(paths)")
return paths[0]
}
}
The method getDocument should return the first URL of the directory, which should be the video file, but it does not work. Where is the problem?
You can check Apple's demo
Recording and Streaming Your macOS App
The relevant code is
func exportClip() {
let clipURL = getDirectory()
let interval = TimeInterval(5)
print("Generating clip at URL: ", clipURL)
RPScreenRecorder.shared().exportClip(to: clipURL, duration: interval) { error in
if error != nil {
print("Error attempting to start Clip Buffering")
} else {
// There isn't an error, so save the clip at the URL to Photos.
self.saveToPhotos(tempURL: clipURL)
}
}
}
func getDirectory() -> URL {
var tempPath = URL(fileURLWithPath: NSTemporaryDirectory())
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd-hh-mm-ss"
let stringDate = formatter.string(from: Date())
print(stringDate)
tempPath.appendPathComponent(String.localizedStringWithFormat("output-%#.mp4", stringDate))
return tempPath
}
now use stopRecording(withOutput: , instead of exportClip(to:
And more:
according to apple's doc
stopRecording(withOutput:completionHandler:)
the movie is written to the specified output URL.
we need the mp4 file

Swift IOS - How can I obtain permission to upload a document when testing on a device?

I am integrating a documentPicker into my IOS app. Selected files will be uploaded using Firebase Storage. I am getting this error when attempting to upload the file while testing the app on my iPhone:
Error uploading file: The file “file.pdf” couldn’t be opened because you don’t have permission to view it.
On the other hand, am not getting this or any other error when testing using the simulator, and the error occurs whether I select a file from iCloud or on local storage.
Here is the code for picker:
struct DocumentPicker: UIViewControllerRepresentable {
#Binding var filePath: URL?
func makeCoordinator() -> DocumentPicker.Coordinator {
return DocumentPicker.Coordinator(parent1: self)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentPicker>) -> UIDocumentPickerViewController {
let picker = UIDocumentPickerViewController(documentTypes: ["public.item"], in: .open)
picker.allowsMultipleSelection = false
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: DocumentPicker.UIViewControllerType, context: UIViewControllerRepresentableContext<DocumentPicker>) {
}
class Coordinator: NSObject, UIDocumentPickerDelegate {
var parent: DocumentPicker
init(parent1: DocumentPicker){
parent = parent1
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
// Here is where I get the path for the file to be uploaded
parent.filePath = urls[0]
print(urls[0].absoluteString)
}
}
}
Here is the upload function, and where the error is caught:
do {
let fileName = (PickedDocument?.lastPathComponent)!
let fileData = try Data(contentsOf: PickedDocument!)
let StorageRef = Storage.storage().reference().child(uid + "/" + doc.documentID + "/" + fileName)
StorageRef.putData(fileData, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
return
}
StorageRef.downloadURL { (url, error) in
guard let urlStr = url else{
completion(nil)
return
}
let urlFinal = (urlStr.absoluteString)
ShortenUrl(from: urlFinal) { result in
if (result != "") {
print("Short URL:")
print(result)
completion(result)
}
else {
completion(nil)
return
}
}
}
}
}
catch {
print("Error uploading file: " + error.localizedDescription)
self.IsLoading = false
return
}
}
Obviously there is some sort of permission that I need to request in order to be able to access and upload files on a physical iPhone, but am not sure how to get that? I have tried adding the following in info.plist but it still didn't work.
<key>NSDocumentsFolderUsageDescription</key>
<string>To access device files for upload upon user request</string>
I figured it out based on an answer I found here.
What I was doing is: I was storing a reference to the local file, which is a security scoped resource. Hence the permission error was thrown.
What I did to get around this: At the moment when the user picks the file, I will use url.startAccessingSecurityScopedResource() to start accessing its content and make a copy of the file instead of holding its reference.
Here is the updated code for the picker didPickDocumentsAt:
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
guard controller.documentPickerMode == .open, let url = urls.first, url.startAccessingSecurityScopedResource() else { return }
defer {
DispatchQueue.main.async {
url.stopAccessingSecurityScopedResource()
}
}
do {
let document = try Data(contentsOf: url.absoluteURL)
parent.file = document
parent.fileName = url.lastPathComponent
print("File Selected: " + url.path)
}
catch {
print("Error selecting file: " + error.localizedDescription)
}
}
And then my Storage upload function is:
func UploadFile(doc: DocumentReference, completion:#escaping((Bool?, String?) -> () )) {
do {
let StorageReference = Storage.storage().reference().child(self.User + "/" + doc.documentID + "/" + fileName!)
StorageReference.putData(file!, metadata: nil) { (metadata, error) in
if let error = error {
self.alertMessage = error.localizedDescription
self.showingAlert = true
completion(false, nil)
}
else {
StorageReference.downloadURL { (url, error) in
if let error = error {
self.alertMessage = error.localizedDescription
self.showingAlert = true
completion(false, nil)
}
else {
ShortenUrl(from: url!.absoluteString) { result in
if (result != "") {
completion(true, result)
}
else {
completion(false, nil)
}
}
}
}
}
}
}
catch {
self.alertMessage = "Error uploading file: " + error.localizedDescription
self.showingAlert = true
completion(false, nil)
}
}

Why am not able to access my model class in Swift Project?

How to access my Model from ViewController and use the Model data to load in table view????
Source Code Link
My ViewController looks like this
import UIKit
class ViewController: UIViewController {
var cclm: CountryCodeListModel?
override func viewDidLoad() {
super.viewDidLoad()
Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(hello), userInfo: nil, repeats: true)
readLocalJSONFile(forName: "countryList")
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
}
#objc func hello()
{
print(cclm?.data?[0].flag)
}
}
and my model class look like this
struct CountryCodeList : Decodable {
var alpha2Code: String?
var alpha3Code: String?
var flag : String?
var name : String?
var code : String?
}
public struct CountryCodeListModel : Decodable {
var data : [CountryCodeList]?
}
var cclm: CountryCodeListModel?
//Method to load json
func readLocalJSONFile(forName name: String) {
do {
if let filePath = Bundle.main.path(forResource: name, ofType: "json") {
let fileUrl = URL(fileURLWithPath: filePath)
let data = try Data(contentsOf: fileUrl)
if let countryCodeObject = parse(jsonData: data) {
cclm = countryCodeObject
print(cclm?.data?[1].alpha2Code ?? "") //Printing Correct Value
}
}
} catch {
print("error: \(error)")
}
}
func parse(jsonData: Data) -> CountryCodeListModel?{
var dataArray : [Dictionary<String,Any>] = [[:]]
var country = Dictionary<String,Any>()
var modelData = Dictionary<String,Any>()
do {
// make sure this JSON is in the format we expect
if let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as? Dictionary<String,Any> {
dataArray.removeAll()
for item in json["data"] as! [Dictionary<String, Any>] {
country = item
let url = URL(string: country["flag"] as? String ?? "")
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
let image = UIImage(data: data!)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileName = url?.lastPathComponent // name of the image to be saved
let fileURL = documentsDirectory.appendingPathComponent(fileName ?? "")
if let data = image?.jpegData(compressionQuality: 1.0){
do {
try data.write(to: fileURL)
country["flag"] = fileURL.absoluteString
//print("file saved")
//urlAsString = fileURL.absoluteString
} catch {
print("error saving file:", error)
}
}
dataArray.append(country)
country.removeAll()
}
modelData["data"] = dataArray
//print(modelData)
let jsonData1 = try JSONSerialization.data(withJSONObject: modelData, options: [])
do {
let decodedData = try JSONDecoder().decode(CountryCodeListModel.self, from: jsonData1)
return decodedData
} catch {
print("error: \(error)")
}
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
return nil
}
Problem statement:
Iam reading local json and take the url value of flag key and download corresponding images to local. Once i download then am taking the localpath and update in the dictionary and then create JSON object and update my model class.
Now, am trying to access my model class from ViewController like below
print(CountryCodeListModel?.data?[0].name) //check screenshot for error
print(cclm?.data?[0].flag) // this prints nil always
Please check the error screenshots attached2
My JSON look like this
{
"meta":{
"success":true,
"message":"Successfully retrieved country details",
"code":"200"
},
"data":[
{
"alpha2Code":"AF",
"alpha3Code":"AFG",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/afg.png",
"name":"Afghanistan",
"code":"+93"
},
{
"alpha2Code":"AX",
"alpha3Code":"ALA",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/ala.png",
"name":"Aland Islands",
"code":"+358"
},
{
"alpha2Code":"AL",
"alpha3Code":"ALB",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/alb.png",
"name":"Albania",
"code":"+355"
},
{
"alpha2Code":"DZ",
"alpha3Code":"DZA",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/dza.png",
"name":"Algeria",
"code":"+213"
},
{
"alpha2Code":"AS",
"alpha3Code":"ASM",
"flag":"https://raw.githubusercontent.com/DevTides/countries/master/asm.png",
"name":"American Samoa",
"code":"+1684"
}
]
}
You are trying to decode something that doesn't exist.
print(CountryCodeListModel?.data?[0].name) //check screenshot for error
print(cclm?.data?[0].flag) // this prints nil always
The above code states that you want:
the name of
the variable data at position 0 of
the struct CountryCodeListModel.
What you want to do is:
the name of
the variable at position 0 of
an INSTANCE of the struct CountryCodeListModel.
For example...
func readLocalJSONFile(forName name: String) {
do {
if let filePath = Bundle.main.path(forResource: name, ofType: "json") {
let fileUrl = URL(fileURLWithPath: filePath)
let data = try Data(contentsOf: fileUrl)
if let countryCodeObject = parse(jsonData: data) {
cclm = countryCodeObject
print(cclm?.data?[1].alpha2Code ?? "") //Printing Correct Value
print(cclm?.data?[0].flag ?? "")
print(countryCodeObject?.data[0].flag ?? "") // Same as the line above
}
}
} catch {
print("error: \(error)")
}
}
Unless you are trying to use a static variable (at which you would use CountryCodeListModel.data), you need to make sure you are actually using an instance of the structure or an object of a class to reference your properties.
CAVEAT
CountryCodeListModel is a structure. CountryCodeListModel() is an instance of the structure CountryCodeListModel. Since you can have multiple instances of a structure, you need to reference a specific structure when accessing data. Thus, CountryCodeListModel.data will not work and it needs to be CountryCodeListModel().data. In this case, you have cclm.data.

PropertyList Decoder decode returns nil

I encode a array of structs. When encoded the returned data has some bytes, which tells me that something was written to the file. But when I decode it returns nil. I don't get any error while decoding. I dont understand why it returns nil after decode.
var allEndpts = [EndPt]()
struct EndPt : Codable {
var contactStruct = ContactStruct()
var purpose: String = String()
}
struct ContactStruct: Codable {
var firstName:String? = nil
var lastName:String? = nil
}
private func saveEndPoints() {
do {
delegate.documentDirectoryUrl = try FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false
)
let localFileUrl:URL =
delegate.documentDirectoryUrl!
.appendingPathComponent("EndPoints")
UserDefaults.standard.set(localFileUrl, forKey: "localEndPtUrl")
print("localEndPtUrl: \(localFileUrl)")
do {
let encoder = PropertyListEncoder()
let data = try encoder.encode(self.allEndpts)
try data.write(to: localFileUrl)
} catch {
print(error)
}
} catch {
print("error")
}
retrieveFromFile()
}
func retrieveFromFile() {
typealias TempArray = [EndPt]
var temp: TempArray?
let localFileUrl = UserDefaults.standard.url( forKey: "localEndPtUrl")
print("localEndPtUrl: \(localFileUrl)")
do {
let data = try Data(contentsOf: localFileUrl!)
let temp = try PropertyListDecoder().decode(TempArray.self, from: data)
print("EndPt Array Dump: ", temp)
} catch {
print("read error:", error)
}
}
The problem is that
var temp: TempArray?
will always be nil unless you change it. And you never change it. When you say
let temp = try PropertyListDecoder().decode(TempArray.self, from: data)
that is a different temp.

Swift decodable can't access nested data in array

I get a critical error stating the following. I've tried everything but I can't seem to access the Movie struct as it says the parent 'Type' has no member called 'data', even though it clearly does.
"Value of type '[Type?]' has no member 'data'"
MODEL
struct SearchData: Decodable {
let data: [Type?]
}
struct Type: Decodable {
let data: [Movie?]
}
struct Movie: Decodable {
let title: String?
}
CONTROLLER
fileprivate var searchResults = [Movie?]()
func fetchTitles() {
let urlString = "https://www.what-song.com/api/search?limit=10&field=america"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
// if error occurs
if let err = err {
print("Failed to fetch titles", err)
return
}
// if success
guard let data = data else { return }
do {
let searchResult = try JSONDecoder().decode(SearchData.self, from: data)
self.searchResults = searchResult.data.data
print(searchResult)
} catch {
print("Failed to decode JSON:", error)
}
}.resume()
}
Try this :
var movieTitles = [String]()
for type in searchResult.data {
for movie in type.data {
guard let title = movie.title else { return }
print(title)
movieTitles.append(title)
}
}
you are doing a small mistake here I think
searchResult.data
will return you an array of
Type
You need to parse that array as well, something like this
searchResults = (searchResult.data[0]?.data)!

Resources