Swift 4 vCard on touch add to contacts - swift

I have scoured the internet looking to find a way to save a persons contact information. Currently on my mobile website I have a vCard that when clicked on a phone asks the user if they would like to add the contact to their contacts. In swift however loading that same link generates a 404 error page. So how would I go about doing this? I want when the user clicks on the icon a pop up displays asking the user if they want to save the contact to their phone. The data is being pulled in through a json api. I assume I need to take this data and format it in a specific way. Any suggestions or pointers in a direction to take this is much appreciated.
Thanks
Update: Here is my attempt at some code for this. When this prints to the console I get the vcard output, but an error is thrown on the JSONSerialization. Maybe someone can point me in the right direction.
#IBAction func contactTapped(_ sender: Any) {
let contact = createContact()
do {
try shareContacts(contacts: [contact])
} catch {
print("Error printing contact")
}
}
func shareContacts(contacts: [CNContact]) throws {
guard let directoryURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
return
}
var filename = NSUUID().uuidString
if let contact = contacts.first, contacts.count == 1 {
if let fullname = CNContactFormatter().string(from: contact) {
filename = fullname.components(separatedBy: " ").joined(separator: "")
}
}
let fileURL = directoryURL
.appendingPathComponent(filename)
.appendingPathComponent("vcf")
let data = try CNContactVCardSerialization.data(with: contacts)
print("filename: \(filename)")
print("contact: \(String(describing: String(data: data, encoding: String.Encoding.utf8)))")
try JSONSerialization.data(withJSONObject: JSONEncoder(), options: JSONSerialization.WritingOptions.prettyPrinted)
let activityViewController = UIActivityViewController(
activityItems: [fileURL],
applicationActivities: nil
)
present(activityViewController, animated: true, completion: {})
}
func createContact() -> CNContact {
// Creating a mutable object to add to the contact
let contact = CNMutableContact()
contact.imageData = NSData() as Data // The profile picture as a NSData object
contact.givenName = fullNameLbl.text!
//contact.familyName = "Appleseed"
let workEmail = CNLabeledValue(label:CNLabelWork, value: emailLbl.text! as NSString)
contact.emailAddresses = [workEmail]
contact.phoneNumbers = [CNLabeledValue(
label:CNLabelPhoneNumberiPhone,
value:CNPhoneNumber(stringValue: phoneLabel.text!))]
let store = CNContactStore()
let saveRequest = CNSaveRequest()
saveRequest.add(contact, toContainerWithIdentifier: nil)
try! store.execute(saveRequest)
return contact
}

Related

Facing issues with ContentBlockerRequestHandler of Safari extension

I am currently working on a safari app extension that blocks content. I want the user to configure the rule (turning a rule on and off). Since I can’t overwrite the bundled JSON files and we can’t write to the documents folder, as it’s not accessible to the extension I decided to use App Groups. My approach looks like this:
Within the ContentBlockerRequestHandler I want to save the blockerList.json into the app group (Only when launched for the first time)
When this is done I want that the handler reads from the app group by taking the url of my json which is within the app group instead of taking the default json in the extension
Since I can not debug the handler I don't know if I am on the right path. The following shows my code:
class ContentBlockerRequestHandler: NSObject, NSExtensionRequestHandling {
func beginRequest(with context: NSExtensionContext) {
guard let rulesUrl = loadRules() else {
let clonedRules = cloneBlockerList()
save(rules: clonedRules)
return
}
guard let attachment = NSItemProvider(contentsOf: rulesUrl) else { return }
let item = NSExtensionItem()
item.attachments = [attachment]
context.completeRequest(returningItems: [item], completionHandler: nil)
}
private func cloneBlockerList() -> [Rule] {
var rules: [Rule] = []
if let url = Bundle.main.url(forResource: "blockerList", withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(ResponseData.self, from: data)
rules = jsonData.rules
} catch {
print("error:(error)")
}
}
return rules
}
private func save(rules: [Rule]) {
let documentsDirectory = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "my group identifier")
let archiveURL = documentsDirectory?.appendingPathComponent("rules.json")
let encoder = JSONEncoder()
if let dataToSave = try? encoder.encode(rules) {
do {
try dataToSave.write(to: archiveURL!)
} catch {
// TODO: ("Error: Can't save Counters")
return;
}
}
}
private func loadRules() -> URL? {
let documentFolder = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "my group identifier")
guard let jsonURL = documentFolder?.appendingPathComponent("rules.json") else {
return nil
}
return jsonURL
}
}
Thankful for any help

