Search code examples
objective-cclosuresobjective-c-blocksswiftreactive-cocoa

Converting Objective-C Block into Closure the Swift way


I have successfully converted the following method from Obj-C to Swift:

After learning how blocks are replaced by closures in Swift.

Obj-C:

- (RACSignal *)fetchCurrentConditionsForLocation:(CLLocationCoordinate2D)coordinate {
    NSString *urlString = [NSString stringWithFormat:@"http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&units=imperial", coordinate.latitude, coordinate.longitude];
    NSURL *url = [NSURL URLWithString:urlString];

    return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
        return [MTLJSONAdapter modelOfClass:[WXCondition class] fromJSONDictionary:json error:nil];
    }];
}

Swift:

func fetchCurrentConditionsForLocation(coordinate: CLLocationCoordinate2D) -> RACSignal {
    let urlString = NSString(format: "http://api.openweathermap.org/data/2.5/weather?lat=%f&lon=%f&units=metric", coordinate.latitude, coordinate.longitude)
    let url = NSURL.URLWithString(urlString)

    return fetchJSONFromURL(url).map { json in
        return MTLJSONAdapter.modelOfClass(WXCondition.self, fromJSONDictionary: json as NSDictionary, error: nil)
    }
}

However, I'm having trouble converting the following return map block to Swift:

enter image description here

func fetchHourlyForecastForLocation(coordinate: CLLocationCoordinate2D) -> RACSignal {
    var urlString = NSString(format: "http://api.openweathermap.org/data/2.5/forecast?lat=%f&lon=%f&units=metric&cnt=12", coordinate.latitude, coordinate.longitude)
    let url = NSURL.URLWithString(urlString)

    /* Original Obj-C:

    return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
        RACSequence *list = [json[@"list"] rac_sequence];
            return [[list map:^(NSDictionary *item) {
                return [MTLJSONAdapter modelOfClass:[WXCondition class] fromJSONDictionary:item error:nil];
            }] array];
        }];
    }
     */

    // My attempt at conversion to Swift
    // (I can't resolve `rac_sequence` properly). Kind of confused
    // as to how to cast it properly and return
    // it as an "rac_sequence" array.

    return fetchJSONFromURL(url).map { json in
        let list = RACSequence()
        list = [json["list"] rac_sequence]
        return (list).map { item in {
            return MTLJSONAdapter.modelOfClass(WXCondition.self, fromJSONDictionary: item as NSDictionary, error: nil)
        } as NSArray
    }
}

If it helps, this is what rac_sequence is:

- (RACSequence *)rac_sequence {
    return [RACArraySequence sequenceWithArray:self offset:0];
}

RACArraySequence():

+ (instancetype)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset;

EDIT: The fetch method returns a RACSignal not an NSArray:

func fetchJSONFromURL(url: NSURL) -> RACSignal {

}

Solution

  • It looks like you simply forgot the return type in the function declaration. The declaration should look something like this:

    func fetchHourlyForecastForLocation(coordinate: CLLocationCoordinate2D) -> RACSignal { //...rest of function
    

    Because the return type is now named you can then remove the as NSArray cast in your return statement at the end of the function. Hope this helps!