Swift callbacks syntax issue - ios

I'm new to Swift, and I'm trying to declare a function that receives a callback.
func getAll(callback: (students: [Student]!) -> Void) {
// http request to get a list of students and parse it
callback(students: students)
}
And when calling the function, I'm doing:
obj.getAll() {
(students: [Student]!) in
// Callback code
}
But it won't build, it says: Cannot invoke getAll with an argument list of type '(([Student]!) -> _)'
I was following this thread as a guide, what did I miss?

struct Student {
}
func getAll(callback: (students: [Student]!) -> Void) {
// http request to get a list of students and parse it
let students = [Student]()
callback(students: students)
}
getAll { (students) -> Void in
println(students)
}

You do not send an argument students, but receive a parameter, that is why you implement it like this:
obj.getAll { (students) -> Void in
// Callback code
}
If you are not sure about the closures, always use autocomplete and you won't have to worry about the syntax. Hope this helps.

Remove type declaration in your call
obj.getAll() {
students in
// Callback code
}

Related

Transforming a function's return type from AnyPushliser<ReturnObj, error> to ReturnObj in Swift

I am new to Swift Combine and re-writing the code for my functionality in Swift Combine where the function is returning AnyPublisher<Person, Error>.
public func newPublisherFunction() -> AnyPublisher<Person, Error> {
.....
}
However, the callers of my code are still on Obj-C (even Swift too but not using Combine) and the function which they are invoking, in my code presently, returns a direct object type Person as shown below
// this is the function being used by different callers
public func existingFunction() -> Person {
....
}
Is there a way possible to create a bridging function which can take value (of type Person) emitted by someFunction1() and just return that value.
Specifically asking, I want to implement something like below
public func newPublisherFunction() -> AnyPublisher<Person, Error> {
.....
}
func bridgingFunction() -> Person {
var person: Person
// logic to consume value from newPublisherFunction()
// and assign it to 'person'
return person
}
public func existingFunction() -> Person {
return self.bridgingFunction()
}

Why this produces compiler error in Combine?

