ReactiveCocoa combine SignalProducers into one - ios

I'm using ReactiveCocoa and I have several SignalProducers
let center = NSNotificationCenter.defaultCenter()
let signalProducer1 = center.rac_notification(name: notificationName1, object: nil)
let signalProducer2 = center.rac_notification(name: notificationName2, object: nil)
let signalProducer3 = center.rac_notification(name: notificationName3, object: nil)
I want to combine them into a single signal producer that produces a signal whenever one of them produces a signal.
At first the combineLatest function looked like a good solution
let combinedProducer = combineLatest(signalProducer1, signalProducer2, signalProducer3)
However, according to this article, the resulting producer only produces its first signal when all the three have produced a signal.
This interactive diagram shows exactly what I want, so I want to use the flatten function with the .Merge FlatteningStrategy. However, I'm having a hard time figuring out the syntax to achieve this.

Update: RAC 4.2.1 and upwards
Due to changes in how flatten works we need to help the compiler an be more explicit about the types:
let s1: SignalProducer<Int, NSError> = ...
let s2: SignalProducer<Int, NSError> = ...
let s3: SignalProducer<Int, NSError> = ...
let _: SignalProducer<Int, NSError> =
SignalProducer<SignalProducer<Int, NSError>, NSError>(values: [s1, s2, s3])
.flatten(.Merge)
That becomes a bit cumbersome, so you might want to split it:
let producers: SignalProducer<SignalProducer<Int, NSError>, NSError> =
SignalProducer(values: [s1, s2, s3])
let merged: SignalProducer<Int, NSError> = x.flatten(.Merge)
Thanks #Harry for the comment pointing the new version issue out.
RAC 4.2 and below
In RAC 4 this would be
let merged = SignalProducer(values: [signalProducer1, signalProducer2, signalProducer3])
.flatten(.Merge)
At the moment Xcode 7.1.1 doesn't suggest .flatten in the autocompletion window, which might result in you (or just me) thinking it is not there, but if you type it all it will work.

You can achieve that as follows:
let merged = SignalProducer(values: [ signalProducer1, signalProducer2, signalProducer3 ])
|> flatten(.Merge)

Related

Which swift Document or video I need to read for me to understand how this ResultsHandler works?

