iOS Swift Share Extension crashes when I open a URL - ios

How the title says, my Share extension crashes when I open a url.
Xcode says: Thread 3: EXC_BAD_ACCESS (code=1, address=0x10)
I already tried with other url schemes and the result is same.
This is my code:
import UIKit
import MobileCoreServices
class ShareViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let content = extensionContext!.inputItems[0] as! NSExtensionItem
let text = kUTTypeText as String
for attachment in content.attachments as! [NSItemProvider] {
if attachment.hasItemConformingToTypeIdentifier(text) {
attachment.loadItem(forTypeIdentifier: text, options: nil, completionHandler: { (data, error) in
if error == nil {
let url = data as! URL
print(url)
print("mSwift://?\(url.absoluteString)")
let mSwift = URL(string: "mSwift://?\(url.absoluteString)")
self.extensionContext!.open(mSwift!, completionHandler: nil) // Crash here
}else {
}
})
}
}
}
}

Related

IOS application freezes after link share

I am new to ios development. I am doing my first project and I want to implement a small application that will allow saving favorite links from the browser. I am using SwiftUI for main app + share extension component to pass link to main app from browser.
The flow looks like this: I find an interesting site, click the Share button and select the icon for my application. After that, my application opens and there I see my link. This works successfully, but after that the browser freezes completely. If I post a link from other apps, the same thing happens.
Below is the code of the share extension and the component of the main application that reads the link:
#objc(ShareExtensionViewController)
class ShareViewController: UIViewController {
private static let URL_STORAGE_KEY = "url"
private static let APP_URL = "MyApp://"
private static let APP_GROUP_NAME = "group.ru.myapp"
override func viewDidLoad() {
super.viewDidLoad()
// set link to UserDefaults
handleShared()
// open main app from extension
openMainApp()
}
#objc
#discardableResult
func openURL(_ url: URL) -> Bool {
var responder: UIResponder? = self
while responder != nil {
if let application = responder as? UIApplication {
return application.perform(#selector(openURL(_:)), with: url) != nil
}
responder = responder?.next
}
return false
}
private func openMainApp() {
DispatchQueue.global(qos: .background).async {
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: { _ in
let url = URL(string: ShareViewController.APP_URL)!
self.openURL(url)
})
}
}
private func handleShared() {
let attachments = (extensionContext?.inputItems.first as? NSExtensionItem)?.attachments ?? []
let contentType = kUTTypeURL as String
for provider in attachments {
if provider.hasItemConformingToTypeIdentifier(contentType) {
provider.loadItem(forTypeIdentifier: contentType, options: nil) {
[unowned self] (data, error) in
guard error == nil else {
return
}
let userDefaults = UserDefaults(suiteName: ShareViewController.APP_GROUP_NAME)!
userDefaults.set(data, forKey: ShareViewController.URL_STORAGE_KEY)
}
}
}
}
}
Snippet of the main application code:
static func tryGetLink() -> String? {
let userDefaults = UserDefaults(suiteName: DataStorage.APP_GROUP)
return userDefaults?.object(forKey: URL_KEY) as? String
}
What could be the reason for these freezes?

Share Extension not working when I long press a link

I've built a sharing extension for my iOS app. The goal is to store all URLs user shared in UserDefaults variable. Here is my code:
import UIKit
import Social
import MobileCoreServices
class ShareViewController: SLComposeServiceViewController {
override func viewDidLoad() {
let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem
let itemProvider = extensionItem.attachments?.first as! NSItemProvider
let propertyList = String(kUTTypePropertyList)
if itemProvider.hasItemConformingToTypeIdentifier(propertyList) {
itemProvider.loadItem(forTypeIdentifier: propertyList, options: nil, completionHandler: { (item, error) -> Void in
guard let dictionary = item as? NSDictionary else { return }
OperationQueue.main.addOperation {
if let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as? NSDictionary,
let urlString = results["URL"] as? String,
let url = NSURL(string: urlString) {
print("URL retrieved: \(urlString)")
let userDefaults = UserDefaults(suiteName: "group.my.app")
if userDefaults?.object(forKey: "extensionArticles") == nil {
userDefaults?.setValue([urlString], forKey: "extensionArticles")
} else {
var urls = userDefaults?.object(forKey: "extensionArticles") as? [String]
urls?.append(urlString)
print("im here in the extension")
dump(urls)
userDefaults?.setValue(urls, forKey: "extensionArticles")
}
}
}
})
} else {
print("error")
}
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector: #selector(self.didSelectPost), userInfo: nil, repeats: false)
}
override func isContentValid() -> Bool {
// Do validation of contentText and/or NSExtensionContext attachments here
return true
}
override func didSelectPost() {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
print("posted")
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return []
}
}
When I go to the webpage in Safari and share it being on that page, everything works fine. However, when I long press (aka 3d touch) on a link in Safari, and use my extension from there, it won't add a link to UserDefaults. I couldn't find how to fix this, plz help 😭

