With functions that contain a lot of closures, such as an init file using RxSwift for binding, when adding more closures, at a certain point SwiftFormat will just break. This occurs for us in an init function with 30+ closures.
Running SwiftFormat...
warning: trailingClosures rule timed out.
error: Failed to format any files.
Running SwiftFormat...
SwiftFormat completed in 0.05s.
0/3 files formatted.
Running SwiftFormat...
SwiftFormat completed in 0.09s.
0/1 files formatted.
Command PhaseScriptExecution emitted errors but did not return a nonzero exit code to indicate failure
@realityworks can you provide a sample file that demonstrates the problem?
Same here. I have a warning: braces rule timed out here. Please, check out my example.
Update: my version is 0.44.5
Details
enum ManagersAssembly {
// swiftlint:disable:next closure_body_length
static let container: DependencyContainer = {
let container = DependencyContainer()
// MARK: - Cocoa
container.register {
UserDefaults.standard
}
container.register { _ -> JSONDecoder in
let jsonDecoder = JSONDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
jsonDecoder.dateDecodingStrategy = .secondsSince1970
return jsonDecoder
}
container.register { JSONEncoder() }
// MARK: - Managers
container.register(.singleton) {
CartManager(cartService: $0, storageManager: $1) as CartManagerProtocol
}
container.register {
KeyboardManager(notificationCenter: .default) as KeyboardManagerProtocol
}
container.register {
RequestProvider(decoder: $0, requestInterceptor: $1) as RequestProviderProtocol
}
container.register {
UserDefaultsManager(defaults: $0, encoder: $1, decoder: $2) as StorageProtocol
}
container.register {
FirstLaunchManager(defaults: $0) as FirstLaunchManagerProtocol
}
container.register {
PrecacheManager(requestProvider: $0, storage: $1, firstLaunchManager: $2) as PrecacheManagerProtocol
}
container.register {
GeocoderManager() as GeocoderManagerProtocol
}
container.register(.singleton) {
StoriesManager(storiesService: $0, storiesListService: $1) as StoriesManagerProtocol
}
// MARK: - Services
container.register {
CartService(requestProvider: $0) as CartServiceProtocol
}
container.register {
BackendListService<ProductList>(requestProvider: $0) as ProductsServiceProtocol
}
container.register {
BackendListService<ShopList>(requestProvider: $0) as ShopsServiceProtocol
}
container.register {
BackendListService<Stories>(requestProvider: $0) as StoriesListServiceProtocol
}
container.register {
ContactService(requestProvider: $0) as ContactServiceProtocol
}
container.register {
BackendListService<CollectionMetadata>(requestProvider: $0) as CollectionsServiceProtocol
}
container.register {
MetadataService(requestProvider: $0) as MetadataServiceProtocol
}
container.register {
ProductService(requestProvider: $0) as ProductServiceProtocol
}
container.register {
ProfileService(requestProvider: $0) as ProfileServiceProtocol
}
container.register {
AuthManager(requestProvider: $0, tokenHolder: $1) as AuthManagerProtocol
}
container.register(.singleton) {
AuthTokenHolderManager() as AuthTokenHolder
}
container.register(.singleton) {
AuthRequestAdapter(tokenHolder: $0, decoder: $1) as RequestInterceptor
}
container.register {
MetadataGetService(requestProvider: $0, request: MetadataEndpoint.sizes) as SizesServiceProtocol
}
container.register {
StoriesService(requestProvider: $0) as StoriesServiceProtocol
}
container.register {
OrderManager(requestProvider: $0) as OrderManagerProtocol
}
container.register {
MetadataGetService(requestProvider: $0, request: MetadataEndpoint.banners) as BannersManagerProtocol
}
container.register {
MetadataGetService(requestProvider: $0, request: MetadataEndpoint.cities) as CitiesManagerProtocol
}
container.register {
MetadataGetService(requestProvider: $0, request: MetadataEndpoint.faq) as FaqServiceProtocol
}
container.register {
SavedStoreManager() as SavedStoreManagerProtocol
}
container.register {
ReviewsService(requestProvider: $0) as ReviewsServiceProtocol
}
container.register {
MetadataGetService(requestProvider: $0, request: MetadataEndpoint.about) as AboutCompanyServiceProtocol
}
container.register { WishlistService(requestProvider: $0) as WishlistServiceProtocol }
container.register { ContactsTransitionGenerator() as ContactsTransitionGeneratorProtocol }
container.register { SubscriptionService(requestProvider: $0) as SubscriptionServiceProtocol }
container.register {
BackendListService<Suggests>(requestProvider: $0) as SuggestServiceProtocol
}
container.register { FeedbackService(requestProvider: $0) as FeedbackServiceProtocol }
return container
}()
}
In my program code, some of the Quick-based test code had similar problems.
This problem is most likely to occur when using frameworks that require heavy use of closures.
I'm also running into this. I use Combine a lot in my app and have a few files that fail to format. I can add them here if need be.
@bryanforbes Can you provide the code so I need it?
I want to check if it is fixed by https://github.com/nicklockwood/SwiftFormat/pull/579.
Here's one of the files I'm having an issue with:
PreferencesStoreTests.swift
//
// PreferencesStoreTests.swift
// SilenceMeTests
//
// Created by Bryan Forbes on 2/25/20.
// Copyright 漏 2020 Bryan Forbes. All rights reserved.
//
import EntwineTest
import Foundation
@testable import SilenceMe
import XCTest
class PreferencesStoreTests: XCTestCase {
var storage: ConstantsPreferencesStorage!
override func setUp() {
super.setUp()
storage = ConstantsPreferencesStorage()
storage.shortcut = .init(key: nil, modifiers: [.control, .option])
storage.firstRun = false
}
override func tearDown() {
storage = nil
super.tearDown()
}
func testInitFirstRun() {
storage = ConstantsPreferencesStorage()
let store = PreferencesStore(storage: storage)
XCTAssertFalse(store.startAtLogin)
XCTAssertTrue(store.showInMenuBar)
XCTAssertTrue(store.pushToTalk)
XCTAssertFalse(store.doubleTapToggle)
XCTAssertEqual(store.shortcut, .init(key: nil, modifiers: [.control, .fn]))
XCTAssertFalse(storage.firstRun)
}
func testInit() {
storage.startAtLogin = true
storage.showInMenuBar = false
storage.pushToTalk = false
storage.doubleTapToggle = true
storage.shortcut = nil
let store = PreferencesStore(storage: storage)
XCTAssertTrue(store.startAtLogin)
XCTAssertFalse(store.showInMenuBar)
XCTAssertFalse(store.pushToTalk)
XCTAssertTrue(store.doubleTapToggle)
XCTAssertEqual(store.shortcut, nil)
XCTAssertFalse(storage.firstRun)
}
func testSetters() {
let store = PreferencesStore(storage: storage)
store.startAtLogin = true
store.showInMenuBar = false
store.pushToTalk = false
store.doubleTapToggle = true
store.shortcut = nil
XCTAssertTrue(storage.startAtLogin)
XCTAssertFalse(storage.showInMenuBar)
XCTAssertFalse(storage.pushToTalk)
XCTAssertTrue(storage.doubleTapToggle)
XCTAssertNil(storage.shortcut)
}
func testPublisher() {
let store = PreferencesStore(storage: storage)
let scheduler = TestScheduler(initialClock: 0)
let showInMenuBar = scheduler.createTestableSubscriber(Bool.self, Never.self)
let doubleTapToggle = scheduler.createTestableSubscriber(Bool.self, Never.self)
let pushToTalk = scheduler.createTestableSubscriber(Bool.self, Never.self)
let shortcut = scheduler.createTestableSubscriber(Shortcut?.self, Never.self)
let startAtLogin = scheduler.createTestableSubscriber(Bool.self, Never.self)
scheduler.schedule(after: 100) { store.publisher(for: \.showInMenuBar).subscribe(showInMenuBar) }
scheduler.schedule(after: 110) { store.publisher(for: \.doubleTapToggle).subscribe(doubleTapToggle) }
scheduler.schedule(after: 120) { store.publisher(for: \.pushToTalk).subscribe(pushToTalk) }
scheduler.schedule(after: 130) { store.publisher(for: \.shortcut).subscribe(shortcut) }
scheduler.schedule(after: 140) { store.publisher(for: \.startAtLogin).subscribe(startAtLogin) }
scheduler.schedule(after: 200) { store.showInMenuBar = false }
scheduler.schedule(after: 210) { store.showInMenuBar = false }
scheduler.schedule(after: 220) { store.showInMenuBar = true }
scheduler.schedule(after: 230) { store.doubleTapToggle = true }
scheduler.schedule(after: 240) { store.doubleTapToggle = true }
scheduler.schedule(after: 250) { store.doubleTapToggle = false }
scheduler.schedule(after: 260) { store.pushToTalk = false }
scheduler.schedule(after: 270) { store.pushToTalk = false }
scheduler.schedule(after: 280) { store.pushToTalk = true }
scheduler.schedule(after: 290) { store.shortcut = nil }
scheduler.schedule(after: 300) { store.shortcut = .init(key: 24, modifiers: [.capsLock, .shift]) }
scheduler.schedule(after: 310) { store.shortcut = .init(key: 24, modifiers: [.capsLock, .shift]) }
scheduler.schedule(after: 320) { store.shortcut = .init(key: nil, modifiers: [.fn, .shift, .command]) }
scheduler.schedule(after: 330) { store.startAtLogin = true }
scheduler.schedule(after: 340) { store.startAtLogin = true }
scheduler.schedule(after: 350) { store.startAtLogin = false }
scheduler.resume()
XCTAssertEqual([
(100, .subscription),
(200, .input(false)),
(220, .input(true)),
], showInMenuBar.recordedOutput)
XCTAssertEqual([
(110, .subscription),
(230, .input(true)),
(250, .input(false)),
], doubleTapToggle.recordedOutput)
XCTAssertEqual([
(120, .subscription),
(260, .input(false)),
(280, .input(true)),
], pushToTalk.recordedOutput)
XCTAssertEqual([
(130, .subscription),
(290, .input(nil)),
(300, .input(.init(key: 24, modifiers: [.capsLock, .shift]))),
(320, .input(.init(key: nil, modifiers: [.fn, .shift, .command]))),
], shortcut.recordedOutput)
XCTAssertEqual([
(140, .subscription),
(330, .input(true)),
(350, .input(false)),
], startAtLogin.recordedOutput)
}
func testPublisherCurrent() {
let store = PreferencesStore(storage: storage)
let scheduler = TestScheduler(initialClock: 0)
let showInMenuBar = scheduler.createTestableSubscriber(Bool.self, Never.self)
let doubleTapToggle = scheduler.createTestableSubscriber(Bool.self, Never.self)
let pushToTalk = scheduler.createTestableSubscriber(Bool.self, Never.self)
let shortcut = scheduler.createTestableSubscriber(Shortcut?.self, Never.self)
let startAtLogin = scheduler.createTestableSubscriber(Bool.self, Never.self)
scheduler.schedule(after: 100) { store.publisher(current: \.showInMenuBar).subscribe(showInMenuBar) }
scheduler.schedule(after: 110) { store.publisher(current: \.doubleTapToggle).subscribe(doubleTapToggle) }
scheduler.schedule(after: 120) { store.publisher(current: \.pushToTalk).subscribe(pushToTalk) }
scheduler.schedule(after: 130) { store.publisher(current: \.shortcut).subscribe(shortcut) }
scheduler.schedule(after: 140) { store.publisher(current: \.startAtLogin).subscribe(startAtLogin) }
scheduler.schedule(after: 200) { store.showInMenuBar = false }
scheduler.schedule(after: 210) { store.showInMenuBar = false }
scheduler.schedule(after: 220) { store.showInMenuBar = true }
scheduler.schedule(after: 230) { store.doubleTapToggle = true }
scheduler.schedule(after: 240) { store.doubleTapToggle = true }
scheduler.schedule(after: 250) { store.doubleTapToggle = false }
scheduler.schedule(after: 260) { store.pushToTalk = false }
scheduler.schedule(after: 270) { store.pushToTalk = false }
scheduler.schedule(after: 280) { store.pushToTalk = true }
scheduler.schedule(after: 290) { store.shortcut = nil }
scheduler.schedule(after: 300) { store.shortcut = .init(key: 24, modifiers: [.capsLock, .shift]) }
scheduler.schedule(after: 310) { store.shortcut = .init(key: 24, modifiers: [.capsLock, .shift]) }
scheduler.schedule(after: 320) { store.shortcut = .init(key: nil, modifiers: [.fn, .shift, .command]) }
scheduler.schedule(after: 330) { store.startAtLogin = true }
scheduler.schedule(after: 340) { store.startAtLogin = true }
scheduler.schedule(after: 350) { store.startAtLogin = false }
scheduler.resume()
XCTAssertEqual([
(100, .subscription),
(100, .input(true)),
(200, .input(false)),
(220, .input(true)),
], showInMenuBar.recordedOutput)
XCTAssertEqual([
(110, .subscription),
(110, .input(false)),
(230, .input(true)),
(250, .input(false)),
], doubleTapToggle.recordedOutput)
XCTAssertEqual([
(120, .subscription),
(120, .input(true)),
(260, .input(false)),
(280, .input(true)),
], pushToTalk.recordedOutput)
XCTAssertEqual([
(130, .subscription),
(130, .input(.init(key: nil, modifiers: [.control, .option]))),
(290, .input(nil)),
(300, .input(.init(key: 24, modifiers: [.capsLock, .shift]))),
(320, .input(.init(key: nil, modifiers: [.fn, .shift, .command]))),
], shortcut.recordedOutput)
XCTAssertEqual([
(140, .subscription),
(140, .input(false)),
(330, .input(true)),
(350, .input(false)),
], startAtLogin.recordedOutput)
}
}
If I take out lines 85-105 and 147-167, swiftformat has no issue with the file.
@bryanforbes I can confirm that @zizi4n5's fix works for your file. It's up on the develop branch now - I'll try to get a release out shortly.
Fixed in 0.44.6.
I just wanted to confirm that 0.44.6 fixes the crash for my source files. Thank you, @nicklockwood, for the quick turn around!
Most helpful comment
@bryanforbes I can confirm that @zizi4n5's fix works for your file. It's up on the develop branch now - I'll try to get a release out shortly.