IBM Watson Visual Recognition API Key fail - Xcode Swift - swift

I'm trying to connect to Watson using VisualRecognitionV3.framework.
The framework's compiled fine (via Carthage), and I think I've got the Service Credentials configured correctly, but when I compile Xcode to the simulator I get an invalid-api-key error.
Has anyone experienced the same issues?
What am I doing wrong?
private let apiKey = "Xn5DUtQU8WzgFTL9qNEFwBjxxxxxxxxxxxxxxxxxx"
private let classifierId = "DefaultCustomModel_2051029379"
private let version = "2018-07-01"
var visualRecognition: VisualRecognition!
override func viewDidLoad() {
super.viewDidLoad()
self.visualRecognition = VisualRecognition(apiKey: apiKey, version: version)
}
override func viewDidAppear(_ animated: Bool) {
let localModels = try? visualRecognition.listLocalModels()
if let models = localModels, models.contains(self.classifierId) {
print("local model found")
} else {
self.updateModel()
}
}
Xcode Error:
Error Domain=com.ibm.watson.developer-cloud.VisualRecognitionV3 Code=403 "ERROR: invalid-api-key" UserInfo={NSLocalizedDescription=ERROR: invalid-api-key}
Watson configuration screenshot:
Watson Config

You need to initialize your VisualRecognition using another initializer, like the following
let visualRecognition = VisualRecognition(version: version, apiKey: apiKey, iamUrl: nil)
The difference is that you will need to call this 3 argument-ed constructor with the third argument, i.e. the iamUrl, even though you make it nil and anything else is the same. This tells the VisualRecognition class to authenticate your app using the IAM.
The git documentation is very confusing, which says in here https://github.com/watson-developer-cloud/swift-sdk#visual-recognition
Note: a different initializer is used for authentication with instances created before May 23, 2018:
Which means the old way should just work, UNLESS, they made a mistake and they actually mean AFTER May 23, 2018
Anyways, if you try it should just work. I was having this issue 30 mins before writing this answer.

Related

Privileged file copy in macOS (Installing a helper binary to /usr/local/bin)

