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
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?
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 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()
}
}
}
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.
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...