just trying to implement SwiftUI and Combine in my new project.
But stuck in this:
func task() -> AnyPublisher<Int, Error> {
return AnyPublisher { subscriber in
subscriber.receive(Int(arc4random()))
subscriber.receive(completion: .finished)
}
}
This produces the following compiler error:
Type '(_) -> ()' does not conform to protocol 'Publisher'
Why?
Update
Actually Random here is just as an example. The real code will look like this:
func task() -> AnyPublisher<SomeCodableModel, Error> {
return AnyPublisher { subscriber in
BackendCall.MakeApiCallWithCompletionHandler { response, error in
if let error == error {
subscriber.receive(.failure(error))
} else {
subscriber.receive(.success(response.data.filter))
subscriber.receive(.finished)
}
}
}
}
Unfortunately, I don't have access to BackendCall API since it is private.
It's kind of pseudocode but, it pretty close to the real one.
You cannot initialise an AnyPublisher with a closure accepting a Subscriber. You can only initialise an AnyPublisher from a Publisher. If you want to create a custom Publisher that emits a single random Int as soon as it receives a subscriber and then completes, you can create a custom type conforming to Publisher and in the required method, receive(subscriber:), do exactly what you were doing in your closure.
struct RandomNumberPublisher: Publisher {
typealias Output = Int
typealias Failure = Never
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
subscriber.receive(Int.random(in: 0...Int.max))
subscriber.receive(completion: .finished)
}
}
Then in your task method, you simply need to create a RandomNumberPublisher and then type erase it.
func task() -> AnyPublisher<Int, Never> {
return RandomNumberPublisher().eraseToAnyPublisher()
}
If all you want is a single random value, use Just
fun task() -> AnyPublisher<Int, Never> {
return Just(Int.random(in: 0...Int.max)).eraseToAnyPublisher()
}
Sidenote: don't use Int(arc4random()) anymore.
You're likely better off wrapping this in a Future publisher, possibly also wrapped with Deferred if you want it to response when subscriptions come in. Future is an excellent way to wrap external async API calls, especially ones that you can't fully control or otherwise easily adapt.
There's an example in Using Combine for "wrapping an async call with a Future to create a one-shot publisher" that looks like it might map quite closely to what you're trying to do.
If you want it to return more than a single value, then you may want to compose something out of PassthoughSubject or CurrentValueSubject that gives you an interface of -> AnyPublisher<YourType, Error> (or whatever you're looking for).

Proper use of Strategy Pattern for simple networking layer in Swift 3

Does the following code demonstrate proper use of Strategy design pattern for a simple networking layer in swift 3?
Some code smells I'm unsure about:
violates Single responsibiility principle. Each strategy class such as Find, has a method for a different type of implementation. This is because I could want to find an image, or a user, or a chatroom. which are stored at different nodes in Firebase. all these different find methods are clumped together in Find class.
At the call sight of a request, if I need to make multiple async request, I nest the next request call inside the closure of the call back. Is this Ok?
The request object allows access to every type of insert, and find method. so in my signup VC I could i have the option to download a chatroom. Is even having access to that kind of implementation bad?
I have posted the code below, and left out all the actual implementation for brevity.
Any tips or guidance is much appreciated!
// USE CASE: Would go in viewDidLoad of ViewController
func testMyRequest () {
let myRequest = Request(insert: Insert(), find: Find())
myRequest.find?.user(with: "id", handler: { (user) in
myRequest.find?.nearbyUsers(user: user, handler: { (users) in
// update collectionView datasource
})
})
}
// Is this protocol necessary?
protocol RequestProtocol {
// - Family of algorithms, related actions.
var insert: Insert? { get set }
var find: Find? { get set }
}
// ---------------------------
class Request: RequestProtocol {
var insert: Insert?
var find: Find?
init(insert: Insert?, find: Find?) {
self.insert = insert
self.find = find
}
}
// Use a singleton maybe for the classes below? Why wouldn't I?
class Insert {
init() { }
func user(_ user: User) {
// insert user to firebase implementation
}
func message(_ message: Message) -> Void {
// insert message to firebase impelmentation
}
func image(data: Data, user: User) {
// insert image to firebase impelmentation
}
}
class Find {
init() { }
func user(with id: String, handler: #escaping (_ user: User) -> Void ) {
// find user implementation
}
func allChatrooms(handler: #escaping ([Chatroom]) -> Void) {
// find all chatrooms implementation
}
func nearbyUsers(user: User, handler: #escaping ([User]) -> Void ) {
// find nearby Users relative to current User location implementation
}
// Private helper method
private func findChatPartners (currentUser: User, chatrooms: [Chatroom] ) -> Set<String> {
}
}

Swift 3: Array.contains(customObject)

I am trying to implement the if block seen below:
if loggedInUser.following.contains(userToView) {
}
where loggedInUser.following is an array of a custom User class and userToView is a single instance of the User class.
class User {
....
}
However, loggedInUser.following.contains(userToView) is throwing the error seen below:
Cannot convert value of type 'User' to expected argument type '(User) throws -> Bool'
I am under the impression I will need to implement some sort of a function that determines if two users are equal, but i have no idea how to implement such a function. Any help would be much appreciated; thank you in advance!
Yo can use the code below
if (loggedInUser.following as NSArray).contains(userToView) {
}
Thank you all for the suggestions.
Everything worked as expected after creating the following function and implementing the "Equatable" Protocol:
class User: Equatable {
//...
var id = Int() //Unique Identifier
//...
}
func ==(lhs: User, rhs: User) -> Bool {
return lhs.id == rhs.id
}
and after implementing the above code i was able to call the following without any problems:
if loggedInUser.following.contains(userToView) {
...
}

Swift: Returning a recursive function (currying)

I am wanting to return a function which will in turn call back itself.
Is it possible to do through returning a closure calling itself?
My problem is that I'm unsure of the correct syntax to use here, as well as I'm not sure if it is even possible due to having a cyclic reference to itself (and swift being heavy on compiler type checking)
I am currying my functions so that the models and presenters do not need to know about the dataGateway further decoupling my code
Some background information about the problem, the API expects a page number to be passed into itself, I do not want to store this state. I want the function to pass something back so that the model can just call the next function when it needs to.
I know the curried function definition looks like this:
function (completion: ([Example.Product], UInt) -> Void) -> Example.Task?
look for __function_defined_here__ in my code samples
Original - example code
func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: [Product] -> Void) -> Task? {
return dataGateway.productMap(category, page: page) { products in
completion(products.map { $0.build })
}
}
Idea 1 - return as tuple
func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: [Product] -> Void) -> (Task?, __function_defined_here__) {
return (dataGateway.productMap(category, page: page) { products in
completion(products.map { $0.build })
}, fetch(dataGateway, category: category)(page: page + 1))
}
Idea 2 - pass back in the completion
func fetch(dataGateway: DataGateway, category: String)(page: UInt)(completion: ([Product], __function_defined_here__) -> Void) -> Task? {
return dataGateway.productMap(category, page: page) { products in
completion(products.map { $0.build }, fetch(dataGateway, category: category)(page: page + 1))
}
}
I ended up solving it with something like the following, what it does is create a class reference to store the next function in. I pass a reference to this object in the completion of the asynchronous operation.
extension Product {
class Next {
typealias FunctionType = (([Product], Next) -> Void) -> Task?
let fetch: FunctionType
init(_ fetch: FunctionType) {
self.fetch = fetch
}
}
func fetch(dataGateway: DataGateway, inCategory category: String)(page: UInt)(completion: ([Product], Next) -> Void) -> Task? {
return dataGateway.products(inCategory: category, page: page)() { products in
completion(products.map { $0.build }, Next(fetch(dataGateway, inCategory: category)(page: page + 1)))
}
}
}
let initial = Product.fetch(dataGateway, inCategory: "1")(page: 0)
pass the function in to a data model
data() { [weak self] products, next in
self?.data = products
self?.setNeedsUpdate()
self?.next = next
}
scrolling down to bottom of table view triggers the above again, using the next function instead of data

Resources