Add SwiftFormat to the project. (#129)

* Run swiftformat for whitespace.
* Run swiftformat with some rules.
This commit is contained in:
Doug
2022-07-06 14:49:05 +01:00
committed by GitHub
parent 26d8affaa8
commit 02eb4b35e2
188 changed files with 759 additions and 717 deletions
+17
View File
@@ -0,0 +1,17 @@
--swiftversion 5.6
--exclude ElementX/Sources/Generated
--disable wrapMultiLineStatementBraces
--disable blankLinesAtStartOfScope
--disable blankLinesAtEndOfScope
--disable hoistPatternLet
--commas inline
--ifdef no-indent
--nospaceoperators ...,..<
--stripunusedargs closure-only
--trimwhitespace nonblank-lines
--wrapparameters after-first
--redundanttype inferred
--emptybraces spaced
+3 -3
View File
@@ -1,5 +1,5 @@
import Foundation
import Danger import Danger
import Foundation
SwiftLint.lint(inline: true) SwiftLint.lint(inline: true)
@@ -19,7 +19,7 @@ if danger.github.pullRequest.body?.isEmpty ?? true {
let editedFiles = danger.git.modifiedFiles + danger.git.createdFiles let editedFiles = danger.git.modifiedFiles + danger.git.createdFiles
let changelogFiles = editedFiles.filter { $0.hasPrefix("changelog.d/") } let changelogFiles = editedFiles.filter { $0.hasPrefix("changelog.d/") }
if editedFiles.count > 0 && changelogFiles.isEmpty { if editedFiles.count > 0, changelogFiles.isEmpty {
warn("Please add a changelog.") warn("Please add a changelog.")
} }
@@ -63,7 +63,7 @@ if requiresSignOff {
!$0.message.contains(signOff) !$0.message.contains(signOff)
}.isEmpty }.isEmpty
if !hasPRBodySignOff && isMissingCommitsSignOff { if !hasPRBodySignOff, isMissingCommitsSignOff {
fail("Please add a sign-off to either the PR description or to the commits themselves.") fail("Please add a sign-off to either the PR description or to the commits themselves.")
} }
} }
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import DesignTokens import DesignTokens
import SwiftUI
public extension ButtonStyle where Self == ElementActionButtonStyle { public extension ButtonStyle where Self == ElementActionButtonStyle {
/// The CTA button style as defined in Compound. /// The CTA button style as defined in Compound.
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import DesignTokens import DesignTokens
import SwiftUI
public extension ButtonStyle where Self == ElementGhostButtonStyle { public extension ButtonStyle where Self == ElementGhostButtonStyle {
/// The Ghost button style as defined in Compound. /// The Ghost button style as defined in Compound.
@@ -35,110 +35,111 @@ public class ElementSharedFonts {
/// Returns an instance of the font associated with the text style and scaled appropriately for the content size category defined in the trait collection. /// Returns an instance of the font associated with the text style and scaled appropriately for the content size category defined in the trait collection.
/// Keep this method private method at the moment and create a DesignKit.Fonts.TextStyle if needed. /// Keep this method private method at the moment and create a DesignKit.Fonts.TextStyle if needed.
fileprivate func font(forTextStyle textStyle: UIFont.TextStyle, compatibleWith traitCollection: UITraitCollection? = nil) -> UIFont { private func font(forTextStyle textStyle: UIFont.TextStyle, compatibleWith traitCollection: UITraitCollection? = nil) -> UIFont {
return UIFont.preferredFont(forTextStyle: textStyle, compatibleWith: traitCollection) UIFont.preferredFont(forTextStyle: textStyle, compatibleWith: traitCollection)
} }
} }
// MARK: - Fonts protocol // MARK: - Fonts protocol
extension ElementSharedFonts {
public var largeTitle: SharedFont { public extension ElementSharedFonts {
var largeTitle: SharedFont {
let uiFont = font(forTextStyle: .largeTitle) let uiFont = font(forTextStyle: .largeTitle)
return SharedFont(uiFont: uiFont, font: .largeTitle) return SharedFont(uiFont: uiFont, font: .largeTitle)
} }
public var largeTitleB: SharedFont { var largeTitleB: SharedFont {
let uiFont = largeTitle.uiFont.bold let uiFont = largeTitle.uiFont.bold
return SharedFont(uiFont: uiFont, font: .largeTitle.bold()) return SharedFont(uiFont: uiFont, font: .largeTitle.bold())
} }
public var title1: SharedFont { var title1: SharedFont {
let uiFont = font(forTextStyle: .title1) let uiFont = font(forTextStyle: .title1)
return SharedFont(uiFont: uiFont, font: .title) return SharedFont(uiFont: uiFont, font: .title)
} }
public var title1B: SharedFont { var title1B: SharedFont {
let uiFont = title1.uiFont.bold let uiFont = title1.uiFont.bold
return SharedFont(uiFont: uiFont, font: .title.bold()) return SharedFont(uiFont: uiFont, font: .title.bold())
} }
public var title2: SharedFont { var title2: SharedFont {
let uiFont = font(forTextStyle: .title2) let uiFont = font(forTextStyle: .title2)
return SharedFont(uiFont: uiFont, font: .title2) return SharedFont(uiFont: uiFont, font: .title2)
} }
public var title2B: SharedFont { var title2B: SharedFont {
let uiFont = title2.uiFont.bold let uiFont = title2.uiFont.bold
return SharedFont(uiFont: uiFont, font: .title2.bold()) return SharedFont(uiFont: uiFont, font: .title2.bold())
} }
public var title3: SharedFont { var title3: SharedFont {
let uiFont = font(forTextStyle: .title3) let uiFont = font(forTextStyle: .title3)
return SharedFont(uiFont: uiFont, font: .title3) return SharedFont(uiFont: uiFont, font: .title3)
} }
public var title3SB: SharedFont { var title3SB: SharedFont {
let uiFont = title3.uiFont.semiBold let uiFont = title3.uiFont.semiBold
return SharedFont(uiFont: uiFont, font: .title3.weight(.semibold)) return SharedFont(uiFont: uiFont, font: .title3.weight(.semibold))
} }
public var headline: SharedFont { var headline: SharedFont {
let uiFont = font(forTextStyle: .headline) let uiFont = font(forTextStyle: .headline)
return SharedFont(uiFont: uiFont, font: .headline) return SharedFont(uiFont: uiFont, font: .headline)
} }
public var subheadline: SharedFont { var subheadline: SharedFont {
let uiFont = font(forTextStyle: .subheadline) let uiFont = font(forTextStyle: .subheadline)
return SharedFont(uiFont: uiFont, font: .subheadline) return SharedFont(uiFont: uiFont, font: .subheadline)
} }
public var body: SharedFont { var body: SharedFont {
let uiFont = font(forTextStyle: .body) let uiFont = font(forTextStyle: .body)
return SharedFont(uiFont: uiFont, font: .body) return SharedFont(uiFont: uiFont, font: .body)
} }
public var bodySB: SharedFont { var bodySB: SharedFont {
let uiFont = body.uiFont.semiBold let uiFont = body.uiFont.semiBold
return SharedFont(uiFont: uiFont, font: .body.weight(.semibold)) return SharedFont(uiFont: uiFont, font: .body.weight(.semibold))
} }
public var callout: SharedFont { var callout: SharedFont {
let uiFont = font(forTextStyle: .callout) let uiFont = font(forTextStyle: .callout)
return SharedFont(uiFont: uiFont, font: .callout) return SharedFont(uiFont: uiFont, font: .callout)
} }
public var calloutSB: SharedFont { var calloutSB: SharedFont {
let uiFont = callout.uiFont.semiBold let uiFont = callout.uiFont.semiBold
return SharedFont(uiFont: uiFont, font: .callout.weight(.semibold)) return SharedFont(uiFont: uiFont, font: .callout.weight(.semibold))
} }
public var footnote: SharedFont { var footnote: SharedFont {
let uiFont = font(forTextStyle: .footnote) let uiFont = font(forTextStyle: .footnote)
return SharedFont(uiFont: uiFont, font: .footnote) return SharedFont(uiFont: uiFont, font: .footnote)
} }
public var footnoteSB: SharedFont { var footnoteSB: SharedFont {
let uiFont = footnote.uiFont.semiBold let uiFont = footnote.uiFont.semiBold
return SharedFont(uiFont: uiFont, font: .footnote.weight(.semibold)) return SharedFont(uiFont: uiFont, font: .footnote.weight(.semibold))
} }
public var caption1: SharedFont { var caption1: SharedFont {
let uiFont = font(forTextStyle: .caption1) let uiFont = font(forTextStyle: .caption1)
return SharedFont(uiFont: uiFont, font: .caption) return SharedFont(uiFont: uiFont, font: .caption)
} }
public var caption1SB: SharedFont { var caption1SB: SharedFont {
let uiFont = caption1.uiFont.semiBold let uiFont = caption1.uiFont.semiBold
return SharedFont(uiFont: uiFont, font: .caption.weight(.semibold)) return SharedFont(uiFont: uiFont, font: .caption.weight(.semibold))
} }
public var caption2: SharedFont { var caption2: SharedFont {
let uiFont = font(forTextStyle: .caption2) let uiFont = font(forTextStyle: .caption2)
return SharedFont(uiFont: uiFont, font: .caption2) return SharedFont(uiFont: uiFont, font: .caption2)
} }
public var caption2SB: SharedFont { var caption2SB: SharedFont {
let uiFont = caption2.uiFont.semiBold let uiFont = caption2.uiFont.semiBold
return SharedFont(uiFont: uiFont, font: .caption2.weight(.semibold)) return SharedFont(uiFont: uiFont, font: .caption2.weight(.semibold))
} }
+3 -3
View File
@@ -44,14 +44,14 @@ public extension UIFont {
// MARK: - Shortcuts // MARK: - Shortcuts
var bold: UIFont { var bold: UIFont {
return withTraits(.traitBold) withTraits(.traitBold)
} }
var semiBold: UIFont { var semiBold: UIFont {
return withWeight(weight: .semibold) withWeight(weight: .semibold)
} }
var italic: UIFont { var italic: UIFont {
return withTraits(.traitItalic) withTraits(.traitItalic)
} }
} }
@@ -14,9 +14,9 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import DesignTokens import DesignTokens
import Introspect import Introspect
import SwiftUI
/// A bordered style of text input /// A bordered style of text input
/// ///
@@ -48,7 +48,7 @@ public struct BorderedInputFieldStyle: TextFieldStyle {
} }
private var backgroundColor: Color { private var backgroundColor: Color {
if !isEnabled && colorScheme == .dark { if !isEnabled, colorScheme == .dark {
return .element.quinaryContent return .element.quinaryContent
} }
return .element.background return .element.background
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import DesignTokens import DesignTokens
import SwiftUI
@available(iOS 15.0, *) @available(iOS 15.0, *)
public extension TextFieldStyle where Self == ElementTextFieldStyle { public extension TextFieldStyle where Self == ElementTextFieldStyle {
+1 -1
View File
@@ -1,5 +1,5 @@
import XCTest
@testable import DesignKit @testable import DesignKit
import XCTest
final class DesignKitTests: XCTestCase { final class DesignKitTests: XCTestCase {
func testExample() throws { func testExample() throws {
+23 -4
View File
@@ -130,6 +130,7 @@
62BBF5BE7B905222F0477FF2 /* MediaSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8210612D17A39369480FC183 /* MediaSource.swift */; }; 62BBF5BE7B905222F0477FF2 /* MediaSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8210612D17A39369480FC183 /* MediaSource.swift */; };
63C9AF0FB8278AF1C0388A0C /* TemplateModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAB10E673916D2B8D21FD197 /* TemplateModels.swift */; }; 63C9AF0FB8278AF1C0388A0C /* TemplateModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAB10E673916D2B8D21FD197 /* TemplateModels.swift */; };
6647430A45B4A8E692909A8F /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77C060C2ACC4CB7336A29E7 /* EmoteRoomTimelineItem.swift */; }; 6647430A45B4A8E692909A8F /* EmoteRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77C060C2ACC4CB7336A29E7 /* EmoteRoomTimelineItem.swift */; };
67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */; };
67E391A2E00709FB41903B36 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6920A4869821BF72FFC58842 /* MockMediaProvider.swift */; }; 67E391A2E00709FB41903B36 /* MockMediaProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6920A4869821BF72FFC58842 /* MockMediaProvider.swift */; };
6832733838C57A7D3FE8FEB5 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 04C28663564E008DB32B5972 /* Introspect */; }; 6832733838C57A7D3FE8FEB5 /* Introspect in Frameworks */ = {isa = PBXBuildFile; productRef = 04C28663564E008DB32B5972 /* Introspect */; };
684BDE198AE5AA1392288A73 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CE6D4FF64C9A3C18619224 /* SplashScreen.swift */; }; 684BDE198AE5AA1392288A73 /* SplashScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CE6D4FF64C9A3C18619224 /* SplashScreen.swift */; };
@@ -279,7 +280,6 @@
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; }; EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; }; EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; };
EF99A92701E401C4CD5ADC50 /* SplashScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE978A6118C131D7F2A04B3 /* SplashScreenModels.swift */; }; EF99A92701E401C4CD5ADC50 /* SplashScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCE978A6118C131D7F2A04B3 /* SplashScreenModels.swift */; };
F03E16ED043C62FED5A07AE0 /* MatrixEntityRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B81C8227BBEA95CCE86037 /* MatrixEntityRegex.swift */; };
F040ABFEB0A2B142D948BA12 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; }; F040ABFEB0A2B142D948BA12 /* Untranslated.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = F75DF9500D69A3AAF8339E69 /* Untranslated.stringsdict */; };
F2DD8661B5C0BA2BB526FA6C /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD51F9FDC91C231906D76C8 /* KeychainControllerProtocol.swift */; }; F2DD8661B5C0BA2BB526FA6C /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CD51F9FDC91C231906D76C8 /* KeychainControllerProtocol.swift */; };
F4C3FEDB1B3A05376A1723A3 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4427F9E0571B4E6E048A2B /* KeychainController.swift */; }; F4C3FEDB1B3A05376A1723A3 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4427F9E0571B4E6E048A2B /* KeychainController.swift */; };
@@ -479,6 +479,7 @@
6920A4869821BF72FFC58842 /* MockMediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaProvider.swift; sourceTree = "<group>"; }; 6920A4869821BF72FFC58842 /* MockMediaProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMediaProvider.swift; sourceTree = "<group>"; };
6A152791A2F56BD193BFE986 /* MemberDetailsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDetailsProvider.swift; sourceTree = "<group>"; }; 6A152791A2F56BD193BFE986 /* MemberDetailsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemberDetailsProvider.swift; sourceTree = "<group>"; };
6A901D95158B02CA96C79C7F /* InfoPlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPlist.swift; sourceTree = "<group>"; }; 6A901D95158B02CA96C79C7F /* InfoPlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoPlist.swift; sourceTree = "<group>"; };
6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegex.swift; sourceTree = "<group>"; };
6B73A8C3118EAC7BF3F3EE7A /* SplashScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 6B73A8C3118EAC7BF3F3EE7A /* SplashScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashScreenViewModelProtocol.swift; sourceTree = "<group>"; };
6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationUITests.swift; sourceTree = "<group>"; }; 6D4777F0142E330A75C46FE4 /* SessionVerificationUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationUITests.swift; sourceTree = "<group>"; };
6DB53055CB130F0651C70763 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; }; 6DB53055CB130F0651C70763 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -691,7 +692,6 @@
F6A8C632CEF4600107792899 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = "<group>"; }; F6A8C632CEF4600107792899 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = "<group>"; };
F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyText.swift; sourceTree = "<group>"; }; F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattedBodyText.swift; sourceTree = "<group>"; };
F77C060C2ACC4CB7336A29E7 /* EmoteRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItem.swift; sourceTree = "<group>"; }; F77C060C2ACC4CB7336A29E7 /* EmoteRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmoteRoomTimelineItem.swift; sourceTree = "<group>"; };
F7B81C8227BBEA95CCE86037 /* MatrixEntityRegex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixEntityRegex.swift; sourceTree = "<group>"; };
F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineView.swift; sourceTree = "<group>"; }; F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineView.swift; sourceTree = "<group>"; };
FA154570F693D93513E584C1 /* RoomMessageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageFactory.swift; sourceTree = "<group>"; }; FA154570F693D93513E584C1 /* RoomMessageFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMessageFactory.swift; sourceTree = "<group>"; };
FAB10E673916D2B8D21FD197 /* TemplateModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModels.swift; sourceTree = "<group>"; }; FAB10E673916D2B8D21FD197 /* TemplateModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateModels.swift; sourceTree = "<group>"; };
@@ -1443,7 +1443,7 @@
CF4B39D52CAE7D21D276ABEE /* ElementNavigationController.swift */, CF4B39D52CAE7D21D276ABEE /* ElementNavigationController.swift */,
1027BB9A852F445B7623897F /* ElementSettings.swift */, 1027BB9A852F445B7623897F /* ElementSettings.swift */,
12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */, 12A626D74BBE9F4A60763B45 /* ImageAnonymizer.swift */,
F7B81C8227BBEA95CCE86037 /* MatrixEntityRegex.swift */, 6AD1A853D605C2146B0DC028 /* MatrixEntityRegex.swift */,
44BBB96FAA2F0D53C507396B /* Extensions */, 44BBB96FAA2F0D53C507396B /* Extensions */,
8F9A844EB44B6AD7CA18FD96 /* HTMLParsing */, 8F9A844EB44B6AD7CA18FD96 /* HTMLParsing */,
06501F0E978B2D5C92771DC7 /* Logging */, 06501F0E978B2D5C92771DC7 /* Logging */,
@@ -1659,6 +1659,7 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = B15427F8699AD5A5FC75C17E /* Build configuration list for PBXNativeTarget "ElementX" */; buildConfigurationList = B15427F8699AD5A5FC75C17E /* Build configuration list for PBXNativeTarget "ElementX" */;
buildPhases = ( buildPhases = (
B35AB66424BB30087EEE408C /* 🧹 SwiftFormat */,
A7130911BCB2DF3D249A1836 /* 🛠 SwiftGen */, A7130911BCB2DF3D249A1836 /* 🛠 SwiftGen */,
9797D588420FCBBC228A63C9 /* Sources */, 9797D588420FCBBC228A63C9 /* Sources */,
215E1D91B98672C856F559D0 /* Resources */, 215E1D91B98672C856F559D0 /* Resources */,
@@ -1886,6 +1887,24 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftgen >/dev/null; then\n swiftgen config run --config Tools/SwiftGen/swiftgen-config.yml\nelse\n echo \"warning: SwiftGen not installed, download from https://github.com/SwiftGen/SwiftGen\"\nfi\n"; shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftgen >/dev/null; then\n swiftgen config run --config Tools/SwiftGen/swiftgen-config.yml\nelse\n echo \"warning: SwiftGen not installed, download from https://github.com/SwiftGen/SwiftGen\"\nfi\n";
}; };
B35AB66424BB30087EEE408C /* 🧹 SwiftFormat */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "🧹 SwiftFormat";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "export PATH=\"$PATH:/opt/homebrew/bin\"\nif which swiftformat >/dev/null; then\n swiftformat $PROJECT_DIR\nelse\n echo \"warning: SwiftFormat not installed, download from https://github.com/nicklockwood/SwiftFormat\"\nfi\n";
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@@ -1997,7 +2016,7 @@
2F30EFEB7BD39242D1AD96F3 /* LoginViewModelProtocol.swift in Sources */, 2F30EFEB7BD39242D1AD96F3 /* LoginViewModelProtocol.swift in Sources */,
B94368839BDB69172E28E245 /* MXLog.swift in Sources */, B94368839BDB69172E28E245 /* MXLog.swift in Sources */,
BCC3EDB7AD0902797CB4BBC2 /* MXLogger.m in Sources */, BCC3EDB7AD0902797CB4BBC2 /* MXLogger.m in Sources */,
F03E16ED043C62FED5A07AE0 /* MatrixEntityRegex.swift in Sources */, 67C05C50AD734283374605E3 /* MatrixEntityRegex.swift in Sources */,
EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */, EA1E7949533E19C6D862680A /* MediaProvider.swift in Sources */,
7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */, 7002C55A4C917F3715765127 /* MediaProviderProtocol.swift in Sources */,
62BBF5BE7B905222F0477FF2 /* MediaSource.swift in Sources */, 62BBF5BE7B905222F0477FF2 /* MediaSource.swift in Sources */,
+2 -1
View File
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import Combine import Combine
import UIKit
class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator { class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
private let window: UIWindow private let window: UIWindow
@@ -149,6 +149,7 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
fatalError("Failed transition with context: \(context)") fatalError("Failed transition with context: \(context)")
} }
} }
// swiftlint:enable cyclomatic_complexity function_body_length // swiftlint:enable cyclomatic_complexity function_body_length
private func restoreUserSession() { private func restoreUserSession() {
-2
View File
@@ -14,8 +14,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
private lazy var appCoordinator: Coordinator = isRunningUITests ? UITestsAppCoordinator() : AppCoordinator() private lazy var appCoordinator: Coordinator = isRunningUITests ? UITestsAppCoordinator() : AppCoordinator()
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
// fixme: Use `Bundle.elementLanguage = ".."` when we have the functionality
// use `en` as fallback language // use `en` as fallback language
Bundle.elementFallbackLanguage = "en" Bundle.elementFallbackLanguage = "en"
+7 -3
View File
@@ -11,22 +11,26 @@ import Foundation
final class BuildSettings { final class BuildSettings {
// MARK: - Servers // MARK: - Servers
static let defaultHomeserverURLString = "https://matrix.org" static let defaultHomeserverURLString = "https://matrix.org"
// MARK: - Bug report // MARK: - Bug report
static let bugReportServiceBaseUrlString = "https://riot.im/bugreports" static let bugReportServiceBaseUrlString = "https://riot.im/bugreports"
static let bugReportSentryEndpoint = "https://f39ac49e97714316965b777d9f3d6cd8@sentry.tools.element.io/44" static let bugReportSentryEndpoint = "https://f39ac49e97714316965b777d9f3d6cd8@sentry.tools.element.io/44"
// Use the name allocated by the bug report server // Use the name allocated by the bug report server
static let bugReportApplicationId = "riot-ios" static let bugReportApplicationId = "riot-ios"
static let bugReportUISIId = "element-auto-uisi" static let bugReportUISIId = "element-auto-uisi"
static let bugReportGHLabels: [String] = ["Element-X"] static let bugReportGHLabels = ["Element-X"]
// MARK: - Settings screen // MARK: - Settings screen
static let settingsCrashButtonVisible: Bool = true
static let settingsShowTimelineStyle: Bool = true static let settingsCrashButtonVisible = true
static let settingsShowTimelineStyle = true
// MARK: - Room screen // MARK: - Room screen
static let defaultRoomTimelineStyle: TimelineStyle = .bubbles static let defaultRoomTimelineStyle: TimelineStyle = .bubbles
} }
+2 -2
View File
@@ -9,9 +9,9 @@
import Foundation import Foundation
struct Benchmark { struct Benchmark {
static private var trackingIdentifiers = [String: CFAbsoluteTime]() private static var trackingIdentifiers = [String: CFAbsoluteTime]()
static public var trackingEnabled = false public static var trackingEnabled = false
static func startTrackingForIdentifier(_ identifier: String, message: String? = nil) { static func startTrackingForIdentifier(_ identifier: String, message: String? = nil) {
guard trackingEnabled else { guard trackingEnabled else {
+6 -8
View File
@@ -2,8 +2,8 @@ import UIKit
// swiftlint:disable all // swiftlint:disable all
extension UIImage { public extension UIImage {
public convenience init?(blurHash: String, size: CGSize, punch: Float = 1) { convenience init?(blurHash: String, size: CGSize, punch: Float = 1) {
guard blurHash.count >= 6 else { return nil } guard blurHash.count >= 6 else { return nil }
let sizeFlag = String(blurHash[0]).decode83() let sizeFlag = String(blurHash[0]).decode83()
@@ -90,7 +90,7 @@ private func decodeAC(_ value: Int, maximumValue: Float) -> (Float, Float, Float
} }
private func signPow(_ value: Float, _ exp: Float) -> Float { private func signPow(_ value: Float, _ exp: Float) -> Float {
return copysign(pow(abs(value), exp), value) copysign(pow(abs(value), exp), value)
} }
private func linearTosRGB(_ value: Float) -> Int { private func linearTosRGB(_ value: Float) -> Int {
@@ -105,9 +105,7 @@ private func sRGBToLinear<Type: BinaryInteger>(_ value: Type) -> Float {
else { return pow((v + 0.055) / 1.055, 2.4) } else { return pow((v + 0.055) / 1.055, 2.4) }
} }
private let encodeCharacters: [String] = { private let encodeCharacters: [String] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~".map { String($0) }
return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~".map { String($0) }
}()
private let decodeCharacters: [String: Int] = { private let decodeCharacters: [String: Int] = {
var dict: [String: Int] = [:] var dict: [String: Int] = [:]
@@ -119,7 +117,7 @@ private let decodeCharacters: [String: Int] = {
extension String { extension String {
func decode83() -> Int { func decode83() -> Int {
var value: Int = 0 var value = 0
for character in self { for character in self {
if let digit = decodeCharacters[String(character)] { if let digit = decodeCharacters[String(character)] {
value = value * 83 + digit value = value * 83 + digit
@@ -131,7 +129,7 @@ extension String {
private extension String { private extension String {
subscript(offset: Int) -> Character { subscript(offset: Int) -> Character {
return self[index(startIndex, offsetBy: offset)] self[index(startIndex, offsetBy: offset)]
} }
subscript(bounds: CountableClosedRange<Int>) -> Substring { subscript(bounds: CountableClosedRange<Int>) -> Substring {
+1 -1
View File
@@ -47,6 +47,6 @@ extension Coordinator {
} }
func remove(childCoordinator: Coordinator) { func remove(childCoordinator: Coordinator) {
self.childCoordinators = self.childCoordinators.filter { $0 !== childCoordinator } childCoordinators = childCoordinators.filter { $0 !== childCoordinator }
} }
} }
@@ -36,7 +36,7 @@ public extension Bundle {
} }
/// Preferred languages in the priority order. /// Preferred languages in the priority order.
static private(set) var preferredLanguages: [String] = calculatePreferredLanguages() private(set) static var preferredLanguages: [String] = calculatePreferredLanguages()
private static func calculatePreferredLanguages() -> [String] { private static func calculatePreferredLanguages() -> [String] {
var set = Set<String>() var set = Set<String>()
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import DTCoreText import DTCoreText
import Foundation
struct AttributedStringBuilder: AttributedStringBuilderProtocol { struct AttributedStringBuilder: AttributedStringBuilderProtocol {
@@ -65,7 +65,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
DTDefaultFontFamily: defaultFont.familyName, DTDefaultFontFamily: defaultFont.familyName,
DTDefaultFontName: defaultFont.fontName, DTDefaultFontName: defaultFont.fontName,
DTDefaultFontSize: defaultFont.pointSize, DTDefaultFontSize: defaultFont.pointSize,
DTDefaultStyleSheet: DTCSSStylesheet(styleBlock: self.defaultCSS) as Any DTDefaultStyleSheet: DTCSSStylesheet(styleBlock: defaultCSS) as Any
] ]
guard let builder = DTHTMLAttributedStringBuilder(html: data, options: parsingOptions, documentAttributes: nil) else { guard let builder = DTHTMLAttributedStringBuilder(html: data, options: parsingOptions, documentAttributes: nil) else {
@@ -96,7 +96,7 @@ struct AttributedStringBuilder: AttributedStringBuilderProtocol {
return nil return nil
} }
return attributedString.runs[\.blockquote].map { (value, range) in return attributedString.runs[\.blockquote].map { value, range in
var attributedString = AttributedString(attributedString[range]) var attributedString = AttributedString(attributedString[range])
// Remove trailing new lines if any // Remove trailing new lines if any
@@ -240,7 +240,7 @@ extension UIColor {
getRed(&red, green: &green, blue: &blue, alpha: &alpha) getRed(&red, green: &green, blue: &blue, alpha: &alpha)
let rgb: Int = (Int)(red*255)<<16 | (Int)(green*255)<<8 | (Int)(blue*255)<<0 let rgb = Int(red * 255) << 16 | Int(green * 255) << 8 | Int(blue * 255) << 0
return NSString(format: "#%06x", rgb) as String return NSString(format: "#%06x", rgb) as String
} }
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import DTCoreText import DTCoreText
import Foundation
public extension DTHTMLElement { public extension DTHTMLElement {
/// Sanitize the element using the given parameters. /// Sanitize the element using the given parameters.
@@ -26,6 +26,6 @@ extension AttributeScopes {
extension AttributeDynamicLookup { extension AttributeDynamicLookup {
subscript<T: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeScopes.ElementXAttributes, T>) -> T { subscript<T: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeScopes.ElementXAttributes, T>) -> T {
return self[T.self] self[T.self]
} }
} }
+2 -2
View File
@@ -7,14 +7,14 @@
// //
import Foundation import Foundation
import Vision
import UIKit import UIKit
import Vision
enum ImageAnonymizerError: Error { enum ImageAnonymizerError: Error {
case noCgImageBased case noCgImageBased
} }
struct ImageAnonymizer { enum ImageAnonymizer {
private static var allowedTextItems: [String] = [ private static var allowedTextItems: [String] = [
"#", "#",
+7 -7
View File
@@ -21,10 +21,10 @@ import SwiftyBeaver
@objc public class MXLogConfiguration: NSObject { @objc public class MXLogConfiguration: NSObject {
/// the desired log level. `.verbose` by default. /// the desired log level. `.verbose` by default.
@objc public var logLevel: MXLogLevel = MXLogLevel.verbose @objc public var logLevel = MXLogLevel.verbose
/// whether logs should be written directly to files. `false` by default. /// whether logs should be written directly to files. `false` by default.
@objc public var redirectLogsToFiles: Bool = false @objc public var redirectLogsToFiles = false
/// the maximum total space to use for log files in bytes. `100MB` by default. /// the maximum total space to use for log files in bytes. `100MB` by default.
@objc public var logFilesSizeLimit: UInt = 100 * 1024 * 1024 // 100MB @objc public var logFilesSizeLimit: UInt = 100 * 1024 * 1024 // 100MB
@@ -64,7 +64,7 @@ private var logger: SwiftyBeaver.Type = {
/// Please see `MXLogConfiguration` for all available options. /// Please see `MXLogConfiguration` for all available options.
/// - Parameters: /// - Parameters:
/// - configuration: the `MXLogConfiguration` instance to use /// - configuration: the `MXLogConfiguration` instance to use
@objc static public func configure(_ configuration: MXLogConfiguration) { @objc public static func configure(_ configuration: MXLogConfiguration) {
configureLogger(logger, withConfiguration: configuration) configureLogger(logger, withConfiguration: configuration)
} }
@@ -107,8 +107,8 @@ private var logger: SwiftyBeaver.Type = {
logger.info(message, file, function, line: line) logger.info(message, file, function, line: line)
} }
public static func warning(_ message: @autoclosure () -> Any, _ public static func warning(_ message: @autoclosure () -> Any,
file: String = #file, _ file: String = #file,
_ function: String = #function, _ function: String = #function,
line: Int = #line, line: Int = #line,
context: Any? = nil) { context: Any? = nil) {
@@ -133,8 +133,8 @@ private var logger: SwiftyBeaver.Type = {
logger.error(message, file, function, line: line) logger.error(message, file, function, line: line)
} }
public static func failure(_ message: @autoclosure () -> Any, _ public static func failure(_ message: @autoclosure () -> Any,
file: String = #file, _ file: String = #file,
_ function: String = #function, _ function: String = #function,
line: Int = #line, line: Int = #line,
context: Any? = nil) { context: Any? = nil) {
@@ -30,7 +30,7 @@ struct NavigationModule {
extension NavigationModule: CustomStringConvertible { extension NavigationModule: CustomStringConvertible {
var description: String { var description: String {
return "NavigationModule: \(presentable), pop completion: \(String(describing: popCompletion))" "NavigationModule: \(presentable), pop completion: \(String(describing: popCompletion))"
} }
} }
@@ -34,21 +34,21 @@ final class NavigationRouter: NSObject, NavigationRouterType {
/// Returns the presentables associated to each view controller /// Returns the presentables associated to each view controller
var modules: [Presentable] { var modules: [Presentable] {
return self.viewControllers.map { (viewController) -> Presentable in viewControllers.map { viewController -> Presentable in
return self.module(for: viewController) self.module(for: viewController)
} }
} }
/// Return the view controllers stack /// Return the view controllers stack
var viewControllers: [UIViewController] { var viewControllers: [UIViewController] {
return navigationController.viewControllers navigationController.viewControllers
} }
// MARK: - Setup // MARK: - Setup
init(navigationController: UINavigationController) { init(navigationController: UINavigationController) {
self.navigationController = navigationController self.navigationController = navigationController
self.completions = [:] completions = [:]
super.init() super.init()
self.navigationController.delegate = self self.navigationController.delegate = self
@@ -88,9 +88,9 @@ final class NavigationRouter: NSObject, NavigationRouterType {
return return
} }
self.addModule(module, for: controller) addModule(module, for: controller)
let controllersToPop = self.navigationController.viewControllers.reversed() let controllersToPop = navigationController.viewControllers.reversed()
controllersToPop.forEach { controllersToPop.forEach {
self.willPopViewController($0) self.willPopViewController($0)
@@ -100,7 +100,7 @@ final class NavigationRouter: NSObject, NavigationRouterType {
completions[controller] = popCompletion completions[controller] = popCompletion
} }
self.willPushViewController(controller) willPushViewController(controller)
navigationController.setViewControllers([controller], animated: animated) navigationController.setViewControllers([controller], animated: animated)
navigationController.isNavigationBarHidden = hideNavigationBar navigationController.isNavigationBarHidden = hideNavigationBar
@@ -111,22 +111,22 @@ final class NavigationRouter: NSObject, NavigationRouterType {
} }
// Add again controller to module association, in case same module instance is added back // Add again controller to module association, in case same module instance is added back
self.addModule(module, for: controller) addModule(module, for: controller)
self.didPushViewController(controller) didPushViewController(controller)
} }
func setModules(_ modules: [NavigationModule], hideNavigationBar: Bool, animated: Bool) { func setModules(_ modules: [NavigationModule], hideNavigationBar: Bool, animated: Bool) {
MXLog.debug("[NavigationRouter] Set modules \(modules)") MXLog.debug("[NavigationRouter] Set modules \(modules)")
let controllers = modules.map { (module) -> UIViewController in let controllers = modules.map { module -> UIViewController in
let controller = module.presentable.toPresentable() let controller = module.presentable.toPresentable()
self.addModule(module.presentable, for: controller) self.addModule(module.presentable, for: controller)
return controller return controller
} }
let controllersToPop = self.navigationController.viewControllers.reversed() let controllersToPop = navigationController.viewControllers.reversed()
controllersToPop.forEach { controllersToPop.forEach {
self.willPopViewController($0) self.willPopViewController($0)
@@ -146,7 +146,7 @@ final class NavigationRouter: NSObject, NavigationRouterType {
} }
// Add again controller to module association, in case same modules instance are added back // Add again controller to module association, in case same modules instance are added back
modules.forEach { (module) in modules.forEach { module in
self.addModule(module.presentable, for: module.presentable.toPresentable()) self.addModule(module.presentable, for: module.presentable.toPresentable())
} }
@@ -158,7 +158,7 @@ final class NavigationRouter: NSObject, NavigationRouterType {
func popToRootModule(animated: Bool) { func popToRootModule(animated: Bool) {
MXLog.debug("[NavigationRouter] Pop to root module") MXLog.debug("[NavigationRouter] Pop to root module")
let controllers = self.navigationController.viewControllers let controllers = navigationController.viewControllers
if controllers.count > 1 { if controllers.count > 1 {
let controllersToPop = controllers[1..<controllers.count] let controllersToPop = controllers[1..<controllers.count]
@@ -179,7 +179,7 @@ final class NavigationRouter: NSObject, NavigationRouterType {
MXLog.debug("[NavigationRouter] Pop to module \(module)") MXLog.debug("[NavigationRouter] Pop to module \(module)")
let controller = module.toPresentable() let controller = module.toPresentable()
let controllersBeforePop = self.navigationController.viewControllers let controllersBeforePop = navigationController.viewControllers
if let controllerIndex = controllersBeforePop.firstIndex(of: controller) { if let controllerIndex = controllersBeforePop.firstIndex(of: controller) {
let controllersToPop = controllersBeforePop[controllerIndex..<controllersBeforePop.count] let controllersToPop = controllersBeforePop[controllerIndex..<controllersBeforePop.count]
@@ -207,17 +207,17 @@ final class NavigationRouter: NSObject, NavigationRouterType {
return return
} }
self.addModule(module, for: controller) addModule(module, for: controller)
if let completion = popCompletion { if let completion = popCompletion {
completions[controller] = completion completions[controller] = completion
} }
self.willPushViewController(controller) willPushViewController(controller)
navigationController.pushViewController(controller, animated: animated) navigationController.pushViewController(controller, animated: animated)
self.didPushViewController(controller) didPushViewController(controller)
} }
func push(_ modules: [NavigationModule], animated: Bool) { func push(_ modules: [NavigationModule], animated: Bool) {
@@ -231,22 +231,22 @@ final class NavigationRouter: NSObject, NavigationRouterType {
for module in modules { for module in modules {
let controller = module.presentable.toPresentable() let controller = module.presentable.toPresentable()
self.addModule(module.presentable, for: controller) addModule(module.presentable, for: controller)
if let completion = module.popCompletion { if let completion = module.popCompletion {
completions[controller] = completion completions[controller] = completion
} }
self.willPushViewController(controller) willPushViewController(controller)
} }
var viewControllers = navigationController.viewControllers var viewControllers = navigationController.viewControllers
viewControllers.append(contentsOf: modules.map({ $0.presentable.toPresentable() })) viewControllers.append(contentsOf: modules.map { $0.presentable.toPresentable() })
navigationController.setViewControllers(viewControllers, animated: animated) navigationController.setViewControllers(viewControllers, animated: animated)
for module in modules { for module in modules {
let controller = module.presentable.toPresentable() let controller = module.presentable.toPresentable()
self.didPushViewController(controller) didPushViewController(controller)
} }
} }
@@ -254,18 +254,18 @@ final class NavigationRouter: NSObject, NavigationRouterType {
MXLog.debug("[NavigationRouter] Pop module") MXLog.debug("[NavigationRouter] Pop module")
if let lastController = navigationController.viewControllers.last { if let lastController = navigationController.viewControllers.last {
self.willPopViewController(lastController) willPopViewController(lastController)
} }
if let controller = navigationController.popViewController(animated: animated) { if let controller = navigationController.popViewController(animated: animated) {
self.didPopViewController(controller) didPopViewController(controller)
} }
} }
func popAllModules(animated: Bool) { func popAllModules(animated: Bool) {
MXLog.debug("[NavigationRouter] Pop all modules") MXLog.debug("[NavigationRouter] Pop all modules")
let controllersToPop = self.navigationController.viewControllers.reversed() let controllersToPop = navigationController.viewControllers.reversed()
controllersToPop.forEach { controllersToPop.forEach {
self.willPopViewController($0) self.willPopViewController($0)
@@ -281,31 +281,31 @@ final class NavigationRouter: NSObject, NavigationRouterType {
func contains(_ module: Presentable) -> Bool { func contains(_ module: Presentable) -> Bool {
let controller = module.toPresentable() let controller = module.toPresentable()
return self.navigationController.viewControllers.contains(controller) return navigationController.viewControllers.contains(controller)
} }
// MARK: Presentable // MARK: Presentable
func toPresentable() -> UIViewController { func toPresentable() -> UIViewController {
return navigationController navigationController
} }
// MARK: - Private // MARK: - Private
private func module(for viewController: UIViewController) -> Presentable { private func module(for viewController: UIViewController) -> Presentable {
guard let module = self.storedModules[viewController] as? Presentable else { guard let module = storedModules[viewController] as? Presentable else {
return viewController return viewController
} }
return module return module
} }
private func addModule(_ module: Presentable, for viewController: UIViewController) { private func addModule(_ module: Presentable, for viewController: UIViewController) {
self.storedModules[viewController] = module as AnyObject storedModules[viewController] = module as AnyObject
} }
private func removeModule(for viewController: UIViewController) { private func removeModule(for viewController: UIViewController) {
self.storedModules[viewController] = nil storedModules[viewController] = nil
} }
private func runCompletion(for controller: UIViewController) { private func runCompletion(for controller: UIViewController) {
@@ -317,30 +317,30 @@ final class NavigationRouter: NSObject, NavigationRouterType {
} }
private func willPushViewController(_ viewController: UIViewController) { private func willPushViewController(_ viewController: UIViewController) {
self.postNotification(withName: NavigationRouter.willPushModule, for: viewController) postNotification(withName: NavigationRouter.willPushModule, for: viewController)
} }
private func didPushViewController(_ viewController: UIViewController) { private func didPushViewController(_ viewController: UIViewController) {
self.postNotification(withName: NavigationRouter.didPushModule, for: viewController) postNotification(withName: NavigationRouter.didPushModule, for: viewController)
} }
private func willPopViewController(_ viewController: UIViewController) { private func willPopViewController(_ viewController: UIViewController) {
self.postNotification(withName: NavigationRouter.willPopModule, for: viewController) postNotification(withName: NavigationRouter.willPopModule, for: viewController)
} }
private func didPopViewController(_ viewController: UIViewController) { private func didPopViewController(_ viewController: UIViewController) {
self.postNotification(withName: NavigationRouter.didPopModule, for: viewController) postNotification(withName: NavigationRouter.didPopModule, for: viewController)
// Call completion closure associated to the view controller // Call completion closure associated to the view controller
// So associated coordinator can be deallocated // So associated coordinator can be deallocated
runCompletion(for: viewController) runCompletion(for: viewController)
self.removeModule(for: viewController) removeModule(for: viewController)
} }
private func postNotification(withName name: Notification.Name, for viewController: UIViewController) { private func postNotification(withName name: Notification.Name, for viewController: UIViewController) {
let module = self.module(for: viewController) let module = module(for: viewController)
let userInfo: [String: Any] = [ let userInfo: [String: Any] = [
NotificationUserInfoKey.navigationRouter: self, NotificationUserInfoKey.navigationRouter: self,
@@ -352,6 +352,7 @@ final class NavigationRouter: NSObject, NavigationRouterType {
} }
// MARK: - UINavigationControllerDelegate // MARK: - UINavigationControllerDelegate
extension NavigationRouter: UINavigationControllerDelegate { extension NavigationRouter: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
@@ -369,11 +370,12 @@ extension NavigationRouter: UINavigationControllerDelegate {
MXLog.debug("[NavigationRouter] Popped module: \(poppedViewController)") MXLog.debug("[NavigationRouter] Popped module: \(poppedViewController)")
self.didPopViewController(poppedViewController) didPopViewController(poppedViewController)
} }
} }
// MARK: - NavigationRouter notification constants // MARK: - NavigationRouter notification constants
extension NavigationRouter { extension NavigationRouter {
// MARK: Notification names // MARK: Notification names
@@ -388,7 +390,7 @@ extension NavigationRouter {
// MARK: Notification keys // MARK: Notification keys
public struct NotificationUserInfoKey { public enum NotificationUserInfoKey {
/// The associated view controller (UIViewController). /// The associated view controller (UIViewController).
static let viewController = "viewController" static let viewController = "viewController"
@@ -33,14 +33,14 @@ class NavigationRouterStore: NavigationRouterStoreProtocol {
/// As we are ensuring that there is only one navigation controller per NavigationRouter, the class here should be used as a singleton. /// As we are ensuring that there is only one navigation controller per NavigationRouter, the class here should be used as a singleton.
private init() { private init() {
self.registerNavigationRouterNotifications() registerNavigationRouterNotifications()
} }
// MARK: - Public // MARK: - Public
func navigationRouter(for navigationController: UINavigationController) -> NavigationRouterType { func navigationRouter(for navigationController: UINavigationController) -> NavigationRouterType {
if let existingNavigationRouter = self.findNavigationRouter(for: navigationController) { if let existingNavigationRouter = findNavigationRouter(for: navigationController) {
return existingNavigationRouter return existingNavigationRouter
} }
@@ -51,11 +51,11 @@ class NavigationRouterStore: NavigationRouterStoreProtocol {
// MARK: - Private // MARK: - Private
private func findNavigationRouter(for navigationController: UINavigationController) -> NavigationRouterType? { private func findNavigationRouter(for navigationController: UINavigationController) -> NavigationRouterType? {
return self.navigationRouters[navigationController] navigationRouters[navigationController]
} }
private func removeNavigationRouter(for navigationController: UINavigationController) { private func removeNavigationRouter(for navigationController: UINavigationController) {
self.navigationRouters[navigationController] = nil navigationRouters[navigationController] = nil
} }
private func registerNavigationRouterNotifications() { private func registerNavigationRouterNotifications() {
@@ -71,12 +71,12 @@ class NavigationRouterStore: NavigationRouterStoreProtocol {
return return
} }
if let existingNavigationRouter = self.findNavigationRouter(for: navigationController) { if let existingNavigationRouter = findNavigationRouter(for: navigationController) {
fatalError("\(existingNavigationRouter) is already tied to the same navigation controller as \(navigationRouter). We should have only one NavigationRouter per navigation controller") fatalError("\(existingNavigationRouter) is already tied to the same navigation controller as \(navigationRouter). We should have only one NavigationRouter per navigation controller")
} else { } else {
// WeakDictionary does not work with protocol // WeakDictionary does not work with protocol
// Find a way to avoid this cast // Find a way to avoid this cast
self.navigationRouters[navigationController] = navigationRouter as? NavigationRouter navigationRouters[navigationController] = navigationRouter as? NavigationRouter
} }
} }
@@ -87,10 +87,10 @@ class NavigationRouterStore: NavigationRouterStoreProtocol {
return return
} }
if let existingNavigationRouter = self.findNavigationRouter(for: navigationController), existingNavigationRouter !== navigationRouter { if let existingNavigationRouter = findNavigationRouter(for: navigationController), existingNavigationRouter !== navigationRouter {
fatalError("\(existingNavigationRouter) is already tied to the same navigation controller as \(navigationRouter). We should have only one NavigationRouter per navigation controller") fatalError("\(existingNavigationRouter) is already tied to the same navigation controller as \(navigationRouter). We should have only one NavigationRouter per navigation controller")
} }
self.removeNavigationRouter(for: navigationController) removeNavigationRouter(for: navigationController)
} }
} }
@@ -24,7 +24,7 @@ protocol Presentable {
extension UIViewController: Presentable { extension UIViewController: Presentable {
public func toPresentable() -> UIViewController { public func toPresentable() -> UIViewController {
return self self
} }
} }
@@ -33,7 +33,7 @@ extension Presentable {
/// Returns a new module from the presentable without a pop completion block /// Returns a new module from the presentable without a pop completion block
/// - Returns: Module /// - Returns: Module
func toModule() -> NavigationModule { func toModule() -> NavigationModule {
return NavigationModule(presentable: self, popCompletion: nil) NavigationModule(presentable: self, popCompletion: nil)
} }
} }
@@ -35,7 +35,7 @@ final class RootRouter: RootRouterType {
/// The root view controller currently presented /// The root view controller currently presented
var rootViewController: UIViewController? { var rootViewController: UIViewController? {
return self.window.rootViewController window.rootViewController
} }
// MARK: - Setup // MARK: - Setup
@@ -47,23 +47,23 @@ final class RootRouter: RootRouterType {
// MARK: - Public methods // MARK: - Public methods
func setRootModule(_ module: Presentable) { func setRootModule(_ module: Presentable) {
self.updateRootViewController(rootViewController: module.toPresentable(), animated: false, completion: nil) updateRootViewController(rootViewController: module.toPresentable(), animated: false, completion: nil)
self.window.makeKeyAndVisible() window.makeKeyAndVisible()
} }
func dismissRootModule(animated: Bool, completion: (() -> Void)?) { func dismissRootModule(animated: Bool, completion: (() -> Void)?) {
self.updateRootViewController(rootViewController: nil, animated: animated, completion: completion) updateRootViewController(rootViewController: nil, animated: animated, completion: completion)
} }
func presentModule(_ module: Presentable, animated: Bool, completion: (() -> Void)?) { func presentModule(_ module: Presentable, animated: Bool, completion: (() -> Void)?) {
let viewControllerPresenter = self.rootViewController?.presentedViewController ?? self.rootViewController let viewControllerPresenter = rootViewController?.presentedViewController ?? rootViewController
viewControllerPresenter?.present(module.toPresentable(), animated: animated, completion: completion) viewControllerPresenter?.present(module.toPresentable(), animated: animated, completion: completion)
self.presentedModule = module presentedModule = module
} }
func dismissModule(animated: Bool, completion: (() -> Void)?) { func dismissModule(animated: Bool, completion: (() -> Void)?) {
self.presentedModule?.toPresentable().dismiss(animated: animated, completion: completion) presentedModule?.toPresentable().dismiss(animated: animated, completion: completion)
} }
// MARK: - Private methods // MARK: - Private methods
@@ -76,11 +76,11 @@ final class RootRouter: RootRouterType {
UIView.setAnimationsEnabled(false) UIView.setAnimationsEnabled(false)
self.window.rootViewController = rootViewController self.window.rootViewController = rootViewController
UIView.setAnimationsEnabled(oldState) UIView.setAnimationsEnabled(oldState)
}, completion: { (_: Bool) -> Void in }, completion: { _ in
completion?() completion?()
}) })
} else { } else {
self.window.rootViewController = rootViewController window.rootViewController = rootViewController
completion?() completion?()
} }
} }
@@ -16,11 +16,9 @@
import SwiftUI import SwiftUI
// swiftlint:disable private_over_fileprivate
/// Positions this view within an invisible frame that fills the width of its parent view, /// Positions this view within an invisible frame that fills the width of its parent view,
/// whilst limiting the width of the content to a readable size (which is customizable). /// whilst limiting the width of the content to a readable size (which is customizable).
fileprivate struct ReadableFrameModifier: ViewModifier { private struct ReadableFrameModifier: ViewModifier {
var maxWidth: CGFloat var maxWidth: CGFloat
func body(content: Content) -> some View { func body(content: Content) -> some View {
@@ -27,9 +27,7 @@ extension BindableState where BindStateType == Void {
/// We provide a default implementation for the Void type so that we can have `ViewState` that /// We provide a default implementation for the Void type so that we can have `ViewState` that
/// just doesn't include/take advantage of the bindings. /// just doesn't include/take advantage of the bindings.
var bindings: Void { var bindings: Void {
get { get { }
}
set { set {
fatalError("Can't bind to the default Void binding.") fatalError("Can't bind to the default Void binding.")
} }
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import Foundation
import Combine import Combine
import Foundation
/// A constrained and concise interface for interacting with the ViewModel. /// A constrained and concise interface for interacting with the ViewModel.
/// ///
@@ -96,8 +96,8 @@ class StateStoreViewModel<State: BindableState, ViewAction> {
// MARK: Setup // MARK: Setup
init(initialViewState: State) { init(initialViewState: State) {
self.context = Context(initialViewState: initialViewState) context = Context(initialViewState: initialViewState)
self.context.viewActions.sink { [weak self] action in context.viewActions.sink { [weak self] action in
guard let self = self else { return } guard let self = self else { return }
Task { await self.process(viewAction: action) } Task { await self.process(viewAction: action) }
@@ -52,7 +52,7 @@ final class ActivityIndicatorPresenter: ActivityIndicatorPresenterType {
view.isUserInteractionEnabled = false view.isUserInteractionEnabled = false
let backgroundOverlayView = self.createBackgroundOverlayView(with: view.frame) let backgroundOverlayView = createBackgroundOverlayView(with: view.frame)
let activityIndicatorView = ActivityIndicatorView() let activityIndicatorView = ActivityIndicatorView()
@@ -89,9 +89,9 @@ final class ActivityIndicatorPresenter: ActivityIndicatorPresenterType {
} }
func removeCurrentActivityIndicator(animated: Bool, completion: (() -> Void)? = nil) { func removeCurrentActivityIndicator(animated: Bool, completion: (() -> Void)? = nil) {
guard let presentingView = self.presentingView, guard let presentingView = presentingView,
let backgroundOverlayView = self.backgroundOverlayView, let backgroundOverlayView = backgroundOverlayView,
let activityIndicatorView = self.activityIndicatorView else { let activityIndicatorView = activityIndicatorView else {
return return
} }
@@ -29,14 +29,14 @@ final class ActivityIndicatorView: UIView {
// MARK: Outlets // MARK: Outlets
@IBOutlet private weak var activityIndicatorView: UIActivityIndicatorView! @IBOutlet private var activityIndicatorView: UIActivityIndicatorView!
@IBOutlet private weak var activityIndicatorBackgroundView: UIView! @IBOutlet private var activityIndicatorBackgroundView: UIView!
// MARK: Public // MARK: Public
var color: UIColor? { var color: UIColor? {
get { get {
return activityIndicatorView.color activityIndicatorView.color
} }
set { set {
activityIndicatorView.color = newValue activityIndicatorView.color = newValue
@@ -68,7 +68,7 @@ final class ActivityIndicatorView: UIView {
// MARK: - Overrides // MARK: - Overrides
override var intrinsicContentSize: CGSize { override var intrinsicContentSize: CGSize {
return CGSize(width: activityIndicatorView.intrinsicContentSize.width + Constants.activityIndicatorMargin.width, CGSize(width: activityIndicatorView.intrinsicContentSize.width + Constants.activityIndicatorMargin.width,
height: activityIndicatorView.intrinsicContentSize.height + Constants.activityIndicatorMargin.height) height: activityIndicatorView.intrinsicContentSize.height + Constants.activityIndicatorMargin.height)
} }
@@ -91,7 +91,7 @@ final class ActivityIndicatorView: UIView {
private extension UIView { private extension UIView {
static var nib: UINib { static var nib: UINib {
return UINib(nibName: String(describing: self), bundle: Bundle(for: self)) UINib(nibName: String(describing: self), bundle: Bundle(for: self))
} }
func loadNibContent() { func loadNibContent() {
@@ -49,15 +49,14 @@ final class LabelledActivityIndicatorView: UIView {
return view return view
}() }()
private let label: UILabel = { private let label = UILabel()
UILabel()
}()
init(text: String) { init(text: String) {
super.init(frame: .zero) super.init(frame: .zero)
setup(text: text) setup(text: text)
} }
@available(*, unavailable)
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -20,7 +20,7 @@ import UIKit
class RectangleToastView: UIView { class RectangleToastView: UIView {
private enum Constants { private enum Constants {
static let padding: UIEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) static let padding = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
static let cornerRadius: CGFloat = 8.0 static let cornerRadius: CGFloat = 8.0
} }
@@ -81,6 +81,7 @@ class RectangleToastView: UIView {
layer.masksToBounds = true layer.masksToBounds = true
} }
@available(*, unavailable)
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -23,7 +23,7 @@ class RoundedToastView: UIView {
let opacity: Float let opacity: Float
} }
private struct Constants { private enum Constants {
static let padding = UIEdgeInsets(top: 10, left: 12, bottom: 10, right: 12) static let padding = UIEdgeInsets(top: 10, left: 12, bottom: 10, right: 12)
static let activityIndicatorScale = CGFloat(0.75) static let activityIndicatorScale = CGFloat(0.75)
static let imageViewSize = CGFloat(15) static let imageViewSize = CGFloat(15)
@@ -57,15 +57,14 @@ class RoundedToastView: UIView {
return stack return stack
}() }()
private let label: UILabel = { private let label = UILabel()
UILabel()
}()
init(viewState: ToastViewState) { init(viewState: ToastViewState) {
super.init(frame: .zero) super.init(frame: .zero)
setup(viewState: viewState) setup(viewState: viewState)
} }
@available(*, unavailable)
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
@@ -19,7 +19,7 @@ import UIKit
/// A presenter responsible for showing / hiding a toast view for loading spinners or success messages. /// A presenter responsible for showing / hiding a toast view for loading spinners or success messages.
/// It is managed by an `UserIndicator`, meaning the `present` and `dismiss` methods will be called when the parent `UserIndicator` starts or completes. /// It is managed by an `UserIndicator`, meaning the `present` and `dismiss` methods will be called when the parent `UserIndicator` starts or completes.
class ToastViewPresenter: UserIndicatorViewPresentable { class ToastViewPresenter: UserIndicatorViewPresentable {
struct Constants { enum Constants {
static let navigationBarPatting = CGFloat(10) static let navigationBarPatting = CGFloat(10)
} }
@@ -18,7 +18,7 @@ public struct WeakDictionary<Key: Hashable, Value: AnyObject> {
public init(dictionary: [Key: Value]) { public init(dictionary: [Key: Value]) {
var newStorage = [Key: WeakDictionaryReference<Value>]() var newStorage = [Key: WeakDictionaryReference<Value>]()
dictionary.forEach({ key, value in newStorage[key] = WeakDictionaryReference<Value>(value: value) }) dictionary.forEach { key, value in newStorage[key] = WeakDictionaryReference<Value>(value: value) }
self.init(storage: newStorage) self.init(storage: newStorage)
} }
@@ -31,7 +31,7 @@ public struct WeakDictionary<Key: Hashable, Value: AnyObject> {
} }
public func weakDictionary() -> WeakDictionary<Key, Value> { public func weakDictionary() -> WeakDictionary<Key, Value> {
return self[startIndex ..< endIndex] self[startIndex..<endIndex]
} }
public func dictionary() -> [Key: Value] { public func dictionary() -> [Key: Value] {
@@ -52,15 +52,15 @@ extension WeakDictionary: Collection {
public typealias Index = DictionaryIndex<Key, WeakDictionaryReference<Value>> public typealias Index = DictionaryIndex<Key, WeakDictionaryReference<Value>>
public var startIndex: Index { public var startIndex: Index {
return storage.startIndex storage.startIndex
} }
public var endIndex: Index { public var endIndex: Index {
return storage.endIndex storage.endIndex
} }
public func index(after index: Index) -> Index { public func index(after index: Index) -> Index {
return storage.index(after: index) storage.index(after: index)
} }
public subscript(position: Index) -> (Key, WeakDictionaryReference<Value>) { public subscript(position: Index) -> (Key, WeakDictionaryReference<Value>) {
@@ -90,15 +90,15 @@ extension WeakDictionary: Collection {
let subStorage = storage[bounds.lowerBound..<bounds.upperBound] let subStorage = storage[bounds.lowerBound..<bounds.upperBound]
var newStorage = [Key: WeakDictionaryReference<Value>]() var newStorage = [Key: WeakDictionaryReference<Value>]()
subStorage.filter { _, value in return value.value != nil } subStorage.filter { _, value in value.value != nil }
.forEach { key, value in newStorage[key] = value } .forEach { key, value in newStorage[key] = value }
return WeakDictionary<Key, Value>(storage: newStorage) return WeakDictionary<Key, Value>(storage: newStorage)
} }
} }
extension Dictionary where Value: AnyObject { public extension Dictionary where Value: AnyObject {
public func weakDictionary() -> WeakDictionary<Key, Value> { func weakDictionary() -> WeakDictionary<Key, Value> {
return WeakDictionary<Key, Value>(dictionary: self) WeakDictionary<Key, Value>(dictionary: self)
} }
} }
@@ -22,7 +22,7 @@ public struct WeakDictionaryKey<Key: AnyObject & Hashable, Value: AnyObject> : H
} }
public static func == (lhs: WeakDictionaryKey, rhs: WeakDictionaryKey) -> Bool { public static func == (lhs: WeakDictionaryKey, rhs: WeakDictionaryKey) -> Bool {
return (lhs.baseKey != nil && rhs.baseKey != nil && lhs.baseKey == rhs.baseKey) (lhs.baseKey != nil && rhs.baseKey != nil && lhs.baseKey == rhs.baseKey)
|| lhs.hashValue == rhs.hashValue || lhs.hashValue == rhs.hashValue
} }
@@ -35,6 +35,6 @@ public struct WeakDictionaryKey<Key: AnyObject & Hashable, Value: AnyObject> : H
} }
public var key: Key? { public var key: Key? {
return baseKey baseKey
} }
} }
@@ -16,6 +16,6 @@ public struct WeakDictionaryReference<Value: AnyObject> {
} }
public var value: Value? { public var value: Value? {
return referencedValue referencedValue
} }
} }
@@ -48,11 +48,11 @@ public struct WeakKeyDictionary<Key: AnyObject & Hashable, Value: AnyObject> {
} }
public func weakDictionary() -> WeakDictionary<Key, Value> { public func weakDictionary() -> WeakDictionary<Key, Value> {
return dictionary().weakDictionary() dictionary().weakDictionary()
} }
public func weakKeyDictionary() -> WeakKeyDictionary<Key, Value> { public func weakKeyDictionary() -> WeakKeyDictionary<Key, Value> {
return self[startIndex ..< endIndex] self[startIndex..<endIndex]
} }
public func dictionary() -> [Key: Value] { public func dictionary() -> [Key: Value] {
@@ -73,15 +73,15 @@ extension WeakKeyDictionary: Collection {
public typealias Index = DictionaryIndex<WeakDictionaryKey<Key, Value>, WeakDictionaryReference<Value>> public typealias Index = DictionaryIndex<WeakDictionaryKey<Key, Value>, WeakDictionaryReference<Value>>
public var startIndex: Index { public var startIndex: Index {
return storage.startIndex storage.startIndex
} }
public var endIndex: Index { public var endIndex: Index {
return storage.endIndex storage.endIndex
} }
public func index(after index: Index) -> Index { public func index(after index: Index) -> Index {
return storage.index(after: index) storage.index(after: index)
} }
public subscript(position: Index) -> (WeakDictionaryKey<Key, Value>, WeakDictionaryReference<Value>) { public subscript(position: Index) -> (WeakDictionaryKey<Key, Value>, WeakDictionaryReference<Value>) {
@@ -90,7 +90,7 @@ extension WeakKeyDictionary: Collection {
public subscript(key: Key) -> Value? { public subscript(key: Key) -> Value? {
get { get {
return storage[WeakDictionaryKey<Key, Value>(key: key)] storage[WeakDictionaryKey<Key, Value>(key: key)]
} }
set { set {
@@ -104,21 +104,21 @@ extension WeakKeyDictionary: Collection {
let subStorage = storage[bounds.lowerBound..<bounds.upperBound] let subStorage = storage[bounds.lowerBound..<bounds.upperBound]
var newStorage = WeakDictionary<WeakDictionaryKey<Key, Value>, Value>() var newStorage = WeakDictionary<WeakDictionaryKey<Key, Value>, Value>()
subStorage.filter { key, value in return key.key != nil && value.value != nil } subStorage.filter { key, value in key.key != nil && value.value != nil }
.forEach { key, value in newStorage[key] = value.value } .forEach { key, value in newStorage[key] = value.value }
return WeakKeyDictionary<Key, Value>(storage: newStorage) return WeakKeyDictionary<Key, Value>(storage: newStorage)
} }
} }
extension WeakDictionary where Key: AnyObject { public extension WeakDictionary where Key: AnyObject {
public func weakKeyDictionary(valuesRetainedByKey: Bool = false) -> WeakKeyDictionary<Key, Value> { func weakKeyDictionary(valuesRetainedByKey: Bool = false) -> WeakKeyDictionary<Key, Value> {
return WeakKeyDictionary<Key, Value>(dictionary: dictionary(), valuesRetainedByKey: valuesRetainedByKey) WeakKeyDictionary<Key, Value>(dictionary: dictionary(), valuesRetainedByKey: valuesRetainedByKey)
} }
} }
extension Dictionary where Key: AnyObject, Value: AnyObject { public extension Dictionary where Key: AnyObject, Value: AnyObject {
public func weakKeyDictionary(valuesRetainedByKey: Bool = false) -> WeakKeyDictionary<Key, Value> { func weakKeyDictionary(valuesRetainedByKey: Bool = false) -> WeakKeyDictionary<Key, Value> {
return WeakKeyDictionary<Key, Value>(dictionary: self, valuesRetainedByKey: valuesRetainedByKey) WeakKeyDictionary<Key, Value>(dictionary: self, valuesRetainedByKey: valuesRetainedByKey)
} }
} }
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import MatrixRustSDK import MatrixRustSDK
import UIKit
@MainActor @MainActor
protocol AuthenticationCoordinatorDelegate: AnyObject { protocol AuthenticationCoordinatorDelegate: AnyObject {
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import MatrixRustSDK import MatrixRustSDK
import SwiftUI
struct LoginCoordinatorParameters { struct LoginCoordinatorParameters {
/// The service used to authenticate the user. /// The service used to authenticate the user.
@@ -73,6 +73,7 @@ final class LoginCoordinator: Coordinator, Presentable {
} }
// MARK: - Public // MARK: - Public
func start() { func start() {
MXLog.debug("[LoginCoordinator] did start.") MXLog.debug("[LoginCoordinator] did start.")
@@ -53,7 +53,7 @@ struct LoginViewState: BindableState {
/// Data about the selected homeserver. /// Data about the selected homeserver.
var homeserver: LoginHomeserver var homeserver: LoginHomeserver
/// Whether a new homeserver is currently being loaded. /// Whether a new homeserver is currently being loaded.
var isLoading: Bool = false var isLoading = false
/// View state that can be bound to from SwiftUI. /// View state that can be bound to from SwiftUI.
var bindings: LoginBindings var bindings: LoginBindings
@@ -50,7 +50,7 @@ class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol {
files: files) files: files)
MXLog.info("[BugReportViewModel] submitBugReport succeeded, result: \(result.reportUrl)") MXLog.info("[BugReportViewModel] submitBugReport succeeded, result: \(result.reportUrl)")
callback?(.submitFinished) callback?(.submitFinished)
} catch let error { } catch {
MXLog.error("[BugReportViewModel] submitBugReport failed: \(error)") MXLog.error("[BugReportViewModel] submitBugReport failed: \(error)")
callback?(.submitFailed(error: error)) callback?(.submitFailed(error: error))
} }
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import Combine import Combine
import SwiftUI
struct HomeScreenCoordinatorParameters { struct HomeScreenCoordinatorParameters {
let userSession: UserSessionProtocol let userSession: UserSessionProtocol
@@ -108,12 +108,11 @@ final class HomeScreenCoordinator: Coordinator, Presentable {
} }
// MARK: - Public // MARK: - Public
func start() {
} func start() { }
func toPresentable() -> UIViewController { func toPresentable() -> UIViewController {
return self.hostingController hostingController
} }
// MARK: - Private // MARK: - Private
@@ -34,14 +34,14 @@ struct HomeScreenViewState: BindableState {
var userDisplayName: String? var userDisplayName: String?
var userAvatar: UIImage? var userAvatar: UIImage?
var showSessionVerificationBanner: Bool = false var showSessionVerificationBanner = false
var rooms: [HomeScreenRoom] = [] var rooms: [HomeScreenRoom] = []
var isLoadingRooms: Bool = false var isLoadingRooms = false
var visibleDMs: [HomeScreenRoom] { var visibleDMs: [HomeScreenRoom] {
searchFilteredRooms.filter { $0.isDirect } searchFilteredRooms.filter(\.isDirect)
} }
var visibleRooms: [HomeScreenRoom] { var visibleRooms: [HomeScreenRoom] {
@@ -62,7 +62,7 @@ struct HomeScreenViewState: BindableState {
} }
struct HomeScreenViewStateBindings { struct HomeScreenViewStateBindings {
var searchQuery: String = "" var searchQuery = ""
} }
struct HomeScreenRoom: Identifiable, Equatable { struct HomeScreenRoom: Identifiable, Equatable {
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import Combine import Combine
import SwiftUI
typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState, HomeScreenViewAction> typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState, HomeScreenViewAction>
@@ -65,7 +65,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
roomUpdateListeners.removeAll() roomUpdateListeners.removeAll()
roomSummaries.forEach({ roomSummary in roomSummaries.forEach { roomSummary in
roomSummary.callbacks roomSummary.callbacks
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink { [weak self] callback in .sink { [weak self] callback in
@@ -81,7 +81,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
} }
} }
.store(in: &roomUpdateListeners) .store(in: &roomUpdateListeners)
}) }
} }
@@ -55,11 +55,10 @@ final class RoomScreenCoordinator: Coordinator, Presentable {
} }
// MARK: - Public // MARK: - Public
func start() {
} func start() { }
func toPresentable() -> UIViewController { func toPresentable() -> UIViewController {
return self.roomScreenHostingController roomScreenHostingController
} }
} }
@@ -17,9 +17,7 @@
import Foundation import Foundation
import UIKit import UIKit
enum RoomScreenViewModelAction { enum RoomScreenViewModelAction { }
}
enum TimelineItemContextMenuAction: Hashable { enum TimelineItemContextMenuAction: Hashable {
case copy case copy
@@ -35,7 +33,7 @@ enum RoomScreenViewAction {
} }
struct RoomScreenViewState: BindableState { struct RoomScreenViewState: BindableState {
var roomTitle: String = "" var roomTitle = ""
var roomAvatar: UIImage? var roomAvatar: UIImage?
var roomEncryptionBadge: UIImage? var roomEncryptionBadge: UIImage?
var items: [RoomTimelineViewProvider] = [] var items: [RoomTimelineViewProvider] = []
@@ -20,7 +20,7 @@ typealias RoomScreenViewModelType = StateStoreViewModel<RoomScreenViewState, Roo
class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol { class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol {
private struct Constants { private enum Constants {
static let backPaginationPageSize: UInt = 30 static let backPaginationPageSize: UInt = 30
} }
@@ -106,13 +106,13 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
// MARK: ContextMenus // MARK: ContextMenus
private func buildContexMenuForItemId(_ itemId: String) -> TimelineItemContextMenu { private func buildContexMenuForItemId(_ itemId: String) -> TimelineItemContextMenu {
TimelineItemContextMenu(contextMenuActions: self.contextMenuActionsForItemId(itemId)) { [weak self] action in TimelineItemContextMenu(contextMenuActions: contextMenuActionsForItemId(itemId)) { [weak self] action in
self?.processContentMenuAction(action, itemId: itemId) self?.processContentMenuAction(action, itemId: itemId)
} }
} }
private func contextMenuActionsForItemId(_ itemId: String) -> [TimelineItemContextMenuAction] { private func contextMenuActionsForItemId(_ itemId: String) -> [TimelineItemContextMenuAction] {
guard let timelineItem = self.timelineController.timelineItems.first(where: { $0.id == itemId }), guard let timelineItem = timelineController.timelineItems.first(where: { $0.id == itemId }),
timelineItem is EventBasedTimelineItemProtocol else { timelineItem is EventBasedTimelineItemProtocol else {
return [] return []
} }
@@ -121,7 +121,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
} }
private func processContentMenuAction(_ action: TimelineItemContextMenuAction, itemId: String) { private func processContentMenuAction(_ action: TimelineItemContextMenuAction, itemId: String) {
guard let timelineItem = self.timelineController.timelineItems.first(where: { $0.id == itemId }), guard let timelineItem = timelineController.timelineItems.first(where: { $0.id == itemId }),
let item = timelineItem as? EventBasedTimelineItemProtocol else { let item = timelineItem as? EventBasedTimelineItemProtocol else {
return return
} }
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import Combine import Combine
import UIKit
class ListTableViewAdapter: NSObject, UITableViewDelegate { class ListTableViewAdapter: NSObject, UITableViewDelegate {
@@ -89,7 +89,7 @@ class ListTableViewAdapter: NSObject, UITableViewDelegate {
case .bottomOffset: case .bottomOffset:
tableView.scrollToRow(at: .init(row: max(0, currentItemCount - 1), section: 0), at: .bottom, animated: false) tableView.scrollToRow(at: .init(row: max(0, currentItemCount - 1), section: 0), at: .bottom, animated: false)
case .topOffset(let indexPath, let previousItemCount): case .topOffset(let indexPath, let previousItemCount):
let row = indexPath.row + max(0, (currentItemCount - previousItemCount)) let row = indexPath.row + max(0, currentItemCount - previousItemCount)
if row < currentItemCount { if row < currentItemCount {
tableView.scrollToRow(at: .init(row: row, section: 0), at: .top, animated: false) tableView.scrollToRow(at: .init(row: row, section: 0), at: .top, animated: false)
} }
@@ -180,9 +180,9 @@ class ListTableViewAdapter: NSObject, UITableViewDelegate {
scrollViewBottomVisiblePublisher.send(isBottomVisible) scrollViewBottomVisiblePublisher.send(isBottomVisible)
} }
if !draggingInitiated && tableView.isDragging { if !draggingInitiated, tableView.isDragging {
draggingInitiated = true draggingInitiated = true
} else if draggingInitiated && !tableView.isDragging { } else if draggingInitiated, !tableView.isDragging {
draggingInitiated = false draggingInitiated = false
scrollViewDidRestPublisher.send(()) scrollViewDidRestPublisher.send(())
} }
@@ -23,7 +23,7 @@ struct MessageComposerTextField: View {
init(placeholder: String, text: Binding<String>, maxHeight: CGFloat) { init(placeholder: String, text: Binding<String>, maxHeight: CGFloat) {
self.placeholder = placeholder self.placeholder = placeholder
self._text = text _text = text
self.maxHeight = maxHeight self.maxHeight = maxHeight
} }
@@ -36,7 +36,7 @@ struct MessageComposerTextField: View {
} }
private var borderWidth: CGFloat { private var borderWidth: CGFloat {
return isEditing ? 2.0 : 1.0 isEditing ? 2.0 : 1.0
} }
var body: some View { var body: some View {
@@ -89,15 +89,15 @@ private struct UITextViewWrapper: UIViewRepresentable {
} }
func updateUIView(_ view: UITextView, context: UIViewRepresentableContext<UITextViewWrapper>) { func updateUIView(_ view: UITextView, context: UIViewRepresentableContext<UITextViewWrapper>) {
if view.text != self.text { if view.text != text {
view.text = self.text view.text = text
} }
UITextViewWrapper.recalculateHeight(view: view, result: $calculatedHeight, maxHeight: maxHeight) UITextViewWrapper.recalculateHeight(view: view, result: $calculatedHeight, maxHeight: maxHeight)
} }
func makeCoordinator() -> Coordinator { func makeCoordinator() -> Coordinator {
return Coordinator(text: $text, Coordinator(text: $text,
height: $calculatedHeight, height: $calculatedHeight,
isEditing: $isEditing, isEditing: $isEditing,
maxHeight: maxHeight) maxHeight: maxHeight)
@@ -124,7 +124,7 @@ private struct UITextViewWrapper: UIViewRepresentable {
init(text: Binding<String>, height: Binding<CGFloat>, isEditing: Binding<Bool>, maxHeight: CGFloat) { init(text: Binding<String>, height: Binding<CGFloat>, isEditing: Binding<Bool>, maxHeight: CGFloat) {
self.text = text self.text = text
self.calculatedHeight = height calculatedHeight = height
self.isEditing = isEditing self.isEditing = isEditing
self.maxHeight = maxHeight self.maxHeight = maxHeight
} }
@@ -137,7 +137,7 @@ private struct UITextViewWrapper: UIViewRepresentable {
} }
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
return true true
} }
func textViewDidBeginEditing(_ textView: UITextView) { func textViewDidBeginEditing(_ textView: UITextView) {
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import SwiftUI import SwiftUI
import Combine
import Introspect import Introspect
@@ -65,8 +65,7 @@ struct RoomHeaderView_Previews: PreviewProvider {
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(), let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
timelineViewFactory: RoomTimelineViewFactory(), timelineViewFactory: RoomTimelineViewFactory(),
roomName: "Some Room name", roomName: "Some Room name",
roomAvatar: Asset.Images.appLogo.image roomAvatar: Asset.Images.appLogo.image)
)
RoomHeaderView(context: viewModel.context) RoomHeaderView(context: viewModel.context)
.previewLayout(.sizeThatFits) .previewLayout(.sizeThatFits)
@@ -79,8 +78,7 @@ struct RoomHeaderView_Previews: PreviewProvider {
timelineViewFactory: RoomTimelineViewFactory(), timelineViewFactory: RoomTimelineViewFactory(),
roomName: "Some Room name", roomName: "Some Room name",
roomAvatar: nil, roomAvatar: nil,
roomEncryptionBadge: Asset.Images.encryptionTrusted.image roomEncryptionBadge: Asset.Images.encryptionTrusted.image)
)
RoomHeaderView(context: viewModel.context) RoomHeaderView(context: viewModel.context)
.previewLayout(.sizeThatFits) .previewLayout(.sizeThatFits)
@@ -89,7 +89,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
} }
private var shouldAvoidBubbling: Bool { private var shouldAvoidBubbling: Bool {
return timelineItem is ImageRoomTimelineItem timelineItem is ImageRoomTimelineItem
} }
private var bubbleColor: Color { private var bubbleColor: Color {
@@ -108,7 +108,7 @@ struct TimelineItemBubbledStylerView_Previews: PreviewProvider {
@ViewBuilder @ViewBuilder
static var body: some View { static var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
ForEach((1..<MockRoomTimelineController().timelineItems.count), id: \.self) { index in ForEach(1..<MockRoomTimelineController().timelineItems.count, id: \.self) { index in
let item = MockRoomTimelineController().timelineItems[index] let item = MockRoomTimelineController().timelineItems[index]
RoomTimelineViewFactory().buildTimelineViewFor(timelineItem: item) RoomTimelineViewFactory().buildTimelineViewFor(timelineItem: item)
} }
@@ -48,7 +48,7 @@ struct TimelineItemPlainStylerView_Previews: PreviewProvider {
@ViewBuilder @ViewBuilder
static var body: some View { static var body: some View {
VStack(alignment: .leading) { VStack(alignment: .leading) {
ForEach((1..<MockRoomTimelineController().timelineItems.count), id: \.self) { index in ForEach(1..<MockRoomTimelineController().timelineItems.count, id: \.self) { index in
let item = MockRoomTimelineController().timelineItems[index] let item = MockRoomTimelineController().timelineItems[index]
RoomTimelineViewFactory().buildTimelineViewFor(timelineItem: item) RoomTimelineViewFactory().buildTimelineViewFor(timelineItem: item)
} }
@@ -48,7 +48,7 @@ struct EmoteRoomTimelineView_Previews: PreviewProvider {
} }
private static func itemWith(text: String, timestamp: String, senderId: String) -> EmoteRoomTimelineItem { private static func itemWith(text: String, timestamp: String, senderId: String) -> EmoteRoomTimelineItem {
return EmoteRoomTimelineItem(id: UUID().uuidString, EmoteRoomTimelineItem(id: UUID().uuidString,
text: text, text: text,
timestamp: timestamp, timestamp: timestamp,
shouldShowSenderDetails: true, shouldShowSenderDetails: true,
@@ -63,7 +63,8 @@ Text after blockquote
<code><b>Hello</b> <i>world</i></code> <code><b>Hello</b> <i>world</i></code>
<p>Text</p> <p>Text</p>
<code>Hello world</code> <code>Hello world</code>
"""] """
]
let attributedStringBuilder = AttributedStringBuilder() let attributedStringBuilder = AttributedStringBuilder()
VStack(alignment: .leading, spacing: 24.0) { VStack(alignment: .leading, spacing: 24.0) {
@@ -49,7 +49,7 @@ struct NoticeRoomTimelineView_Previews: PreviewProvider {
} }
private static func itemWith(text: String, timestamp: String, senderId: String) -> NoticeRoomTimelineItem { private static func itemWith(text: String, timestamp: String, senderId: String) -> NoticeRoomTimelineItem {
return NoticeRoomTimelineItem(id: UUID().uuidString, NoticeRoomTimelineItem(id: UUID().uuidString,
text: text, text: text,
timestamp: timestamp, timestamp: timestamp,
shouldShowSenderDetails: true, shouldShowSenderDetails: true,
@@ -65,7 +65,7 @@ struct TextRoomTimelineView_Previews: PreviewProvider {
} }
private static func itemWith(text: String, timestamp: String, shouldShowSenderDetails: Bool, isOutgoing: Bool, senderId: String) -> TextRoomTimelineItem { private static func itemWith(text: String, timestamp: String, shouldShowSenderDetails: Bool, isOutgoing: Bool, senderId: String) -> TextRoomTimelineItem {
return TextRoomTimelineItem(id: UUID().uuidString, TextRoomTimelineItem(id: UUID().uuidString,
text: text, text: text,
timestamp: timestamp, timestamp: timestamp,
shouldShowSenderDetails: shouldShowSenderDetails, shouldShowSenderDetails: shouldShowSenderDetails,
@@ -6,13 +6,13 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import SwiftUI
import Combine import Combine
import Introspect import Introspect
import SwiftUI
struct TimelineItemList: View { struct TimelineItemList: View {
@State private var tableViewObserver: ListTableViewAdapter = ListTableViewAdapter() @State private var tableViewObserver = ListTableViewAdapter()
@State private var timelineItems: [RoomTimelineViewProvider] = [] @State private var timelineItems: [RoomTimelineViewProvider] = []
@State private var hasPendingChanges = false @State private var hasPendingChanges = false
@ObservedObject private var settings = ElementSettings.shared @ObservedObject private var settings = ElementSettings.shared
@@ -65,7 +65,7 @@ struct TimelineItemList: View {
} }
tableViewObserver = ListTableViewAdapter(tableView: tableView, tableViewObserver = ListTableViewAdapter(tableView: tableView,
topDetectionOffset: (tableView.bounds.size.height / 3.0), topDetectionOffset: tableView.bounds.size.height / 3.0,
bottomDetectionOffset: 10.0) bottomDetectionOffset: 10.0)
tableViewObserver.scrollToBottom() tableViewObserver.scrollToBottom()
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import SwiftUI import SwiftUI
import Combine
import Introspect import Introspect
@@ -115,6 +115,7 @@ class SessionVerificationStateMachine {
} }
} }
} }
// swiftlint:enable cyclomatic_complexity // swiftlint:enable cyclomatic_complexity
/// Attempt to move the state machine to another state through an event /// Attempt to move the state machine to another state through an event
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import MatrixRustSDK import MatrixRustSDK
import SwiftUI
struct SessionVerificationScreen: View { struct SessionVerificationScreen: View {
@@ -109,7 +109,7 @@ final class SettingsCoordinator: Coordinator, Presentable {
add(childCoordinator: coordinator) add(childCoordinator: coordinator)
coordinator.start() coordinator.start()
self.parameters.navigationRouter.push(coordinator, animated: true) { [weak self] in parameters.navigationRouter.push(coordinator, animated: true) { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.remove(childCoordinator: coordinator) self.remove(childCoordinator: coordinator)
@@ -32,9 +32,7 @@ struct SettingsViewState: BindableState {
var bindings: SettingsViewStateBindings var bindings: SettingsViewStateBindings
} }
struct SettingsViewStateBindings { struct SettingsViewStateBindings { }
}
enum SettingsViewAction { enum SettingsViewAction {
case reportBug case reportBug
@@ -8,6 +8,4 @@
import UIKit import UIKit
class SplashViewController: UIViewController { class SplashViewController: UIViewController { }
}
@@ -46,6 +46,7 @@ final class SplashScreenCoordinator: Coordinator, Presentable {
} }
// MARK: - Public // MARK: - Public
func start() { func start() {
MXLog.debug("[SplashScreenCoordinator] did start.") MXLog.debug("[SplashScreenCoordinator] did start.")
splashScreenViewModel.callback = { [weak self] action in splashScreenViewModel.callback = { [weak self] action in
@@ -59,7 +60,7 @@ final class SplashScreenCoordinator: Coordinator, Presentable {
} }
func toPresentable() -> UIViewController { func toPresentable() -> UIViewController {
return splashScreenHostingController splashScreenHostingController
} }
/// Stops any ongoing activities in the coordinator. /// Stops any ongoing activities in the coordinator.
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import Combine import Combine
import SwiftUI
typealias SplashScreenViewModelType = StateStoreViewModel<SplashScreenViewState, SplashScreenViewAction> typealias SplashScreenViewModelType = StateStoreViewModel<SplashScreenViewState, SplashScreenViewAction>
@@ -14,8 +14,8 @@
// limitations under the License. // limitations under the License.
// //
import SwiftUI
import DesignKit import DesignKit
import SwiftUI
/// The splash screen shown at the beginning of the onboarding flow. /// The splash screen shown at the beginning of the onboarding flow.
struct SplashScreen: View { struct SplashScreen: View {
@@ -152,7 +152,7 @@ struct SplashScreen: View {
private func showPreviousPage() { private func showPreviousPage() {
// Prevent the hidden page at index -1 from being shown. // Prevent the hidden page at index -1 from being shown.
context.pageIndex = max(0, (context.pageIndex - 1)) context.pageIndex = max(0, context.pageIndex - 1)
} }
private func showHiddenPage() { private func showHiddenPage() {
@@ -15,7 +15,7 @@ class AuthenticationService: AuthenticationServiceProtocol {
// MARK: Private // MARK: Private
private(set) var homeserver: LoginHomeserver = LoginHomeserver(address: BuildSettings.defaultHomeserverURLString) private(set) var homeserver = LoginHomeserver(address: BuildSettings.defaultHomeserverURLString)
private let userSessionStore: UserSessionStoreProtocol private let userSessionStore: UserSessionStoreProtocol
// MARK: - Setup // MARK: - Setup
@@ -19,20 +19,20 @@ protocol BackgroundTaskServiceProtocol {
extension BackgroundTaskServiceProtocol { extension BackgroundTaskServiceProtocol {
func startBackgroundTask(withName name: String) -> BackgroundTaskProtocol? { func startBackgroundTask(withName name: String) -> BackgroundTaskProtocol? {
return startBackgroundTask(withName: name, startBackgroundTask(withName: name,
expirationHandler: nil) expirationHandler: nil)
} }
func startBackgroundTask(withName name: String, func startBackgroundTask(withName name: String,
isReusable: Bool) -> BackgroundTaskProtocol? { isReusable: Bool) -> BackgroundTaskProtocol? {
return startBackgroundTask(withName: name, startBackgroundTask(withName: name,
isReusable: isReusable, isReusable: isReusable,
expirationHandler: nil) expirationHandler: nil)
} }
func startBackgroundTask(withName name: String, func startBackgroundTask(withName name: String,
expirationHandler: (() -> Void)?) -> BackgroundTaskProtocol? { expirationHandler: (() -> Void)?) -> BackgroundTaskProtocol? {
return startBackgroundTask(withName: name, startBackgroundTask(withName: name,
isReusable: false, isReusable: false,
expirationHandler: expirationHandler) expirationHandler: expirationHandler)
} }
@@ -15,16 +15,17 @@ class UIKitBackgroundTask: BackgroundTaskProtocol {
var isRunning: Bool { var isRunning: Bool {
identifier != .invalid identifier != .invalid
} }
let isReusable: Bool let isReusable: Bool
let expirationHandler: BackgroundTaskExpirationHandler? let expirationHandler: BackgroundTaskExpirationHandler?
var elapsedTime: TimeInterval { var elapsedTime: TimeInterval {
return Date().timeIntervalSince(startDate) * 1000 Date().timeIntervalSince(startDate) * 1000
} }
private let application: ApplicationProtocol private let application: ApplicationProtocol
private var identifier: UIBackgroundTaskIdentifier = .invalid private var identifier: UIBackgroundTaskIdentifier = .invalid
private var useCounter: Int = 0 private var useCounter = 0
private let startDate: Date = Date() private let startDate = Date()
/// Initializes and starts a new background task /// Initializes and starts a new background task
/// - Parameters: /// - Parameters:
@@ -35,7 +35,7 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol {
expirationHandler?() expirationHandler?()
return nil return nil
} }
var created: Bool = false var created = false
var result: BackgroundTaskProtocol? var result: BackgroundTaskProtocol?
if isReusable { if isReusable {
@@ -85,8 +85,8 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol {
} }
private func avoidStartingNewTasks(for application: ApplicationProtocol) -> Bool { private func avoidStartingNewTasks(for application: ApplicationProtocol) -> Bool {
if application.applicationState == .background if application.applicationState == .background,
&& application.backgroundTimeRemaining < .backgroundTimeRemainingThresholdToStartTasks { application.backgroundTimeRemaining < .backgroundTimeRemainingThresholdToStartTasks {
return true return true
} }
return false return false
@@ -7,10 +7,10 @@
// //
import Foundation import Foundation
import MatrixRustSDK
import UIKit
import GZIP import GZIP
import MatrixRustSDK
import Sentry import Sentry
import UIKit
enum BugReportServiceError: Error { enum BugReportServiceError: Error {
case invalidBaseUrlString case invalidBaseUrlString
@@ -33,7 +33,7 @@ class BugReportService: BugReportServiceProtocol {
guard !sentryEndpoint.isEmpty else { guard !sentryEndpoint.isEmpty else {
throw BugReportServiceError.invalidSentryEndpoint throw BugReportServiceError.invalidSentryEndpoint
} }
self.baseURL = url baseURL = url
self.sentryEndpoint = sentryEndpoint self.sentryEndpoint = sentryEndpoint
self.applicationId = applicationId self.applicationId = applicationId
self.session = session self.session = session
@@ -69,7 +69,7 @@ class BugReportService: BugReportServiceProtocol {
// MARK: - BugReportServiceProtocol // MARK: - BugReportServiceProtocol
var crashedLastRun: Bool { var crashedLastRun: Bool {
return SentrySDK.crashedLastRun SentrySDK.crashedLastRun
} }
func crash() { func crash() {
@@ -182,8 +182,8 @@ class BugReportService: BugReportServiceProtocol {
filesToCompress.append(URL(fileURLWithPath: crashLogFile)) filesToCompress.append(URL(fileURLWithPath: crashLogFile))
} }
var totalSize: Int = 0 var totalSize = 0
var totalZippedSize: Int = 0 var totalZippedSize = 0
var zippedFiles: [URL] = [] var zippedFiles: [URL] = []
for url in filesToCompress { for url in filesToCompress {
@@ -16,10 +16,10 @@ class MockBugReportService: BugReportServiceProtocol {
includeCrashLog: Bool, includeCrashLog: Bool,
githubLabels: [String], githubLabels: [String],
files: [URL]) async throws -> SubmitBugReportResponse { files: [URL]) async throws -> SubmitBugReportResponse {
return SubmitBugReportResponse(reportUrl: "https://www.example/com/123") SubmitBugReportResponse(reportUrl: "https://www.example/com/123")
} }
var crashedLastRun: Bool = false var crashedLastRun = false
func crash() { func crash() {
// no-op // no-op
@@ -7,8 +7,8 @@
// //
import Foundation import Foundation
import UIKit
import Photos import Photos
import UIKit
enum ScreenshotDetectorError: String, Error { enum ScreenshotDetectorError: String, Error {
case loadFailed case loadFailed
@@ -38,7 +38,7 @@ class ScreenshotDetector {
let authStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite) let authStatus = PHPhotoLibrary.authorizationStatus(for: .readWrite)
if authStatus == .authorized { if authStatus == .authorized {
findScreenshot() findScreenshot()
} else if authStatus == .notDetermined && autoRequestPHAuthorization { } else if authStatus == .notDetermined, autoRequestPHAuthorization {
Task { Task {
self.handleAuthStatus(await PHPhotoLibrary.requestAuthorization(for: .readWrite)) self.handleAuthStatus(await PHPhotoLibrary.requestAuthorization(for: .readWrite))
} }
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import MatrixRustSDK import MatrixRustSDK
import Combine
import UIKit import UIKit
private class WeakClientProxyWrapper: ClientDelegate { private class WeakClientProxyWrapper: ClientDelegate {
@@ -128,7 +128,7 @@ class ClientProxy: ClientProxyProtocol {
Benchmark.endTrackingForIdentifier("ClientRooms", message: "Retrieved \(sdkRooms.count) rooms") Benchmark.endTrackingForIdentifier("ClientRooms", message: "Retrieved \(sdkRooms.count) rooms")
Benchmark.startTrackingForIdentifier("ProcessingRooms", message: "Started processing \(sdkRooms.count) rooms") Benchmark.startTrackingForIdentifier("ProcessingRooms", message: "Started processing \(sdkRooms.count) rooms")
let diff = sdkRooms.map({ $0.id() }).difference(from: currentRooms.map(\.id)) let diff = sdkRooms.map { $0.id() }.difference(from: currentRooms.map(\.id))
for change in diff { for change in diff {
switch change { switch change {
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import MatrixRustSDK import MatrixRustSDK
import Combine
enum ClientProxyCallback { enum ClientProxyCallback {
case updatedRoomsList case updatedRoomsList
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import Kingfisher import Kingfisher
import UIKit
struct MediaProvider: MediaProviderProtocol { struct MediaProvider: MediaProviderProtocol {
private let clientProxy: ClientProxyProtocol private let clientProxy: ClientProxyProtocol
@@ -21,7 +21,7 @@ struct MediaProvider: MediaProviderProtocol {
self.clientProxy = clientProxy self.clientProxy = clientProxy
self.imageCache = imageCache self.imageCache = imageCache
self.backgroundTaskService = backgroundTaskService self.backgroundTaskService = backgroundTaskService
self.processingQueue = DispatchQueue(label: "MediaProviderProcessingQueue", attributes: .concurrent) processingQueue = DispatchQueue(label: "MediaProviderProcessingQueue", attributes: .concurrent)
} }
func imageFromSource(_ source: MediaSource?) -> UIImage? { func imageFromSource(_ source: MediaSource?) -> UIImage? {
@@ -23,6 +23,6 @@ struct MediaSource: Equatable {
// MARK: - Equatable // MARK: - Equatable
static func == (lhs: MediaSource, rhs: MediaSource) -> Bool { static func == (lhs: MediaSource, rhs: MediaSource) -> Bool {
return lhs.underlyingSource.url() == rhs.underlyingSource.url() lhs.underlyingSource.url() == rhs.underlyingSource.url()
} }
} }
@@ -12,18 +12,18 @@ import UIKit
struct MockMediaProvider: MediaProviderProtocol { struct MockMediaProvider: MediaProviderProtocol {
func imageFromSource(_ source: MediaSource?) -> UIImage? { func imageFromSource(_ source: MediaSource?) -> UIImage? {
return nil nil
} }
func loadImageFromSource(_ source: MediaSource) async -> Result<UIImage, MediaProviderError> { func loadImageFromSource(_ source: MediaSource) async -> Result<UIImage, MediaProviderError> {
return .failure(.failedRetrievingImage) .failure(.failedRetrievingImage)
} }
func imageFromURLString(_ urlString: String?) -> UIImage? { func imageFromURLString(_ urlString: String?) -> UIImage? {
return nil nil
} }
func loadImageFromURLString(_ urlString: String) async -> Result<UIImage, MediaProviderError> { func loadImageFromURLString(_ urlString: String) async -> Result<UIImage, MediaProviderError> {
return .failure(.failedRetrievingImage) .failure(.failedRetrievingImage)
} }
} }
@@ -7,8 +7,8 @@
// //
import Foundation import Foundation
import UIKit
import MatrixRustSDK import MatrixRustSDK
import UIKit
struct EmoteRoomMessage: RoomMessageProtocol { struct EmoteRoomMessage: RoomMessageProtocol {
private let message: MatrixRustSDK.EmoteMessage private let message: MatrixRustSDK.EmoteMessage
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import MatrixRustSDK import MatrixRustSDK
import UIKit
struct ImageRoomMessage: RoomMessageProtocol { struct ImageRoomMessage: RoomMessageProtocol {
private let message: MatrixRustSDK.ImageMessage private let message: MatrixRustSDK.ImageMessage
@@ -7,8 +7,8 @@
// //
import Foundation import Foundation
import UIKit
import MatrixRustSDK import MatrixRustSDK
import UIKit
struct NoticeRoomMessage: RoomMessageProtocol { struct NoticeRoomMessage: RoomMessageProtocol {
private let message: MatrixRustSDK.NoticeMessage private let message: MatrixRustSDK.NoticeMessage
@@ -7,8 +7,8 @@
// //
import Foundation import Foundation
import UIKit
import MatrixRustSDK import MatrixRustSDK
import UIKit
struct TextRoomMessage: RoomMessageProtocol { struct TextRoomMessage: RoomMessageProtocol {
private let message: MatrixRustSDK.TextMessage private let message: MatrixRustSDK.TextMessage
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import UIKit import UIKit
import Combine
struct MockRoomProxy: RoomProxyProtocol { struct MockRoomProxy: RoomProxyProtocol {
let id = UUID().uuidString let id = UUID().uuidString
@@ -29,26 +29,24 @@ struct MockRoomProxy: RoomProxyProtocol {
var callbacks = PassthroughSubject<RoomProxyCallback, Never>() var callbacks = PassthroughSubject<RoomProxyCallback, Never>()
func loadDisplayNameForUserId(_ userId: String) async -> Result<String?, RoomProxyError> { func loadDisplayNameForUserId(_ userId: String) async -> Result<String?, RoomProxyError> {
return .failure(.failedRetrievingMemberDisplayName) .failure(.failedRetrievingMemberDisplayName)
} }
func loadAvatarURLForUserId(_ userId: String) async -> Result<String?, RoomProxyError> { func loadAvatarURLForUserId(_ userId: String) async -> Result<String?, RoomProxyError> {
return .failure(.failedRetrievingMemberAvatarURL) .failure(.failedRetrievingMemberAvatarURL)
} }
func loadDisplayName() async -> Result<String, RoomProxyError> { func loadDisplayName() async -> Result<String, RoomProxyError> {
return .failure(.failedRetrievingDisplayName) .failure(.failedRetrievingDisplayName)
} }
func startLiveEventListener() { func startLiveEventListener() { }
}
func paginateBackwards(count: UInt) async -> Result<Void, RoomProxyError> { func paginateBackwards(count: UInt) async -> Result<Void, RoomProxyError> {
return .failure(.backwardStreamNotAvailable) .failure(.backwardStreamNotAvailable)
} }
func sendMessage(_ message: String) async -> Result<Void, RoomProxyError> { func sendMessage(_ message: String) async -> Result<Void, RoomProxyError> {
return .failure(.failedSendingMessage) .failure(.failedSendingMessage)
} }
} }
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import UIKit import UIKit
import Combine
import MatrixRustSDK import MatrixRustSDK
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import Combine import Combine
import UIKit
enum RoomProxyError: Error { enum RoomProxyError: Error {
case failedRetrievingDisplayName case failedRetrievingDisplayName
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import Combine import Combine
import UIKit
struct MockRoomSummary: RoomSummaryProtocol { struct MockRoomSummary: RoomSummaryProtocol {
var id: String = UUID().uuidString var id: String = UUID().uuidString
@@ -18,21 +18,19 @@ struct MockRoomSummary: RoomSummaryProtocol {
var topic: String? var topic: String?
var isDirect: Bool = false var isDirect = false
var isEncrypted: Bool = false var isEncrypted = false
var isSpace: Bool = false var isSpace = false
var isTombstoned: Bool = false var isTombstoned = false
var lastMessage: EventBrief? var lastMessage: EventBrief?
var avatar: UIImage? var avatar: UIImage?
func loadDetails() async { func loadDetails() async { }
}
var callbacks = PassthroughSubject<RoomSummaryCallback, Never>() var callbacks = PassthroughSubject<RoomSummaryCallback, Never>()
} }
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import UIKit import UIKit
import Combine
class RoomSummary: RoomSummaryProtocol { class RoomSummary: RoomSummaryProtocol {
private let roomProxy: RoomProxyProtocol private let roomProxy: RoomProxyProtocol
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import UIKit
import Combine import Combine
import UIKit
enum RoomSummaryCallback { enum RoomSummaryCallback {
case updatedData case updatedData
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import Combine import Combine
import Foundation
class UserSession: UserSessionProtocol { class UserSession: UserSessionProtocol {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import Combine import Combine
import Foundation
enum UserSessionCallback { enum UserSessionCallback {
case sessionVerificationNeeded case sessionVerificationNeeded
@@ -6,14 +6,14 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import MatrixRustSDK import MatrixRustSDK
import Combine
struct MockSessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol { struct MockSessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol {
var callbacks = PassthroughSubject<SessionVerificationControllerProxyCallback, Never>() var callbacks = PassthroughSubject<SessionVerificationControllerProxyCallback, Never>()
var isVerified: Bool = false var isVerified = false
func requestVerification() async -> Result<Void, SessionVerificationControllerProxyError> { func requestVerification() async -> Result<Void, SessionVerificationControllerProxyError> {
DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 2.0) { DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + 2.0) {
@@ -50,7 +50,8 @@ struct MockSessionVerificationControllerProxy: SessionVerificationControllerProx
// MARK: - Private // MARK: - Private
static var emojis: [SessionVerificationEmoji] { static var emojis: [SessionVerificationEmoji] {
[SessionVerificationEmoji(symbol: "🦋", description: "Butterfly"), [
SessionVerificationEmoji(symbol: "🦋", description: "Butterfly"),
SessionVerificationEmoji(symbol: "🐘", description: "Elephant"), SessionVerificationEmoji(symbol: "🐘", description: "Elephant"),
SessionVerificationEmoji(symbol: "🦋", description: "Butterfly"), SessionVerificationEmoji(symbol: "🦋", description: "Butterfly"),
SessionVerificationEmoji(symbol: "🎂", description: "Cake"), SessionVerificationEmoji(symbol: "🎂", description: "Cake"),
@@ -6,9 +6,9 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Combine
import Foundation import Foundation
import MatrixRustSDK import MatrixRustSDK
import Combine
private class WeakSessionVerificationControllerProxy: SessionVerificationControllerDelegate { private class WeakSessionVerificationControllerProxy: SessionVerificationControllerDelegate {
private weak var proxy: SessionVerificationControllerProxy? private weak var proxy: SessionVerificationControllerProxy?
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import Combine import Combine
import Foundation
enum SessionVerificationControllerProxyError: Error { enum SessionVerificationControllerProxyError: Error {
case failedRequestingVerification case failedRequestingVerification
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import Combine import Combine
import Foundation
class MockRoomTimelineController: RoomTimelineControllerProtocol { class MockRoomTimelineController: RoomTimelineControllerProtocol {
@@ -40,18 +40,12 @@ class MockRoomTimelineController: RoomTimelineControllerProtocol {
senderDisplayName: "Bob")] senderDisplayName: "Bob")]
func paginateBackwards(_ count: UInt) async -> Result<Void, RoomTimelineControllerError> { func paginateBackwards(_ count: UInt) async -> Result<Void, RoomTimelineControllerError> {
return .failure(.generic) .failure(.generic)
} }
func processItemAppearance(_ itemId: String) async { func processItemAppearance(_ itemId: String) async { }
} func processItemDisappearance(_ itemId: String) async { }
func processItemDisappearance(_ itemId: String) async { func sendMessage(_ message: String) async { }
}
func sendMessage(_ message: String) async {
}
} }
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import Combine import Combine
import Foundation
import UIKit import UIKit
class RoomTimelineController: RoomTimelineControllerProtocol { class RoomTimelineController: RoomTimelineControllerProtocol {
@@ -79,9 +79,7 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
} }
} }
func processItemDisappearance(_ itemId: String) { func processItemDisappearance(_ itemId: String) { }
}
func sendMessage(_ message: String) async { func sendMessage(_ message: String) async {
switch await timelineProvider.sendMessage(message) { switch await timelineProvider.sendMessage(message) {
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import Combine import Combine
import Foundation
enum RoomTimelineControllerCallback { enum RoomTimelineControllerCallback {
case updatedTimelineItems case updatedTimelineItems
@@ -6,8 +6,8 @@
// Copyright © 2022 Element. All rights reserved. // Copyright © 2022 Element. All rights reserved.
// //
import Foundation
import Combine import Combine
import Foundation
class RoomTimelineProvider: RoomTimelineProviderProtocol { class RoomTimelineProvider: RoomTimelineProviderProtocol {
private let roomProxy: RoomProxyProtocol private let roomProxy: RoomProxyProtocol

Some files were not shown because too many files have changed in this diff Show More