I have my View defined as below
struct detailPage: View {
var body: some View {
VStack() {
let returnedValue = RNPayPassConnect.canAddPass("Text")
if (returnedValue) {
ProgressView()
}
}
}
}
I need to check the returned value from from canAddPass function, so I have written the code for that, but I am getting an error as
Instance member 'canAddPass' cannot be used on type 'RNPayPassConnect'; did you mean to use a value of this type instead?
Below is my RNPayPassConnect code
#objc(RNPayPassConnect)
class RNPayPassConnect: NSObject {
let addPassViewController = PKAddPassViewController()
#objc func canAddPass(_ someValue : String) -> Bool {
let result = PKAddPaymentPassViewController.canAddPaymentPass()
return result
}
}
I tried with all possible solutions, but no help, so please can someone help me on this?
You made canAddPass an instance method, but you are trying to call it on the RNPayPassConnect type rather than on an instance.
You either need to call it on an instance or change it into a static method
Static method:
#objc static func canAddPass(_ someValue : String) -> Bool {
let result = PKAddPaymentPassViewController.canAddPaymentPass()
return result
}
Or if you want to keep it an instance method, make sure you store an instance in your view and call it on that:
struct DetailPage: View {
private let payPassConnect = RNPayPassConnect()
var body: some View {
VStack() {
let returnedValue = payPassConnect.canAddPass("Text")
if (returnedValue) {
ProgressView()
}
}
}
}
I have used blocks to get callbacks from other classes. I am a beginner in swift. So I need to find a way to define a closure in one class and assign it to a closure variable in another class. I will be calling this closure to get callback of second class in first class.
What I want is something like this,
Class A {
func viewdidload() {
let b:B = B()
b.closure(string:NSString) = {
print string
}
}
}
class B {
var closure(NSString);
func () {
closure(string)
}
}
Here, but you should really learn Swift first
class A {
func viewdidload() {
let b = B()
b.closure = { str in
print(str)
}
}
}
class B {
var closure : ((String) -> Void)?
func t() {
closure?("hi")
}
}
Try this in a Playground:
class B {
var closure: (NSString) -> NSString
init() {
closure = { input in
return "NSString from closure: \(input)"
}
}
}
class A {
init() {
let b:B = B()
print(b.closure("hello from A"))
b.closure = { input in
return "NSString from another closure: \(input)"
}
print(b.closure("hello from A"))
}
}
A();
will print
NSString from closure: hello from A
NSString from another closure: hello from A
I'm a Swift and ReactiveCocoa noob at the same time. Using MVVM and Reactive Cocoa v3.0-beta.4 framework, I'd like to implement this setup, to learn the basics of the new RAC 3 framework.
I have a text field and I want the text input to contain more than 3 letters, for validation. If the text passes the validation, the button underneath should be enabled. When the button receives the touch down event, I want to trigger an action using the view model's property.
Since there are very few resources about RAC 3.0 beta at the moment, I implemented the following by reading the QAs on the framework's Github repo. Here's what I could come up with so far:
ViewModel.swift
class ViewModel {
var text = MutableProperty<String>("")
let action: Action<String, Bool, NoError>
let validatedTextProducer: SignalProducer<AnyObject?, NoError>
init() {
let validation: Signal<String, NoError> -> Signal<AnyObject?, NoError> = map ({
string in
return (count(string) > 3) as AnyObject?
})
validatedTextProducer = text.producer.lift(validation)
//Dummy action for now. Will make a network request using the text property in the real app.
action = Action { _ in
return SignalProducer { sink, disposable in
sendNext(sink, true)
sendCompleted(sink)
}
}
}
}
ViewController.swift
class ViewController: UIViewController {
private lazy var txtField: UITextField = {
return createTextFieldAsSubviewOfView(self.view)
}()
private lazy var button: UIButton = {
return createButtonAsSubviewOfView(self.view)
}()
private lazy var buttonEnabled: DynamicProperty = {
return DynamicProperty(object: self.button, keyPath: "enabled")
}()
private let viewModel = ViewModel()
private var cocoaAction: CocoaAction?
override func viewDidLoad() {
super.viewDidLoad()
view.setNeedsUpdateConstraints()
bindSignals()
}
func bindSignals() {
viewModel.text <~ textSignal(txtField)
buttonEnabled <~ viewModel.validatedTextProducer
cocoaAction = CocoaAction(viewModel.action, input:"Actually I don't need any input.")
button.addTarget(cocoaAction, action: CocoaAction.selector, forControlEvents: UIControlEvents.TouchDown)
viewModel.action.values.observe(next: {value in
println("view model action result \(value)")
})
}
override func updateViewConstraints() {
super.updateViewConstraints()
//Some autolayout code here
}
}
RACUtilities.swift
func textSignal(textField: UITextField) -> SignalProducer<String, NoError> {
return textField.rac_textSignal().toSignalProducer()
|> map { $0! as! String }
|> catch {_ in SignalProducer(value: "") }
}
With this setup, the button gets enabled when the view model's text is longer than 3 characters. When the user taps on the button, the view model's action runs and I can get the return value as true. So far so good.
My question is: Inside the view model's action, I want to use its stored text property and update the code to make a network request using it. So, I don't need an input from the view controller's side. How can I not require an input for my Action property?
From the ReactiveCocoa/CHANGELOG.md:
An action must indicate the type of input it accepts, the type of output it produces, and what kinds of errors can occur (if any).
So currently there is no way to define an Action without an input.
I suppose you could declare that you don't care about input by making it AnyObject? and creating CocoaAction with convenience initialiser:
cocoaAction = CocoaAction(viewModel.action)
Additional remarks
I dont't like using AnyObject? instead of Bool for validatedTextProducer. I suppose you preferred it because binding to the buttonEnabled property requires AnyObject?. I would rather cast it there though, instead of sacrificing type clarity of my view model (see example below).
You might want to restrict execution of the Action on the view model level as well as UI, e.g.:
class ViewModel {
var text = MutableProperty<String>("")
let action: Action<AnyObject?, Bool, NoError>
// if you want to provide outside access to the property
var textValid: PropertyOf<Bool> {
return PropertyOf(_textValid)
}
private let _textValid = MutableProperty(false)
init() {
let validation: Signal<String, NoError> -> Signal<Bool, NoError> = map { string in
return count(string) > 3
}
_textValid <~ text.producer |> validation
action = Action(enabledIf:_textValid) { _ in
//...
}
}
}
And binding to buttonEnabled:
func bindSignals() {
buttonEnabled <~ viewModel.action.enabled.producer |> map { $0 as AnyObject }
//...
}
If you take a look at Colin Eberhardt blog post on ReactiveCocoa 3 there's a very nice approach to this problem.
Basically because it's still in beta there's no extension on UIView that makes those properties easy to use with RAC3 but you can add them easily. I would recommend adding a UIKit+RAC3.swift extension and adding them as you need:
import UIKit
import ReactiveCocoa
struct AssociationKey {
static var hidden: UInt8 = 1
static var alpha: UInt8 = 2
static var text: UInt8 = 3
static var enabled: UInt8 = 4
}
func lazyAssociatedProperty<T: AnyObject>(host: AnyObject,
key: UnsafePointer<Void>, factory: ()->T) -> T {
var associatedProperty = objc_getAssociatedObject(host, key) as? T
if associatedProperty == nil {
associatedProperty = factory()
objc_setAssociatedObject(host, key, associatedProperty,
UInt(OBJC_ASSOCIATION_RETAIN))
}
return associatedProperty!
}
func lazyMutableProperty<T>(host: AnyObject, key: UnsafePointer<Void>,
setter: T -> (), getter: () -> T) -> MutableProperty<T> {
return lazyAssociatedProperty(host, key) {
var property = MutableProperty<T>(getter())
property.producer
.start(next: {
newValue in
setter(newValue)
})
return property
}
}
extension UIView {
public var rac_alpha: MutableProperty<CGFloat> {
return lazyMutableProperty(self, &AssociationKey.alpha, { self.alpha = $0 }, { self.alpha })
}
public var rac_hidden: MutableProperty<Bool> {
return lazyMutableProperty(self, &AssociationKey.hidden, { self.hidden = $0 }, { self.hidden })
}
}
extension UIBarItem {
public var rac_enabled: MutableProperty<Bool> {
return lazyMutableProperty(self, &AssociationKey.enabled, { self.enabled = $0 }, { self.enabled })
}
}
That way you simply replace the RAC = RACObserve logic by (for example):
var date = MutableProperty<NSDate?>(nil)
var time = MutableProperty<Int?>(nil)
let doneItem = UIBarButtonItem()
doneItem.rac_enabled <~ date.producer
|> combineLatestWith(time.producer)
|> map { return $0.0 != nil && $0.1 != nil }
Again this is all taken from his blog post which far more descriptive than this answer. I highly recommend anyone interested in using RAC 3 reads his amazing posts and tutorials:
A first look at RAC 3
Signal Producers and API Clarity
MVVM and RAC 3
I'm developing first app using Swift.
in one of my Class I need to store closure in an Array. Like an event manager :
typealias eventCallback = (eventName:String, data:[MasterModel]?) -> Void;
class MHttpLoader: NSObject
{
var eventRegister : [String: [eventCallback]];
class var instance : MHttpLoader
{
struct Static {
static let instance : MHttpLoader = MHttpLoader(baseURL:NSURL(string:"http://192.168.0.11:3000"));
}
return Static.instance;
}
class func registerEvent(eventName:String, callback:eventCallback)
{
if var tab = MHttpLoader.instance.eventRegister[eventName]
{
tab.append(callback);
}
else
{
MHttpLoader.instance.eventRegister[eventName] = [callback];
}
}
func fireEvent(eventName: String, data:[MasterModel]?)
{
if let tab = self.eventRegister[eventName]
{
for callback in tab
{
callback(eventName:eventName, data:data);
}
}
}
}
All this code work pretty well, the problem is when i want to remove a closure from my array.
For exemple :
class func removeEvent(eventName:String, callback:eventCallback)
{
if var tab :Array = MHttpLoader.instance.eventRegister[eventName]
{
if let index = find(tab, callback) as? Int
{
tab.removeAtIndex(index);
}
}
}
I have the error which says that my closure is not conform to protocol "Equatable"
I also tried :
class func removeEvent(eventName:String, callback:eventCallback)
{
if var tab :Array = MHttpLoader.instance.eventRegister[eventName]
{
tab = tab.filter({ (currentElement) -> Bool in
return currentElement != callback;
});
}
}
But I have the error : Cannot invoke '!=' with an argument list of type '((eventCallback), eventCallback)'
Here is my question how can i find the index of closure in array or simply compare closure?
Thank you
I want to create a Swift dictionary that holds String type as its keys and Closures as its values. Following is the code that I have but it gives me the error:
'#lvalue is not identical to '(String, () -> Void)'
class CommandResolver {
private var commandDict:[String : () -> Void]!
init() {
self.setUpCommandDict();
}
func setUpCommandDict() {
self.commandDict["OpenAssessment_1"] = {
println("I am inside closure");
}
}
}
I tried looking at other question on StackOverflow regarding closures in dictionaries but it does not give me any satisfactory answer. So I would really appreciate some help here.
Here is the way to go. I am not sure exactly why your implementation does not work though.
class CommandResolver {
typealias MyBlock = () -> Void
private var commandDict:[String : MyBlock] = [String:MyBlock]()
init() {
self.setUpCommandDict();
}
func setUpCommandDict() {
self.commandDict["OpenAssessment_1"] = {
print("I am inside closure");
}
}
}
If you initialize your dictionary in your init before calling your setup function, it should work:
class CommandResolver {
private var commandDict: [String: () -> Void]
init() {
commandDict = [:]
setUpCommandDict()
}
func setUpCommandDict() {
commandDict["OpenAssessment_1"] = {
println("I am inside closure")
}
}
}