HI I'm a newbi coder who is code for fun.
I want to get my stepscount from my iPhone
so I start to learn Xcode and swift.
I can write bash script and a little bit python.
but I really can't understand the following.
From https://developer.apple.com/documentation/healthkit/hkstatisticscollectionquery
query.initialResultsHandler = {
query, results, error in
// My Understanding This Handler is a unknow type or thing for me how can I know what is it and how to use it from apple's web page?
I can tell from reading apple's page it have three components and
Declaration says it's a var? like
var initialResultsHandler: ((HKStatisticsCollectionQuery, HKStatisticsCollection?, Error?) -> Void)? { get set }
so it's a var with three things in it? so it's a tuple? and can be get and set!?
So I think the above code means put var query to hander's HKStatisticsCollectionQuery, get from hander's HKStatisticsCollectionQuery to var results and handle's Error to local var error
The Most important thing is what is the "in" after the error?
I only know you can for xxxx in yyyy and create a loop but without a for use in means what? what document explain this in stuff?
guard let statsCollection = results else {
// Perform proper error handling here
fatalError("*** An error occurred while calculating the statistics: \(error?.localizedDescription) ***")
}
let endDate = NSDate()
guard let startDate = calendar.dateByAddingUnit(.Month, value: -3, toDate: endDate, options: []) else {
fatalError("*** Unable to calculate the start date ***")
}
That is a closure you are looking at. The three variables are the input arguments of the closure that you name query, results and error. You could give these any other name as well, since they only exist inside the closure and only their types are used in the function type signature.
The in keyword indicates the start of the closure body.
For more information on the topic, have a look at the Closures chapter of the Swift Programming Language.

Best way to show realtime data in iOS with Charts API

I know there are some posts about it but I can't find something useful. Therefore, I'm opening new post. I have a device which sends some values over bluetooth. I have function which gets this value like every second. I need to show this data in line chart realtime (Two line). I know Android API has a function for realtime but I can't find it in iOS API.
How I'm doing now is like this;
if (uuid == kUUIDECGSensor) {
let dataDict: [AnyHashable: Any] = LBValueConverter.manageValueElectrocardiography(dataValue)
// print("kUUIDECGSensor dict: \(dataDict)")
let allValues = Array(dataDict.values)
print(allValues)
data_array1.append(allValues[0] as! Double)
data_array2.append(allValues[1] as! Double)
//print(data_array1)
//print(data_array2)
temp_time += 5
time.append("\(temp_time)")
setChart(dataPoints: time, values: data_array1)
}
It is working but It doesnt seem like realtime. What am I missing?
Thank You!

Swift 2 - Type casting and optional chaining

I am relatively new to Swift and programming. I'm developing an app which heavily relies on information downloaded from the server. So in a lot of ViewControllers, I use NSURLSession and NSJSONSerialization to download the JSON into my app.
Every time I wanted to subscript the dictionary, for example timetableDict?["timetable"]["class"]["day"]["lesson"][0]["name"], something like Cannot subscript a value of type [String : AnyObject] with an index type of String shows up as an error.
I understand that I should avoid using AnyObject in my code, but the dictionary from the server is heavily nested with structures like this one:
"timetable": ["class": ({
day = ({
lesson = ({
name = (MATHEMATICS, ENGLISH),
classOrder = 0,
teacher = (Someone)
}),
({
name = FRENCH,
classOrder = 1,
teacher = (Someone)
)}
)}
)}]
The problem with this structure is that it is heavily nested and has different types when it gets to "name", "classOrder" and "teacher". It is very hard for me not to use AnyObject. However, this error has been annoying for me for a very long time. I would greatly appreciate it if someone could help me out on this. Thanks in advance!
I suggest taking a look at SwiftyJSON : https://github.com/SwiftyJSON/SwiftyJSON
It's a framework/library designed to handle JSON in a very much more elegant way than what's build into swift (especially for heavy nested structures like yours). It's easy to use and has an excellent tutorial.
EDIT: (Added sample code)
Example from the swiftyJSON tutorial :
let JSONObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil)
if let statusesArray = JSONObject as? [AnyObject],
let status = statusesArray[0] as? [String: AnyObject],
let user = status["user"] as? [String: AnyObject],
let username = user["name"] as? String {
// Finally we got the username
}
even with optional chaining quite messy :
let JSONObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil)
if let username = (((JSONObject as? [AnyObject])?[0] as? [String: AnyObject])?["user"] as? [String: AnyObject])?["name"] as? String {
// What a disaster
}
With swiftyJSON
let json = JSON(data: dataFromNetworking)
if let userName = json[0]["user"]["name"].string {
//Now you got your value
}
SwiftlyJSON that #Glenn mentions is not a bad system (though I find it over-reliant on string lookups, which is fragile). The deeper point, however, is that you want to validate and parse your JSON in one place, and turn it into a non-JSON Swift data structure for use by the rest of your program. SwiftlyJSON can be a decent tool for doing that, but I would use it to unload data into a an array of structs.
Working with JSON throughout your system is extremely error-prone and cumbersome, even wrapped up in SwiftlyJSON. If you unload the data once, then you can check for errors one time and everywhere else you know that the data is correct. If you pass around JSON structures, then you must check every single time and deal with possibly missing or incorrect data. Think through the case where the server sends you JSON in a format you didn't expect. How many times do you want to test for that?

Repeat code for whole array