I have a helper binary mytool inside my main app bundle that I need to copy to /usr/local/bin.
Now bin might not always exist or have write access, so the standard NSWorkspace calls will fail on it. I looked into different ways to do this, but none are satisfactory (or I am doing it wrong)
Getting an authorization for replaceFile for NSWorkspace.requestAuthorization
This does not seem to work, as I still get a privileges error after trying to "replace" the file in /usr/local/bin/mytool with the one from my bundle.
Manually getting Authorization via AuthorizationCreate.
The problem here is that AuthorizationExecuteWithPrivileges is deprecated (or in my case not even available in Swift), and SMJobBless seems to be only for longer running helper processes. Also SMJobBlessrequires my helper tool to have an Info.plist of its own, which it doesn't have since its just a plain binary
So how do I manage to perform a privileged file copy in Swift?
PS: The app is not sandboxed, so NSOpenPanel does not help.
Well I dug out the deprecated API using dlsym, because there is simply no other way besides asking the user manually for his password, which I don't want to do unless the deprecated API disappears entirely.
So what I do now is authenticate a call to mytool --install using AuthorizationExecuteWithPrivileges like this:
import Foundation
import Security
public struct Sudo {
private typealias AuthorizationExecuteWithPrivilegesImpl = #convention(c) (
AuthorizationRef,
UnsafePointer<CChar>, // path
AuthorizationFlags,
UnsafePointer<UnsafeMutablePointer<CChar>?>, // args
UnsafeMutablePointer<UnsafeMutablePointer<FILE>>?
) -> OSStatus
/// This wraps the deprecated AuthorizationExecuteWithPrivileges
/// and makes it accessible by Swift
///
/// - Parameters:
/// - path: The executable path
/// - arguments: The executable arguments
/// - Returns: `errAuthorizationSuccess` or an error code
public static func run(path: String, arguments: [String]) -> Bool {
var authRef: AuthorizationRef!
var status = AuthorizationCreate(nil, nil, [], &authRef)
guard status == errAuthorizationSuccess else { return false }
defer { AuthorizationFree(authRef, [.destroyRights]) }
var item = kAuthorizationRightExecute.withCString { name in
AuthorizationItem(name: name, valueLength: 0, value: nil, flags: 0)
}
var rights = withUnsafeMutablePointer(to: &item) { ptr in
AuthorizationRights(count: 1, items: ptr)
}
status = AuthorizationCopyRights(authRef, &rights, nil, [.interactionAllowed, .preAuthorize, .extendRights], nil)
guard status == errAuthorizationSuccess else { return false }
status = executeWithPrivileges(authorization: authRef, path: path, arguments: arguments)
return status == errAuthorizationSuccess
}
private static func executeWithPrivileges(authorization: AuthorizationRef,
path: String,
arguments: [String]) -> OSStatus {
let RTLD_DEFAULT = dlopen(nil, RTLD_NOW)
guard let funcPtr = dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges") else { return -1 }
let args = arguments.map { strdup($0) }
defer { args.forEach { free($0) }}
let impl = unsafeBitCast(funcPtr, to: AuthorizationExecuteWithPrivilegesImpl.self)
return impl(authorization, path, [], args, nil)
}
}
If you want to do this using public APIs (meaning not using deprecated APIs, invoking Apple Script, shelling out via Process, etc.) then the only way to achieve this is using SMJobBless. For better or worse, that's the only option still officially supported by Apple.
If you want to install your binary in /usr/local/bin then that binary itself doesn't need to have an Info.plist. You'd want to create a different helper tool which would be installed via SMJobBless that could copy your binary to /usr/bin/local. It will be able to do that because a helper tool installed by SMJobBless always runs as root. Once you're done with all of this you could have the helper tool you installed with SMJobBless uninstall itself. No denying it's rather involved.
If you do want to go down this route, take a look at SwiftAuthorizationSample.

Why can't my app find Firebase Functions?

I can't figure out why I keep getting the Swift warning: Use of unresolved identifier 'Functions on this line of my code: let functions = Functions.functions()
My imports for the viewController includes import Firebase and it works fine when I declare let db = Firestore.firestore() right above the line let functions = Functions.functions()
My podfile includes pod 'Firebase/Functions' and I've installed the pod.
I'm calling functions later using the following code and when I type "functions" it recommends adding .httpsCallable which leads me to believe that it actually does recognize the object "functions":
func getData(){
functions.httpsCallable("helloWorld").call(userData) { (result, error) in
if let error = error {
print(error)
}
if let data = result?.data {
print(data)
}
}
}
Figured it out. Importing Firebase is not enough, one must also import FirebaseFunctions (despite what Swift thinks, see screenshot below).

Swift Privileged Helper (XPC Listener) Crashing with Illegal Instruction Error

