Issue with KotlinPair after updating to Kotlin 1.4.10 - swift

I updated Kotlin Multiplatform project from Kotlin 1.3.72 to 1.4.10 and when I tried to built the code I got issues like:
LocationStepCardView.swift:13:19: Reference to generic type 'KotlinPair' requires arguments in <...>
LocationStepCardView.swift:28:24: Generic class 'KotlinPair' requires that 'Double' be a class type
When I rollback to branch without update to kotlin 1.4.10, everything is working fine.
Code:
commonMain.kt
interface LocationView {
var location: Pair<Latitude, Longitude>?
fun setOnSavingLocationPermissionProceedAction(action: () -> Unit)
fun setOnSavingLocationPermissionSkipAction(action: () -> Unit)
fun setOnSavingLocationPermissionAllowAction(action: (Pair<Latitude, Longitude>?) -> Unit)
fun setOnSavingLocationPermissionDenyAction(action: () -> Unit)
}
LatitudeCommon.kt
inline class Latitude(val value: Double)
LongitudeCommon.kt
inline class Longitude(val value: Double)
LocationStepCardView.swift
import kotlinProjectCommon
extension LocationStepCardView: LocationView {
var location: KotlinPair? {
get { currentLocation as? KotlinPair }
set { currentLocation = newValue }
}
func setOnSavingLocationPermissionProceedAction(action: #escaping () -> Void) {
proceedAction = action
}
func setOnSavingLocationPermissionSkipAction(action: #escaping () -> Void) {
skipAction = action
}
func setOnSavingLocationPermissionAllowAction(action: #escaping (KotlinPair?) -> Void) {
onLocationUpdateAction = { (latitude: Double, longitude: Double) in
let pair = KotlinPair(first: latitude, second: longitude)
action(pair)
}
}
func setOnSavingLocationPermissionDenyAction(action: #escaping () -> Void) {
onDeniedLocationAction = action
}
}

I would guess because Objective-C generics are now enabled by default. You can disable them with -Xno-objc-generics.
https://kotlinlang.org/docs/reference/native/objc_interop.html#generics
As for "needs to be a class type", I assume that means you need to explicitly convert to KotlinDouble. Disabling generics may revert that issue, but YMMV.

Related

How to create closure calling closure with less parameters in swift

