How to make a serial queue with GCD - swift

I tried to make a serial queue for network operations with GCD like this:
let mySerialQueue = dispatch_queue_create("com.myApp.mySerialQueue", dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0))
func myFunc() {
dispatch_async(mySerialQueue) {
do {
// Get object from the database if it exists
let query = PFQuery(className: aClass)
query.whereKey(user, equalTo: currentUser)
let result = try? query.getFirstObject()
// Use existing object or create a new one
let object = result ?? PFObject(className: aClass)
object.setObject(currentUser, forKey: user)
try object.save()
} catch {
print(error)
}
}
}
The code first looks for an existing object in the database.
If it finds one, it updates it. If it doesn't find one, it creates a new one. This is using the Parse SDK and only synchronous network functions (.getFirstObject, .save).
For some reason it seems that this is not executed serially, because a new object is sometimes written into the database, although one existed already that should have been updated only.
Am I missing something about the GCD?

From the documentation on dispatch_queue_attr_make_with_qos_class:
relative_priority: A negative offset from the maximum supported scheduler priority for the given quality-of-service class. This value must be less than 0 and greater than MIN_QOS_CLASS_PRIORITY
Therefore you should be passing in a value less than 0 for this.
However, if you have no need for a priority, you can simply pass DISPATCH_QUEUE_SERIAL into the attr argument when you create your queue. For example:
let mySerialQueue = dispatch_queue_create("com.myApp.mySerialQueue", DISPATCH_QUEUE_SERIAL)

Related

Are Type Casts an Atomic Operation in Swift? - BAD_ACCESS_ERROR

