Background HTTP fetcher and JSON parser example. (#2177)
This commit is contained in:
@@ -1,9 +1,27 @@
|
|||||||
import kotlin.native.concurrent.*
|
/*
|
||||||
|
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
import kotlinx.cinterop.*
|
import kotlinx.cinterop.*
|
||||||
import platform.AppKit.*
|
import platform.AppKit.*
|
||||||
import platform.Foundation.*
|
import platform.Foundation.*
|
||||||
import platform.darwin.*
|
import platform.darwin.NSObject
|
||||||
import platform.posix.*
|
import platform.darwin.dispatch_async_f
|
||||||
|
import kotlin.native.concurrent.DetachedObjectGraph
|
||||||
|
import kotlin.native.concurrent.attach
|
||||||
|
import kotlin.native.concurrent.freeze
|
||||||
|
|
||||||
|
inline fun <reified T> executeAsync(queue: NSOperationQueue, crossinline producerConsumer: () -> Pair<T, (T) -> Unit>) {
|
||||||
|
dispatch_async_f(queue.underlyingQueue, DetachedObjectGraph {
|
||||||
|
producerConsumer()
|
||||||
|
}.asCPointer(), staticCFunction { it ->
|
||||||
|
val result = DetachedObjectGraph<Pair<T, (T) -> Unit>>(it).attach()
|
||||||
|
result.second(result.first)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
data class QueryResult(val json: Map<String, *>?, val error: String?)
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
autoreleasepool {
|
autoreleasepool {
|
||||||
@@ -11,53 +29,97 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val appDelegate = MyAppDelegate()
|
||||||
|
|
||||||
private fun runApp() {
|
private fun runApp() {
|
||||||
val app = NSApplication.sharedApplication()
|
val app = NSApplication.sharedApplication()
|
||||||
|
|
||||||
app.delegate = MyAppDelegate()
|
app.delegate = appDelegate
|
||||||
app.setActivationPolicy(NSApplicationActivationPolicy.NSApplicationActivationPolicyRegular)
|
app.setActivationPolicy(NSApplicationActivationPolicy.NSApplicationActivationPolicyRegular)
|
||||||
app.activateIgnoringOtherApps(true)
|
app.activateIgnoringOtherApps(true)
|
||||||
|
|
||||||
app.run()
|
app.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Data(val stamp: ULong)
|
class Controller : NSObject() {
|
||||||
|
private var index = 1
|
||||||
private class Controller : NSObject() {
|
private var httpDelegate = HttpDelegate()
|
||||||
private val asyncQueue = dispatch_queue_create("com.jetbrains.CustomQueue", null)
|
|
||||||
|
|
||||||
@ObjCAction
|
@ObjCAction
|
||||||
fun onClick() {
|
fun onClick() {
|
||||||
// Execute some async action on button click.
|
if (!appDelegate.canClick) {
|
||||||
dispatch_async_f(asyncQueue, DetachedObjectGraph {
|
appDelegate.contentText.string = "Another load in progress..."
|
||||||
Data(clock_gettime_nsec_np(CLOCK_REALTIME))
|
return
|
||||||
}.asCPointer(), staticCFunction {
|
}
|
||||||
it ->
|
appDelegate.canClick = false
|
||||||
initRuntimeIfNeeded()
|
// Fetch URL in the background on the button click.
|
||||||
val data = DetachedObjectGraph<Data>(it).attach()
|
httpDelegate.fetchUrl("https://jsonplaceholder.typicode.com/todos/${index++}")
|
||||||
println("in async: $data")
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ObjCAction
|
@ObjCAction
|
||||||
fun onQuit() {
|
fun onQuit() {
|
||||||
NSApplication.sharedApplication().stop(this)
|
NSApplication.sharedApplication().stop(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HttpDelegate: NSObject(), NSURLSessionDataDelegateProtocol {
|
||||||
|
private val asyncQueue = NSOperationQueue()
|
||||||
|
internal val receivedData = NSMutableData()
|
||||||
|
|
||||||
|
init {
|
||||||
|
freeze()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchUrl(url: String) {
|
||||||
|
receivedData.setLength(0)
|
||||||
|
val session = NSURLSession.sessionWithConfiguration(
|
||||||
|
NSURLSessionConfiguration.defaultSessionConfiguration(),
|
||||||
|
this,
|
||||||
|
delegateQueue = asyncQueue
|
||||||
|
)
|
||||||
|
session.dataTaskWithURL(NSURL(string = url)).resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData: NSData) {
|
||||||
|
initRuntimeIfNeeded()
|
||||||
|
receivedData.appendData(didReceiveData)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError: NSError?) {
|
||||||
|
initRuntimeIfNeeded()
|
||||||
|
|
||||||
|
executeAsync(NSOperationQueue.mainQueue) {
|
||||||
|
val response = task.response as? NSHTTPURLResponse
|
||||||
|
Pair(when {
|
||||||
|
response == null -> QueryResult(null, didCompleteWithError?.localizedDescription)
|
||||||
|
response.statusCode.toInt() != 200 -> QueryResult(null, "${response.statusCode.toInt()})")
|
||||||
|
else -> QueryResult(
|
||||||
|
NSJSONSerialization.JSONObjectWithData(receivedData, 0, null) as? Map<String, *>,
|
||||||
|
null
|
||||||
|
)
|
||||||
|
}, { result: QueryResult ->
|
||||||
|
appDelegate.contentText.string = result.json?.toString() ?: "Error: ${result.error}"
|
||||||
|
appDelegate.canClick = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyAppDelegate() : NSObject(), NSApplicationDelegateProtocol {
|
class MyAppDelegate() : NSObject(), NSApplicationDelegateProtocol {
|
||||||
|
|
||||||
private val window: NSWindow
|
private val window: NSWindow
|
||||||
private val controller = Controller()
|
private val controller = Controller()
|
||||||
|
val contentText: NSText
|
||||||
|
var canClick = true
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val mainDisplayRect = NSScreen.mainScreen()!!.frame
|
val mainDisplayRect = NSScreen.mainScreen()!!.frame
|
||||||
val windowRect = mainDisplayRect.useContents {
|
val windowRect = mainDisplayRect.useContents {
|
||||||
NSMakeRect(
|
NSMakeRect(
|
||||||
origin.x + size.width * 0.25,
|
origin.x + size.width * 0.25,
|
||||||
origin.y + size.height * 0.25,
|
origin.y + size.height * 0.25,
|
||||||
size.width * 0.5,
|
size.width * 0.5,
|
||||||
size.height * 0.5
|
size.height * 0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -66,7 +128,7 @@ private class MyAppDelegate() : NSObject(), NSApplicationDelegateProtocol {
|
|||||||
NSWindowStyleMaskClosable or NSWindowStyleMaskResizable
|
NSWindowStyleMaskClosable or NSWindowStyleMaskResizable
|
||||||
|
|
||||||
window = NSWindow(windowRect, windowStyle, NSBackingStoreBuffered, false).apply {
|
window = NSWindow(windowRect, windowStyle, NSBackingStoreBuffered, false).apply {
|
||||||
title = "Окошко Konan"
|
title = "URL async fetcher"
|
||||||
opaque = true
|
opaque = true
|
||||||
hasShadow = true
|
hasShadow = true
|
||||||
preferredBackingLocation = NSWindowBackingLocationVideoMemory
|
preferredBackingLocation = NSWindowBackingLocationVideoMemory
|
||||||
@@ -83,7 +145,7 @@ private class MyAppDelegate() : NSObject(), NSApplicationDelegateProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val buttonPress = NSButton(NSMakeRect(10.0, 10.0, 100.0, 40.0)).apply {
|
val buttonPress = NSButton(NSMakeRect(10.0, 10.0, 100.0, 40.0)).apply {
|
||||||
title = "Press me"
|
title = "Click"
|
||||||
target = controller
|
target = controller
|
||||||
action = NSSelectorFromString("onClick")
|
action = NSSelectorFromString("onClick")
|
||||||
}
|
}
|
||||||
@@ -94,11 +156,16 @@ private class MyAppDelegate() : NSObject(), NSApplicationDelegateProtocol {
|
|||||||
action = NSSelectorFromString("onQuit")
|
action = NSSelectorFromString("onQuit")
|
||||||
}
|
}
|
||||||
window.contentView!!.addSubview(buttonQuit)
|
window.contentView!!.addSubview(buttonQuit)
|
||||||
|
contentText = NSText(NSMakeRect(10.0, 80.0, 600.0, 350.0)).apply {
|
||||||
|
string = "Press 'Click' to start fetching"
|
||||||
|
verticallyResizable = false
|
||||||
|
horizontallyResizable = false
|
||||||
|
|
||||||
|
}
|
||||||
|
window.contentView!!.addSubview(contentText)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applicationWillFinishLaunching(notification: NSNotification) {
|
override fun applicationWillFinishLaunching(notification: NSNotification) {
|
||||||
window.makeKeyAndOrderFront(this)
|
window.makeKeyAndOrderFront(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user