rac_textSignal in Swift 3 - ios

I am attempting to migrate a Swift 2.2 app that I did not write to Swift 3. All was going well until I ran into some RAC calls. I am not familiar with ReactiveCocoa, which makes this even more challenging.
/**
* Method name: Enable Login Button
* Description: Enable or disable login button based on
* username and password entry
* Parameters: None
*/
func enableLoginButton() -> Void {
self.btnLogin.isEnabled = false
let serviceUrlSignal = self.txtServiceUrl.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
let usernameSignal = self.txtFieldUsername.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
let userNameCharSignal = usernameSignal.map { $0.characters.count }
let passwordSignal = self.txtFieldPassword.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
let passwordCharSignal = passwordSignal.map { $0.characters.count }
userNameCharSignal.combineLatestWith(passwordCharSignal)
.map
{
charArray in charArray.0 > 0 && charArray.1 > 0
}
.observe { event in
if let buttonEnabled = event.value {
self.btnLogin.enabled = buttonEnabled
}
}
viewModel.username <~ usernameSignal
viewModel.password <~ passwordSignal
viewModel.serviceUrl <~ serviceUrlSignal
btnLogin.rac_signalForControlEvents(.TouchUpInside)
.subscribeNext{
button in
print("Authenticate Click!")
}
}
From what I understand, rac_textSignal does not exist in ReactiveSwift. Do I need to install ReactiveObjC or is there a Swift approach that I could use to replace this functionality? Thanks.

You'll need to add ReactiveCocoa in addition to ReactiveSwift.
Since the repo split, the core of what was ReactiveCocoa is now implemented in ReactiveSwift, but ReactiveCocoa adds Cocoa specific things like UIKit bindings which you'll need.
UIKit bindings are accessible via the reactive property, e.g. textfield.reactive.continuousTextValues.
Since these bindings are already correctly typed (as opposed to rac_textSignal), you can replace the whole chunk of
let usernameSignal = self.txtFieldUsername.rac_textSignal()
.toSignalAssumingHot()
.assumeNoErrors()
.map { text in text as! String }
...
viewModel.username <~ usernameSignal
with
viewModel.username <~ self.txtFieldUsername.reactive.continuousTextValues

Related

How to capture/extract text value from a text field in EarlGrey?

