SwiftUI Universal Links not working for NFC - swift

Our app uses universal links and the AASA file seems to be working fine because hyperlinks in emails, as well as QR coded URLs both launch the app. They both also cause the SwiftUI .onOpenURL(perform:) function to be called.
Things are a bit more confusing with NFC. The URL is recognised and the app launches, suggesting the issue is not related to AASA. However, the onOpenURL function is not called. Can anyone tell me how to handle universal links from NFC? Ideally I'd like to keep it pure SwiftUI but if I need to use the AppDelegate so be it.
The code in the main SwiftUI file is:
import SwiftUI
#main
struct MyApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL(perform: { url in
print("url opened:\(url)")
// prints when opened from URL in email,
// Notes or QR code, but not when opened
// from URL embedded in NFC NDEF
})
}
}
}
I have also tried defining the application(_:continue:restorationHandler:) function in the AppDelegate, but that doesn't get called either.
Thanks for any help.

The solution is to add a continue user activity handler on a suitable view:
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { userActivity in
print("Continue activity \(userActivity)")
guard let url = userActivity.webpageURL else {
return
}
print("User wants to open URL: \(url)")
// TODO same handling as done in onOpenURL()
}
It's not obvious why the same Universal Link received through a click on a link in Safari should be handled differently than the same link read from a tag, but apparently it's not the same thing.

In case it helps anyone with the same issue, I solved the issue by registering a URL schema for the app and then using .onOpenURL(perform:).
Seems a strange way to have to do it but it works as required, so happy days!

Related

Why does the YouTube IFrame API for iOS devices send messages with a youtubeplayer:// protocol?

I am trying to use pieces of the YouTubePlayerKit for a project I am working on. It looks like the library does the following:
Creates a player instance from a videoId
Creates a webview to load the video in
Evaluates some JS to get the video loaded
Re: the 3rd step, I was browsing the source code and i saw this function:
// Send YouTubePlayer Event with optional data
function sendYouTubePlayerEvent(event, data) {
var locationHref = 'youtubeplayer://' + event;
if (data) {
locationHref = locationHref + '?data=' + data;
}
window.location.href = locationHref
}
What I can't understand is, why are they using a youtubeplayer:// protocol? Is it inferring where to send events based on the name of the player class? Just for reference, I was looking at this library as well, and they use the protocol ytplayer://. What's going on here?
ytplayer:// is the start of a URL
https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app
https://developer.apple.com/ios/universal-links/
From the looks of your code it would ultimately look like
youtubeplayer://play?data=fghdfgjhdfgjdfgj
With fghdfgjhdfgjdfgj being some data object.
window.location would take that URL and do something with it like set the location of the current view or direct the webview to that website.
If you look at the code for the "Widget Code Along" You will find a good example for this setup, they only glance over it in the video but a
Link in a Widget uses a URL to open the detail view of a character.
https://developer.apple.com/news/?id=yv6so7ie

My CPU Usgage is constant 100% (Xcode, using SwiftUI)

My SwiftUI App is extremly lagging and I have no Idea why. Even if I only compile this code, my CPU Power goes up to 100%, even if I dont touch anything:
#main
struct Order_Corner_V8App: App {
var body: some Scene {
return WindowGroup {
TabView{
Text("").tabItem {
Text("click")
}
Text("").tabItem {
Text("click")
}
}
}
}
}
I need the TabView. I put my whole Files in an other new Project, the same thing happend again. Maybe its usefull to know that im working with Firebase.
I'm targeting iOS.
I've already watched to my App in 'Instruments', it only says that the problem is my main View.
Please help me.
Here is some image that shows my CPU Data
Thanks, Boothosh
Okay I got the solution: I tried the App on an other phone and everything works fine. I think this is an mistake of my phone, but I don't know what I can do against that.
Thanks, Boothosh
In my case, I found Xcode source control caused the problem
To fix this problem Xcode -> Preferences -> Source Control uncheck Enable Source control. Then restart Xcode, everything will be fine

Unable to find resource located in assets.xcassets

