Firefox add-on SDK -- "TypeError: can't convert null to object" Only on some computers - firefox-addon-sdk

My addon works on most computers, but not all. I recently discovered that it doesn't work on a computer in my apartment building so I am trying to get to the bottom of it (this isn't the only computer that I have had this same issue with, but its the first one that I can actually do some troubleshooting with).
The error seems to stem from the use of "sdk/ui/frame" inside a toolbar. The error is reproduced in the example provided by mozilla as well. If I do a "cfx run" on the "addon-sdk-1.17/examples/toolbar-api" I get the following error:
main.js:
"use strict";
const { Toolbar } = require("sdk/ui/toolbar");
const { Frame } = require("sdk/ui/frame");
const { ActionButton } = require("sdk/ui/button/action");
let button = new ActionButton({
id: "button",
label: "send!",
icon: "./favicon.ico",
onClick: () => {
frame.postMessage({
hello: "content"
});
}
});
let frame = new Frame({
url: "./index.html",
onAttach: () => {
console.log("frame was attached");
},
onReady: () => {
console.log("frame document was loaded");
},
onLoad: () => {
console.log("frame load complete");
},
onMessage: (event) => {
console.log("got message from frame content", event);
if (event.data === "ping!")
event.source.postMessage("pong!", event.source.origin);
}
});
let toolbar = new Toolbar({
items: [frame],
title: "Addon Demo",
hidden: false,
onShow: () => {
console.log("toolbar was shown");
},
onHide: () => {
console.log("toolbar was hidden");
}
});
index.html:
<html>
<body>
<button id=post>post!</button>
</body>
<script>
window.addEventListener("message", event => {
console.log("Document message", event, event.data, event.source !== window && event.source === window.parent);
});
window.addEventListener("click", event => {
if (event.target.id === "post") {
console.log("click!")
window.parent.postMessage("ping!", "*");
}
});
console.log(window.parent === window)
</script>
</html>
console error:
console.error: toolbar-api:
Message: TypeError: can't convert null to object
Stack:
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gr
e/modules/commonjs/diffpatcher/diff.js:24:3
calculate/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/diffpatcher/diff.js:36:17
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mo
dules/commonjs/diffpatcher/diff.js:29:3
calculate/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/diffpatcher/diff.js:36:17
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mo
dules/commonjs/diffpatcher/diff.js:29:3
calculate/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/diffpatcher/diff.js:36:17
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mo
dules/commonjs/diffpatcher/diff.js:29:3
#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/com
monjs/diffpatcher/diff.js:18:10
dispatch#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/method/core.js:119:12
reactor<.onStep#resource://gre/modules/commonjs/toolkit/loader.js -> resource://
gre/modules/commonjs/sdk/ui/frame/view.js:130:19
Reactor.prototype.onNext#resource://gre/modules/commonjs/toolkit/loader.js -> re
source://gre/modules/commonjs/sdk/event/utils.js:248:3
Reactor.prototype.run/<#resource://gre/modules/commonjs/toolkit/loader.js -> res
ource://gre/modules/commonjs/sdk/event/utils.js:251:32
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
transform/next#resource://gre/modules/commonjs/toolkit/loader.js -> resource://g
re/modules/commonjs/sdk/event/utils.js:36:24
map/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/module
s/commonjs/sdk/event/utils.js:60:60
transform/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/sdk/event/utils.js:43:29
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
merges/</<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/m
odules/commonjs/sdk/event/utils.js:187:31
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
lift/</<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/sdk/event/utils.js:163:7
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
transform/next#resource://gre/modules/commonjs/toolkit/loader.js -> resource://g
re/modules/commonjs/sdk/event/utils.js:36:24
filter/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/sdk/event/utils.js:53:7
transform/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/sdk/event/utils.js:43:29
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
lift/</<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/sdk/event/utils.js:163:7
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
InputPort.prototype.observe#resource://gre/modules/commonjs/toolkit/loader.js ->
resource://gre/modules/commonjs/sdk/input/system.js:100:5
console.error: toolbar-api:
Message: TypeError: can't convert null to object
Stack:
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gr
e/modules/commonjs/diffpatcher/diff.js:24:3
calculate/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/diffpatcher/diff.js:36:17
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mo
dules/commonjs/diffpatcher/diff.js:29:3
calculate/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/diffpatcher/diff.js:36:17
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mo
dules/commonjs/diffpatcher/diff.js:29:3
calculate/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/diffpatcher/diff.js:36:17
calculate#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mo
dules/commonjs/diffpatcher/diff.js:29:3
#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/com
monjs/diffpatcher/diff.js:18:10
dispatch#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/method/core.js:119:12
reactor<.onStep#resource://gre/modules/commonjs/toolkit/loader.js -> resource://
gre/modules/commonjs/sdk/ui/frame/model.js:127:19
Reactor.prototype.onNext#resource://gre/modules/commonjs/toolkit/loader.js -> re
source://gre/modules/commonjs/sdk/event/utils.js:248:3
Reactor.prototype.run/<#resource://gre/modules/commonjs/toolkit/loader.js -> res
ource://gre/modules/commonjs/sdk/event/utils.js:251:32
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
transform/next#resource://gre/modules/commonjs/toolkit/loader.js -> resource://g
re/modules/commonjs/sdk/event/utils.js:36:24
map/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/module
s/commonjs/sdk/event/utils.js:60:60
transform/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/sdk/event/utils.js:43:29
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
merges/</<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/m
odules/commonjs/sdk/event/utils.js:187:31
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
lift/</<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/sdk/event/utils.js:163:7
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
transform/next#resource://gre/modules/commonjs/toolkit/loader.js -> resource://g
re/modules/commonjs/sdk/event/utils.js:36:24
filter/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/sdk/event/utils.js:53:7
transform/<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/
modules/commonjs/sdk/event/utils.js:43:29
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
lift/</<#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/mod
ules/commonjs/sdk/event/utils.js:163:7
emit#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules
/commonjs/sdk/event/core.js:97:9
receive#resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modu
les/commonjs/sdk/event/utils.js:115:5
InputPort.prototype.observe#resource://gre/modules/commonjs/toolkit/loader.js ->
resource://gre/modules/commonjs/sdk/input/system.js:100:5
The toolbar with the frame will show up, but no communication between the frame and the main.js will work. Again, this doesn't occur on all computers, just some.
This computer has the latest version of firefox, 38.0.5, and doesn't have any other extensions installed. There aren't any plugins that are not on my computer either. It is running windows 8 as does my computer.