I'm using EarlGrey framework and I want to select the whole text and capture it from the textfield or search bar. Is there a way to do it?
Thanks in advance!
You extracted the value with XCTest APIs method.
The right way can be found in EarlGrey FAQ
https://github.com/google/EarlGrey/blob/master/docs/faq.md
// Swift
//
// Must use a wrapper class to force pass by reference in Swift 3 closures.
// inout params cannot be modified within closures. http://stackoverflow.com/a/28252105
open class Element {
var text = ""
}
/*
* Example Usage:
*
* let element = Element()
* domainField.performAction(grey_replaceText("hello.there"))
* .performAction(grey_getText(element))
*
* GREYAssertTrue(element.text != "", reason: "get text failed")
*/
public func grey_getText(_ elementCopy: Element) -> GREYActionBlock {
return GREYActionBlock.action(withName: "get text",
constraints: grey_respondsToSelector(#selector(getter: UILabel.text))) { element,
errorOrNil -> Bool in
let elementObject = element as? NSObject
let text = elementObject?.perform(#selector(getter: UILabel.text),
with: nil)?.takeRetainedValue() as? String
elementCopy.text = text ?? ""
return true
}
}
Finally, found out !
Using application.navigationBars["Navigation bar Name"].searchFields["Label of the textField / Search Bar"].value
This fetched the value from the textfield. One thing to be noted is that, the value retrieved will be of the type "any"
Can anyone write the below code with objective-c?
// Swift
//
// Must use a wrapper class to force pass by reference in Swift 3 closures.
// inout params cannot be modified within closures. http://stackoverflow.com/a/28252105
open class Element {
var text = ""
}
/*
* Example Usage:
*
* let element = Element()
* domainField.performAction(grey_replaceText("hello.there"))
* .performAction(grey_getText(element))
*
* GREYAssertTrue(element.text != "", reason: "get text failed")
*/
public func grey_getText(_ elementCopy: Element) -> GREYActionBlock {
return GREYActionBlock.action(withName: "get text",
constraints: grey_respondsToSelector(#selector(getter: UILabel.text))) { element,
errorOrNil -> Bool in
let elementObject = element as? NSObject
let text = elementObject?.perform(#selector(getter: UILabel.text),
with: nil)?.takeRetainedValue() as? String
elementCopy.text = text ?? ""
return true
}
}

Use JS library in Flutter

I am trying to use Ether JS in my Flutter application. I know that it is not directly supported and even the existing implementations are not really well documented.
Is there any way I can use this library in my Flutter application for Android and iOS? Any other alternative suggestion is also welcome.
I have tried js.dart but could not figure out how to use it. I am not even sure if it is the right choice for this scenario.
I have also tried Flutter WebView Plugin.
plugin.evalJavascript(
'function add(a,b){return a+b;}add(2,3);'
).then((s) {
print(s);
}
This function rightly returns 5 as the response. But I do not understand how to use the EtherJS library like this.
I am a noob with Flutter, Dart and JS. Any help will be appreciated.
I eventually solved this by using Platform channels as suggested by rmtmckenzie in this answer.
I downloaded the JS file and saved it to android/app/src/main/res/raw/ether.js and ios/runner/ether.js for Android and iOS respectively.
Installing dependencies
Android
Add LiquidCore as a dependency in app level build.gradle
implementation 'com.github.LiquidPlayer:LiquidCore:0.5.0'
iOS
For iOS I used the JavaScriptCore which is part of the SDK.
Platform Channel
In my case, I needed to create a Wallet based on a Mnemonic (look up BIP39) I pass in into the JS function. For this, I created a Platform channel which passes in the Mnemonic (which is basically of type String) as an argument and will return a JSON object when done.
Future<dynamic> getWalletFromMnemonic({#required String mnemonic}) {
return platform.invokeMethod('getWalletFromMnemonic', [mnemonic]);
}
Android Implementation (Java)
Inside MainActivity.java add this after this line
GeneratedPluginRegistrant.registerWith(this);
String CHANNEL = "UNIQUE_CHANNEL_NAME";
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() {
#Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.equals("getWalletFromMnemonic")) {
ArrayList<Object> args = (ArrayList<Object>) methodCall.arguments;
String mnemonic = (String) args.get(0);
JSObject walletFromMnemonic = getWalletFromMnemonic(mnemonic);
if (walletFromMnemonic == null) {
result.error("Could not create", "Wallet generation failed", null);
return;
}
String privateKey = walletFromMnemonic.property("privateKey").toString();
String address = walletFromMnemonic.property("address").toString();
HashMap<String, String> map = new HashMap<>();
map.put("privateKey", privateKey);
map.put("address", address);
JSONObject obj = new JSONObject(map);
result.success(obj.toString());
} else {
result.notImplemented();
}
}
}
);
Declare the following methods which perform the actual action of interacting with the JS library and returning the result to the platform channel.
#Nullable
#VisibleForTesting
private JSObject getWalletFromMnemonic(String mnemonic) {
JSContext jsContext = getJsContext(getEther());
JSObject wallet = getWalletObject(jsContext);
if (wallet == null) {
return null;
}
if (!wallet.hasProperty("fromMnemonic")) {
return null;
}
JSFunction walletFunction = wallet.property("fromMnemonic").toObject().toFunction();
return walletFunction.call(null, mnemonic).toObject();
}
#Nullable
#VisibleForTesting
private JSObject getWalletObject(JSContext context) {
JSObject jsEthers = context.property("ethers").toObject();
if (jsEthers.hasProperty("Wallet")) {
return jsEthers.property("Wallet").toObject();
}
return null;
}
#VisibleForTesting
String getEther() {
String s = "";
InputStream is = getResources().openRawResource(R.raw.ether);
try {
s = IOUtils.toString(is);
} catch (IOException e) {
s = null;
e.printStackTrace();
} finally {
IOUtils.closeQuietly(is);
}
return s;
}
#VisibleForTesting
JSContext getJsContext(String code) {
JSContext context = new JSContext();
context.evaluateScript(code);
return context;
}
iOS Implementation (Swift)
Add the following lines in AppDelegate.swift inside the override application method.
final let methodChannelName: String = "UNIQUE_CHANNEL_NAME"
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
let methodChannel = FlutterMethodChannel.init(name: methodChannelName, binaryMessenger: controller)
methodChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: FlutterResult)-> Void in
if call.method == "getWalletFromMnemonic" {
guard let mnemonic = call.arguments as? [String] else {
return
}
if let wallet = self.getWalletFromMnemonic(mnemonic: mnemonic[0]) {
result(wallet)
} else {
result("Invalid")
}
}
})
Add the logic to interact with the JavaScriptCore.
private func getWalletFromMnemonic(mnemonic: String) -> Dictionary<String, String>? {
let PRIVATE_KEY = "privateKey"
let ADDRESS = "address"
guard let jsContext = self.initialiseJS(jsFileName: "ether") else { return nil }
guard let etherObject = jsContext.objectForKeyedSubscript("ethers") else { return nil }
guard let walletObject = etherObject.objectForKeyedSubscript("Wallet") else { return nil }
guard let walletFromMnemonicObject = walletObject.objectForKeyedSubscript("fromMnemonic") else {
return nil
}
guard let wallet = walletFromMnemonicObject.call(withArguments: [mnemonic]) else { return nil }
guard let privateKey = wallet.forProperty(PRIVATE_KEY)?.toString() else { return nil }
guard let address = wallet.forProperty(ADDRESS)?.toString() else { return nil }
var walletDictionary = Dictionary<String, String>()
walletDictionary[ADDRESS] = address
walletDictionary[PRIVATE_KEY] = privateKey
return walletDictionary
}
private func initialiseJS(jsFileName: String) -> JSContext? {
let jsContext = JSContext()
guard let jsSourcePath = Bundle.main.path(forResource: jsFileName, ofType: "js") else {
return nil
}
do {
let jsSourceContents = try String(contentsOfFile: jsSourcePath)
jsContext!.evaluateScript(jsSourceContents)
return jsContext!
} catch {
print(error.localizedDescription)
}
return nil
}
Honestly, if you're new to Flutter, Dart, and JS you are going to have some trouble with this unless you're willing to invest a fair amount of time. It does depend on what exactly you're trying to make with the Ether JS library, but in general you're going to have a hard time integrating it with flutter. There is an Ethereum package but it seems much narrower in scope than the ether.js library you've been looking at - it mostly seems focused on communication with the RPC api rather than dealing with wallets etc.
If you're dead set on using Flutter, your best bet would be to use Android & iOS specific libraries to do the actual ethereum stuff and to communicate through Platform Channels to a common api in your dart code. This could be a significant undertaking depending on how much of the API you need to expose, especially for someone new to flutter/dart and possibly to android/ios development as well. This will be much more performant than communicating back and forth with javascript running in a webview though, and realistically probably easier to code as well because flutter doesn't really have any good mechanisms for calling js code right now.
There is another option - to use a different client UI framework entirely. React native might do everything you need and has the advantage of being in Javascript so it can most likely integrate the Ether.js library easily, although I can't guarantee that it will actually fully support the ether.js library (its runtime might not have the necessary crypto extensions for example).
Future<String> loadJS(String name) async {
var givenJS = rootBundle.loadString('assets/$name.js');
return givenJS.then((String js) {
flutterWebViewPlugin.onStateChanged.listen((viewState) async {
if (viewState.type == WebViewState.finishLoad) {
flutterWebViewPlugin.evalJavascript(js);
}
});
});
}

