I am integrating autocomplete from heremap, NMAResultListener.
Upon adding protocol stubs, it's asking the same again and again
I cleared derived data folder also. Still no help
class AddWorkVC: UIViewController, UITextFieldDelegate, NMAResultListener {
func request(_ request: NMARequest, didCompleteWithData data: Any?, error: Error?) {
}
var mainWorkTextField : SearchTextField! = nil
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
It should not ask for "add protocol stubs" again and again
The protocol method is defined differently for objective-c and swift. It's possible that something is annotated wrongly, or that the protocol stubs are being added wrongly.
According to the documentation, the Swift method signature is
func requestDidComplete(_ request: NMARequest, data: Any?, error: Error?)
Related
In my drag destination view (NSView subclass), I have implemented:
override func performDragOperation(_ draggingInfo: NSDraggingInfo) -> Bool {
// check for Photos promises
var gotPromised = false
draggingInfo.enumerateDraggingItems(options: [], for: self, classes: [NSFilePromiseReceiver.self], searchOptions: [:], using: {(draggingItem, idx, stop) in
let filePromiseReceiver = draggingItem.item
print("got a file promise receiver: \(filePromiseReceiver)")
gotPromised = true
// Use filePromiseReceiver here for your task.
})
return gotPromised
}
When I run this and drag something over from Photos.app, I get this warning:
How do I fix my drag destination to not have this warning? I would like to get full-quality photos into my app.
Well. Figured it out myself after some investigation. You need to do multiple things (register for correct types, enumerate the items correctly etc), and the warning is not there if you just implement the promise receiving completely and correctly. Apple has provided an example project which implements everything correctly.
I'm trying to figure out how to make a network extension so that my iOS app can programmatically open an custom VPN tunnel in C#, but looking at some similar Obj-C projects I'm not sure if it's possible in Xamarin (as I don't see a network extension project in Visual Studio) and how to port a what I gather is a required PacketTunnelProvider class which I think must be present and listed as an extension in the plist.info first...I'm in particular having most trouble in how to port the parts of that class which appear at the end as an extension and some event handlers named like this func Adapter(adapter: Adapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings, completionHandler: #escaping (AdapterPacketFlow?) -> Void) and func Adapter(adapter: Adapter, handleEvent event: AdapterEvent, message: String?) as they both have a different signature than an event handler in C# which accepts sender and eventArgs (or something derived)…if anyone did this in C# I'd like to know at least if it's possible if not how to port such a class?
I've found this one project https://github.com/ss-abramchuk/OpenVPNAdapter (since it seems to do most of what I want) that I managed to translate into a Xamarin binding library but I'm unsure how and if to incorporate its PacketTunnelProvider class in Xamarin (as that is what the readme says you should use to incorporate something like that adapter)...I gather one should add to plist.info something like this first:
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.networkextension.packet-tunnel</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
</dict>
but where do you go from there to use the binding library? This is the Obj-C code that says and seemingly does what I want to do to after i.e. add that custom VPN protocol tunnel to an app using the library:
import NetworkExtension
import OpenVPNAdapter
class PacketTunnelProvider : NEPacketTunnelProvider
{
lazy var vpnAdapter: OpenVPNAdapter = {
let adapter = OpenVPNAdapter()
adapter.delegate = self
return adapter
}
()
let vpnReachability = OpenVPNReachability()
var startHandler: ((Error?) -> Void)?
var stopHandler: (() -> Void)?
override func startTunnel(options: [String: NSObject]?, completionHandler: #escaping (Error?) -> Void)
{
// There are many ways to provide OpenVPN settings to the tunnel provider. For instance,
// you can use `options` argument of `startTunnel(options:completionHandler:)` method or get
// settings from `protocolConfiguration.providerConfiguration` property of `NEPacketTunnelProvider`
// class. Also you may provide just content of a ovpn file or use key:value pairs
// that may be provided exclusively or in addition to file content.
// In our case we need providerConfiguration dictionary to retrieve content
// of the OpenVPN configuration file. Other options related to the tunnel
// provider also can be stored there.
guard
let protocolConfiguration = protocolConfiguration as? NETunnelProviderProtocol,
let providerConfiguration = protocolConfiguration.providerConfiguration
else
{
fatalError()
}
guard let ovpnFileContent: Data = providerConfiguration["ovpn"] as? Data else
{
fatalError()
}
let configuration = OpenVPNConfiguration()
configuration.fileContent = ovpnFileContent
configuration.settings = [
// Additional parameters as key:value pairs may be provided here
]
// Apply OpenVPN configuration
let properties: OpenVPNProperties
do
{
properties = try vpnAdapter.apply(configuration: configuration)
}
catch
{
completionHandler(error)
return
}
// Provide credentials if needed
if !properties.autologin {
// If your VPN configuration requires user credentials you can provide them by
// `protocolConfiguration.username` and `protocolConfiguration.passwordReference`
// properties. It is recommended to use persistent keychain reference to a keychain
// item containing the password.
guard let username: String = protocolConfiguration.username else
{
fatalError()
}
// Retrieve a password from the keychain
guard let password: String = ... {
fatalError()
}
let credentials = OpenVPNCredentials()
credentials.username = username
credentials.password = password
do
{
try vpnAdapter.provide(credentials: credentials)
}
catch
{
completionHandler(error)
return
}
}
// Checking reachability. In some cases after switching from cellular to
// WiFi the adapter still uses cellular data. Changing reachability forces
// reconnection so the adapter will use actual connection.
vpnReachability.startTracking { [weak self] status in
guard status != .notReachable else { return }
self?.vpnAdapter.reconnect(interval: 5)
}
// Establish connection and wait for .connected event
startHandler = completionHandler
vpnAdapter.connect()
}
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: #escaping () -> Void)
{
stopHandler = completionHandler
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
vpnAdapter.disconnect()
}
}
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
// `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
// protocol if the tunnel is configured without errors. Otherwise send nil.
// `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so
// you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and
// send `self.packetFlow` to `completionHandler` callback.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings, completionHandler: #escaping (OpenVPNAdapterPacketFlow?) -> Void)
{
setTunnelNetworkSettings(settings) {
(error) in
completionHandler(error == nil ? self.packetFlow : nil)
}
}
// Process events returned by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?)
{
switch event {
case .connected:
if reasserting {
reasserting = false
}
guard let startHandler = startHandler else { return }
startHandler(nil)
self.startHandler = nil
case .disconnected:
guard let stopHandler = stopHandler else { return }
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
stopHandler()
self.stopHandler = nil
case .reconnecting:
reasserting = true
default:
break
}
}
// Handle errors thrown by the OpenVPN library
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error)
{
// Handle only fatal errors
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool, fatal == true else
{
return
}
if vpnReachability.isTracking {
vpnReachability.stopTracking()
}
if let startHandler = startHandler {
startHandler(error)
self.startHandler = nil
} else
{
cancelTunnelWithError(error)
}
}
// Use this method to process any log message returned by OpenVPN library.
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String)
{
// Handle log messages
}
}
// Extend NEPacketTunnelFlow to adopt OpenVPNAdapterPacketFlow protocol so that
// `self.packetFlow` could be sent to `completionHandler` callback of OpenVPNAdapterDelegate
// method openVPNAdapter(openVPNAdapter:configureTunnelWithNetworkSettings:completionHandler).
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
How do I port to C# then or maybe I'm doing it all wrong (because of the comment bellow - the binding dll is bigger than 15MB - or is that limit in regard to use of memory which isn't related to file size)? Should I actually be just referencing the custom VPN library to open up a VPN tunnel from code directly and go on from there like it's business as usual (as I also found a project/app https://github.com/passepartoutvpn which uses a TunnelKit cocoapod, but that app's lib won't work with sharpie to make the binding library, and if so would the app like that even be admissible to the AppStore)? Thank you for any help in advance.
Per #SushiHangover advice, I've tried binding TunnelKit, as that project seemed smaller, and succeeded, partially... I've managed to build ~3MB dll, which seems much smaller than 21MB OpenVPNAdapter, and I think I'm almost there with the NetworkExtension project...I've got just to figure out the did I do ok with #escaping completionHandler and how to get some group constants which I guess should be set within the Host app somehow?
public override void StartTunnel(NSDictionary<NSString, NSObject> options, Action<NSError> completionHandler)
{
//appVersion = "\(GroupConstants.App.name) \(GroupConstants.App.versionString)";
//dnsTimeout = GroupConstants.VPN.dnsTimeout;
//logSeparator = GroupConstants.VPN.sessionMarker;
base.StartTunnel(options, completionHandler);
}
I've commented out the groupcontants for now but at least I'm hoping that's good enough porting of Swift3's:
override func startTunnel(options: [String : NSObject]?, completionHandler: #escaping (Error?) -> Void) {
appVersion = "\(GroupConstants.App.name) \(GroupConstants.App.versionString)"
dnsTimeout = GroupConstants.VPN.dnsTimeout
logSeparator = GroupConstants.VPN.sessionMarker
super.startTunnel(options: options, completionHandler: completionHandler)
}
If anyone else knows about the group constants and how to get them I'd be grateful (but I should also note that sharpie pod didn't give/expose any of those fields I should be assigning. Maybe I did it wrong as that's TunnelKit is a completely Swift3 project unlike the OpenVPNAdapter :/
Should I actually be just using the a custom VPN library to open up a VPN tunnel and go from there, but would the app then be admissible to the AppStore?
For iOS 12+, you absolutely have to use the Network Extension framework to be Store eligible.
The Xamarin.iOS build task (ValidateAppBundle) correctly identifies com.apple.networkextension.packet-tunnel as a valid extension (.appex) so yes you can build an NEPacketTunnelProvider extension.
You are correct the VS does not have build-in templating for Network Provider .appex's for tunnels, dns proxy, filter control|data, proxy types, but that does not mean you can not just use another one of the templates (or create the project from scratch) and modify it (I create an Xcode iOS project and start adding extension targets and just mirror those changes in VS).
(FYI: That is Swift code in your example, not ObjC...)
Now due to limitations in .appex size (and performance issues in some cases), a lot of extensions are very difficult to do via Xamarin.iOS. Most devs that encounter this, go native using ObjC/Swift for at least the appex development...
The new Xcode 7.3 passing the parameter via addTarget usually works for me but in this case it's throwing the error in the title. Any ideas? It throws another when I try to change it to #objc
Thank you!
cell.commentButton.addTarget(self, action: #selector(FeedViewController.didTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
The selector it's calling
func didTapCommentButton(post: Post) {
}
In my case the function of the selector was private. Once I removed the private the error was gone. Same goes for fileprivate.
In Swift 4
You will need to add #objc to the function declaration. Until swift 4 this was implicitly inferred.
You need to use the #objc attribute on didTapCommentButton(_:) to use it with #selector.
You say you did that but you got another error. My guess is that the new error is that Post is not a type that is compatible with Objective-C. You can only expose a method to Objective-C if all of its argument types, and its return type, are compatible with Objective-C.
You could fix that by making Post a subclass of NSObject, but that's not going to matter, because the argument to didTapCommentButton(_:) will not be a Post anyway. The argument to an action function is the sender of the action, and that sender will be commentButton, which is presumably a UIButton. You should declare didTapCommentButton like this:
#objc func didTapCommentButton(sender: UIButton) {
// ...
}
You'll then face the problem of getting the Post corresponding to the tapped button. There are multiple ways to get it. Here's one.
I gather (since your code says cell.commentButton) that you're setting up a table view (or a collection view). And since your cell has a non-standard property named commentButton, I assume it's a custom UITableViewCell subclass. So let's assume your cell is a PostCell declared like this:
class PostCell: UITableViewCell {
#IBOutlet var commentButton: UIButton?
var post: Post?
// other stuff...
}
Then you can walk up the view hierarchy from the button to find the PostCell, and get the post from it:
#objc func didTapCommentButton(sender: UIButton) {
var ancestor = sender.superview
while ancestor != nil && !(ancestor! is PostCell) {
ancestor = view.superview
}
guard let cell = ancestor as? PostCell,
post = cell.post
else { return }
// Do something with post here
}
Try having the selector point to a wrapper function, which in turn calls your delegate function. That worked for me.
cell.commentButton.addTarget(self, action: #selector(wrapperForDidTapCommentButton(_:)), forControlEvents: UIControlEvents.TouchUpInside)
-
func wrapperForDidTapCommentButton(post: Post) {
FeedViewController.didTapCommentButton(post)
}
As you know selector[About] says that Objective-C runtime[About] should be used. Declarations that are marked as private or fileprivate are not exposed to the Objective-C runtime by default. That is why you have two variants:
Mark your private or fileprivate method declaration by #objc[About]
Use internal, public, open method access modifier[About]
Hi so my friend gave me his client's existing project and it got too much bugs. I have been debugging the app, and just cam across this line of code
class func saveFile(#data: NSData, filename: String, directory: NSSearchPathDirectory = .DocumentDirectory) -> Bool {
var file = filePath(filename, directory: directory)
return data.writeToFile(file, atomically: true)
}
Noticed #? So what exactly is #?
Here's a screenshot of function with # too.
Additional Info: I think they used this library Service Stack and I think it's for xamarin only.
In Swift 1, # was used to give the same external and internal name to a function parameter. For example, the function definition:
func save(#data: Float) {
print(data)
}
Was equivalent to:
func save(data data: Float) {
print(data)
}
This was removed in Swift 2, and external names must be declared explicitly.
External parameter names are used to make function calls more idiomatic. For example:
func send(sender: String, receiver: String) {
print("Sending from \(sender) to \(receiver)")
}
Is called like this:
send("Cupertino", "New York")
By adding external parameters, you can make that function call more idiomatic without changing the body:
func send(from sender: String, to receiver: String) {
print("Sending from \(sender) to \(receiver)")
}
Making the code more readable:
send(from: "Cupertino", to: "New York")
More information in the Apple docs.
I am currently developing an application on xCode7 beta 2 using Swift 2 (it is a requirement at the moment).
Here is what I am trying to call:
let fileManager = NSFileManager.defaultManager()
let tempDirectoryURL = NSURL(string: NSTemporaryDirectory())!
let directoryURL = tempDirectoryURL.URLByAppendingPathComponent("com.test.manager/multipart.form.data")
var error: NSError?
if fileManager.createDirectoryAtURL(directoryURL, createIntermediates: true, attributes: nil) {
...
}
Here is the error I am getting:
Cannot invoke 'createDirectoryAtURL' with an argument list of type
'(NSURL, createIntermediates: Bool, attributes: nil)'
Which is confusing because the definition for createDirectoryAtURL I am getting when I right click and "view definition" is:
func createDirectoryAtURL(
url: NSURL,
withIntermediateDirectories createIntermediates: Bool,
attributes: [String : AnyObject]?
) throws
The only only paramater that doesn't match verbatim is the last parameter "attributes", which the documentation (and all example usage) explicitly states can accept the value nil.
Apple's Documentation:
If you specify nil for this parameter, the directory is created
according to the umask(2) Mac OS X Developer Tools Manual Page of the
process.
Two problems here:
You've switched an parameter label for its internal name. The second parameter's label — part of the function name, required for calling it — is withIntermediateDirectories. The implementor of that function refers to that parameter's value as createIntermediates. So your call should look like this:
fileManager.createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil)
Note the signature you quoted:
func createDirectoryAtURL( ... ) throws
You're using this call as the condition of an if statement — that means you need a function that returns Bool. The compiler is trying to satisfy the requirement of the if statement by looking for a function called createDirectoryAtURL whose type signature is (NSURL, Bool, [String : AnyObject]?) -> Bool, and complaining because it only sees one whose signature is (NSURL, Bool, [String : AnyObject]?) throws -> Void.
The error handling system in Swift 2 takes ObjC methods that return BOOL and have an NSError out parameter and turns them into throwing methods with no return type (that is, they return Void). So, if you're looking at Swift 1.x code that uses such methods, or porting ObjC code, you need to change patterns like the following:
var error: NSError?
if fileManager.createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil, error: &error) {
// all good
} else {
// handle error
}
And use patterns like this instead:
do {
try fileManager.createDirectoryAtURL(directoryURL, withIntermediateDirectories: true, attributes: nil)
// if here, all is good
} catch {
// handle error
}