Related

Can I get out of callback hell with RxSwift?

I heard that I can get out of callback hell using RxSwift.
But I don't have an idea how to improve callback hell.
The samples below need to be called in the order getItem1() -> getItem2() -> getItem3()
Is there a way out of this callback hell using RxSwift?
class MyItem {
// MARK: - Public
// Callback hell
public func getMyItem(success: #escaping (String) -> Void, failure: #escaping (Error) -> Void) {
self.getItem1(success: { [weak self] item1 in
self?.getItem2(item1: item1, success: { [weak self] item2 in
self?.getItem3(item2: item2, success: { item3 in
success(item3)
}, failure: { err3 in
print(err3)
failure(err3)
})
}, failure: { err2 in
print(err2)
failure(err2)
})
}, failure: { err1 in
print(err1)
failure(err1)
})
}
// MARK: - Private
private func getItem1(success: #escaping (String) -> Void, failure: #escaping (Error) -> Void) {
// Request to sever
}
private func getItem2(item1: String, success: #escaping (String) -> Void, failure: #escaping (Error) -> Void) {
// Request to sever
}
private func getItem3(item2: String, success: #escaping (String) -> Void, failure: #escaping (Error) -> Void) {
// Request to sever
}
}
I'm waiting for your answer to teach me.
Your sample, using RxSwift, would look like this:
class MyItem {
// MARK: - Public
public func getMyItem() -> Observable<String> {
getItem1()
.flatMap(getItem2(item1:))
.flatMap(getItem3(item2:))
.do(onError: { print($0) })
}
}
private func getItem1() -> Observable<String> {
// Request to server
}
private func getItem2(item1: String) -> Observable<String> {
// Request to server
}
private func getItem3(item2: String) -> Observable<String> {
// Request to server
}
There are seven different systems used in iOS code to push data from one object to another:
closures
target/action (UIControl aka IBAction)
delegates
notifications (NotificationCenter)
KVO
setting variables
sub-classing abstract base classes
A lot of the complexity of writing an application comes from trying to integrate these various systems into a unified whole. Each of the systems individually is simple to use, but weaving them together makes our code complex. One of the benefits of using RxSwift is that it wraps all of the above systems into a single powerful mechanism, thus making our code less complex overall.
Knowing the above gives a clue on how to go about integrating RxSwift into an existing project. If RxSwift can replace all those other technologies, then to integrate means to replace them with it. Once this is done, we will be able to make the code more declarative and less complex.
From Integrating RxSwift into Your Brain and Code Base
If for some reason you don't want to change the signatures of any of the functions you have, then you could do something crazy like this:
public func getMyItem(success: #escaping (String) -> Void, failure: #escaping (Error) -> Void) {
_ = rx_(self.getItem1)
.flatMap(rx_(self.getItem2))
.flatMap(rx_(self.getItem3))
.do(onError: { print($0) })
.subscribe(onNext: success, onError: failure)
}
The above uses the following support functions (all of these are free functions. Do not put them in a class.)
func rx_<A, B>(_ fn: #escaping (A, #escaping (B) -> Void, #escaping (Error) -> Void) -> Void) -> (A) -> Observable<B> {
{ input in
.create(observe(curry(fn, input)))
}
}
func rx_<A>(_ fn: #escaping (#escaping (A) -> Void, #escaping (Error) -> Void) -> Void) -> Observable<A> {
.create(observe(fn))
}
func curry<A, B>(_ fn: #escaping (A, #escaping (B) -> Void, #escaping (Error) -> Void) -> Void, _ a: A) -> (#escaping (B) -> Void, #escaping (Error) -> Void) -> Void {
{ success, failure in
fn(a, success, failure)
}
}
func observe<A>(_ fn: #escaping (#escaping (A) -> Void, #escaping (Error) -> Void) -> Void) -> (AnyObserver<A>) -> Disposable {
{ observer in
fn(singleObserve(observer), observer.onError)
return Disposables.create()
}
}
func singleObserve<A>(_ observer: AnyObserver<A>) -> (A) -> Void {
{ element in
observer.onNext(element)
observer.onCompleted()
}
}
I don't recommend this sort of thing for production code. IMO, something like the above trades callback hell for abstraction hell, but it's a fun exercise.

