Reputation: 5644
I'm trying to think in terms of API Design because ultimately I want to ship code to myself or others.
Let's make some assumptions in order to get a succinct scenario. We will assume I have some Code that authenticates with my server and returns a user object. Defined simply like this:
public struct User: Codable {
public let id: UUID
public let email: String
public let name: String
}
I'm writing this code as an SDK I would ship to myself or a third party where all the guts of AUTH are handled. Using Apples new Combine framework I might expose a publisher for the consumer of my SDK like this.
public enum CurrentUserError: Error {
case loggedOut
case sessionExpired
}
public struct MyAuthFrameworkPublishers {
static let currentUser: CurrentValueSubject<User?, CurrentUserError> = CurrentValueSubject<User?, CurrentUserError>(nil)
}
Now, my private auth could accomplish it's task and retrieve a user then publish that to anything outside the SDK that it listening like so:
class AuthController {
func authenticate() {
///Get authenticated user.
let user = User.init(id: UUID(), email: "[email protected]", name: "some")
MyAuthFrameworkPublishers.currentUser.send(user)
}
}
let authController = AuthController.init()
authController.authenticate()
Is there a way to keep or stop the user of this SDK from sending it's own User
object to the publisher? Like a private or access controller .send()
function in combine?
Upvotes: 0
Views: 52
Reputation: 385970
Do you really want to use CurrentUserError
as your Failure
type? Sending a failure ends any subscriptions to the subject and fails new ones immediately. If the session expires, don't you want to let the user log in again? That would require publishing another User?
output, which you cannot do if you have published a failure.
Instead, use Result<User?, CurrentUserError>
as your Output
type, and Never
as your Failure
type.
Now, on to your question. In general, the way to prevent the user from calling send
is to vend an AnyPublisher
instead of a Subject
:
public class AuthController {
public let currentUser: AnyPublisher<Result<User?, AuthenticationError>, Never>
public func authenticate() {
let user: User = ...
subject.send(user)
}
public init() {
currentUser = subject.eraseToAnyPublisher()
}
private let subject = CurrentValueSubject<Result<User?, AuthenticationError>, Never>(nil)
}
Upvotes: 2