I am using some Facebook IDs in my app, and I have an array of serveral ID's, the array can be 10 numbers but can also be 500 numbers..
Right now the numbers are displayed in a tableview, and I want all the results there too, so they need to be in an array.
let profileUrl = NSURL(string:"http://www.facebook.com/" + newArray[0])!
let task = NSURLSession.sharedSession().dataTaskWithURL(profileUrl) {
(data, response, error) -> Void in
// Will happen when task completes
if let urlContent = data {
let webContent = NSString(data: urlContent, encoding: NSUTF8StringEncoding)
dispatch_async(dispatch_get_main_queue(),
{ () -> Void in
let websiteArray = webContent!.componentsSeparatedByString("pageTitle\">")
//print(websiteArray[1])
let secondArray = websiteArray[1].componentsSeparatedByString("</title>")
print(secondArray[0])
})
}
}
this code takes the first number of the array, goes to facebook.com/[the actual number], and then downloads the data and splits the data into pieces, so that the data that I want it in the secondArray[0]. I want to do this for every number of the array, take the result data and put it back into an array. I have no idea how to do this because you don't know how much numbers there are gonna be etc, does someone has a good solution for this?
Any help would be appreciated, really!
Thanks
You have several problems here, and you should take them one at at a time to build up to your solution.
First, forget the table for the moment. Don't worry at all about how you're going to display these results. Just focus on getting the results in a simple form, and then you'll go back and convert that simple form into something easy to display, and then you'll display it.
So first, we want this in a simple form. That's a little bit complicated because it's all asynchronous. But that's not too hard to fix.
func fetchTitle(identifier: String, completion: (title: String) -> Void) {
let profileUrl = NSURL(string:"http://www.facebook.com/" + identifier)!
let task = NSURLSession.sharedSession().dataTaskWithURL(profileUrl) {
(data, response, error) -> Void in
if let urlContent = data {
let webContent = NSString(data: urlContent, encoding: NSUTF8StringEncoding)
let websiteArray = webContent!.componentsSeparatedByString("pageTitle\">")
let secondArray = websiteArray[1].componentsSeparatedByString("</title>")
let title = secondArray[0]
completion(title: title)
}
}
task.resume()
}
Now this is still pretty bad code because it doesn't handle errors at all, but it's a starting point, and the most important parts are here. A function that takes a string, and when it's done fetching things, calls some completion handler.
(Regarding error handling, note how many places this code would crash if it were returned surprising data. Maybe the data you get isn't a proper string. Maybe it's not formatted like you think it is. Every time you use ! or subscript an array, you run the risk of crashing. Try to minimize those.)
So you might then wrap it up in something like:
var titles = [String]()
let identifiers = ["1","2","3"]
let queue = dispatch_queue_create("titles", DISPATCH_QUEUE_SERIAL)
dispatch_apply(identifiers.count, queue) { index in
let identifier = identifiers[index]
fetchTitle(identifier) { title in
dispatch_async(queue) {
titles.append(title)
}
}
}
This is just code to get you on the right track and start studying the right things. It certainly would need work to be production quality (particularly to handle errors).
Once you have something that returns your titles correctly, you should be able to write a program that does nothing but take a list of identifiers and prints out the list of titles. Then you can add code to integrate that list into your tableview. Keep the parts separate. The titles are the Model. The table is the View. Read up on the Model-View-Controller paradigm, and you'll be in good shape.
To repeat code for whole array put your code in a loop and run that loop from 0 to array.count-1
You don't need to know how many items there will be an array. You can just get the count at run time array.count here array is your array.
I hope this is what you wanted to know, your question doesn't make much sense though.

Application crashes on device - works perfectly on Simulator