Issue with KotlinPair after updating to Kotlin 1.4.10

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.

Swift - implementing repository pattern with generics

I have been doing some research into repository pattern as want to use it new project I am working on. However I am having issues getting it working with generics
I have been following this guide here as an example
https://medium.com/#frederikjacques/repository-design-pattern-in-swift-952061485aa
Which does a fairly good job of explaining it. However, the guide leaves off one important detail.. which is using dependancy injection with generics.
In example code, he shows this
class ArticleFeedViewModel {
let articleRepo:ArticleRepository
init( articleRepo:ArticleRepository = WebArticleRepository() ) {
self.articleRepo = articleRepo
}
}
Which works fine if you are not using generics. But once you change ArticleRepository to Repository example... so from
protocol ArticleRepository {
func getAll() -> [Article]
func get( identifier:Int ) -> Article?
func create( article:Article ) -> Bool
func update( article:Article ) -> Bool
func delete( article:Article ) -> Bool
}
to this
protocol Repository {
associatedtype T
func getAll() -> [T]
func get( identifier:Int ) -> T?
func create( a:T ) -> Bool
func update( a:T ) -> Bool
func delete( a:T ) -> Bool
}
I can no longer get the dependancy injection working. So If I were to try re-creating the model shown above.
class WebArticleRepository: Repository {
func getAll() -> [Article] {
return [Article()]
}
func get(identifier: Int) -> Article? {
return Article()
}
func create(a: Article) -> Bool {
return true
}
func update(a: Article) -> Bool {
return false
}
func delete(a: Article) -> Bool {
return true
}
}
class ArticleFeedViewModel {
let articleRepo:Repository
init( articleRepo:Repository = WebArticleRepository() ) {
self.articleRepo = articleRepo
}
}
This no longer works. I now get an error saying
Protocol 'Repository' can only be used as a generic constraint because
it has Self or associated type requirements
Any ideas on what I am doing wrong here. It seems adding associatedType causes this to stop working. I would really like to get this functionality working as I want to be able to inject either local or web based repository pattern depending on current state of the app
Any help would be much appriecated
You need to make everything else generic as well:
protocol Repository {
associatedtype RepositoryType
func getAll() -> [RepositoryType]
func get( identifier:Int ) -> RepositoryType?
func create( a:RepositoryType ) -> Bool
func update( a:RepositoryType ) -> Bool
func delete( a:RepositoryType ) -> Bool
}
class WebArticle { }
class WebArticleRepository: Repository {
typealias RepositoryType = WebArticle
func getAll() -> [WebArticle] {
return [WebArticle()]
}
func get(identifier: Int) -> WebArticle? {
return WebArticle()
}
func create(a: WebArticle) -> Bool {
return true
}
func update(a: WebArticle) -> Bool {
return false
}
func delete(a: WebArticle) -> Bool {
return true
}
}
class ArticleFeedViewModel<T : Repository> {
let articleRepo: T
init( articleRepo: T) {
self.articleRepo = articleRepo
}
}
// you cannot have the optional parameter in the init, instead, you can extract the following line to a method
ArticleFeedViewModel(articleRepo: WebArticleRepository())
In Swift you can't use a protocol with associated types as the type of a property/parameter etc. It's supposed to make your code more type-safe.