Let's say I am trying to access a shared variable between two threads. One thread will continuously set the shared variable to either nil or to the reference of an object that can be deallocated.
Class Code
class ConcurrentPrinter {
var value: AnyObject?
}
Thread one
// called 30 times per second
func setter(){
value = shouldSet ? nil : valueArray[0]
// where the value is an instance type
}
Thread two
// also called 30 times per second
func getter() {
if value != nil {
guard let desiredObject = value as? desiredObjectType else {
return
}
}
For some reason, I am getting a Bad_Address error in the guard statement when it tries to cast value into the desiredObjectType. Is this happening because the cast operation gets the address of value and then it gets deallocated before it can finish the cast operation?
Okay, I figured it out. The answer is to place each of the operations on a DispatchQueue and run each of the code using an async request. This ensures that the two pieces of code are running simultaneously

.prepare vs. .select

I have a working connection to a database in an iOS10 app, using SQLite.swift.
I want to select details for a specific university where I have an IHE_ID passed in from another view controller.
I would like to just select the row for that specific university, but I can't get a query to work. I can however loop through all the data with a prepare and then choose the one I need from that, which of course is more resource intensive than I need since I already have the specific IHE_ID passed in as anIHE Int from the sending view controller.
connection is working so omitting that code...
do {
let db = try Connection(destinationDBPath, readonly: true)
let IHEs = Table("IHE")
let IHE_ID = Expression<Int>("IHE_ID")
let IHE_Name = Expression<String>("IHE_Name")
let IHE_COE_Url = Expression<String>("IHE_COE_URL")
let IHE_Sector = Expression<Int>("IHE_Sector")
let IHE_COE_Name = Expression<String>("IHE_COE_Name")
for ihe in try db.prepare(IHEs){
if (ihe[IHE_ID] == anIHE){
// build this string, otherwise ignore rest of dataset (doing this because query isn't working)
buildIHE = "Name: \(ihe[IHE_Name])\n"
buildIHE.append("URL: \(ihe[IHE_COE_Url])\n")
// buildIHE.append("Sector: \(ihe[IHE_Sector])\n")
if (ihe[IHE_Sector] == 0) {
buildIHE.append("Sector: Public\n")
} else {
buildIHE.append("Sector: Private\n")
}
buildIHE.append("College of Education Name: \(ihe[IHE_COE_Name])\n")
}
}
print ("Got through building IHE portion of view")
What I'd like to do is use this instead of the for loop.
if let query = IHEs.select(IHE_ID,IHE_Name,IHE_COE_Url,IHE_Sector,IHE_COE_Name).filter(IHE_ID == anIHE){
print("Query successful for \(anIHE) with name \(query[IHE_Name])")
// more actions to build the string I need would then occur
} else {
print("Query has failed or returned nil")
}
Finally, I'll use the selected elements if I can get the query to work.
I think I probably just have something wrong with my syntax on the query, but any help is appreciated.
The line with the "if let query" has this error in Xcode:
Initializer for conditional binding must have Optional type, not 'Table'.
This leads me to think it's something with my use of the .select statement and just new to using SQLite.swift and swift in general.
Last thing is that anIHE comes into this function as an Int, and IHE_ID is Expression as shown in this code. I'm thinking this may be part of the problem.
The Initializer for conditional binding must have Optional type error means that the expression on the right of the if let v = expr statement is not an Optional: there is no point using if let, and the Swift compiler says that you should just write let v = expr.
And indeed, IHEs.select(...).filter(...) returns a non-optional value of type Table.
It is not the database row you expect, because the query has been defined, but not executed yet. After all, you weren't using db: where would the rows be loaded from?
The solution is to bring back the database connection, and load a single row. This is done with the pluck method.
if let ihe = try db.pluck(IHEs.select(...).filter(...)) {
print("Name \(ihe[IHE_Name])")
}

Swift geolocation latitude and longitude separation and making into a CLLocation2DMake

func forwardGeocoding(address: String) {
CLGeocoder().geocodeAddressString(address, completionHandler: { (placemarks, error) in
if error != nil {
print(error)
return
}
if placemarks?.count > 0 {
let placemark = placemarks?[0]
let location = placemark?.location
let coordinate = location?.coordinate
print("\nlat: \(coordinate!.latitude), long: \(coordinate!.longitude)")
if placemark?.areasOfInterest?.count > 0 {
let areaOfInterest = placemark!.areasOfInterest![0]
print(areaOfInterest)
} else {
print("No area of interest found.")
}
}
})
var INITIAL_DESTINATION = forwardGeocoding(initialDestination)
var DESIRED_DESTINATION = forwardGeocoding(desiredDestination)
var location = CLLocationCoordinate2DMake(<#T##CLLocationDegrees#>, <#T##CLLocationDegrees#>)
Hello, I am trying to make a mapping app, and am having trouble with this part. What I want to do is be able to separate the INITIAL_DESTINATION latitude and longitudes. I have to do this to create a CLLocationCoordinate2DMake. What I have been trying to do is just use INITIAL_DESTINATION.latitude and INITIAL_DESTINATION.longitude, but I am continuingly facing the same error which is "Value of tuple type "()" has no member "latitude". This is also strange because it does not give that error for INITIAL_DESTINATION.longitude.
Any help or suggestions are greatly appreciated, and thank you for reading and taking the time to respond.
Your function returns nothing, and does nothing with the value returned in the asynchronous completion handler. You need to take the asynchronous result and use it in some fashion.
Try this: Put prints at the end of the function, and inside the completion handler, then run the code. What you'll see is that the function is done before the completion handler runs, because the code inside the block does not run until the remote web site returns an answer across the network. At that time Alamofire hands the result to your code in the completion block.
You'll also need to be aware that there are multiple queues in iOS, and UI changes can only be done on the main queue. The completion block does not run on the main queue, however, so likely to use the information returned from the network you'll need to use the dispatch_async function to call a function in your program and have it execute on the main queue.

Passing Data Between Two NSOperations

I watched with a great attention the WWDC 2015 sessions about Advanced NSOperations and I played a little bit with the example code.
The provided abstraction are really great, but there is something I may did not really good understand.
I would like to pass result data between two consequent Operation subclasses without using a MOC.
Imagine I have a APIQueryOperation which has a NSData? property and a second operation ParseJSONOperation consuming this property. How do I provide this NSData? intance to the second operation ?
I tried something like this :
queryOperation = APIQueryOperation(request: registerAPICall)
parseOperation = ParseJSONOperation(data: queryOperation.responseData)
parseOperation.addDependency(queryOperation)
But when I enter in the execute method of the ParseJSONOperation the instance in not the same as the same as in the initialiser.
What did I do wrong ?
Your issue is that you are constructing your ParseJSONOperation with a nil value. Since you have two operations that rely on this NSData object I would suggest you write a wrapper object to house this data.
To try and be aligned with the WWDC talk lets call this object the APIResultContext:
class APIResultContext {
var data: NSData?
}
now we can pass this object into both the APIQueryOperation and the ParseJSONOperation so that we have a valid object that can store the data transferred from the API.
This would make the constructors for the query:
let context = APIResultContext()
APIQueryOperation(request: registerAPICall, context: context)
ParseJSONOperation(context: context)
Inside your ParseJSONOperation you should be able to access the data assuming the query completes after it sets the data.
Thread Safety
As #CouchDeveloper pointed out, data is not strictly speaking thread safe. For this trivial example since the two operations are dependent we can safely write and read knowing that these accesses wont take place at the same time. However, to round the solution up and make the context thread safe we can add a simple NSLock
class APIResultContext {
var data: NSData? {
set {
lock.lock()
_data = newValue
lock.unlock()
}
get {
lock.lock()
var result = _data
lock.unlock()
return result
}
}
private var _data: NSData?
private let lock = NSLock()
}

Cannot borrow captured outer variable in an `Fn` closure as mutable

This is my first day with Rust, but I'm trying to do something trivial, and I'm stuck.
What I'm trying to do is to add an struct to a Vector, and return the result. What I'm trying is to create a very simple REST service which will store the data in memory when posting, and return all the data in JSON format when doing a GET.
This is my current code:
fn main() {
let mut server = Nickel::new();
let mut reservations = Vec::new();
server.post("/reservations/", middleware! { |request, response|
let reservation = request.json_as::<Reservation>().unwrap();
reservations.push(reservation); // <-- error occurs here
format!("Hello {} {}", reservation.name, reservation.email)
});
server.listen("127.0.0.1:3000");
}
I tried this solution with a RefCell, but then I get the error that the trait Sync is not implemented for Vec<reservation::Reservation>
This is a very good example of how Rust protects you from thread unsafety.
If you think about it, in your current code it would be possible that multiple threads try to concurrently mutate reservations without any kind of synchronization. This is a data race and Rust will complain about it.
A possible solution would be to wrap the reservations vector into a Mutex to get synchronization. You will also need an Arc (atomic reference counting), since Rust cannot prove that reservations will live longer than the threads.
With these changes, your code should be like the following:
use std::sync::{Arc, Mutex};
fn main() {
let mut server = Nickel::new();
let reservations = Arc::new(Mutex::new(Vec::new()));
server.post("/reservations/", middleware! { |request, response|
let reservation = request.json_as::<Reservation>().unwrap();
reservations.lock().unwrap().push(reservation); // <-- error occurs here
format!("Hello {} {}", reservation.name, reservation.email)
});
server.listen("127.0.0.1:3000");
}
You can check the documentation for additional info about Mutex and Arc.