Recently, I have been working on an application. The goal of the application is to show you your last 10 photos taken in the Today view of the Notification Center, among other things. The application works perfectly in the iOS Simulator, but as soon as I put it on a real device for testing, it fails. It returns the error:
fatal error: Unexpected nil while unwrapping an Optional value
Typically this is very easy to fix since XCode highlights the code that returned nil and gives you the opportunity to fix it. In this case, none of my code gets highlighted. Instead, it highlights a line from Thread 1 (Is this the correct term? Thread 1?), as seen below:
Also note that above the highlited line is the line
; function signature specialization <Arg[0] = Exploded, Arg[1] = Exploded of Swift.(_fatalErrorMessage (Swift.StaticString, Swift.StaticString, Swift.StaticString, Swift.UInt) -> ()).(closure #2)
I included this line in the picture because of the "fatalErrorMessage" part of it. I suspect this could clue me in on the error, but I have no idea what this means. I'm not yet to the point of understanding that.
Also, after some thought, I placed a breakpoint at the viewDidLoad() function in the hopes of tracking any nil values, but the code appears to never even get to this point. It seems as if none of my code is being run.
Has anyone had problems like this/understands what that error code (If that's what it is) means? I'm pretty desperate now, hence me being here.
Thanks,
CodeIt
EDIT:
I placed a println line inside the viewDidLoad function to double check if it was being run. The println function runs and outputs correctly, so I think I may just have messed up my breakpoint somehow. Anyway - the code runs, but it still doesn't highlight any of my code causing the nil value.
EDIT 2:
As requested, I have inserted parts of my code. Please keep in mind that this code is a "first draft", if you will, and I have not yet gotten around to cleaning it up. I'm just trying to get it to work:
#IBOutlet weak var currentPosLabel: UILabel!
var currentImgPos = 0
#IBOutlet weak var imageView: UIImageView!
var images: NSMutableArray!
var totalImageCountNeeded: Int!
func fetchPhotos() {
images = NSMutableArray()
totalImageCountNeeded = 10
self.fetchPhotoAtIndexFromEnd(0)
}
func fetchPhotoAtIndexFromEnd(index: Int) {
let imgManager = PHImageManager.defaultManager()
var requestOptions = PHImageRequestOptions()
requestOptions.synchronous = true
var fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult = PHAsset.fetchAssetsWithMediaType(PHAssetMediaType.Image, options: fetchOptions) {
if fetchResult.count > 0 {
imgManager.requestImageForAsset(fetchResult.objectAtIndex(fetchResult.count - 1 - index) as? PHAsset, targetSize: view.frame.size, contentMode: PHImageContentMode.AspectFill, options: requestOptions, resultHandler: { (image, _) in
self.images.addObject(image)
if index + 1 < fetchResult.count && self.images.count < self.totalImageCountNeeded {
self.fetchPhotoAtIndexFromEnd(index + 1)
} else {
println("Completed array: \(self.images)")
}
})
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
// NOTE: I am calling the fetchPhotos() function here since earlier in my debugging I thought the problem may be that I was calling it too early in the viewDidLoad function. It an incorrect theory, but it was worth a try.
fetchPhotos()
if images.count > 0 {
imageView.image = images[1] as? UIImage
}
}
I removed some parts of the code that I know have no reasons to cause my error, such as #IBActions, didReceiveMemoryWarning(), etc..
Even it's old question. I found my issue by deleting derived data from xcode.
Xcode -> window->Projects then select and delete your project derived data.
I don't think it is possible to determine exactly where the problem is without seeing your code.
Somewhere in your code you may have forced downcast a variable as!. If you change this to:
if let variable = optionalVariable as? SomeClass {
//Insert your code
}
Then this should fix your problem. Read this to learn more about casting in swift:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html
You should run the same device on simulator and real device.As an example if you run on 5s on real device then try the same device on simulator.By running simulator, it will definitely show the error.My error was the not connecting the #IBActions. Hope that this tip will help you.
I received the same error when trying to set a non-optional value to the value of an implicitly-unwrapped optional that was nil.
Ex. someGlobalFunction() returns an ImplicitlyUnwrappedOptional with a nil value then you try to set that to a regular value.
func someGlobalFunction() -> String! { return nil }
class MyClass {
let aVariable: String
init() {
aVariable = someGlobalFunction()
}
}

Resources