dyld: Library not loaded: #rpath/CoreNFC.framework/CoreNFC, iOS11 and Xcode 9 beta - iphone

I get this error and app get crash while run CoreNFC sample code in Xcode 9.0 beta 2
dyld: Library not loaded: #rpath/CoreNFC.framework/CoreNFC
Referenced from: /var/containers/Bundle/Application/2837709C-C852-4811-B696-38F2725554D4/iOS-11-by-Examples.app/iOS-11-by-Examples
Reason: image not found
Does anyone knows how to fix this?

I combined some answers together to fix this thanks to #Chinchan Zu's comment
here is how to mark Core NFC as Optional stackoverflow question
First you make import to NFCCore in "Linked Frameworks and Libraries" as optional as in this screenshot
Then inside your code you wrap your code with this #if check. Here is the class I used
#if canImport(CoreNFC)
import Foundation
import CoreNFC
#endif
class NFCManagar: NSObject {
#if canImport(CoreNFC)
var session: NFCNDEFReaderSession?
#endif
var items = [Item]()
var completion: ((_ success: Bool, _ error: Error?)-> Void)?
func beginScanning(items: [Item], completion: #escaping (_ success: Bool, _ error: Error?)-> Void) {
self.completion = completion
self.items.removeAll()
self.items.append(contentsOf: items)
#if canImport(CoreNFC)
session = NFCNDEFReaderSession(delegate: self, queue: nil, invalidateAfterFirstRead: true)
session?.alertMessage = "Hold your iPhone near check in/out device."
session?.begin()
#endif
}
}
#if canImport(CoreNFC)
extension NFCManagar: NFCNDEFReaderSessionDelegate {
// MARK: - NFCNDEFReaderSessionDelegate
/// - Tag: processingTagData
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
debugPrint("Nfc is detected")
}
/// - Tag: endScanning
func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
// Check the invalidation reason from the returned error.
if let readerError = error as? NFCReaderError {
// Show an alert when the invalidation reason is not because of a success read
// during a single tag read mode, or user canceled a multi-tag read mode session
// from the UI or programmatically using the invalidate method call.
if (readerError.code != .readerSessionInvalidationErrorFirstNDEFTagRead)
&& (readerError.code != .readerSessionInvalidationErrorUserCanceled) {
debugPrint("Nfc didInvalidateWithError \(error)")
}
}
// A new session instance is required to read new tags.
self.session = nil
}
}
#endif

CoreNFC is only available on iPhone 7 and iPhone 7 Plus devices. Make sure you're running your code on one of those.
See the WWDC session and the relevant documentation for more information.
https://developer.apple.com/videos/play/wwdc2017/718/
https://developer.apple.com/documentation/corenfc

For Xcode 11 make CoreNFC.framework "Do not Embed". And also wrap your code with #if check.

Related

ReplayKit: RPScreenRecorder.shared().startCapture() NOT WORKING

