How can I pass a Swift array as an argument to a thread? - arrays

I'm trying to spawn a new thread to do some background processing, based on a String that I've broken down into an array of characters. Here's what my code looks like:
var testString : String = NSString(data:data!, encoding:NSUTF8StringEncoding)
var testStringArray : Array<Character> = []
for character in testString
{
if(!(self.isCharacterStrippable(character)))
{
testStringArray.append(character)
}
}
NSThread.detachNewThreadSelector("fillKeysFromArray:", toTarget: self, withObject: testStringArray)
I get a compiler error telling me that "Array does not conform to protocol AnyObject".
Short of writing an object wrapper for my array, or setting it as an instance variable (both of which seem like overkill), is there any way I can get this array passed through to the new thread?

Using Grand Central Dispatch is going to be much easier in the long run. You can run your function on a background thread with something like:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
[weak self] in // This is so that we don't create a reference cycle
self?.fillKeysFromArray(testStringArray);
return
}
You can read up on Grand Central Dispatch and all the nice things it provides when it comes to threading and concurrency in Apple's Concurrency Programming Guide.

The root cause is that detachNewThreadSelector can only accept a NSMutableArray for the withObject parameter. If you make testStringArray a variable of type NSMutableArray you can get rid of the compiler error message.
Having said that, you should seriously consider the advices from Bryan Chen and Mike S to switch to GCD.

Related

Displaying text from an array into text box

First off, I'd like to apologize for the newbie question, I'm trying to learn as I go with this. I've made a couple basic iOS apps but this is my first venture into macOS Storyboard apps with no formal training in programming.
I'm trying to create a program to help my partner (a writer by profession) with their creative blocks by displaying character, setting, and action attributes that can be used in a story. (Screenshots attached for a better representation)
I believe I have the basic window formatting down but I'm getting stuck on how to link the "Attribute" buttons to the text fields to display elements in the array. I just have placeholder elements until I get it working but ideally you would click the attribute button and it would display a random attribute from the array into the text box.
I've included what I was able to piece together so far but it's failing to build at the line to output to the text box and I can't seem to figure it out.
The error is:
Cannot assign value of type () to type String
Any assistance is appreciated!
import Cocoa
class SecondViewController: NSViewController {
#IBOutlet weak var CharAtt1: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func CharAttBut1(_ sender: NSButton) {
let array = ["Swift", "SwiftUI", "UIKit"]
let randomElement = array.randomElement()!
CharAtt1.stringValue = print("\(randomElement)")
}
}
Obviously, the offending line of code is:
CharAtt1.stringValue = print("\(randomElement)")
It's got a fair number of issues. The most prudent is that print has a type of (approximately, I'm simplifying) (Any) -> Void. You're calling it, passing it a string ("\(randomElement)"). This will print the string to the console, but it will also return back a () (a.k.a. the empty Tuple, of type Void). As the error message suggests, this can't be assigned to CharAtt1.stringValue, which is expecting a String.
The fix is simple, don't call print:
// If you do want to print it, do it in a separate expression
print("\(randomElement)")
CharAtt1.stringValue = "\(randomElement)"
But there's another issue: "\(randomElement)" is useless. randomElement is already a String. You could just write:
print(randomElement)
CharAtt1.stringValue = randomElement
I'd say that "\(anything)" is kind of anti-pattern. If you need to convert something to a string, I think it's better to do so in a way that's more explicit about the conversion you want. E.g. using String(anything) (if such an initializer exists), or String(describing: anything), or String(reflecting: anything) (depending on your usecase)

Swift Array multiple appending on click