How do I create classes that conform to protocols with nested PATs?

I'm attempting to use type-erasure to create a Repository protocol that can be conformed to (similar to Swift's AnyCollection). This protocol needs to be wrapped in a type-erased class because it contains a PAT.
However, since this protocol has a nested protocol which also has a PAT, it somewhat complicates things.
Keyable is something that provides a key (which eventually I'd like to ensure is also Hashable... one thing at a time).
Repository is my generalized "container" protocol
AnyKeyable type-erased wrapper for Keyable
AnyRepository type-erased wrapper for Repository
I’ve got a playground snippet that ALMOST compiles:
protocol Keyable {
associatedtype KeyType// where KeyType: Hashable
func key() -> KeyType
}
protocol Repository {
associatedtype DataType: Keyable
func all() -> [DataType]
func get(id: DataType.KeyType) throws -> DataType?
func create(object: DataType) throws -> Bool
func update(object: DataType) throws -> Bool
func delete(object: DataType) throws -> Bool
func clear() -> Bool
}
final class AnyKeyable<T>: Keyable {
private let _key: () -> T
init<U: Keyable>(_ keyable: U) where U.KeyType == T {
_key = keyable.key
}
public func key() -> T {
return _key()
}
}
final class AnyRepository<T: Keyable>: Repository {
private let _all: () -> [T]
private let _get: (_ id: T.KeyType) throws -> T?
private let _create: (_ object: T) throws -> Bool
private let _update: (_ object: T) throws -> Bool
private let _delete: (_ object: T) throws -> Bool
private let _clear: () -> Bool
init<U: Repository>(_ repository: U) where U.DataType == T {
_get = repository.get
_create = repository.create
_delete = repository.delete
_update = repository.update
_clear = repository.clear
_all = repository.all
}
func all() -> [T] {
return _all()
}
func get<K: Keyable>(id: K.KeyType) throws -> T? where T.KeyType: Keyable, T.KeyType == K.KeyType {
let anyKeyable = AnyKeyable(id)
return try _get(anyKeyable)
}
func create(object: T) throws -> Bool {
return try _create(object)
}
func update(object: T) throws -> Bool {
return try _update(object)
}
func delete(object: T) throws -> Bool {
return try _delete(object)
}
func clear() -> Bool {
return _clear()
}
}
final class Contact {
var name: String = ""
var email: String = ""
}
extension Contact: Keyable {
public typealias KeyType = String
public func key() -> String {
return "im the key"
}
}
// Just a dummy class to see
final class ContactRepository: Repository {
typealias DataType = Contact
private var someContacts: [Contact] = []
func all() -> [Contact] {
return someContacts
}
func clear() -> Bool {
someContacts.removeAll()
return someContacts.count == 0
}
func get(id: Contact.KeyType) throws -> Contact? {
return nil
}
func update(object: Contact) throws -> Bool {
return false
}
func create(object: Contact) throws -> Bool {
return false
}
func delete(object: Contact) throws -> Bool {
return false
}
}
// Testing
let i = AnyRepository<Contact>(ContactRepository())
i.all()
i.clear()
The problem with that is the Swift compiler complains that I’m not using K in the method signature of get<K: Keyable>(id: K.KeyType)... but it looks to me like I definitely am.
My thought is that the compiler is complaining because of the DataType.KeyType declaration of get() in the protocol, rather than in the AnyRepository concrete subclass, but I'm uncertain on how to correct this to provide more context for the compiler.
Is there a better way to structure this to allow me to accomplish this pattern? What about also allowing for the first associatedtype in Keyable to become associatedtype KeyType where KeyType: Hashable?
Any help is greatly appreciated,
Thank you!
As Hamish pointed out, there was no need to introduce another generic, K. That was complicating things. Cleaning it all up, this now works without the complication of an AnyKeyable class:
public protocol Keyable {
associatedtype KeyType where KeyType: Hashable
func key() -> KeyType
}
public protocol Repository {
associatedtype DataType: Keyable
func all() -> [DataType]
func get(id: DataType.KeyType) throws -> DataType?
func create(object: DataType) throws
func create(objects: [DataType]) throws
func update(object: DataType) throws
func delete(object: DataType) throws
func delete(objects: [DataType]) throws
func clear()
}
public class AnyRepository<T: Keyable>: Repository {
private let _all: () -> [T]
private let _get: (_ id: T.KeyType) throws -> T?
private let _create: (_ object: T) throws -> Void
private let _createAll: (_ objects: [T]) throws -> Void
private let _update: (_ object: T) throws -> Void
private let _delete: (_ object: T) throws -> Void
private let _deleteAll: (_ objects: [T]) throws -> Void
private let _clear: () -> Void
public init<R: Repository>(_ repository: R) where R.DataType == T {
_get = repository.get
_create = repository.create
_createAll = repository.create
_delete = repository.delete
_deleteAll = repository.delete
_update = repository.update
_clear = repository.clear
_all = repository.all
}
public func all() -> [T] {
return _all()
}
public func get(id: T.KeyType) throws -> T? {
return try _get(id)
}
public func create(object: T) throws {
return try _create(object)
}
public func create(objects: [T]) throws {
return try _createAll(objects)
}
public func update(object: T) throws {
return try _update(object)
}
public func delete(object: T) throws {
return try _delete(object)
}
public func delete(objects: [T]) throws {
return try _deleteAll(objects)
}
public func clear() {
return _clear()
}
}

Static generic extension for Array for help function for iterated async callbacks

I have two help methods that I want to move to an extension to increase it's reusability. The two methods manages multiple callbacks when calling parallell http request. However when making the methods static and moving them to an extension I get this error:
Cannot convert value of type '[Action]' to expected argument type '[_]'
The code is
extension Array
{
private static func iterateObjectList<Type>(objectList:[Type], multiplier:Int=1, foreach:(object:Type, (newObject:Type?, error:NSError?) -> Void) -> (), finally: (objectList:[Type], errorList:[NSError]) -> Void)
{
var iterationsLeft = objectList.count * multiplier
var errorList:[NSError] = []
var returnObjectList:[Type] = []
if (iterationsLeft == 0) {
finally (objectList:objectList, errorList:[])
}
for object:Type in objectList {
foreach (object:object, { (requestObject, requestError) -> Void in
iterationsLeft -= 1
if (requestError != nil) {
errorList.append(requestError!);
}
if (requestObject != nil) {
returnObjectList.append(requestObject!)
}
if (iterationsLeft <= 0) {
finally (objectList:returnObjectList, errorList:errorList)
}
})
}
}
private static func simpleIterate<Type>(objectList:[Type], multiplier:Int=1, foreach:(object:Type, Void -> Void) -> (), finally: Void -> Void)
{
var iterationsLeft = objectList.count * multiplier
if (iterationsLeft == 0) {
finally ()
}
for object:Type in objectList {
foreach (object:object, { Void -> Void in
iterationsLeft -= 1
if (iterationsLeft <= 0) {
finally ()
}
})
}
}
}
The error is when using the methods:
Array.iterateObjectList(actions, foreach: { (action, iterationComplete) -> () in
self.fetchStatusAndUpdateAction(action, callback: { (error) -> Void in
iterationComplete(newObject: action, error:error)
})
}, finally: { (objectList, errorList) -> Void in
callback(error: errorList.first)
})
where actions is of type [Action] where Action is a custom object.
The problem is that
public struct Array<Element>
is a generic type, and in
Array.iterateObjectList(actions, foreach: { (action, iterationComplete) -> () in
// ...
}, finally: { (objectList, errorList) -> Void in
// ...
})
the compiler cannot infer what Element should be. You could
make it compile as
Array<Action>.iterateObjectList(actions, foreach: { (action, iterationComplete) -> () in
// ...
}, finally: { (objectList, errorList) -> Void in
// ...
})
or even
Array<Int>.iterateObjectList(...)
The array Element is unrelated to your generic placeholder Type,
so any type will do.
But the better solution would be to make the static method an
instance method:
func iterateObjectList(multiplier:Int=1, foreach:(object:Element, (newObject:Element?, error:NSError?) -> Void) -> (), finally: (objectList:[Element], errorList:[NSError]) -> Void)
{
// Your code with `Type` replaced by `Element`,
// and `objectList` replaced by `self`.
// ...
}
and call it on the actions array:
actions.iterateObjectList(foreach: { (action, iterationComplete) -> () in
// ...
}, finally: { (objectList, errorList) -> Void in
// ...
})