generic tap function for XCUIApplication

We are trying to migrate from UIAutomation to XCUITests.
For the UIAutomation we came up with a handy 'tapOnName' function which just crawled thru a whole sub element tree and tapped on the element with the first match.
function log(msg) {
UIALogger.logDebug(msg);
}
//recursive function crawling thru an elements hierarchy
//and tapping on the first match of accessibilityIdentifier
//or button text
function tapOnNameWithRoot(name,el) {
if (el.name()==name && el.isVisible()) {
log("tap on itt!!!")
el.tap();
return true;
}
if (el.toString()=="[object UIAButton]" && el.label()==name) {
log("tap on Button!!!")
el.tap();
return true;
}
var elements=el.elements();
if (elements===null || elements===undefined) {
log("elements null or undefined for:"+el.toString());
return false;
}
for(var i=0,len=elements.length ;i<len;i++) {
if (tapOnNameWithRoot(name,elements[i])) {
return true;
}
}
return false;
}
var win = UIATarget.localTarget().frontMostApp().mainWindow();
//for ex taps on a button with the text "pushme" in the
//main UIWindow
tapOnNameWithRoot("pushme",win);
No the question : is it possible to implement the same function using XCUIApplication ?
There is shorthand support for this function in XCTest.
For tapping the first match out of any element, you can get all elements and tap the first one:
let app = XCUIApplication()
let element = app.descendentsMatchingType(.Any)["someIdentifier"]
element.tap()
If you know what type of element it is going to be, it's better to filter by that type first:
let app = XCUIApplication()
let element = app.buttons["someIdentifier"]
element.tap()
Are you looking for something like this:
func tapBasedOnAccessibilityIdentifier(elementType elementType: XCUIElementQuery, accessibilityIdentifier: String) {
var isElementExist = false
for element in elementType.allElementsBoundByIndex {
if element.label == accessibilityIdentifier {
element.tap()
isElementExist = true
break
}
}
if !isElementExist {
XCTFail("Failed to find element")
}
}
where you call the method in the test like:
tapBasedOnAccessibilityIdentifier(elementType: app.staticTexts, accessibilityIdentifier: "Accessibility Identifier")
You can tweak it a little so that it cover all the requirements.