Hey guys I am still kind of new to Stackoverflow so please bear with me if I am doing something wrong, I am trying my best.
I am trying to make a simple app with the new apple watchOS6 and swiftUI in the new Xcode11 beta that plays a sound file from assets.xcassets.
I think I am doing something wrong when trying to find the file I have in the assets, here's the error code:
Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/phillipeismark/Desktop/WatchOS6/WatchOS6Demo/WatchOS6Demo WatchKit Extension/Model/AudioPlayer.swift, line 21
2019-08-28 15:06:58.396058+0200 WatchOS6Demo WatchKit Extension[12691:794523] Fatal error: Unexpectedly found nil while unwrapping an Optional value: file /Users/phillipeismark/Desktop/WatchOS6/WatchOS6Demo/WatchOS6Demo WatchKit Extension/Model/AudioPlayer.swift, line 21
My code is built like this:
I have a ContentView that is a list that just displays my button that calls the my audioplayer when its pressed. The code for ContentView is not included.
The AudioPlayerButton struct looks like this and it basically just calls play() on the player:
struct AudioPlayerButton: View {
#State var player = AudioPlayer()
var body: some View {
Button(action: player.play) {
Image("newPlayButton")
}
}
}
This is my AudioPlayer class where I get the error:
import Foundation
import AVFoundation
import Combine
import SwiftUI
class AudioPlayer {
var didChange = PassthroughSubject <Void, Never>()
var isPlaying: Bool = false
var AudioPlayer: AVAudioPlayer?
let url = URL.init(fileURLWithPath: Bundle.main.path(forResource: "Glad", ofType: ".wav")!)
//let url = URL.init(fileURLWithPath: Bundle.main.path(forResource: "Glad", ofType: ".wav", inDirectory:"/Users/phillipeismark/Desktop/WatchOS6/WatchOS6Demo/WatchOS6Demo WatchKit Extension/Assets.xcassets/" )!)
func play() {
do {
AudioPlayer = try AVAudioPlayer(contentsOf: url)
AudioPlayer?.play()
} catch {
print(error)
}
}
}
I noticed that there are multiple asset.xcassets folders, one inside watchOS6 WatchKit App and one inside of WatchOS6 WatchKit Extension, to be sure I hadn't put it in the wrong place I just added the file to both of the folders.
Here is what I have tried:
1: I noticed that there is multiple asset.xcassets folders and I have tried to add the file to both of them.
2: I have tried to set both the ForResource and ofType parameters as nil to kind of try to "hit more targets" as the Documentation says: "If you specify nil, the method returns the first resource file it finds that matches the remaining criteria".
3: I tried to give a value to the the 3rd parameter in path(forResource:ofType:inDirectory:) as you can see in my AudioPlayer class with the outcommented line.
4: I took an old sound from an old project that I knew worked. I also tested if that project could run and it could.
Anything that could send me in the right direction are very much appreciated! Thanks.
I uploaded the project to GitHub if anybody wanted to look at it.
Update: IT WORKS I ran the normal iPhone simulator in another project. I did not do anything but make a new project and start the simulator. Then I opened the browser went on YouTube and played a video. It still didn't work so I let the video play in the simulator and continued to google around. Then when the video ended and YouTube had to play an add the sound magically started to work - there was an add even before the video started but that one didn't make it work, it was at the videos end. I was speechless. my GUESS is that the iPhone simulator made a configuration but I have no idea. Any theories about how this work or could work is very much appreciated

CloudKit CKShare userDidAcceptCloudKitShareWith Never Fires on Mac App