I’ve created a Swift macOS app which uses SMJobBless to create a helper with escalated privileges. This works fine—the helper gets installed to /Library/Privileged Helper Tools and an accompanying LaunchDaemon gets created in /Library/LaunchDaemons. However, the helper is unable to start successfully. Instead, it crashes with an “Illegal instruction: 4” message.
I’ve prepared the helper to respond to XML connections by implementing the NSXPCListenerDelegate protocol. Here‘s my Helper main.swift code:
import Foundation
class HelperDelegate: NSObject, NSXPCListenerDelegate {
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
newConnection.exportedInterface = NSXPCInterface(with: HelperToolProtocol.self)
newConnection.exportedObject = HelperTool()
newConnection.resume()
return true
}
}
let delegate = HelperDelegate()
let listener = NSXPCListener.service()
listener.delegate = delegate
listener.resume()
The crash occurs on the last line, listener.resume().
I tried to launch the helper app manually from the command line (which is identical to what the LaunchDaemon does) and, again, it crashes with the above error message printed to stdout. I don’t have any more ideas on how to test this for the root cause. My implementation is more than rudimentary, following Apple’s guidlines for implementing XM services. Also, the various posts on SO regarding XML services haven’t helped me in resolving this issue. Has anyone of you tried to create a privileged helper in Swift successfully? BTW, the app is not sandboxed.
For the sake of completeness, here’s the code for the HelperTool class referenced in my HelperDelegate class above:
import Foundation
class HelperTool: NSObject, HelperToolProtocol {
func getVersion(withReply reply: (NSData?) -> ()) {
let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString" as String) as? String ?? "<unknown version>"
let build = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String ?? "<unknown build>"
if let d = "v\(version) (\(build))".data(using: .utf8, allowLossyConversion: false) {
reply(d as NSData)
}
}
}
And finally the HelperToolProtocol:
import Foundation
#objc(HelperToolProtocol) protocol HelperToolProtocol {
func getVersion(withReply: (NSData?) -> ())
}
Thanks for any help!
After days of testing I finally found a solution which makes my XPC helper launch correctly and respond to any messages. The problem lies in the last three lines of the main.swift module which currently read
let listener = NSXPCListener.service()
listener.delegate = delegate
listener.resume()
which, as put in the question, make the helper crash immediately upon the very last line.
I took these lines directly from Apple’s Creating XPC Services documentation. Here’s the documentation for the NSXPCListener resume() function:
If called on the service() object, this method never returns. Therefore, you should call it as the last step inside the XPC service's main function after setting up any desired initial state and configuring the listener itself.
The solution is to not call the NSXPCListener.service() singleton object but rather instantiate a new NSXPCListener object using the init(machServiceName:)initializer passing the same Mach service name that is being used on the main app’s XPC connection. As resume() in this case would resume immediately—thus terminating the helper—you have to put it on the current run loop to have it run indeterminately. Here’s the new, working code:
let listener = NSXPCListener(machServiceName: "Privilege-Escalation-Sample.Helper")
listener.delegate = delegate
listener.resume()
RunLoop.current.run()

Sinch VoIP ManagedPush Notifications Not Working (Swift)

I have been trying to implement VoIP ManagedPush notifications however they aren't working. The following are the steps I have taken to implement ManagedPush:
1) Created a VoIP Services Certificate in Apple Developer. Uploaded a .p12 file of the certificate to the Sinch Dashboard.
2) Instantiated the sinchClient and the push client var sinchClient: SINClient!; var push: SINManagedPush!
3) Initialized the push client and sinch client in didFinishLaunchingWithOptions function:
// Register for Sinch Notifications (for calling)
self.push = Sinch.managedPushWithAPSEnvironment(.Development)
self.push.delegate = self
self.push.setDesiredPushTypeAutomatically()
// Sinch
NSNotificationCenter.defaultCenter().addObserverForName("UserDidLoginNotification", object: nil, queue: nil, usingBlock: {(note: NSNotification) -> Void in
self.initializeSinch(note.userInfo!["userId"] as! String)
})
4) Initialized sinchClient. called when a user is logged in via facebook.
func initializeSinch(loginId: String){
sinchClient = Sinch.clientWithApplicationKey("key", applicationSecret: "secret", environmentHost: "sandbox.sinch.com", userId: loginId)
sinchClient.callClient().delegate = self
sinchClient.setSupportCalling(true)
sinchClient.enableManagedPushNotifications()
sinchClient.start()
sinchClient.startListeningOnActiveConnection()
self.push.registerUserNotificationSettings()
}
5) In function didRegisterForRemoteNotificationsWithDeviceToken I placed the following line of code:
self.push.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
6) In function didRecieveRemoteNotification I placed the following line of code:
self.push.application(application, didReceiveRemoteNotification: userInfo)
7) Created the delegate function for SINManagedPushDelegate:
extension AppDelegate: SINManagedPushDelegate{
func managedPush(managedPush: SINManagedPush!, didReceiveIncomingPushWithPayload payload: [NSObject : AnyObject]!, forType pushType: String!) {
self.handleRemoteNotification(payload)
print("HERE GOT A REMOTE NOTIFICATIOn")
}
func handleRemoteNotification(userInfo: [NSObject : AnyObject]) {
if (sinchClient == nil) {
let userManager = UserManager()
initializeSinch(String(userManager.user.facebookId))
}
self.sinchClient.relayRemotePushNotification(userInfo)
}
}
So my question really comes down to what am I doing wrong that the ManagedPush is not working. I have read SinchCallingPush and converted and tried to follow the code almost exactly yet it doesn't work. I think that maybe its something to do with not implementing PushKit. If this is the case could someone please point out how to correctly import it and where to use it?
I have also looked at a similar question at Instant Message Push Notification Using Sinch not coming on iOS
Something happened and it started working. Now I am getting an error that says SIN_INCOMING_CALL. I am sure I can google and fix this up.