objc_sync_enter / objc_sync_exit not working with DISPATCH_QUEUE_PRIORITY_LOW

I need a read\write lock for my application. I've read https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
and wrote my own class, cause there are no read/write lock in swift
class ReadWriteLock {
var logging = true
var b = 0
let r = "vdsbsdbs" // string1 for locking
let g = "VSDBVSDBSDBNSDN" // string2 for locking
func waitAndStartWriting() {
log("wait Writing")
objc_sync_enter(g)
log("enter writing")
}
func finishWriting() {
objc_sync_exit(g)
log("exit writing")
}
// ждет пока все чтение завершится чтобы начать чтение
// и захватить мютекс
func waitAndStartReading() {
log("wait reading")
objc_sync_enter(r)
log("enter reading")
b++
if b == 1 {
objc_sync_enter(g)
log("read lock writing")
}
print("b = \(b)")
objc_sync_exit(r)
}
func finishReading() {
objc_sync_enter(r)
b--
if b == 0 {
objc_sync_exit(g)
log("read unlock writing")
}
print("b = \(b)")
objc_sync_exit(r)
}
private func log(s: String) {
if logging {
print(s)
}
}
}
It works good, until i try to use it from GCD threads.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
When i try to use this class from different async blocks at some moment it allows to write when write is locked
here is sample log:
wait reading
enter reading
read lock writing
b = 1
wait reading
enter reading
b = 2
wait reading
enter reading
b = 3
wait reading
enter reading
b = 4
wait reading
enter reading
b = 5
wait reading
enter reading
b = 6
wait reading
enter reading
b = 7
wait reading
enter reading
b = 8
wait reading
enter reading
b = 9
b = 8
b = 7
b = 6
b = 5
wait Writing
enter writing
exit writing
wait Writing
enter writing
So, as you can see g was locked, but objc_sync_enter(g) allows to continue.
Why could this happen ?
BTW i checked how many times ReadWriteLock constructed, and it's 1.
Why objc_sync_exit not working and allowing to objc_sync_enter(g) when it's not freed ?
PS Readwirtelock defined as
class UserData {
static let lock = ReadWriteLock()
Thanks.
objc_sync_enter is an extremely low-level primitive, and isn't intended to be used directly. It's an implementation detail of the old #synchronized system in ObjC. Even that is extremely out-dated and should generally be avoided.
Synchronized access in Cocoa is best achieved with GCD queues. For example, this is a common approach that achieves a reader/writer lock (concurrent reading, exclusive writing).
public class UserData {
private let myPropertyQueue = dispatch_queue_create("com.example.mygreatapp.property", DISPATCH_QUEUE_CONCURRENT)
private var _myProperty = "" // Backing storage
public var myProperty: String {
get {
var result = ""
dispatch_sync(myPropertyQueue) {
result = self._myProperty
}
return result
}
set {
dispatch_barrier_async(myPropertyQueue) {
self._myProperty = newValue
}
}
}
}
All your concurrent properties can share a single queue, or you can give each property its own queue. It depends on how much contention you expect (a writer will lock the entire queue).
The "barrier" in "dispatch_barrier_async" means that it is the only thing allowed to run on the queue at that time, so all previous reads will have completed, and all future reads will be prevented until it completes. This scheme means that you can have as many concurrent readers as you want without starving writers (since writers will always be serviced), and writes are never blocking. On reads are blocking, and only if there is actual contention. In the normal, uncontested case, this is extremely very fast.
Are you 100% sure your blocks are actually executing on different threads?
objc_sync_enter() / objc_sync_exit() are guarding you only from object being accessed from different threads. They use a recursive mutex under the hood, so they won't either deadlock or prevent you from repeatedly accessing object from the same thread.
So if you lock in one async block and unlock in another one, the third block executed in-between can have access to the guarded object.
This is one of those very subtle nuances that is easy to miss.
Locks in Swift
You have to really careful what you use as a Lock. In Swift, String is a struct, meaning it's pass-by-value.
Whenever you call objc_sync_enter(g), you are not giving it g, but a copy of g. So each thread is essentially creating its own lock, which in effect, is like having no locking at all.
Use NSObject
Instead of using a String or Int, use a plain NSObject.
let lock = NSObject()
func waitAndStartWriting() {
log("wait Writing")
objc_sync_enter(lock)
log("enter writing")
}
func finishWriting() {
objc_sync_exit(lock)
log("exit writing")
}
That should take care of it!
In addition to #rob-napier's solution. I've updated this to Swift 5.1, added generic typing and a couple of convenient append methods. Note that only methods that access resultArray via get/set or append are thread safe, so I added a concurrent append also for my practical use case where the result data is updated over many result calls from instances of Operation.
public class ConcurrentResultData<E> {
private let resultPropertyQueue = dispatch_queue_concurrent_t.init(label: UUID().uuidString)
private var _resultArray = [E]() // Backing storage
public var resultArray: [E] {
get {
var result = [E]()
resultPropertyQueue.sync {
result = self._resultArray
}
return result
}
set {
resultPropertyQueue.async(group: nil, qos: .default, flags: .barrier) {
self._resultArray = newValue
}
}
}
public func append(element : E) {
resultPropertyQueue.async(group: nil, qos: .default, flags: .barrier) {
self._resultArray.append(element)
}
}
public func appendAll(array : [E]) {
resultPropertyQueue.async(group: nil, qos: .default, flags: .barrier) {
self._resultArray.append(contentsOf: array)
}
}
}
For an example running in a playground add this
//MARK:- helpers
var count:Int = 0
let numberOfOperations = 50
func operationCompleted(d:ConcurrentResultData<Dictionary<AnyHashable, AnyObject>>) {
if count + 1 < numberOfOperations {
count += 1
}
else {
print("All operations complete \(d.resultArray.count)")
print(d.resultArray)
}
}
func runOperationAndAddResult(queue:OperationQueue, result:ConcurrentResultData<Dictionary<AnyHashable, AnyObject>> ) {
queue.addOperation {
let id = UUID().uuidString
print("\(id) running")
let delay:Int = Int(arc4random_uniform(2) + 1)
for _ in 0..<delay {
sleep(1)
}
let dict:[Dictionary<AnyHashable, AnyObject>] = [[ "uuid" : NSString(string: id), "delay" : NSString(string:"\(delay)") ]]
result.appendAll(array:dict)
DispatchQueue.main.async {
print("\(id) complete")
operationCompleted(d:result)
}
}
}
let q = OperationQueue()
let d = ConcurrentResultData<Dictionary<AnyHashable, AnyObject>>()
for _ in 0..<10 {
runOperationAndAddResult(queue: q, result: d)
}
I had the same problem using queues in background. The synchronization is not working all the time in queues with "background" (low) priority.
One fix I found was to use semaphores instead of "obj_sync":
static private var syncSemaphores: [String: DispatchSemaphore] = [:]
static func synced(_ lock: String, closure: () -> ()) {
//get the semaphore or create it
var semaphore = syncSemaphores[lock]
if semaphore == nil {
semaphore = DispatchSemaphore(value: 1)
syncSemaphores[lock] = semaphore
}
//lock semaphore
semaphore!.wait()
//execute closure
closure()
//unlock semaphore
semaphore!.signal()
}
The function idea comes from What is the Swift equivalent to Objective-C's "#synchronized"?, an answer of #bryan-mclemore.

Create a moving average (and other FIR filters) using ReactiveCocoa

I'm still getting started with ReactiveCocoa and functional reactive programming concepts, so maybe this is a dumb question.
ReactiveCocoa seem naturally designed to react to streams of live data, touch events or accelerometer sensor input etc.
Is it possible to apply finite impulse response filters in ReactiveCocoa in an easy, reactive fashion? Or if not, what would be the least-ugly hacky way of doing this? How would one go about implementing something like a simple moving average?
Ideally looking for an Swift 2 + RA4 solution but also interested in if this is possible at all in Objective C and RA2/RA3.
What you actually need is a some sort of period buffer, which will keep a period of values buffered and only start sending out when the buffer has reached capacity (the code below is heavenly inspired on takeLast operator)
extension SignalType {
func periodBuffer(period:Int) -> Signal<[Value], Error> {
return Signal { observer in
var buffer: [Value] = []
buffer.reserveCapacity(period)
return self.observe { event in
switch event {
case let .Next(value):
// To avoid exceeding the reserved capacity of the buffer, we remove then add.
// Remove elements until we have room to add one more.
while (buffer.count + 1) > period {
buffer.removeAtIndex(0)
}
buffer.append(value)
if buffer.count == period {
observer.sendNext(buffer)
}
case let .Failed(error):
observer.sendFailed(error)
case .Completed:
observer.sendCompleted()
case .Interrupted:
observer.sendInterrupted()
}
}
}
}
}
based on that you can map it to any algorithm you want
let pipe = Signal<Int,NoError>.pipe()
pipe.0
.periodBuffer(3)
.map { Double($0.reduce(0, combine: +))/Double($0.count) } // simple moving average
.observeNext { print($0) }
pipe.1.sendNext(10) // does nothing
pipe.1.sendNext(11) // does nothing
pipe.1.sendNext(15) // prints 12
pipe.1.sendNext(7) // prints 11
pipe.1.sendNext(9) // prints 10.3333
pipe.1.sendNext(6) // prints 7.3333
Probably the scan signal operator is what you're looking for. Inspired by Andy Jacobs' answer, I came up with something like this (a simple moving average implementation):
let (signal, observer) = Signal<Int,NoError>.pipe()
let maxSamples = 3
let movingAverage = signal.scan( [Int]() ) { (previousSamples, nextValue) in
let samples : [Int] = previousSamples.count < maxSamples ? previousSamples : Array(previousSamples.dropFirst())
return samples + [nextValue]
}
.filter { $0.count >= maxSamples }
.map { $0.average }
movingAverage.observeNext { (next) -> () in
print("Next: \(next)")
}
observer.sendNext(1)
observer.sendNext(2)
observer.sendNext(3)
observer.sendNext(4)
observer.sendNext(42)
Note: I had to move average method into a protocol extension, otherwise the compiler would complain that the expression was too complex. I used a nice solution from this answer:
extension Array where Element: IntegerType {
var total: Element {
guard !isEmpty else { return 0 }
return reduce(0){$0 + $1}
}
var average: Double {
guard let total = total as? Int where !isEmpty else { return 0 }
return Double(total)/Double(count)
}
}

Resources