I'm creating a button that when clicked adds the current date and time to an array but when I try to append to the array it only appends once and not repeating the process
the Entries struct:
struct Enteries {
var dates:[String] = []
}
convert date to String:
func DateConverter(){
format.timeZone = .current
format.dateFormat = "yyyy-MM-dd HH:mm"
dateString = format.string(from: currentDate)
}
The function that appends: also its called later whenever an IBAction is triggered
func AddToDatabase () {
var entery = Enteries()
entery.dates.append(dateString)
print(entery.dates)
}
`
Yikes, there's a lot going on here.
First of all, Swift's convention is to use lowerCamelCase for functions. Only type names should be UpperCamelCase.
Secondly, function names should be verbs or verb phrases, type names should be nouns. If I saw DateConverter in some code, I would expect it to be a type. It's an UpperCamelCase noun, that's how types should be named. But yours is a function (which would be a total surprise to every other Swift developer, because it violates the expectations they've built up from Swift's naming conventions), that function should probably be called parseDate.
Which way does DateConverter convert? From String to Date, Date to String, or both? What's its input? What's it's output? These things should be obvious from a good function name, but are totally unknown here without looking at the implementation.
Critically, the DateConverter function doesn't take input from parameters, and doesn't return a result, instead it takes input from a side effect (accessing the variable currentDate) and returns a result via side effect (writing to an a variable dateString). This is really bad, for several reasons:
It's not reusable. You have no way to use this date parsing code somewhere else without copy/pasting it, which is how code duplication and complexity arise. If you ever decide to change the date format in your app, you won't have a central source-of-truth that you can change, instead you'll have to manually hunt down every copy of this function, and change it, hoping you don't miss any. Not good.
It's not thread safe
It's more complex than a simple function that has type (Date) -> String. It obfuscates what's going on.
It defies peoples' expectations, without justification.
Enteries.dates has a default value of [], which doesn't seem to be a good idea if you're going to be appending to it as soon as you create it. Instead, take the array via an initializer parameter.
Enteries.dates has type [String]. Why?! You already have Date objects, store those!
They're smaller (in memory)
They're presentation-agnostic, meaning you can properly format them for different interfaces and different locales at a later time, as necessary
They support date math. I often see people storing dates as strings, and ask questions like "How do I sort my array of dates?" (which are actually stored as strings), "How do I add 1 day to "2019-12-24", and they start doing funky parsing, splitting, joining, and it's all just an absolute mess
Here's how I would improve this code:
struct Entries {
var entries: [Entry]
}
struct Entry {
let date: Date
}
// Call this from your view layer, only when you're about to present a `Date` to a user.
func parse(date: Date) -> String {
let df = DateFormatter()
df.timeZone = .current
df.dateFormat = "yyyy-MM-dd HH:mm"
return format.string(from: currentDate)
}
var entries = Entries(entries: [])
func addToDatabase(entry: Entry) {
entries.append(entry)
print(enteries.entries)
}
you are creating a new entery object eveytime the function is called. SO its creating a new object everytime. Declare your entery object outside the function.
var entery = Enteries()
func AddToDatabase () {
entery.dates.append(dateString)
print(entery.dates)
}

Swift can't call self from c callback in linphone sdk

I have problem when try to call Swift instance method from c callback.
Error: "A C function pointer cannot be formed from a closure that captures context."
linphone_core_cbs_set_registration_state_changed(cbs) { (lc, cfg, state, message) in
switch state{
case LinphoneRegistrationOk:
print("LinphoneRegistrationOk")
self.call()
}
func call() {
let account = "test"
let domain = "sip.linphone.org"
let identity = "sip:" + account + "#" + domain
linphone_core_invite(lc, identity)
}
If you have the ability to pass an arbitrary void* as a context then using Unmanaged<SelfType>.passUnretained(self).toOpaque() and converting it back with Unmanaged<SelfType>.fromOpaque(context).takeUnretainedValue() is the best solution.
But based on your small snippet of (lc, cfg, state, message) none of them seem like a context pointer. So getting self back is still possible but likely more complex. Because you will need to create a global/static piece of data that can be accessed from inside your closure in a safe manner. Some ideas of how you can do this are below:
1) You can do this with just a simple global/static if you know that is safe.
2) You can use thread local storage and store a pointer to self and cast it back and forth in the same way as if you had the void* argument and used Unmanaged. This one of course requires that your C callback be raised on the same thread where you store the value.
3) If you have access to a unique identifier that both Swift and C can access that will continue to be valid and accessible in your callback you can create a map of type [UniqueIdentifierAccessibleBySwiftAndC: TypeOfSelf] and use that to get self back.

Using a method on a custom named (IE named at runtime) property within Bucklescript

So, I'm trying to program an AI for the game Screeps, whose docs are found here.
I'm trying to write my AI in OCaml, which I'm then compiling to Javascript via Bucklescript, whose docs are found here.
Anywho, within Screeps' API is the method Game.spawns.SPAWN_NAME.createCreep, in which SPAWN_NAME corresponds to the name of the 'spawn' object in question. It takes in a string array corresponding to the various body parts of the 'Creep' it is helping to spawn, and given a correct function call (with enough energy reserves), your Creep will spawn in game.
An example call (in JS) would be Game.spawns['Spawn1'].createCreep(["body","move"]);
I already have code which gives me a string array of all the spawns in OCaml. That code is:
let spawns : string array = [%bs.raw{|Object.keys(Game.spawns)|}]
let spawnsArray : string array = spawns
Lets say that I have one spawn called Spawn1, and that I also have a string array for the body composition in OCaml:
let spawnName : string = "Spawn1"
let body : string array = [|"body","move|]
I then iterate over each string within that array, using a for loop like the one below:
for i=0 to Array.length spawns - 1 do
// I WANT TO CALL SOMETHING ANALOGOUS TO MY FUNCTION HERE
done
I just can't, for the life of me, figure out how to format the Bucklescript bindings so that I can dynamically call the createCreep function with body : string array and spawnName : string. Any help would be amazing. I know there are the bs.get and bs.set methods described briefly within the Bucklescript docs, but I don't know how to use them.
Thanks in advance for the help.
EDIT:
I managed to 'work around' the issue by writing my own 'interfacing' functions in a Javascript module which I can then call via the bs.module Bucklescript binding.
IE I wrote a function
function spawnCreepHelper(spawnName, body) {
Game.spawns[spawnName].createCreep(body);
}
which I'm then able to call via
external spawnCreepHelper : string -> string array -> unit = ""
[##bs.module "./supplemental", "Supplement"]
Seems kind of hacked together to me, so if anyone has another way of approaching it that doesn't involve rewriting their API myself, please let me know.
You probably want the bs.get_index attribute:
type spawn
type spawns
external spawns : spawns = "" [##bs.val] [##bs.scope "Game"]
external getSpawn : spawns -> string -> spawn = "" [##bs.get_index]
external createCreep : spawn -> string array -> unit = "" [##bs.send]
let _ =
let spawn = getSpawn spawns "spawn1" in
createCreep spawn [|"body"; "move"|]
compiles to
var spawn = Game.spawns["spawn1"];
spawn.createCreep(/* array */[
"body",
"move"
]);
You can then get the keys by providing your own typed external to Object.keys:
external keys : spawns -> string array = "" [##bs.val] [##bs.scope "Object"]
let _ =
spawns |> keys
|> Js.Array.forEach Js.log
which will compile to
Object.keys(Game.spawns).forEach((function (prim) {
console.log(prim);
return /* () */0;
}));
Alternatively, you could type spawns as a spawn Js.Dict and use the functions already provided by BuckleScript to access and manipulate it: https://bucklescript.github.io/bucklescript/api/Js.Dict.html

D-Bus how to create and send a Dict?

I have a process which exposes a method to DBus with one of the arguments taking the following type signature a{sv}:
Dict of {String, Variant}
The libDBus documentation for dbus_message_append_args fails to provide adequate reference for this. Some information appears in the specification under container-types, specifically:
A DICT_ENTRY works exactly like a struct, but rather than parentheses
it uses curly braces, and it has more restrictions. The restrictions
are: it occurs only as an array element type; it has exactly two
single complete types inside the curly braces; the first single
complete type (the "key") must be a basic type rather than a container
type. Implementations must not accept dict entries outside of arrays,
must not accept dict entries with zero, one, or more than two fields,
and must not accept dict entries with non-basic-typed keys. A dict
entry is always a key-value pair.
On attempting to append a dict I receive the following error message:
type dict_entry isn't supported yet in dbus_message_append_args_valist
Although I'm actually using dbus_message_append_args(I guess the error message is somewhat off).
There are two other alternatives to dbus_message_append_args() using either:
dbus_message_iter_append_basic()
and
dbus_message_iter_append_fixed_array()
While I can create an empty Dict container with the following:
const char * container_d_sig = "{sv}";
DBusMessageIter iter, sub;
dbus_message_iter_init_append(msg, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, container_d_sig, &sub);
dbus_message_iter_close_container(&iter, &sub);
Neither of the append methods appear to support adding a struct. Not sure what to try here...
First, about D-Bus libraries: you talk about dbus-glib in several places but the functions you refer to are not part of dbus-glib but libdbus. If you are still trying to find the best way to use D-Bus, I suggest you forget both of these: libdbus is very low-level (it's documentation even starts with "If you use this low-level API directly, you're signing up for some pain") and dbus-glib is deprecated. The best D-Bus API currently is GDBus which is part of GLib GIO: it's a far better designed API than either of the other two, well tested and supported.
Now, as for the actual question, documentation for dbus_message_append_args() does say it quite clearly:
To append variable-length basic types, or any more complex value, you
have to use an iterator rather than this function.
In other words you should use dbus_message_iter_open_container() to prepare the iterator until it is pointing to somewhere where you can use dbus_message_iter_append_basic(). Note that in your example the dictionary is a container, the dictionary entry is a container and the variant is a container... In other words it gets pretty complex quite fast. If you really want to do it, look at e.g. Connman code for examples.
As I mentioned, the sane route is GDBus. There creating even much more complex signatures is pretty easy as you can use the GVariantBuilder API:
GVariantBuilder builder;
g_variant_builder_init (&builder, G_VARIANT_TYPE("a{sv}"));
g_variant_builder_add (&builder, "{sv}", "name1", my_variant);
/* Now use the builder results with g_dbus_connection_call()
or g_dbus_proxy_call() */
I know this question was asked awhile ago, but I had a very similar question recently, and after several hours of trial and error this is some code I came up with that works for me. Hopefully it helps someone else...
DBusMessage* testMessage()
{
DBusMessage* mssg = dbus_message_new_signal("/fi/w1/wpa_supplicant1/Interfaces/0", "fi.w1.wpa_supplicant1.Interface", "PropertiesChanged");
DBusMessageIter iter, aIter;
dbus_message_iter_init_append(mssg, &iter);
if (!dbus_message_iter_open_container(&iter, 'a', "{sv}", &aIter))
return nullptr;
DBusMessageIter eIter;
if (!dbus_message_iter_open_container(&aIter, 'e', NULL, &eIter)) {
dbus_message_iter_abandon_container_if_open(&iter, &aIter);
return nullptr;
}
const char* key = "test key";
dbus_message_iter_append_basic(&eIter, 's', static_cast<void*>(&key));
DBusMessageIter vIter;
if (!dbus_message_iter_open_container(&eIter, 'v', "i", &vIter)) {
dbus_message_iter_abandon_container_if_open(&aIter, &eIter);
dbus_message_iter_abandon_container_if_open(&iter, &aIter);
return nullptr;
}
dbus_int32_t val = 42;
dbus_message_iter_append_basic(&vIter, 'i', static_cast<void*>(&val));
dbus_message_iter_close_container(&eIter, &vIter);
dbus_message_iter_close_container(&aIter, &eIter);
dbus_message_iter_close_container(&iter, &aIter);
return mssg;
}
This is C++ but should be pretty easy to adapt for C. The returned message has a signature of a{sv}. The dbus docs are helpful-ish.

Resources