I am working on accepting a CKShare in a macOS app in Swift 4. I've already done all the following:
Create the CKShare and save it with its rootRecord to CloudKit
Add a participant (CKShare.Participant)
I've confirmed that the CKShare is on the CloudKit server and that the person I invited has access to it. Here's a screenshot: https://d.pr/i/0sMFQq
When I click the share link associated with the CKShare, it opens my app, but nothing happens and userDidAcceptCloudKitShareWith doesn't fire.
func application(_ application: NSApplication, userDidAcceptCloudKitShareWith metadata: CKShareMetadata) {
print("Made it!") //<-- This never gets logged :(
let shareOperation = CKAcceptSharesOperation(shareMetadatas: [metadata])
shareOperation.qualityOfService = .userInteractive
shareOperation.perShareCompletionBlock = {meta, share, error in
print("meta \(meta)\nshare \(share)\nerror \(error)")
}
shareOperation.acceptSharesCompletionBlock = { error in
if let error = error{
print("error in accept share completion \(error)")
}else{
//Send your user to where they need to go in your app
print("successful share:\n\(metadata)")
}
}
CKContainer.default().add(shareOperation)
}
Is there some kind of URL scheme I have to include in my info.plist? Or perhaps a protocol I need to conform to in my NSApplicationDelegate delegate? I can't, for the life of me, figure out what to do. Thanks in advance!
Update
I've tried a few more things on this. When I open the share link in a web browser, I see this:
Clicking OK makes the screen fade away to this:
Not particularly helpful. :) After doing this, the participant's status in CloudKit is still Invited, so the share still hasn't been accepted.
When I click on a share link within Messages, I am shown a popup like this:
After I click open, a new copy of my app shows up in the dock, then the app suddenly closes. The crash log states:
Terminating app due to uncaught exception 'CKException', reason: 'The application is missing required entitlement com.apple.developer.icloud-services'
I've tried turning iCloud off and on again in the Capabilities section of Xcode, but nothing changes. I know this exception can't be right because I can start my app normally and use CloudKit all day long. Only the CKShare causes this crash.
This is a mess. Save me, Obi-wan Kenobi, you're my only hope.
Yes,
You need to add this to your info.plist.
<key>CKSharingSupported</key>
<true/>
** EDITED ANSWER **
I use this code to share, I don't do it manually... not sure if this is an option under OS X I must confess. I am using iOS.
let share = CKShare(rootRecord: record2S!)
share[CKShareTitleKey] = "My Next Share" as CKRecordValue
share.publicPermission = .none
let sharingController = UICloudSharingController(preparationHandler: {(UICloudSharingController, handler:
#escaping (CKShare?, CKContainer?, Error?) -> Void) in
let modifyOp = CKModifyRecordsOperation(recordsToSave:
[record2S!, share], recordIDsToDelete: nil)
modifyOp.savePolicy = .allKeys
modifyOp.modifyRecordsCompletionBlock = { (record, recordID,
error) in
handler(share, CKContainer.default(), error)
}
CKContainer.default().privateCloudDatabase.add(modifyOp)
})
sharingController.availablePermissions = [.allowReadWrite,
.allowPrivate]
sharingController.delegate = self
sharingController.popoverPresentationController?.sourceView = self.view
DispatchQueue.main.async {
self.present(sharingController, animated:true, completion:nil)
}
This presents an activity controller in which you can choose say email and then send a link. You might also want to watch this video, focus on cloudKit JS right at the beginning.
Watch this WWDC video too https://developer.apple.com/videos/play/wwdc2015/710/
It talks about the cloudkit JSON API, using it you can query what has and what hasn't been shared in a terminal window/simple script perhaps. I did the same when using dropbox API a few years back. Hey you can even use the cloudkit JSON API within your code in place of the native calls.
I finally got it to work! I did all of the following:
Deleted my app from ~/Library/Developer/Excode/DerivedData
Made sure I had no other copies of my app archived anywhere on my machine.
Said a prayer.
Rebooted.
Sheesh, that was rough. :)
If your app is a Mac Catalyst app running on any version of macOS Catalina at least up to and including 10.15.4 Beta 1, a UIApplicationDelegate userDidAcceptCloudKitShareWith method will never be invoked.
After some significant debugging, we discovered that the MacCatalyst UIKit doesn’t even have an implementation for userDidAcceptCloudKitShareWithMetadata in its UIApplication delegate. It’s not broken, it’s just not there. So, at least temporarily, our workaround is the following, which seems to work, even if it’s very inelegant:
// Add CloudKit sharing acceptance handling to UINSApplicationDelegate, which is missing it.
#if targetEnvironment(macCatalyst)
extension NSObject {
#objc func application(_ application: NSObject, userDidAcceptCloudKitShareWithMetadata cloudKitShareMetadata: CKShare.Metadata) {
YourClass.acceptCloudKitShare(cloudKitShareMetadata: cloudKitShareMetadata)
}
}
#endif
If you are using a SceneDelegate, implement the delegate callback there, instead of on AppDelegate.
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
// ...
}
You need to create the app delegate for your SwiftUI app using #NSApplicationDelegateAdaptor:
#main
struct Sharing_ServiceApp: App
{
#NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene
{
WindowGroup
{
ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
I put that line in and my code instantly started receiving the share requests.

Why dont my facebook interstitial ads show in my app?

Im in Swift and every-time I call them the delegate func didFailWithError gets called. Im using the same code I use for the facebook intersitial ads in all my apps and it works perfectly except for this new app I created. No ads pop up and I get a message in my console saying:
DiskCookieStorage changing policy from 2 to 0, cookie file:
file:///private/var/mobile/Containers/Data/Application/4E5FA239-208C-4B08-87C6-E4DB1CC3CC76/Library/Cookies/Cookies.binarycookies
This is how I setup the code in my GameViewController:
import UIKit
import SpriteKit
let interstitialFBAD: FBInterstitialAd = FBInterstitialAd(placementID: "2320559454634478_1321860725967683")
class GameViewController: UIViewController, FBInterstitialAdDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
loadFBInterstitialAd()
}
}
//fbAds--------------------------------------------------------------------------------------------------------
func loadFBInterstitialAd() {
interstitialFBAD.delegate = self
interstitialFBAD.load()
print("what")
}
func interstitialAdDidLoad(_ interstitialAd: FBInterstitialAd) {
interstitialFBAD.show(fromRootViewController: self)
print("popup")
}
func interstitialAd(_ interstitialAd: FBInterstitialAd, didFailWithError error: Error) {
print("failed")
}
//fbAds--------------------------------------------------------------------------------------------------------
So from
Im in Swift and every-time I call them the delegate func didFailWithError gets called. Im using the same code I use for the facebook intersitial ads in all my apps and it works perfectly except for this new app I created. No ads pop up and I get a message in my console saying:
This seems like my first solution is not the answer, but it's so obvious I need to point it out nonetheless: from the Facebook docs it seems like there's a couple steps to creating an app that can use the SDK properly (namely creating an app in Facebook and using the proper Info.plist keys, etc).
If that's not what is happening, though, which I imagine it's not, then this could still be due to the application not being properly set up but for a reason other than Facebook SDK.
Without seeing how you are initializing the SDK in your AppDelegate and without confirmation it's not Info.plist keys that are missing for the ads you are attempting to show, it's hard to say what the issue could be here. Have you contacted Facebook support? They'd definitely be able to help here as well. Somewhere off this page I'm sure you can find proper live support as a paying ads customer.