How to navigate Share Extension to host app in Swift?

How to navigate Share Extension to host app in Swift after getting URL from ShareExtension?
import UIKit
import Social
import MobileCoreServices
class ShareViewController: UIViewController {
let sharedKey = "shareappKey"
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Picked URL"
getURL()
}
#IBAction func nextAction(_ sender: Any) {
self.redirectToHostApp()
}
#IBAction func cancelAction(_ sender: Any) {
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
func redirectToHostApp() {
let url = URL(string: "SelectedURL:\(sharedKey)")
var responder = self as UIResponder?
let selectorOpenURL = sel_registerName("openURL:")
while (responder != nil) {
if (responder?.responds(to: selectorOpenURL))! {
let _ = responder?.perform(selectorOpenURL, with: url)
}
responder = responder!.next
}
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
func getURL() {
if let item = extensionContext?.inputItems.first as? NSExtensionItem {
if let itemProvider = item.attachments?.first {
if itemProvider.hasItemConformingToTypeIdentifier("public.url") {
itemProvider.loadItem(forTypeIdentifier: "public.url", options: nil, completionHandler: { (url, error) -> Void in
if let error = error {
print("error :-", error)
}
if (url as? NSURL) != nil {
// send url to server to share the link
do {
if (url as? URL) != nil {
// do what you want to do with shareURL
print("Selected URL :- ", url as Any)
print(url as Any)
let dict: [String : Any] = ["imgData" : url as Any, "name" : "Added" as Any]
print(dict)
let userDefault = UserDefaults.init(suiteName: "group.com.abcd.shareapp")
userDefault?.set(dict, forKey: self.sharedKey)
userDefault?.synchronize()
// Here I got Struct
}
}catch let err{
print(err)
}
}
self.extensionContext?.completeRequest(returningItems: [], completionHandler:nil)
})
}
}
}
}
}
// Host App
import UIKit
class ViewController: UIViewController {
var urlString = String()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let userDefault = UserDefaults.standard
userDefault.addSuite(named: "group.com.abcd.shareapp")
if let dict = userDefault.value(forKey: "img") as? NSDictionary{
let data = dict.value(forKey: "imgData") as! Data
let str = dict.value(forKey: "name") as! String
print("Data is :- ", data)
print("Str is :- ", str)
userDefault.removeObject(forKey: "img")
userDefault.synchronize()
// Here i need to get that URL from Share Extention
}
}
}
Error
2019-07-15 22:01:15.045361+0530 LearingAppShare[3183:73775] [User
Defaults] Attempt to set a non-property-list object {
imgData = "https://m.jagran.com/lite/cricket/headlines-sachin-tendulkar-son-arjun-tendulkar-picked-for-rs-5-lakh-for-t20-mumbai-league-19192257.html";
name = Added; } as an NSUserDefaults/CFPreferences value for key URLKey 2019-07-15 22:01:15.047424+0530 LearingAppShare[3183:73775]
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'Attempt to insert non-property
list object {
imgData = "https://m.jagran.com/lite/cricket/headlines-sachin-tendulkar-son-arjun-tendulkar-picked-for-rs-5-lakh-for-t20-mumbai-league-19192257.html";
name = Added; } for key URLKey'
I have done answer on own Question.
You can store in dictionary.
But UserDefaults can't save dictionary with custom data types like Image or URL .
So you need to convert dictionary to Data first before saving in defaults
import UIKit
import Social
import MobileCoreServices
class ShareViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "Picked URL"
getURL()
}
#IBAction func nextAction(_ sender: Any) {
self.redirectToHostApp()
}
#IBAction func cancelAction(_ sender: Any) {
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
func redirectToHostApp() {
let url = URL(string: "YourOwnURLscheme:\(sharedKey)")
var responder = self as UIResponder?
let selectorOpenURL = sel_registerName("openURL:")
while (responder != nil) {
if (responder?.responds(to: selectorOpenURL))! {
let _ = responder?.perform(selectorOpenURL, with: url)
}
responder = responder!.next
}
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
func getURL() {
if let item = extensionContext?.inputItems.first as? NSExtensionItem {
if let itemProvider = item.attachments?.first {
if itemProvider.hasItemConformingToTypeIdentifier("public.url") {
itemProvider.loadItem(forTypeIdentifier: "public.url", options: nil, completionHandler: { (url, error) -> Void in
if let error = error {
print("error :-", error)
}
if (url as? NSURL) != nil {
// send url to server to share the link
do {
var urlData: Data!
if let url = url as? URL{
urlData = try Data(contentsOf: url)
}
let dict: [String : Any] = ["urlData" : urlData as Any, "name" : self.contentText as Any]
print(dict)
let userDefault = UserDefaults(suiteName: "group.com.abcd.sharecontent1")
userDefault?.set(dict, forKey: "storedURLData")
userDefault?.synchronize()
}catch let err{
print(err)
}
}
})
}
}
}
}
}
// Host App
import UIKit
class ViewController: UIViewController {
#IBOutlet var imgView: UIImageView!
#IBOutlet var lblText: UILabel!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let userDefault = UserDefaults(suiteName: "group.com.abcd.sharecontent1")
if let dict = userDefault?.value(forKey: "storedURLData") as? NSDictionary{
let data = dict.value(forKey: "urlData") as! Data
let str = dict.value(forKey: "name") as! String
print("Data is :- ", data)
print("str is :- ", str)
self.lblText.text = str
userDefault?.removeObject(forKey: "storedURLData")
userDefault?.synchronize()
}
}
}

