How to pass an Objective-C with callback to a Swift method? - ios

I really don't know how to ask this question, but:
I have a bridge between Objective-C and Swift
In my Obj-C class I invoke my Swift class
IAPbridge = [[IAPBridge alloc] init];
[IAPbridge requestProducts];
I have a swift class
public func requestProducts () {
}
My Obj-C class should have a block to receive some data from the Swift class
It should be something like this (this code is wrong, but something like what I think it should be)
Obj-C
[IAPbridge requestProducts:^(id *products) {
NSLog(#"Response:%#", products);
}];
Swift
public typealias ProductsRequestCompletionHandler = (_ products: [SKProduct]?) -> Void
private var productsRequestCompletionHandler: ProductsRequestCompletionHandler?
public func requestProducts (completionHandler) {
productsRequestCompletionHandler = completionHandler
productsRequestCompletionHandler?(true, products)
}
So, any help?

This part is impossible as your spec stands:
productsRequestCompletionHandler?(true, products)
You cannot hand back two values if a ProductsRequestCompletionHandler takes only one value. So you will have to revise your definition of a ProductsRequestCompletionHandler.
We may then imagine that on the Swift side we have a class like this:
#objc class IAPBridge : NSObject {
public typealias ProductsRequestCompletionHandler = (Bool, [SKProduct]?) -> Void
#objc func requestProducts(_ ch:ProductsRequestCompletionHandler) {
let products : [SKProduct]? = // whatever
ch(true, products)
}
}
Over on the Objective-C side, your class's .m file must import the implicitly generated bridging header:
#import "MyApp-Swift.h" // or whatever it is called
In the eyes of your Objective-C class, an IAPBridge has this method:
- (void)requestProducts:(SWIFT_NOESCAPE void (^ _Nonnull)(BOOL, NSArray<SKProduct *> * _Nullable))ch;
So now you just call it:
IAPBridge* b = [[IAPBridge alloc] init];
[b requestProducts:^(BOOL success, NSArray<SKProduct *> * products) {
if (success) {
NSLog(#"Thank you for the products! They are %#", products);
} else {
NSLog(#"%#", #"Darn, something went wrong");
}
}];

Related

Call edit method from a main class in X++

I want to recall a value of an edit method declared inside a form from a class main. How can I do it?
[Form]
public class AdvancedCustomerSchedule extends FormRun
{
Sorting sorting;
edit Sorting edtSorting(boolean set, Sorting _sorting)
{
if (set)
{
sorting = _sorting;
}
return sorting;
}
}
and the class:
class AdvancedCustomerScheduleService
{
static void main(Args args)
{
//I want to call the method edtSorting here.
}
}
UPDATE
FormRun callerForm;
if (args.caller() is FormRun)
{
callerForm = args.caller() as FormRun;
if (formHasMethod(callerForm, identifierStr(edtSorting)))
{
str test = callerForm.edtSorting();
info(test);
}
}
For calling methods defined on forms in general you usually use a pattern like the following:
...
FormRun callerForm;
...
if (_args.caller() is FormRun)
{
callerForm = _args.caller();
if (formHasMethod(callerForm, identifierStr(someMethod)))
{
callerForm.someMethod();
}
...
Have a look at class DirPartyContactInfoFormHandler and its static main method for an example.

Dart 2: How to access a class's type?

In Swift 4, I used to do something like this to locally store objects of variable classes:
class Repo {
var mediaType : MyBaseClass.Type
func doSomething() {
mediaType.someStaticMethod();
}
}
class SpecificClass : MyBaseClass {
static func someStaticMethod() -> void {
// Stuff
}
}
repo = Repo(SpecificClass.self)
repo.doSomething(); // Executes `Stuff`
Moving to Dart 2, this is the closest I've gotten, yet the error specified at the bottom is blocking me.
class Repo {
Type mediaType;
void doSomething() {
mediaType.someStaticMethod();
}
}
class SpecificClass extends MyBaseClass {
static void someStaticMethod() {
// Whatever
}
}
repo = Repo(SpecificClass)
repo.doSomething() // Should execute `Whatever`, but for the error
Which generates this error:
The method 'someStaticMethod' isn't defined for the class 'Type'
Is this sort of trick feasible with Dart 2?

Why does React Native/Objective-C complain that I call my method with the wrong number of arguments?

I am working on a React Native application using both Objective-C and Swift.
At the moment, I am trying to replace the current approach using EventEmitter with a more elegant solution using promises.
However, I have some troubles because I get a complaint from the compiler/interpreter that I am calling my method with the wrong number of arguments:
ExceptionsManager.js:71 RecorderBridge.startRecording was called with 0 arguments but expects 1 arguments. If you haven't changed this method yourself, this usually means that your versions of the native code and JavaScript code are out of sync. Updating both should make this error go away.
However, I actually do not have any arguments, except resolver and rejecter which should not be complaint about by the compiler/interpreter.
My code looks as follows:
Recorder.js
...
startRecording = () => {
RecorderNative.startRecording();
};
...
RecorderNativeModule.js
import { NativeModules } from 'react-native';
const { RecorderBridge } = NativeModules;
export default {
startRecording() {
return RecorderBridge.startRecording();
}
}
RecorderBridge.m
#implementation RecorderBridge
...
RCT_EXPORT_METHOD(startRecording: resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
BOOL result = [myRecorderViewController startRecording];
if (result) {
resolve();
} else {
reject();
}
}
...
#end
RecorderController.swift
#objc open class RecorderViewController : UIViewController {
#objc func startRecording() -> Bool {
do {
// Try to start recording
try recorder.record();
return true
} catch {
print("Errored recording.")
return false
}
}
}
There is an issue with your exported method syntax, you actually are sort of requiring a parameter but you don't get an error because it is passed to the RN macro. In Objective-C you don't label the first parameter.
Instead of this:
RCT_EXPORT_METHOD(startRecording: resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
It should look like this:
RCT_EXPORT_METHOD(startRecording:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)

NSItemProviderReading on Xamarin

To do files drag & drop for files in iOS 11 you need to implement NSItemProviderReading, there is the sample code for swift here: iOS 11 dropInteraction performDrop for files
However, how to do that in Xamarin, I guess the class definition should look like this, but how to implement the methods?
public class DocumentProvider : NSObject, INSItemProviderReading
{
}
You can implement the NSItemProviderReading like this:
class MyItemProvider : UIView, INSItemProviderReading {
[Export ("readableTypeIdentifiersForItemProvider")]
public static string [] ReadableTypeIdentifiersForItemProvider => new string [] { "public.image", "public.data" }
[Export ("objectWithItemProviderData:typeIdentifier:error:")]
public static MyItemProvider GetObject (NSData data, string typeIdentifier, out NSError outError)
{
outError = null;
switch (typeIdentifier) {
case "public.image": return new ...;
case "public.data": return new ...;
default:
outError = new NSError (...);
return null;
}
}
}
Reference:NSItemProviderReading requirements.

NSFastEnumeration in Swift

I am trying to convert an Objective-C project to swift, but I am unable to find how to use NSFastEnumeration for an object of a class that conforms to NSFastEnumeration.
Here is the code in ObjC:
// get the decode results
id<NSFastEnumeration> results = [info objectForKey: ZBarReaderControllerResults];
ZBarSymbol *symbol = nil;
for(symbol in results)
// just grab the first barcode
break;
so far I tried to find how to do this, but this doe not seems work, here is the swift code:
var results: ZBarSymbolSet = infoDictionary?.objectForKey(ZBarReaderControllerResults) as ZBarSymbolSet
var symbol : ZBarSymbol? = nil;
for symbol in results
{ //just grab first barcode
break;
}
the error comes in for condition - "ZBarSymbolSet" does not have a member named "Generator"
What am I doing wrong?
Here is the screen shot
After a while poking around the swift framework files, I finally found this nice class called NSFastGenerator. NSSet and friends seem to be using the same Generator.
For ZBarSymbolSet, here's how you'd extend it to support for-in loops:
extension ZBarSymbolSet: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
Update: Looks like Swift 2.0's protocol extensions fixed this for us!
Your defined class ZBarSymbolSet needs to implement the Swift SequenceType interface in order to be usable in for <identifier> in <sequence> syntax. The SequenceType interface is
protocol SequenceType : _Sequence_Type {
typealias Generator : GeneratorType
func generate() -> Generator
}
and thus you see the mention of Generator as reported in your error message.
Also in the syntax:
for <identifier> in <sequence> {
<statements>
}
the <identifer> is only in scope for <statements>. Thus your second use of symbol in the if will be out of scope and an error. One proper idiom would be:
var symbolFound : ZBarSymbol?
for symbol in result {
symbolFound = symbol
break
}
if symbolFound ...
If course, but the time ZBarSymbolSet implements SequenceType it would also implement CollectionType with subscript and thus the whole 'find the first element' code would be var symbol = result[0]
Here is John Estropia's answer for Swift 3:
extension ZBarSymbolSet: Sequence {
public typealias Iterator = NSFastEnumerationIterator
public func makeIterator() -> NSFastEnumerationIterator {
return NSFastEnumerationIterator(self)
}
}
Then you for-in loop would look like this:
for element in results {
let symbol = element as! ZBarSymbol
// ...
}
This answer could be improved by also adopting the IteratorProtocol so you can specify the element associated type as ZBarSymbol. I have not figured out how to do this yet.
Step1:
extension ZBarSymbolSet: SequenceType {
public func generate() -> NSFastGenerator {
return NSFastGenerator(self)
}
}
Step2:
var results: NSFastEnumeration = info.objectForKey(ZBarReaderControllerResults) as NSFastEnumeration
var symbolFound : ZBarSymbol?
for symbol in results as ZBarSymbolSet {
symbolFound = symbol as? ZBarSymbol
break
}
resultString = NSString(string: symbolFound!.data)
Here's a way to do it without writing an extension
var iterator = NSFastEnumerationIterator(collection)
while let element = iterator.next() {
// use element
}
Also, if you know that all objects in your ZBarSymbolSet are ZBarSymbol objects (since ObjC doesn't enforce all objects being ZBarSymbol objects), you can do the following:
extension ZBarSymbolSet {
public struct ZBarSymbolSetIterator {
public typealias Element = ZBarSymbol
private let enumerator: NSFastEnumerationIterator
init(_ symbols: ZBarSymbolSet) {
self.enumerator = NSFastEnumerationIterator(symbols)
}
public mutating func next() -> ZBarSymbol {
if let object = self.enumerator.next() {
return object as? ZBarSymbol
}
else { return nil }
}
}
public func makeIterator() -> ZBarSymbolSetIterator {
return ZBarSymbolSetIterator(self)
}
}
Now your for-loop will look like this:
for element in results {
// element is a ZBarSymbol
}

Resources