What is the way to save fonts and sizes in Firebase for TextView Swift

I would like to save Fonts, sizes, and alignments in Firebase for TextView in Swift
so that I can call it in another view.
I was only able to save the color in the Firebase.
Here is the code that shows how I did it.
#IBAction func SendBtn(_ sender: Any) {
if let profileImage = SelecSubStorge , let profileIMG = profileImage.jpegData(compressionQuality: 0.1) {
let udidImage = NSUUID().uuidString
let storeg = Storage.storage().reference(withPath: "gs://myproduct-744cb.appspot.com").child(udidImage)
storeg.putData(profileIMG, metadata: nil) { metaDate, error in
if error != nil {
print(error?.localizedDescription)
return
} else {
storeg.downloadURL { url, error in
let imgSub = url?.absoluteString
let ref = Database.database().reference()
let sub = ref.child("Sub")
let udid = sub.childByAutoId().key
let setRef = sub.child(udid!)
let value = ["sub": self.subjectLB.text , "detiles" : self.detilesTextview.text , "ImgSub" : imgSub , "SubID" : udid , self.labelColor : "Color"] as [AnyHashable : Any]
setRef.setValue(value) { error, ref in
if error != nil {
print(error?.localizedDescription)
} else {
let alert = UIAlertController(title: "Done", message: "Done Send", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}
}
}
}
}
#IBAction func ChangeColorBtn(_ sender: Any) {
let colorPicher = UIColorPickerViewController()
colorPicher.delegate = self
present(colorPicher, animated: true, completion: nil)
}
func colorPickerViewControllerDidSelectColor(_ viewController: UIColorPickerViewController) {
let color = viewController.selectedColor
detilesTextview.textColor = color
labelColor = color
}
There are a few ways to do this. First, see my answer to this question Save Attributed Strings To Firestore to see it in done in Firestore.
This answer would apply to the Realmtime Database but he concept is similar.
Cast the attributed string from the textView to NSData and then store it as a string in Firebase.
Firebase supports NSString (String in Swift) but only the actual UTF-8 characters and not the formatting and style.
So the key is to capture the text in the textView as an attributed string, archive it as NSData (Data) and then save the textual version of that, base64Encoded, in Firebase. Then when reading back, do the opposite.
Here's how to store text from a sourceTextView in Firebase
func saveAttrStringToFirebase() {
let attrString = self.sourceTextView.attributedString()
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: attrString, requiringSecureCoding: false)
let dataString = data.base64EncodedString()
let ref = self.ref.child("attributed_string") //self.ref points to my Firebase
ref.setValue(dataString)
} catch let err as NSError {
print(err.localizedDescription)
}
}
So then the Firebase will look something like this
root
attributed_string: "YnBsaXN0MDDUAQIDB..."
Then read back in the value at attributed_string, which will just be a string. Then cast it back to Data and lastly unarchive it
func getAttrStringFromFirebase() {
let ref = self.ref.child("attributed_string")
ref.observeSingleEvent(of: .value, with: { snapshot in
if let dataString = snapshot.value as? String {
do {
guard let data = Data(base64Encoded: dataString) else { return }
guard let attrString = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSAttributedString.self, from: data) else { return }
self.destinationTextView.textStorage?.append(attrString)
} catch let err as NSError {
print(err.localizedDescription)
}
}
})
}
As the code that you provided, you can add more fonts, sizes, alignments, or other data by the line :)
let value = ["sub": self.subjectLB.text , "detiles" : self.detilesTextview.text , "ImgSub" : imgSub , "SubID" : udid , self.labelColor : "Color"] as [AnyHashable : Any]
FYI: Firebase supports a variety of data types but not all, please see this link for The data types that Cloud Firestore supports

open .pkpass file from Wallet using share extension iOS