Thread 1: EXC_BAD_ACCESS (code=2, address=..) using Macaw Pod iOS Swift?

I'm using Macaw to parse and render an SVG file gotten from the server.
Here's the source code: https://pastebin.com/g9vUCpGX
How can I accomplish this task?
class ViewController: UIViewController{
#IBOutlet weak var profileBadge: SVGView!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/couchdb.svg")!
if profileBadge != nil{
profileBadge.loadSVG(from: url)
}
}
}
extension SVGView {
func loadSVG(from url: URL) {
DispatchQueue.global().async {
guard let data = try? Data(contentsOf: url) else {
return
}
guard let svgString = String(data: data, encoding: .utf8) else {
return
}
let node = (try? SVGParser.parse(text: svgString)) ?? Group()
DispatchQueue.main.async {
print(node)
self.node = node
}
}
}
}
You may use XIB or Storyboard.. It works using like below.
import UIKit
import Macaw
import SnapKit
class ViewController: UIViewController{
var profileBadge: SVGView = SVGView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(profileBadge)
profileBadge.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
let url = URL(string: "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/couchdb.svg")!
if profileBadge != nil{
profileBadge.loadSVG(from: url)
}
}
}
extension SVGView {
func loadSVG(from url: URL) {
DispatchQueue.global().async {
guard let data = try? Data(contentsOf: url) else {
return
}
guard let svgString = String(data: data, encoding: .utf8) else {
return
}
let node = (try? SVGParser.parse(text: svgString)) ?? Group()
DispatchQueue.main.async {
print(node)
self.node = node
}
}
}
}
Was able to figure out the issue. :) profileBadge should've been a UIView not UIImageView. And in the Identity Inspector, Class should've been SVGView and Module should've been Macaw.

How to get the page title with swift in Share Extension

I'm developing share extension which is used on safari.
I could get url on share extension. but I cant get page title.
let puclicURL = String(kUTTypeURL)
if itemProvider.hasItemConformingToTypeIdentifier(puclicURL) {
itemProvider.loadItem(forTypeIdentifier: puclicURL, options: nil, completionHandler: {
(item, error) in
if let url: NSURL = item as? NSURL {
print("url", url)
// I want page title also
}
}
}
And, I tried below code.https://stackoverflow.com/a/33139355/5060282
I think below code can run only in Action Extension. not Share Extension.
let propertyList = String(kUTTypePropertyList)
if itemProvider.hasItemConformingToTypeIdentifier(propertyList) {
itemProvider.loadItem(forTypeIdentifier: propertyList, options: nil, completionHandler: { (item, error) -> Void in
let dictionary = item as! NSDictionary
OperationQueue.main.addOperation {
let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary
let title = NSURL(string: (results["title"] as! String))
//yay, you got the title now
print(title)
}
})
} else {
print("error")
}
// But, error...

Resources