I'm trying to create class in swift, that can take closure with 0..N arguments and then when callback function with N arguments will be invoked, will pass only required amount to closure.
I'm trying to do it like that:
class CallbackImpl: AuthServiceLogoutCallback {
private let callback: ((UUID, AuthServiceLogoutType) -> Void )?
public init( cb: #escaping ((UUID, AuthServiceLogoutType) -> Void ))
{
callback = { cb( $0, $1 ) }
}
public init( cb: #escaping ((UUID) -> Void ))
{
callback = { cb( $0 ) }
}
public init( cb: #escaping (() -> Void ))
{
callback = { cb() }
}
public func onEvent(_ userId: UUID, type: AuthServiceLogoutType)
{
callback!( userId, type )
}
}
First init with closure with two arguments is ok, inits with closures with 1 and 0 arguments give me error expression type '()' is ambiguous without more context
What is the right way to do such a thing?
You can achieve what you are trying, if you give more context as to which closure parameter you are using and which not:
class CallbackImpl: AuthServiceLogoutCallback {
private let callback: ((UUID, AuthServiceLogoutType) -> Void)?
public init(cb: #escaping ((UUID, AuthServiceLogoutType) -> Void)) {
callback = { cb($0, $1) }
}
public init(cb: #escaping ((UUID) -> Void)) {
callback = { uuid, _ in cb(uuid) }
}
public init(cb: #escaping (() -> Void)) {
callback = { _, _ in cb() }
}
public func onEvent(_ userId: UUID, type: AuthServiceLogoutType) {
callback!( userId, type )
}
}

Swift: Can I have a protocol that inherits from a protocol and constrains it?

Say I have the following protocols:
protocol RateableItem {
var identifier: String { get } // placeholder. This could be a lot of properties
var name: String { get set }
var rating: Int { get set }
}
protocol RateableItemManager {
/// get some objects matching query criteria
func objects(matching query: RateableItemQuery) -> [RateableItem]
/// get a specific object
func object(withID identifier: String) -> RateableItem?
/// persists them
func save(_ object: RateableItem) throws
/// deletes the objects.
func delete(_ objects: [RateableItem])
/// creates a new object.
func create() -> RateableItem
}
and
struct RateableItemQuery {
let searchPredicate: NSPredicate? // nil means all
let sortingBlock: ((RateableItem, RateableItem) throws -> Bool)?
let groupingSpecifier: (() -> String)?
init(matching predicate: NSPredicate? = nil,
sort: ((RateableItem, RateableItem) throws -> Bool)? = nil,
groupBy: (() -> String)? = nil) {
self.searchPredicate = predicate
self.sortingBlock = sort
self.groupingSpecifier = groupBy
}
}
I can now implement a concrete type of this which returns concrete types that conform to the protocol. The concrete types that are returned are irrelevant to the rest of my code because the rest of the code only cares that they conform to a protocol. This enables me to make 'production' and 'dummy' versions of the models.
Is there a way where I can define this more generally, such as:
struct Query<T> {
let searchPredicate: NSPredicate? // nil means all
let sortingBlock: ((T, T) throws -> Bool)?
let groupingSpecifier: (() -> String)?
init(matching predicate: NSPredicate? = nil,
sort: ((T, T) throws -> Bool)? = nil,
groupBy: (() -> String)? = nil) {
self.searchPredicate = predicate
self.sortingBlock = sort
self.groupingSpecifier = groupBy
}
}
such that
struct RateableItemQuery: Query<RateableItem> {}
and
protocol ItemManager<T> {
func objects(matching query: Query<T>) -> [T]
func object(withID identifier: String) -> T?
func save(_ object: T) throws
func delete(_ objects: [T])
func create() -> T
}
and
protocol RateableItemManager: ItemManager<RateableItem>
Because I want to use this API paradigm, but don't necessarily want to constrain anything at the 'base protocol' level, as I would often just be re-writing these method signatures for the various Protocol-types I'd want to work with.
If I'm not mistaken, associated types have to be concrete, making their return types also concrete, and then I can't easily work with protocol types.
Sorry if I didn't speak "canonically". I hope I was able to convey my intentions.
Could this be what upcoming Swift 5.1 is offering in terms of opaque types, returning -> some ProtocolType ?
So it turns out Opaque types aren't the answer either.
I solved my problem by making abstract baseclasses for Managers, the Query and QueryResults were generic structs, and the concrete subclasses for the Managers could take and return protocol-based Data types.
public struct Query<T> {
var searchPredicate: NSPredicate?
var sortingBlock: ((T, T) throws -> Bool)?
var groupingSpecifier: (() -> String)?
var results: QueryResults<T>?
init(matching predicate: NSPredicate?,
sort: ((T, T) throws -> Bool)?,
groupBy: (() -> String)?) {
}
}
public struct QueryResults<T> {
public enum ChangeType: UInt {
case insert = 1
case delete
case move
case update
}
public struct Section<T> {
var items: [T]
var title: String?
}
public var sections: [Section<T>] = []
public func object(at indexPath: IndexPath) -> T? {
return nil
}
}
public class AnyObjectManager<ObjectType> {
public enum Error: Swift.Error {
case abstractImplementationRequiresOverride
}
typealias QueryDidChangeObjectBlock = ((
_ query: Query<ObjectType>,
_ didChangeObject: ObjectType,
_ atPath: IndexPath?,
_ forChangeType: QueryResults<ObjectType>.ChangeType,
_ newIndexPath: IndexPath?) -> Void)
typealias QueryDidChangeSectionBlock = ((
_ query: Query<ObjectType>,
_ didChangeSection: QueryResults<ObjectType>.Section<ObjectType>,
_ atSectionIndex: Int,
_ forChangeType: QueryResults<ObjectType>.ChangeType) -> Void)
/// get some objects matching query criteria. nil means return all
func objects(matching query: Query<ObjectType>?) -> [ObjectType] {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// get a specific object
func object(withID identifier: String) -> ObjectType? {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// deletes the objects. Does it commit that to disk?
func remove(_ objects: [ObjectType]) {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// creates a new object but does not save it.
func create() -> ObjectType {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// this is basically to mimic the functionality of a fetched results controller...
func monitorQuery(_ query: Query<ObjectType>,
willChangeBlock: ((_ query: Query<ObjectType>) -> Void)?,
didChangeObjectBlock: QueryDidChangeObjectBlock?,
didChangeSectionBlock: QueryDidChangeSectionBlock?,
didFinishChangesBlock:((_ query: Query<ObjectType>) -> Void)?) {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
/// and this is to stop monitoring that.
func stopMonitoringQuery() {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
public func saveChanges() throws {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
public func discardChanges() throws {
fatalError("Abstract implementation. You need to override this method and provide an implementation!")
}
}

Why Swift compiler cannot infer a type?

I've developed this generic method to execute requests:
func executeRequest<T, S, E>(_ request : RequestBuilder<T>,
map: (#escaping (T) -> S)) -> RepositoryResult<S, E> {
return RepositoryResult().doTask { result in
request.execute{ (response, error) in
// do some stuff
result.notifySuccess(value: map(body))
}
}
My map function is defined in a subclass with generic types:
class BaseMapper<R, U> {
class func transform(_ dataModel:R) -> U {
fatalError("Override this method")
}
// other generic methods
}
class HomeMapper:BaseMapper<HomeDTO, Home> {
override class func transform(_ dataModel: HomeDTO) -> Home {
return Home(customerFirstName: dataModel.customerFirstName,
balance: MoneyMapper.transform(dataModel.balance),
accounts: AccountSummaryMapper.listTransform(dataModel.summaries))
}
}
If I call the request executor method passing directly the map function like that:
func getHomeInfo() -> RepositoryResult<Home, HomeError> {
return executeRequest(HomeAPI.getMyHomeWithRequestBuilder(), map: HomeMapper.transform)
}
I believe Swift compiler is crashing because it returns several random errors: "Segmentation fault: 11". Otherwise, if I call the method specifying the "S" return type, it works:
func getHomeInfo() -> RepositoryResult<Home, HomeError>{
return executeRequest(HomeAPI.getMyHomeWithRequestBuilder(), map: { (homeDTO) -> Home in
HomeMapper.transform(homeDTO)
})
}
Furthermore, using a Mapper that doesn't inherit from BaseMapper and passing the function directly, it also works. Another thing that I don't understand is that RxSwift has a map function that works calling it with my first option...
Why swift compiler cannot infer "S" type? Why swift compiler is crashing and can't tell which line is crashing and why?
I've found a solution: associatedtypes
protocol Mappable {
associatedtype T
associatedtype S
static func transform(_ dataModel:T) -> S
}
class MapperHome : Mappable {
static func transform(_ dataModel: HomeDTO) -> Home {
return Home(customerFirstName: dataModel.customerFirstName,
balance: MoneyMapper.transform(dataModel.balance),
accounts: AccountSummaryMapper.listTransform(dataModel.summaries))
}
}
func getHomeInfo() -> RepositoryResult<Home, HomeError>{
return executeRequest(HomeAPI.getMyHomeWithRequestBuilder(), map: MapperHome.transform)
}
I don't understand why this code compiles and using inheritance instance of implementation of a protocol it doesn't... I don't know if it's a compiler bug or my fault.

How to create a generic completion closure?

I have the following protocol:
protocol RESTAPIprotocol {
associatedtype T: Object, Decodable
}
extension RESTAPIprotocol {
func getList(sinceSyncToken: String = "",
pageLimit: Int = 100,
progress: Moya.ProgressBlock? = nil,
completion:#escaping (_ list: [T]?, _ error: AppError?) -> Void) { ... }
and object(s):
final class RLMOrganization: RLMDefaults {
typealias T = RLMOrganization
}
final class RLMProject: RLMDefaults {
typealias T = RLMProject
}
final class RLMLocation: RLMDefaults {
typealias T = RLMLocation
}
wanting to use it like this:
class SyncEngine {
let listCompletionClosure = { (_ list: [T]?, _ error: AppError?) -> Void in ... }
func syncOrganizations() {
// Sync down from server and update our local DB.
organizationsDAL.getList(sinceSyncToken: organizationsDAL.getLastSyncToken(), completion: listCompletionClosure)
}
But get the error:
Which sort of makes sense, but don't understand how I can pass along the generic used as part of RESTAPIprotocol into a generic closure?
The goal is to try to accomplish the following:
func syncOrganizations() {
organizationsDAL.getList(sinceSyncToken: organizationsDAL.getLastSyncToken(), completion: listCompletionClosure)
}
func syncProjects() {
projectsDAL.getList(sinceSyncToken: projectsDAL.getLastSyncToken(), completion: listCompletionClosure)
}
func syncLocations() {
locationsDAL.getList(sinceSyncToken: locationsDAL.getLastSyncToken(), completion: listCompletionClosure)
}
How about change it to"
let listCompletionClosure = { (_ list: [RLMOrganization]?, _ error: AppError?) -> Void in ... }
The completion handle requires a concrete type and you have defined T = RLMOrganization within the context of the RLMOrganization class.
Edit: a closure can’t be generic but a function can:
func listCompletion<T: Decodable>(list: [T]?, error: AppError?) {
// you must cast `list` to a concrete type
}
func syncOrganizations() {
organizationsDAL.getList(sinceSyncToken: organizationsDAL.getLastSyncToken(), completion: listCompletion)
}
func syncProjects() {
projectsDAL.getList(sinceSyncToken: projectsDAL.getLastSyncToken(), completion: listCompletion)
}
func syncLocations() {
locationsDAL.getList(sinceSyncToken: locationsDAL.getLastSyncToken(), completion: listCompletion)
}

How to call a swift function in JS that returns a value?

The following piece of code works like a charm to define a function in Swift (2.0) that I can call from a Javascript resource (tvos). The function storeSetPackageInfo accepts a parameter and returns nothing.
I am trying to understand how I achieve the same goal with a function that accept no parameters and returns a boolean. I don't seem to understand the syntax.
private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = #convention(block) (String) -> Void
func setupStoreSetPackageInfo() {
let selectComponent: JavascriptClosure = {
[unowned self](context: JSContext) -> Void in
let objCompletion: ObjectivecCompletionBlock = {
(str: String) -> Void in
(self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
}
context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
}
evaluateInJavaScriptContext(selectComponent, completion: nil)
}
I tried multiple approaches which compile but resulting in the JSContext in not finding the function. Any help is very appreciated.
I described one possible way just yesterday in another context: How to retrieve values from settings.bundle in TVML?
AppDelegate.swift
func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
let jsInterface: cJsInterface = cJsInterface();
jsContext.setObject(jsInterface, forKeyedSubscript: "swiftInterface")
}
JsInterface.swift
#objc protocol jsInterfaceProtocol : JSExport {
func getSetting(setting: String) -> String
}
class cJsInterface: NSObject, jsInterfaceProtocol {
func getSetting(setting: String) -> String {
return "<yourSetting>"
}
}
on the JS side...
swiftInterface.getSetting(...)
It's definitely a different syntax compared to your example, but known to work. See https://github.com/iBaa/PlexConnectApp.
After multiple attempts, I found it, the answer and the solution was in front of me at all time... I had tried before but I eventually I had other messy attempts around. To benefit whoever runs into this problems, here is the solution for any signature
private typealias ObjectivecCompletionBlock = #convention(block) () -> Bool
the completion block must match the signature with
() -> Bool in
Therefore the final code is
private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = #convention(block) () -> Bool
func setupStoreSetPackageInfo() {
let selectComponent: JavascriptClosure = {
[unowned self](context: JSContext) -> Void in
let objCompletion: ObjectivecCompletionBlock = {
() -> Bool in
(self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
}
context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
}
evaluateInJavaScriptContext(selectComponent, completion: nil)
}
Again really straightforward (once you pull the head out of the bucket...)