Swift MultipeerConnectivity - invitePeer always ends in a timeout

I'm currently working on a game where I need to connect 2 peers. To do this I have gone through this tutorial. I built the described demo app and it worked like a charm, i.e. I was able to chat between my iPhone 5 and my Macbook Pro.
However, now that I'm working on the game application I'm not able to establish a connection anymore. Basically what I do is that I display in a table view all the advertisers. Once one clicks on a given cell, the following code will be executed:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var peer = appDelegate.mcManager.foundPeers.keys.array[indexPath.row] as MCPeerID
self.appDelegate.mcManager.joinGame(peer)
}
In the mcManager the joinGame looks like:
func joinGame(peer : MCPeerID){
// Setup Advertising:
self.setAdvertiserDiscoveryInfo(self.foundPeers[peer] as! [String:String])
self.advertiser.startAdvertisingPeer()
self.appDelegate.isAdvertising = true
// Create Connection
self.browser.invitePeer(peer, toSession: self.session, withContext: nil, timeout: self.connectingTimeout)
}
Obviously there are no error messages from XCode. Every code is executed as it should. Even the invitePeer(...) is being executed. But it always ends in a timeout.
I compared the demo app (from the tutorial above) and the game app in the debugger and I noticed one thing, that is that the demo app's browser variable contains an _netServices variable with one key/value pair, whereas the game app doesn't (_netServices is empty).
Currently I have no clue what is wrong, since I heavily base myself on the given tutorial, i.e. there is a lot of copy/paste involved. And since the first demo app is working I don't know what happened.
Just FYI, here is the init() of the mcManager:
override init() {
super.init()
peer = MCPeerID(displayName: UIDevice.currentDevice().name)
session = MCSession(peer: peer)
session.delegate = self
browser = MCNearbyServiceBrowser(peer: peer, serviceType: serviceType)
browser.delegate = self
advertiser = MCNearbyServiceAdvertiser(peer: peer, discoveryInfo: nil, serviceType: serviceType)
advertiser.delegate = self
}
I finally found the solution!
After I went totally mad, because I did not find any problem, I created a new Project and added step by step the code from the game application. After every step I tested the code.
Finally I found out, that the problem was the advertiser and its discovery info. When the user creates a game I reinitialize the advertiser with the user generated discovery information. The problem was that after the reinitialization I forgot to put the delegate, i.e.:
func setAdvertiserDiscoveryInfo(discoveryInfo: Dictionary<String, String>) {
advertiser = MCNearbyServiceAdvertiser(peer: peer, discoveryInfo: discoveryInfo, serviceType: serviceType)
advertiser.delegate = self // I forgot this line!
}
Furthermore, I initialized the discovery info variable as [String : String]. The compiler did not raise any error so I thought it was correct. However the connection was only possible when I changed the discovery information to Dictionary<String, String>.