How can I implement the following in Swift?
func findMatchForRequest(_ request: GKMatchRequest!,
withCompletionHandler completionHandler: ((GKMatch!,
NSError!) -> Void)!)
When I tried
var request = GKMatchRequest();
request.minPlayers = 2;
request.maxPlayers = 4;
GKMatchmaker.sharedMatchmaker().findMatchForRequest(request, withCompletionHandler: { (match : GKMatch!, error: NSError!) -> Void in {
}
})
it gives a "can not convert the expression type" compilation error. Am I missing something obvious?
You're close. You just have an unnecessary set of braces in your completion handler. Here's the correct version:
GKMatchmaker.sharedMatchmaker().findMatchForRequest(
request,
withCompletionHandler: {(match : GKMatch!, error: NSError!) -> Void in
NSLog("This works")
})
EDIT: I meant braces!
FYI you could use trailing closure:
GKMatchmaker.sharedMatchmaker().findMatchForRequest(request) {
match, error in
println("This works")
}
Related
I have a function that looks like this, and I have tried to add a completionHandler in the code below:
func getValueFromAPI(completionHandler: (_ result: Bool) -> Void){
apii.getVehicle(id!).done {
(vehicle: Vehicle) -> Void in
print("ggg.state: \(vehicle.state!)")
print("ggg.state: \(vehicle.displayName!)")
apii.getAllData(vehicle).done { (extendedVehicle: VehicleExtended) in
let entryBattery = (extendedVehicle.chargeState?.batteryLevel)!
let entryCarState = (extendedVehicle.state)!
print("entryBattery: \(entryBattery)")
print("entryCarState: \(entryCarState)")
completionHandler(true)
}.catch { (error) in
print("ERROOOOR: \(error)")
}
}.catch { error in
print("errorr: \(error)")
}
}
I have already tried to add a complete handler, but I get the following error on these lines:
Line: apii.getVehicle(id!).done {
Error: Escaping closure captures non-escaping parameter 'completionHandler'
Line: apii.getAllData(vehicle).done { (extendedVehicle: VehicleExtended) in
Error: Escaping closure captures non-escaping parameter 'completionHandler'
What am I doing wrong here, and how can I fix this?
I am using Swift 5.
You need to declare your completionHandler to be an escaping closure. E.g.:
func getValueFromAPI(completionHandler: #escaping (Bool) -> Void) {
...
}
Note the #escaping qualifier.
I recently joined a new project and started working on iOS app codebase. However, with latest Xcode 10, the code no longer compiles.
protocol NetworkClientType {
associatedtype CompletionHandler
static func intoRequest(_ url: URL?) -> URLRequest?
}
extension NetworkClientType {
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
static func intoIncompleteURLSessionDataTask(_ request: URLRequest) -> (CompletionHandler) -> URLSessionDataTask {
return { completion in URLSession(configuration: .default).dataTask(with: request, completionHandler: completion) }
}
}
Then on line 10 (static func intoIncompleteURLSessionDataTask...), the compiler error says 'CompletionHandler' is ambiguous for type lookup in this context
Does anyone know how to resolve this? I have googled around and couldn't find working solutions.
Change
extension NetworkClientType {
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
to
extension NetworkClientType
where CompletionHandler == (Data?, URLResponse?, Error?) -> Void {
I'm trying to write failing tests for my network calls but can't find anyway to access the connection settings from within my tests.
This code works fine for testing a success case:
func testRetrieveProducts() {
let expectation = expectationWithDescription("asynchronous request")
Requests().retrieveProducts({ (products) -> () in
// check that we have two arrays returned.
XCTAssert(products.count == 2)
expectation.fulfill()
}) { (error) -> () in
XCTFail("Request failed")
}
waitForExpectationsWithTimeout(5.0, handler: nil)
}
But I've been looking for a way to test network timeouts, and incorrect data being returned.
I can probably test incorrect data by calling functions individually inside the retrieveProducts function and stubbing stuff out, but doing something as simple as turning off the internet is proving to be quite difficult.
I know we have access to the Network Link Conditioner, but turning this on and off for each test isn't an option.
I'd love to have access to something as simple as:
func testFailRetrieveProducts() {
let expectation = expectationWithDescription("asynchronous request")
SomeNetworkClass.disableInternet()
Requests().retrieveProducts({ (products) -> () in
}) { (error) -> () in
XCTAssert(error == SomeError.TimeoutError)
}
waitForExpectationsWithTimeout(5.0, handler: nil)
}
Any solutions out there that can handle what I'm after, or am I going about this all wrong?
Take a look at this NSHipster article about Apple's Network Link Conditioner. There's a lot of presets and you can create your own custom network profile. Unfortunately there doesn't seem to be a way to throttle the network in code.
A somewhat viable alternative however is to use ReactiveCocoa and model all your network events as SignalProducers. Then you can use the throttle or wait function, depending on your intentions.
I ended up just mocking the network calls, which to be honest is a lot better than performing tests over an actual connection, as these can be very unreliable anyway.
Here's my mock NSURLSession
class MockSession: NSURLSession {
var completionHandler:((NSData!, NSURLResponse!, NSError!) -> Void)?
static var mockResponse: (data: NSData?, urlResponse: NSURLResponse?, error: NSError?)
override class func sharedSession() -> NSURLSession {
return MockSession()
}
override func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask {
self.completionHandler = completionHandler
return MockTask(response: MockSession.mockResponse, completionHandler: completionHandler)
}
class MockTask: NSURLSessionDataTask {
typealias Response = (data: NSData?, urlResponse: NSURLResponse?, error: NSError?)
var mockResponse: Response
let completionHandler: ((NSData!, NSURLResponse!, NSError!) -> Void)?
init(response: Response, completionHandler:((NSData!, NSURLResponse!, NSError!) -> Void)?) {
self.mockResponse = response
self.completionHandler = completionHandler
}
override func resume() {
completionHandler!(mockResponse.data, mockResponse.urlResponse, mockResponse.error)
}
}
}
Here's how I use it in a test:
Note that you still have to use an expectation even though there's no network delay.
func testRetrieveProductsValidResponse() {
let testBundle = NSBundle(forClass: self.dynamicType)
let filepath = testBundle.pathForResource("products", ofType: "txt")
let data = NSData(contentsOfFile: filepath!)
let urlResponse = NSHTTPURLResponse(URL: NSURL(string: "https://anyURL.com")!, statusCode: 200, HTTPVersion: nil, headerFields: nil)
MockSession.mockResponse = (data, urlResponse: urlResponse, error: nil)
let requestsClass = RequestManager()
requestsClass.Session = MockSession.self
let expectation = expectationWithDescription("ready")
requestsClass.retrieveProducts("N/R FOR TEST", branchID: "N/R FOR TEST", products: { (products) -> () in
XCTAssertTrue(products.count == 7)
expectation.fulfill()
}) { (error) -> () in
XCTAssertFalse(error == Errors.NetworkError, "Its a network error")
XCTAssertFalse(error == Errors.ParseError, "Its a parse error")
XCTFail("Error not covered by previous asserts. Shouln't get to here anyway.")
expectation.fulfill()
}
waitForExpectationsWithTimeout(3.0, handler: nil)
}
Finally, I have an accessible property on my RequestManager class that I can swap out with the MockSession when doing my tests.
var Session = NSURLSession.self
This is my code:
if GKLocalPlayer.localPlayer().authenticated {
if mode == 60 {
var scoreReporter2 = GKScore(leaderboardIdentifier: "countrymaster60") //leaderboard id here
scoreReporter2.value = Int64(score) //score variable here (same as above)
var scoreArray: [GKScore] = [scoreReporter2]
GKScore.reportScores(scoreArray, withCompletionHandler: {(error : NSError!) -> Void in
if error != nil {
print("error")
}
The error message it gives:
Cannot invoke 'reportScores' with an argument list of type
'([GKScore], withCompletionHandler: (NSError!) -> Void)'
Any suggestions?
The documentation says that the method signature is:
class func reportScores(_ scores: [GKScore], withCompletionHandler completionHandler: ((NSError?) -> Void)?)
So you need to make the error variable in the completion handler an Optional.
Change
GKScore.reportScores(scoreArray, withCompletionHandler: {(error : NSError!) -> Void in
for
GKScore.reportScores(scoreArray, withCompletionHandler: {(error : NSError?) -> Void in
I'm trying to utilize swift's SKStoreProductViewController, but am getting errors with my syntax, specifically with my completion block.
Here is my code:
let storeViewController:SKStoreProductViewController = SKStoreProductViewController();
storeViewController.delegate = self;
var productparameters = [SKStoreProductParameterITunesItemIdentifier:someitunesid];
storeViewController.loadProductWithParameters(productparameters,
(success: Bool!, error: NSError!) -> Void in
if success {
self.presentViewController(storeViewController, animated: true, completion: nil);
} else {
NSLog("%#", error)
}
)
After running this I get an expected "," separator error between the error:NSError!),-> Void
This doesn't make sense to me as the apple docs call for:
func loadProductWithParameters(_ parameters: [NSObject : AnyObject]!,
completionBlock block: ((Bool, NSError!) -> Void)!)
What am I doing wrong?
You're 99% there, you just need braces around your block to have the correct closure syntax:
storeViewController.loadProductWithParameters(productparameters, { (success: Bool!, error: NSError!) -> Void in
if success {
self.presentViewController(storeViewController, animated: true, completion: nil);
} else {
NSLog("%#", error)
}
})
You can read more about closures in Apple's documentation.