I am trying to open .pkPass file from wallet using share extension, but its not opening. Basically its returning data and I want URL. but If I try to open .pkpassfile data in PKAddPassesViewController its not open it says Attempt to present on whose view is not in the window hierarchy!.
ShareViewController
override func didSelectPost() { if let item = self.extensionContext?.inputItems[0] as? NSExtensionItem{
print("Item \(item)")
for ele in item.attachments!{
print("item.attachments!======>>> \(ele as! NSItemProvider)")
let itemProvider = ele as! NSItemProvider
print(itemProvider)
if itemProvider.hasItemConformingToTypeIdentifier("com.apple.pkpass"){
imageType = "com.apple.pkpass"
}
if itemProvider.hasItemConformingToTypeIdentifier("com.apple.pkpass"){
imageType = "com.apple.pkpass"
}
print("imageType : \(imageType)")
if itemProvider.hasItemConformingToTypeIdentifier(imageType){
print("True")
itemProvider.loadItem(forTypeIdentifier: imageType, options: nil, completionHandler: { (item, error) in
print(item)
var imgData: Data!
if let url = item as? Data{
imgData = url
//try! Data(contentsOf: url)
self.openPass(withName: url)
}else {
print("sorry it does not work on others")
}
})
}
}
}self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
open PkPass file Functions
func openPass(withName passData: Data?) {
var error: Error? = nil
var newPass: PKPass? = nil
do {
newPass = try PKPass(data: passData as! Data)
} catch {
}
if error != nil {
UIAlertView(title: "Passes error", message: error!.localizedDescription, delegate: nil, cancelButtonTitle: "Ooops", otherButtonTitles: "").show()
return
}
let addController = PKAddPassesViewController(pass: newPass!)
addController!.delegate = self
present(addController!, animated: true)
}
its not opening it says Attempt to present on whose view is not in the window hierarchy!
basically I want to open .pkpass file from wallet app using share extension in my app and then user can print or share the pass using my application. I want to get url of .pkpass file then save it to userdefults, now the issue is this .pkpass file is returning data instead of url
so can anyone help to fix the issue or give suggestion regarding opening .pkpass file from wallet app using share extension thankx

Swift : Trouble Exporting Database

I'm trying to Export my Database.
I've tried by Email and by sharing it.
The thing is that I want to export the current state of the dataBase (with all the information in it).
I've tried this code :
func exportDatabase(){
var url:String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last! as String
url = url + "/WalletDatabase.sqlite"
let fileManager = FileManager.default
if fileManager.fileExists(atPath: url) {
sendMail(sUrl: url)
}else{
print("error")
}
}
func sendMail(sUrl:String){
if( MFMailComposeViewController.canSendMail() ) {
print("Can send email.")
let fileManager = FileManager.default
let mailComposer = MFMailComposeViewController()
mailComposer.setToRecipients(["***#gmail.com"])
mailComposer.mailComposeDelegate = self
//Set the subject and message of the email
mailComposer.setSubject("Lorem Ipsum")
mailComposer.setMessageBody("Lorem Ipsum.", isHTML: false)
if let fileData = NSData(contentsOfFile: sUrl) {
print("File data loaded.")
mailComposer.addAttachmentData(fileData as Data, mimeType: "application/x-sqlite3", fileName: "WalletDatabase")
}
let fileData = fileManager.contents(atPath: sUrl)
} else {
print("error")
}
But :
The file send doesn't have any type
The database is empty, only the table and col remain
Could you guys give me a little help
I fix it by adding this following code :
static func migrateStoreSwift() -> NSURL
{
let lApp:AppController = UIApplication.shared.delegate as! AppController;
let lCurrentStore:NSPersistentStore = lApp.persistentStoreCoordinator.persistentStores.last!
let lNewDataBase = "Database.sqlite"
let lNewStoreURL:NSURL = lApp.applicationDocumentsDirectory()?.appendingPathComponent(lNewDataBase) as! NSURL
try! lApp.persistentStoreCoordinator.migratePersistentStore(lCurrentStore, to: lNewStoreURL as URL, options: nil, withType: NSSQLiteStoreType)
return lNewStoreURL
}
This code create a copy of the current version of the database.

Setting Alamofire custom destination file name instead of using suggestedDownloadDestination

I got many lists of invoice file at my table view as well as many download buttons at each cell.When I clicked one of it,it will download the invoice file.But,the problem is the server response suggested file name is "invoice.pdf" at every file I downloaded.So,I need to edit the file name manually before I save to document after it was downloaded.So,how to edit the file name manually after it was download successfully and save it in document as temporaryurl without using Alamofire.Request.suggestedDownloadDestination.
Here is my download function.
func downloadInvoice(invoice: Invoice, completionHandler: (Double?, NSError?) -> Void) {
guard isInvoiceDownloaded(invoice) == false else {
completionHandler(1.0, nil) // already have it
return
}
let params = [
"AccessToken" : “xadijdiwjad12121”]
// Can’t use the destination file anymore because my server only return one file name “invoice.pdf” no matter which file i gonna download
// So I have to manually edit my file name which i saved after it was downloaded.
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
// So I have to save file name like that ““2016_04_02_car_invoice_10021.pdf” [Date_car_invoice_timestamp(Long).pdf]
// Please look comment on tableView code
Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders?.updateValue("application/pdf",forKey: "Content-Type")
Alamofire.download(.POST, invoice.url,parameters:params, destination: destination)
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
print(totalBytesRead)
dispatch_async(dispatch_get_main_queue()) {
let progress = Double(totalBytesRead) / Double(totalBytesExpectedToRead)
completionHandler(progress, nil)
}
}
.responseString { response in
print(response.result.error)
completionHandler(nil, response.result.error)
}
}
Here is the table view which gonna check downloaded file and when it click,shown on open in feature.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if let invoice = dataController.invoices?[indexPath.row] {
dataController.downloadInvoice(invoice) { progress, error in
// TODO: handle error
print(progress)
print(error)
if (progress < 1.0) {
if let cell = self.tableView.cellForRowAtIndexPath(indexPath), invoiceCell = cell as? InvoiceCell, progressValue = progress {
invoiceCell.progressBar.hidden = false
invoiceCell.progressBar.progress = Float(progressValue)
invoiceCell.setNeedsDisplay()
}
}
if (progress == 1.0) {
// Here where i gonna get the downloaded file name from my model.
// invoice.filename = (Assume “2016_04_02_car_invoice_10021”)
if let filename = invoice.filename{
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docs = paths[0]
let pathURL = NSURL(fileURLWithPath: docs, isDirectory: true)
let fileURL = NSURL(fileURLWithPath: filename, isDirectory: false, relativeToURL: pathURL)
self.docController = UIDocumentInteractionController(URL: fileURL)
self.docController?.delegate = self
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) {
self.docController?.presentOptionsMenuFromRect(cell.frame, inView: self.tableView, animated: true)
if let invoiceCell = cell as? InvoiceCell {
invoiceCell.accessoryType = .Checkmark
invoiceCell.setNeedsDisplay()
}
}
}
}
}
}
}
So,my question is simple.I just don't want to use that code
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
because it use response.suggestedfilename.And I want to save file name manually on selected table view cell data.Any Help?Please don't mind that I posted some code in my question because I want everyone to see it clearly.
Destination is of type (NSURL, NSHTTPURLResponse) -> NSURL. so you can do something like this
Alamofire.download(.POST, invoice.url,parameters:params, destination: { (url, response) -> NSURL in
let pathComponent = "yourfileName"
let fileManager = NSFileManager.defaultManager()
let directoryURL = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let fileUrl = directoryURL.URLByAppendingPathComponent(pathComponent)
return fileUrl
})
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
print(totalBytesRead)
dispatch_async(dispatch_get_main_queue()) {
let progress = Double(totalBytesRead) / Double(totalBytesExpectedToRead)
completionHandler(progress, nil)
}
}
.responseString { response in
print(response.result.error)
completionHandler(nil, response.result.error)
}
}
Swift 3.0
in swift 3.0 it's DownloadFileDestination
Alamofire.download(url, method: .get, to: { (url, response) -> (destinationURL: URL, options: DownloadRequest.DownloadOptions) in
return (filePathURL, [.removePreviousFile, .createIntermediateDirectories])
})
.downloadProgress(queue: utilityQueue) { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}
for more go to the Alamofire