-
I’ve used the iPad to write a simple Dependency Injection Package using Playgrounds. This is the first time I’ve used Playgrounds on the iPad to build something usable. The Magic Keyboard makes everything so much easier 👨💻
-
Today I updated my network client library and documented all its public methods. For publishing the documentation I’ve used Swift-DocC, Apple’s documentation compiler for Swift modules 👨💻
-
Tomorrow I’ll work on my Micropub Swift framework. At the end of the weekend I want to have a working framework with support for posting, bookmarking, replying, and uploading media 👨🏻💻
-
I’m splitting MicroAPI into two Swift Packages. One for Micro.blog and the other just for Micropub related things. The website Micropub Rocks! is a great way to validate Micropub implementation 👨💻
-
MicroClient has a new house on GitHub. I’ll use the organization for clients which use MicroClient, such as MicroAPI (for Micro.blog) and MicroPinboard (Pinboard.in).
-
I did some changes to Marfa’s CSS to make more room for code blocks. E.g.,
let request = MicropubRequestFactory.makeNewPostRequest( content: "This is the body", isDraft: true ) let postResponse = try await client.run(request)
-
When I wrote MicroAPI, a Swift wrapper to access Micro.blog APIs, I decided to isolate the network client in separate swift package: MicroClient.
Today I ported PinboardKit to also use MicroClient.
MicroClient is pretty generic and can be used by all sorts of projects 👨💻
-
Yesterday Apple launched Swift Playgrounds 4 for iPad, and it’s possible now to build iPad and iOS applications directly from the iPad. I’m looking forward to porting some of my little experiments to the new project format 👨💻
-
The Micro.blog API Client I’m writing consists of two parts: a Swift Package with the MicroClient, a generic/lightweight network client which can be used in completely unrelated projects, and MicroAPI, the Micro.blog API wrapper, which uses the MicroClient underneath 👨💻
-
I tried to make my Micro.blog API Client with simple - yet testable - interfaces 👨💻. Uploading a photo’s straight-forward:
let mediaRequest = MicropubRequestFactory.makeUploadRequest( media: .jpeg(jpegData) ) let uploadResponse = try await client.run(mediaRequest)
-
The first part of my micro.blog API Client in Swift is implemented 👨💻. The API Client implements all the
/posts
methods from the documentation. Later today I’m gonna work on the/users
methods. -
I was planing to use Swift’s async/await for my micro.blog API client, but I’m gonna stick to Combine for now. Apple’s new APIs require iOS 15.0+ and macOS 12.0+ 👨💻
-
Just thinking out loud, is there a demand for a Micro.blog API client in Swift? My plan would be to build an API client and later an Open Source native SwiftUI application for macOS and iOS.
-
Reachability using NWPathMonitor and Combine
About a month ago I decided to build a new app to attend a personal need, a desktop client for Pinboard, a service I’ve been using for over a decade to store and manage my bookmarks.
As any application which requires internet to fetch and update data, my client needed a way to detect network accessibility. For a long time the strategy adopted by engineers in this situation was to rely on Apple’s Reachability class, a sample code the company used to provide for download on their developer portal. Well, either that or one of the many CocoaPods frameworks the community built to replace Apple’s drag-and-drop solution. But since iOS 12.0 and macOS 10.14 there’s a powerfull first-party alternative.
Introduced a couple of years ago,
NWPathMonitor
is the easiest way to detect network changes (and retrieve connection properties). Its interface is extremely simple to use, providing a callback to get notified of updates.let monitor = NWPathMonitor() let queue = DispatchQueue.global(qos: .background) monitor.pathUpdateHandler = { path in print(path.status) // .unsatisfied, .satisfied, .requiresConnection } monitor.start( queue: queue ) // ... monitor.stop()
My application in built in SwiftUI (2.0) and Combine and the solution above wouldn’t feel right in my View Model. So I started with a simple wrapper, hiding the monitor and exposing a publisher which notifies path changes.
let wrapper = NWPathMonitorWrapper() let cancellable = wrapper .pathUpdatePublisher() .receive(on: RunLoop.main) .sink { path in print(path.status) } wrapper.start()
But the implementation (below) still requires
NWPathMonitorWrapper.start()
to be called, hurting the beauty of the publisher-subscriber relationship.public final class NWPathMonitorWrapper { // MARK: - Properties private let monitor: NWPathMonitor private let queue: DispatchQueue = .global(qos: .background) private let pathUpdateSubject = PassthroughSubject<NWPath, Never>() // MARK: - Life cycle public init( monitor: NWPathMonitor = NWPathMonitor() ) { self.monitor = monitor self.monitor.pathUpdateHandler = { self.pathUpdateSubject.send($0) } } // MARK: - Public public func start() { monitor.start( queue: queue ) } public func stop() { monitor.cancel() } public func pathUpdatePublisher() -> AnyPublisher<NWPath, Never> { pathUpdateSubject .eraseToAnyPublisher() } }
So I looked for a similar example in Apple’s frameworks, one which requires a method to be called to trigger the action. Turns out
URLSession
, familiar to every iOS/macOS engineer, is the perfect example since it requiresURLSession.resume()
to be called to fire the network requrest. RecentlyURLSession
got a new method, a publisher which starts the request at the moment there’s demand (a subscriber):let session = URLSession.shared let cancellable = session .dataTaskPublisher(for: request) .receive(on: RunLoop.main) .tryMap { result in // ... } .sink( receiveCompletion: { _ in }, receiveValue: { _ in } )
And that’s the interface I wanted for my monitor. A simple publisher which starts emitting values as soon as there’s demand.
let monitor = NWPathMonitor() let cancellable = monitor .pathUpdatePublisher() .receive(on: RunLoop.main) .sink { path in print(path.status) }
SwiftUI and Combine are new to me, I spiked and re-implemented some UI components I have up in my sleeve, but nothing too complex. Building this client is helping me to explore and learn new APIs.
The protocols
Publisher
,Subscriber
, andSubscription
are examples of these APIs. To implement a custom publisher it’s necessary to implement types conforming to two of these protocols.First, a quick recap:
Publisher
is the type which emits events over time,Subscriber
is the type which receives events published by the publisher, andSubscription
implements the link between publishers and subscribers.
The first step was to define my publisher interface. Since the monitor requires a queue to run, I decided to pass the queue as argument. In order to simplify its interface, a background queue is passed by default to the implementation.
extension NWPathMonitor { public func pathUpdatePublisher( on queue: DispatchQueue = .global(qos: .background) ) -> NWPathMonitor.PathMonitorPublisher { // ... } }
The interface provides a hint of the first type to be implemented,
NWPathMonitor.PathMonitorPublisher
. Most of the code is boilerplate, and for this publisher, theOutput
expected isNWPath
, without aFailure
.When the publisher receives a subscriber, the link between them is established.
extension NWPathMonitor { public struct PathMonitorPublisher: Publisher { // MARK: - Nested types public typealias Output = NWPath public typealias Failure = Never // MARK: - Properties private let monitor: NWPathMonitor private let queue: DispatchQueue // MARK: - Life cycle fileprivate init( monitor: NWPathMonitor, queue: DispatchQueue ) { self.monitor = monitor self.queue = queue } // MARK: - Public public func receive<S>( subscriber: S ) where S: Subscriber, S.Failure == Failure, S.Input == Output { let subscription = PathMonitorSubscription( subscriber: subscriber, monitor: monitor, queue: queue ) subscriber.receive( subscription: subscription ) } } }
The
Subscription
fulfills the demand from the subscriber. As soon as there’s demand, it starts monitoring changes using the monitor’s callback, passing changes to the subscriber. And sinceSubscription
conforms toCancellable
,cancel()
can be used to stop the monitor.extension NWPathMonitor { private final class PathMonitorSubscription< S: Subscriber >: Subscription where S.Input == NWPath { // MARK: - Nested types private let subscriber: S private let monitor: NWPathMonitor private let queue: DispatchQueue // MARK: - Life cycle init( subscriber: S, monitor: NWPathMonitor, queue: DispatchQueue ) { self.subscriber = subscriber self.monitor = monitor self.queue = queue } // MARK: - Public func request( _ demand: Subscribers.Demand ) { guard demand == .unlimited, monitor.pathUpdateHandler == nil else { return } monitor.pathUpdateHandler = { path in _ = self.subscriber.receive(path) } monitor.start( queue: queue ) } func cancel() { monitor.cancel() } } }
With all in place, the View Model becomes extremely simple and elegant. Below a View Model which publishes a boolean indicating the connectivity status to the View.
final class ViewModel: ObservableObject { // MARK: - Properties @Published var isConnected: Bool = false private var monitorCancellable: Cancellable? // MARK: - Life cycle init( pathMonitorPublisher: NWPathMonitor.PathMonitorPublisher ) { monitorCancellable = pathMonitorPublisher .receive(on: RunLoop.main) .map { $0.status == .satisfied } .assign(to: \.isConnected, on: self) } }
Overall, I’m happy with the result, and am looking forward to using Combine (and SwiftUI) in production.
Resources:
-
How to replace type methods in Swift to improve testability
I moved the content to my Digital Garden and can be found here.
-
Map, Filter, Sort, and Reduce in Objective-C
I strongly believe that the best way to learn something new is by experimenting and practicing it.
A few months ago I was studying Functional Programming in Swift. Swift is a modern, powerful, and safe language. Its syntax is so simple and elegante that it works as an invitation to dive into the Functional Programming world.
After practicing Functional Programming in Swift for a few weeks, I decided to try something a little bit different. I decided it was time to experiment with curried functions, map, filter, sort, and reduce in Objective-C.
Swift collections implement map, sort, filter, and reduce, so the first step on my experiment was to reimplement these in a
NSArray
category, trying to match the method signature that Swift implements.- Map: given a transform operation, it returns a new
NSArray
where all its elements are transformed according to the operation. The transform operation takes an object and returns another object (that might be of a different type of the original object). - Filter: given a condition operation, it returns a new
NSArray
where all its elements satisfy the condition. The condition operation takes an object and returns a boolean. - Sort: given a sort condition, it returns a new
NSArray
where all its elements are sorted according to the condition. The sort condition takes two objects and returns a boolean. - Reduce: given a combine operation, it returns a recombined object by recursively processing its constituent parts. The combine operation takes the initial state and an object and returns a recombined object which is of the same type of the initial state.
The
NSArray
category ended up with the following interface:typedef id(^Transform)(id); typedef BOOL(^Condition)(id); typedef BOOL(^SortCondition)(id, id); typedef id(^Combine)(id, id); [@interface](https://micro.blog/interface) NSArray (MFSR) - (NSArray *)map:(Transform)transform; - (NSArray *)filter:(Condition)condition; - (NSArray *)sort:(SortCondition)isOrderedBefore; - (id)reduce:(id)initial andCombine:(Combine)combine; @end
To test the interface above and its implementation, I started with a immutable array of movies — James Bond movies — loaded from a plist file.
NSArray<Movie *> *movies = [[NSBundle mainBundle] moviesFromPlist];
Where each movie contains a title, the actor’s name who played Bond, and some additional information about about the flick. The movie interface is defined as:
@interface Movie : NSObject @property (nonatomic, readonly) NSString *title; @property (nonatomic, readonly) NSString *actor; @property (nonatomic, readonly) NSInteger year; @property (nonatomic, readonly) CGFloat boxOffice; @property (nonatomic, readonly) CGFloat budget; @property (nonatomic, readonly) CGFloat tomatometer; @end
Sorting the array of movies requires a sort condition that takes two movies and returns a boolean that represents the relationship between them. So, for sorting all the British Secret Service agent movies by budget:
BOOL(^byBudget)(Movie *, Movie *) = ^BOOL(Movie *a, Movie *b) { return a.budget > b.budget; }; NSArray<Movie *> *moviesByBudget = [movies sort:byBudget];
Simple.
For filtering, the method requires a movie and returns true when the movie matches the condition and false otherwise. Below, a simple way to create an immutable array containing the Sean Connery movies.
BOOL(^isConnery)(Movie *) = ^BOOL(Movie *a) { return [a.actor isEqual:@"Sean Connery"]; }; NSArray<Movie *> *conneryMovies = [movies filter:isConnery];
But here is the tricky part. To filter movies played by other actors, more blocks like the one above would be required. The functional way to address this is using curried functions — popular technique in Swift (and in other Functional Programming languages).
Since filter expects a block that takes a movie and returns a boolean, another function is required, where its output is a function matching this signature.
The new function takes a string (actor’s name) and returns a function that takes a movie and returns a boolean.
typedef BOOL(^FuncMovieToBool)(Movie *); FuncMovieToBool(^isActor)(NSString *) = ^FuncMovieToBool(NSString *actor) { return ^BOOL(Movie *movie) { return [movie.actor isEqual:actor]; }; }; NSArray<Movie *> *actorMovies = [movies filter:isActor(@"Daniel Craig")];
Without any changes to the filter method signature or implementation, it’s possible to filter the array of movies to get a list of movies played by any actor on the big screen.
Finally, it’s possible to combine all these functions to achieve the desired result. Let’s say I want the movies were Pierce Brosnan played James Bond, sorted by ratings and reduced to a list.
NSArray<Movie *> *moviesByRatings = [[movies filter:isActor(@"Pierce Brosnan")]sort:byRatings]; NSString *description = [moviesByRatings reduce:@"Brosnan movies sorted by ratings:" andCombine:^NSString *(NSString *initial, Movie *movie) { return [NSString stringWithFormat:@"%@\n* %@ (Tomatometer: %@)", initial, movie.title, @(movie.tomatometer)]; }];
Voilà:
Brosnan movies sorted by ratings: * GoldenEye (Tomatometer: 82) * Die Another Day (Tomatometer: 57) * Tomorrow Never Dies (Tomatometer: 57) * The World Is Not Enough (Tomatometer: 51)
But putting all the fun and excitement aside, would I use these methods on an Objective-C project? Probably not —
NSArray
already implements methods for sorting and filtering arrays usingNSPredicate
.My point here was to experiment and practice map, filter, sort, reduce, and curried functions. Programming requires practice. And practicing these techniques and methods (and reimplementing them) in Objective-C improved the way I use them in Swift.
- Map: given a transform operation, it returns a new
-
Curried Function in Objective-C
Function Currying and uncurrying have been extensively discussed since Apple introduced Swift in 2014. I won’t extend myself on this topic because there are some great resources about it on the Internet.
But, shortly, what is currying? It’s a technique that transforms a function that takes several arguments into a sequence of functions, each with a single argument. The result is a chain of functions returning functions. It’s beautiful, it’s mathematics!
Below, a simple example in Swift:
func add(_ a: Int) -> (Int) -> (Int) -> Int { { b in { c in a + b + c } } } let addTwo = add(2) // Int -> Int -> Int let addFive = addTwo(3) // Int -> Int let result = addFive(4) // 9 print("result = \(result)") // result = 9
and the same example in Objective-C:
typedef NSInteger(^FuncInt2Int)(NSInteger); typedef FuncInt2Int(^FuncInt2Int2Int)(NSInteger); FuncInt2Int2Int(^add)(NSInteger) = ^FuncInt2Int2Int(NSInteger a) { return ^FuncInt2Int(NSInteger b) { return ^NSInteger(NSInteger c) { return a + b + c; }; }; }; FuncInt2Int2Int addTwo = add(2); // Int -> Int -> Int FuncInt2Int addFive = addTwo(3); // Int -> Int NSInteger result = addFive(4); // 9 NSLog(@"result = %ld", result); // result = 9
The Swift version is minimal and elegant. But the Objective-C one has its charm, doesn’t it?