ReplayKit has really been frustrating me recently. For some reason
RPScreenRecorder.shared().startCapture(handler: { (sample, bufferType, error) in
does not actually work when I call it because I have a print() statement inside it and it is never called.
My code in the ViewController is:
import UIKit
import AVFoundation
import SpriteKit
import ReplayKit
import AVKit
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, RPPreviewViewControllerDelegate {
var assetWriter:AVAssetWriter!
var videoInput:AVAssetWriterInput!
func startRecording(withFileName fileName: String) {
if #available(iOS 11.0, *)
{
assetWriter = try! AVAssetWriter(outputURL: fileURL, fileType:
AVFileType.mp4)
let videoOutputSettings: Dictionary<String, Any> = [
AVVideoCodecKey : AVVideoCodecType.h264,
AVVideoWidthKey : UIScreen.main.bounds.size.width,
AVVideoHeightKey : UIScreen.main.bounds.size.height
];
videoInput = AVAssetWriterInput (mediaType: AVMediaType.video, outputSettings: videoOutputSettings)
videoInput.expectsMediaDataInRealTime = true
assetWriter.add(videoInput)
print("HERE")
RPScreenRecorder.shared().startCapture(handler: { (sample, bufferType, error) in
print("RECORDING")
}
}
}
func stopRecording(handler: #escaping (Error?) -> Void)
{
if #available(iOS 11.0, *)
{
RPScreenRecorder.shared().stopCapture
{ (error) in
handler(error)
self.assetWriter.finishWriting
{
print("STOPPED")
}
}
}
}
"HERE" is printed, but not "RECORDING"
[p.s. sorry for bad formatting in code, I'm sure you'll understand :)]
I have also tried a different method:
let recorder = RPScreenRecorder.shared()
recorder.startRecording{ [unowned self] (error) in
guard error == nil else {
print("There was an error starting the recording.")
return
}
print("Started Recording Successfully")
}
and to stop the recording...
recorder.stopRecording { [unowned self] (preview, error) in
print("Stopped recording")
guard preview != nil else {
print("Preview controller is not available.")
return
}
onGoingScene = true
preview?.previewControllerDelegate = self
self.present(preview!, animated: true, completion: nil)
}
This method does not stop when I call the recorder.stopRecording() function, "Stopped recording" is never called.
Can someone please help me because this is really frustrating me, how can you PROPERLY use ReplayKit to record your screen in iOS 11? I have searched all over the internet and none of the methods work for me, I don't why. P.S. I have the necessary permission keys in my Info.plist.
Thanks
A huge reminder that ReplayKit doesn't work in simulator. I wasted hours on the exact same issue until realized that ReplayKit will never trigger startCapture handler because it never records in simulator.
Well there are quite few possible causes for this issue.
Some of them are here:
Your Replay kit Shared Recorder might be crashed, For that you can restart your device and check again.
There might be printable issue in your replay kit. For that kindly conform to the RPScreenRecorderDelegateProtocol and add Recording Changes
screenRecorder:didStopRecordingWithPreviewViewController:error:
method to your class and check if any error shows up in this method.

Deep link into macOS app is not recognized

I'm trying to implement a deep link into an macOS application, but nothing seems to work.
So far, my AppDelegate.swift contains the following
func application(app: NSApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
print("opened by link");
return true
}
I also configured the info.plist with URLSchemes beeing my bundle identifier and URLIdentifier beeing simply zst
In a simple html-file I use the following code to test the deep link
Test
My app gets opened (or becomes active when already running), but the print statement is not executed.
What am I doing wrong here?
Thanks to #Ssswift I found a solution.
Used this code:
How do you set your Cocoa application as the default web browser?
and converted it to swift with: https://objectivec2swift.com
works with Swift 3
in AppDelegate.swift added these lines
func applicationDidFinishLaunching(_ aNotification: Notification) {
var em = NSAppleEventManager.shared()
em.setEventHandler(self, andSelector: #selector(self.getUrl(_:withReplyEvent:)), forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL))
}
func getUrl(_ event: NSAppleEventDescriptor, withReplyEvent replyEvent: NSAppleEventDescriptor) {
// Get the URL
var urlStr: String = event.paramDescriptor(forKeyword: keyDirectObject)!.stringValue!
print(urlStr);
}

Very slow communication between apple watch and iPhone

I have wrote a very basic Xcode project that contains 3 targets:
- iOS target
- WatchKit app
- WatchKit extension
First of all, i do not understand why Xcode creates a second target (extension) for WatchKit app ? It seems that WatchKit app contains storyboard, and WatchKit extension contains swift code (controllers). Is there a particular reason for Xcode to design and split 2 targets instead of one single ?
Look at this very basic piece of code:
iOS controller:
override func viewDidLoad()
{
super.viewDidLoad()
if WCSession.isSupported()
{
let session = WCSession.default()
session.delegate = self
session.activate()
}
}
#IBAction func on_btn_tap(_ sender: Any)
{
if WCSession.isSupported()
{
let session = WCSession.default()
session.sendMessage(["mykey": "myvalue"], replyHandler: { (response) -> Void in
NSLog("OK")
}, errorHandler: { (error) -> Void in
NSLog("Error)
})
}
}
On watch extension (InterfaceController.swift):
override func awake(withContext context: Any?)
{
super.awake(withContext: context)
if WCSession.isSupported()
{
let session = WCSession.default()
session.delegate = self
session.activate()
}
}
extension InterfaceController: WCSessionDelegate
{
func session(_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?)
{
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void)
{
self.btn.setBackgroundColor(UIColor.yellow)
}
}
As you certainly understand, i have a button on my iOS App. When i tap on this button, i send a message to Watch App and this app will change a button color.
There is a delay of about 5-6 seconds between the button tap on the iPhone and the color change. Do you know why ?
In the other communication side (watch to iPhone), it is worst (10-15 seconds)
Thanks
Since you are updating your UI you need to wrap it in a DispatchQueue, like this:
DispatchQueue.main.async {
self.btn.setBackgroundColor(UIColor.yellow)
}
These delegate callbacks are not on the main thread and you should never update your UI from any other thread than the main thread. Wrapping it like this results in much faster updating of your UI and safer code.

WatchOS2 connectivity framework doesn't work

I want to pass data from Iphone to Apple Watch. I tried everything but when I am using the didReceiveUserInfo function, nothing happens I check if WCSession is compatible and it is.
Code on my Iphone:
if(ipField.text != ""){
do {
try watchSession?.transferUserInfo(["name" : "test"])
print("context update")
} catch let error as NSError {
NSLog("Updating the context failed: " + error.localizedDescription)
print("failed")
}
Code on my Apple Watch:
func session(session: WCSession, didReceiveUserInfo userInfo: [String : AnyObject]){
let Value = userInfo["name"] as? String
self.currentIpLabel.setText(Value)
print("done1")
}
WCSESSION check Iphone:
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
print("SUPPORT OK")
}
WCSESSION check AppleWatch
if(WCSession.isSupported()){
watchSession = WCSession.defaultSession()
// Add self as a delegate of the session so we can handle messages
watchSession!.delegate = self
watchSession!.activateSession()
}
I have created an issue on github with a suggested patch attached. I tested this version of the app on my own devices and the watch received the userInfo just fine. The main change I made was to move the declaration of the WCSessionDelegate methods from being "nested functions" to top level functions in the file. Nested functions are only available from within the scope of the function they are defined in, which would mean that the delegate object wouldn't have implementations for those methods.

WCSession Failing to Activate

I am having a problem with the WatchKit Connectivity Session failing to activate when I call the session.activateSession() method. This is the code I am using to set up the session.
if (WCSession.isSupported()) {
session = WCSession.defaultSession()
session.delegate = self // conforms to WCSessionDelegate
session.activateSession()
print("Session has been activated")
}
However, I have placed a breakpoint on the print line and when I inspect the session object, it says the sessionActivated property is still false, even after calling activateSession. I don't appear to be getting any sort of bug when I call activate session, so I assume it should have worked, but this does not seem to be the case.
Furthermore, if I try and use the sendMessage method on the session object later in my code like this -
let message = ["request": "fireLocalNotification"]
session.sendMessage(
message, replyHandler: { (replyMessage) -> Void in }) { (error) -> Void in
print(error.localizedDescription)
}
I receive an error code "The operation couldn’t be completed. (WCErrorDomain error 7004.)" which I looked up which means "WCErrorCodeSessionNotActivated." This is yet another reason why I think the activateSession method isn't calling correctly. I have even tried running the activateSession method the line directly before I send the message, but I still receive the error. If anyone could help explain what is going on, that would be wonderful, thank you! :)
You should activate the WatchConnectivity session on both the WatchKit Extension and the iOS app target. For example you might do it in the InterfaceController's
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
if WCSession.isSupported() {
let wcsession = WCSession.defaultSession()
wcsession.delegate = self
wcsession.activateSession()
wcsession.sendMessage(["update": "list"], replyHandler: { (dict) -> Void in
print("InterfaceController session response: \(dict)")
}, errorHandler: { (error) -> Void in
print("InterfaceController session error: \(error)")
})
}
}
and in the AppDelegate
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if WCSession.isSupported() {
let wcsession = WCSession.defaultSession()
wcsession.delegate = self
wcsession.activateSession()
}
return true
}
What I have noticed in several examples is that people tend to set a delegate only in the class which handles requests, e.g. if the watch was to send a message to the iOS app a delegate would only be set in the iOS app. This is WRONG. As the WatchConnectivity clearly states, you MUST set the delegate in both circumstances, otherwise you'll get the 7004 error.
Since "activateSession()" changed to "activate()" in Xcode 8 ,You need to add and extension for your class to delegate the session ( WCSessionDelegate ), and extend it with the function:
func session(_ session: WCSession, activationDidCompleteWith
activationState: WCSessionActivationState, error: Error?)
In order to ensure that the asynchronous method "activate" finishes successfully.
In your case:
extension YourInterfaceControllerClass : WCSessionDelegate {
func session(_ session: WCSession,
didReceiveMessage message: [String : Any],
replyHandler: #escaping ([String : Any]) -> Void)
{
//this function is mandatory and can be empty
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?)
{
// Your code to be executed after asynchronous activation is completed:
let message = ["request": "fireLocalNotification"]
session.sendMessage(
message, replyHandler: { (replyMessage) -> Void in }) { (error) -> Void in
print(error.localizedDescription)
}
//....
}
}
Are you using big number values?
NSDictionary *userInfo = #{
#"a1":#(1000000000), // 1000000000
#"a2":#(10000000000), // 1e+10
#"a3":#(100000000000), // crash!
};
[[WCSession defaultSession] transferUserInfo:userInfo];
On the above code, the value of key "a3" is dangerous, it causes Apple Watch crash.
Once you send the list, it remains in Apple Watch until reinstall the watch app.
(This crash occurs on a device, not on a simulator)