mirror of
				https://github.com/yattee/yattee.git
				synced 2025-11-04 06:32:03 +00:00 
			
		
		
		
	Replace repeating timer implementation
This commit is contained in:
		@@ -3,6 +3,7 @@ import CoreMedia
 | 
			
		||||
import Defaults
 | 
			
		||||
import Foundation
 | 
			
		||||
import Logging
 | 
			
		||||
import Repeat
 | 
			
		||||
import SwiftUI
 | 
			
		||||
 | 
			
		||||
final class MPVBackend: PlayerBackend {
 | 
			
		||||
@@ -41,7 +42,7 @@ final class MPVBackend: PlayerBackend {
 | 
			
		||||
    }}
 | 
			
		||||
 | 
			
		||||
    var isPlaying = true { didSet {
 | 
			
		||||
        networkStateTimer.resume()
 | 
			
		||||
        networkStateTimer.start()
 | 
			
		||||
 | 
			
		||||
        if isPlaying {
 | 
			
		||||
            startClientUpdates()
 | 
			
		||||
@@ -73,8 +74,8 @@ final class MPVBackend: PlayerBackend {
 | 
			
		||||
    #endif
 | 
			
		||||
    var client: MPVClient! { didSet { client.backend = self } }
 | 
			
		||||
 | 
			
		||||
    private var clientTimer: RepeatingTimer!
 | 
			
		||||
    private var networkStateTimer: RepeatingTimer!
 | 
			
		||||
    private var clientTimer: Repeater!
 | 
			
		||||
    private var networkStateTimer: Repeater!
 | 
			
		||||
 | 
			
		||||
    private var handleEOF = false
 | 
			
		||||
    private var onFileLoaded: (() -> Void)?
 | 
			
		||||
@@ -117,11 +118,13 @@ final class MPVBackend: PlayerBackend {
 | 
			
		||||
        self.playerTime = playerTime
 | 
			
		||||
        self.networkState = networkState
 | 
			
		||||
 | 
			
		||||
        clientTimer = .init(timeInterval: Self.controlsUpdateInterval)
 | 
			
		||||
        clientTimer.eventHandler = getClientUpdates
 | 
			
		||||
        clientTimer = .init(interval: .seconds(Self.controlsUpdateInterval), mode: .infinite) { [weak self] _ in
 | 
			
		||||
            self?.getClientUpdates()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        networkStateTimer = .init(timeInterval: Self.networkStateUpdateInterval)
 | 
			
		||||
        networkStateTimer.eventHandler = updateNetworkState
 | 
			
		||||
        networkStateTimer = .init(interval: .seconds(Self.networkStateUpdateInterval), mode: .infinite) { [weak self] _ in
 | 
			
		||||
            self?.updateNetworkState()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    typealias AreInIncreasingOrder = (Stream, Stream) -> Bool
 | 
			
		||||
@@ -339,7 +342,7 @@ final class MPVBackend: PlayerBackend {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func startClientUpdates() {
 | 
			
		||||
        clientTimer.resume()
 | 
			
		||||
        clientTimer.start()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var handleSegmentsThrottle = Throttle(interval: 1)
 | 
			
		||||
@@ -366,7 +369,7 @@ final class MPVBackend: PlayerBackend {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func stopClientUpdates() {
 | 
			
		||||
        clientTimer.suspend()
 | 
			
		||||
        clientTimer.pause()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func updateControlsIsPlaying() {
 | 
			
		||||
@@ -404,13 +407,13 @@ final class MPVBackend: PlayerBackend {
 | 
			
		||||
 | 
			
		||||
        case MPV_EVENT_PAUSE:
 | 
			
		||||
            isPlaying = false
 | 
			
		||||
            networkStateTimer.resume()
 | 
			
		||||
            networkStateTimer.start()
 | 
			
		||||
 | 
			
		||||
        case MPV_EVENT_UNPAUSE:
 | 
			
		||||
            isPlaying = true
 | 
			
		||||
            isLoadingVideo = false
 | 
			
		||||
            isSeeking = false
 | 
			
		||||
            networkStateTimer.resume()
 | 
			
		||||
            networkStateTimer.start()
 | 
			
		||||
 | 
			
		||||
        case MPV_EVENT_SEEK:
 | 
			
		||||
            isSeeking = true
 | 
			
		||||
@@ -482,15 +485,15 @@ final class MPVBackend: PlayerBackend {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if !networkState.needsUpdates {
 | 
			
		||||
            networkStateTimer.suspend()
 | 
			
		||||
            networkStateTimer.pause()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func setNeedsNetworkStateUpdates(_ needsUpdates: Bool) {
 | 
			
		||||
        if needsUpdates {
 | 
			
		||||
            networkStateTimer.resume()
 | 
			
		||||
            networkStateTimer.start()
 | 
			
		||||
        } else {
 | 
			
		||||
            networkStateTimer.suspend()
 | 
			
		||||
            networkStateTimer.pause()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,54 +0,0 @@
 | 
			
		||||
import Foundation
 | 
			
		||||
 | 
			
		||||
final class RepeatingTimer {
 | 
			
		||||
    let timeInterval: TimeInterval
 | 
			
		||||
 | 
			
		||||
    init(timeInterval: TimeInterval) {
 | 
			
		||||
        self.timeInterval = timeInterval
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private lazy var timer: DispatchSourceTimer = {
 | 
			
		||||
        let t = DispatchSource.makeTimerSource()
 | 
			
		||||
        t.schedule(deadline: .now() + self.timeInterval, repeating: self.timeInterval)
 | 
			
		||||
        t.setEventHandler { [weak self] in
 | 
			
		||||
            self?.eventHandler?()
 | 
			
		||||
        }
 | 
			
		||||
        return t
 | 
			
		||||
    }()
 | 
			
		||||
 | 
			
		||||
    var eventHandler: (() -> Void)?
 | 
			
		||||
 | 
			
		||||
    private enum State {
 | 
			
		||||
        case suspended
 | 
			
		||||
        case resumed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var state: State = .suspended
 | 
			
		||||
 | 
			
		||||
    deinit {
 | 
			
		||||
        timer.setEventHandler {}
 | 
			
		||||
        timer.cancel()
 | 
			
		||||
        /*
 | 
			
		||||
         If the timer is suspended, calling cancel without resuming
 | 
			
		||||
         triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
 | 
			
		||||
         */
 | 
			
		||||
        resume()
 | 
			
		||||
        eventHandler = nil
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func resume() {
 | 
			
		||||
        if state == .resumed {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        state = .resumed
 | 
			
		||||
        timer.resume()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    func suspend() {
 | 
			
		||||
        if state == .suspended {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
        state = .suspended
 | 
			
		||||
        timer.suspend()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -135,9 +135,6 @@
 | 
			
		||||
		370F4FFF27CC1756001B35DC /* libxcb.1.1.0.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 370F4FB027CC16CA001B35DC /* libxcb.1.1.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
 | 
			
		||||
		370F500027CC1756001B35DC /* libXdmcp.6.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 370F4FC727CC16CB001B35DC /* libXdmcp.6.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
 | 
			
		||||
		370F500C27CC1821001B35DC /* MPVViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C2211C27ADA33300305B41 /* MPVViewController.swift */; };
 | 
			
		||||
		371114EB27B94C8800C2EF7B /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371114EA27B94C8800C2EF7B /* RepeatingTimer.swift */; };
 | 
			
		||||
		371114EC27B94C8800C2EF7B /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371114EA27B94C8800C2EF7B /* RepeatingTimer.swift */; };
 | 
			
		||||
		371114ED27B94C8800C2EF7B /* RepeatingTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 371114EA27B94C8800C2EF7B /* RepeatingTimer.swift */; };
 | 
			
		||||
		3711403F26B206A6005B3555 /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3711403E26B206A6005B3555 /* SearchModel.swift */; };
 | 
			
		||||
		3711404026B206A6005B3555 /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3711403E26B206A6005B3555 /* SearchModel.swift */; };
 | 
			
		||||
		3711404126B206A6005B3555 /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3711403E26B206A6005B3555 /* SearchModel.swift */; };
 | 
			
		||||
@@ -191,6 +188,9 @@
 | 
			
		||||
		372915E62687E3B900F5A35B /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; };
 | 
			
		||||
		372915E72687E3B900F5A35B /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; };
 | 
			
		||||
		372915E82687E3B900F5A35B /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372915E52687E3B900F5A35B /* Defaults.swift */; };
 | 
			
		||||
		372AA410286D067B0000B1DC /* Repeat in Frameworks */ = {isa = PBXBuildFile; productRef = 372AA40F286D067B0000B1DC /* Repeat */; };
 | 
			
		||||
		372AA412286D06950000B1DC /* Repeat in Frameworks */ = {isa = PBXBuildFile; productRef = 372AA411286D06950000B1DC /* Repeat */; };
 | 
			
		||||
		372AA414286D06A10000B1DC /* Repeat in Frameworks */ = {isa = PBXBuildFile; productRef = 372AA413286D06A10000B1DC /* Repeat */; };
 | 
			
		||||
		372CFD15285F2E2A00B0B54B /* ControlsBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372CFD14285F2E2A00B0B54B /* ControlsBar.swift */; };
 | 
			
		||||
		372CFD16285F2E2A00B0B54B /* ControlsBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372CFD14285F2E2A00B0B54B /* ControlsBar.swift */; };
 | 
			
		||||
		372D85DE283841B800FF3C7D /* PiPDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373031F428383A89000CFD59 /* PiPDelegate.swift */; };
 | 
			
		||||
@@ -881,7 +881,6 @@
 | 
			
		||||
		370F4FC727CC16CB001B35DC /* libXdmcp.6.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libXdmcp.6.dylib; sourceTree = "<group>"; };
 | 
			
		||||
		370F4FC827CC16CB001B35DC /* libbrotlicommon.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libbrotlicommon.1.dylib; sourceTree = "<group>"; };
 | 
			
		||||
		370F500A27CC176F001B35DC /* BridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = "<group>"; };
 | 
			
		||||
		371114EA27B94C8800C2EF7B /* RepeatingTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepeatingTimer.swift; sourceTree = "<group>"; };
 | 
			
		||||
		3711403E26B206A6005B3555 /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = "<group>"; };
 | 
			
		||||
		3712643B2865FF4500D77974 /* Shared Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Shared Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
			
		||||
		37130A5A277657090033018A /* Yattee.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Yattee.xcdatamodel; sourceTree = "<group>"; };
 | 
			
		||||
@@ -1179,6 +1178,7 @@
 | 
			
		||||
				3736A20A286BB72300C9E5EE /* libssl.xcframework in Frameworks */,
 | 
			
		||||
				37C2212727ADA41000305B41 /* CoreFoundation.framework in Frameworks */,
 | 
			
		||||
				37C2212527ADA40A00305B41 /* AudioToolbox.framework in Frameworks */,
 | 
			
		||||
				372AA410286D067B0000B1DC /* Repeat in Frameworks */,
 | 
			
		||||
				37C2212327ADA3F200305B41 /* libiconv.tbd in Frameworks */,
 | 
			
		||||
				37C2212127ADA3A600305B41 /* libbz2.tbd in Frameworks */,
 | 
			
		||||
				37C2211F27ADA3A200305B41 /* libz.tbd in Frameworks */,
 | 
			
		||||
@@ -1210,6 +1210,7 @@
 | 
			
		||||
			isa = PBXFrameworksBuildPhase;
 | 
			
		||||
			buildActionMask = 2147483647;
 | 
			
		||||
			files = (
 | 
			
		||||
				372AA414286D06A10000B1DC /* Repeat in Frameworks */,
 | 
			
		||||
				370F4FD227CC16CB001B35DC /* libavformat.59.16.100.dylib in Frameworks */,
 | 
			
		||||
				370F4FD327CC16CB001B35DC /* libass.9.dylib in Frameworks */,
 | 
			
		||||
				370F4FDF27CC16CB001B35DC /* libxcb-shape.0.0.0.dylib in Frameworks */,
 | 
			
		||||
@@ -1289,6 +1290,7 @@
 | 
			
		||||
				3736A203286BB72300C9E5EE /* libfribidi.xcframework in Frameworks */,
 | 
			
		||||
				3736A207286BB72300C9E5EE /* libfreetype.xcframework in Frameworks */,
 | 
			
		||||
				3772003D27E8EEDB00CB2475 /* libiconv.tbd in Frameworks */,
 | 
			
		||||
				372AA412286D06950000B1DC /* Repeat in Frameworks */,
 | 
			
		||||
				3772003827E8EEB100CB2475 /* AudioToolbox.framework in Frameworks */,
 | 
			
		||||
				3736A209286BB72300C9E5EE /* libcrypto.xcframework in Frameworks */,
 | 
			
		||||
				37FB28462722054C00A57617 /* SDWebImageSwiftUI in Frameworks */,
 | 
			
		||||
@@ -1824,7 +1826,6 @@
 | 
			
		||||
				3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */,
 | 
			
		||||
				3729037D2739E47400EA99F6 /* MenuCommands.swift */,
 | 
			
		||||
				37B7958F2771DAE0001CF27B /* OpenURLHandler.swift */,
 | 
			
		||||
				371114EA27B94C8800C2EF7B /* RepeatingTimer.swift */,
 | 
			
		||||
				3700155E271B12DD0049C794 /* SiestaConfiguration.swift */,
 | 
			
		||||
				37FFC43F272734C3009FFD26 /* Throttle.swift */,
 | 
			
		||||
				378FFBC328660172009E3FBE /* URLParser.swift */,
 | 
			
		||||
@@ -2122,6 +2123,7 @@
 | 
			
		||||
				3765917B27237D21009F956E /* PINCache */,
 | 
			
		||||
				37CF8B8328535E4F00B71E37 /* SDWebImage */,
 | 
			
		||||
				37A5DBC3285DFF5400CA4DD1 /* SwiftUIPager */,
 | 
			
		||||
				372AA40F286D067B0000B1DC /* Repeat */,
 | 
			
		||||
			);
 | 
			
		||||
			productName = "Yattee (iOS)";
 | 
			
		||||
			productReference = 37D4B0C92671614900C925CA /* Yattee.app */;
 | 
			
		||||
@@ -2155,6 +2157,7 @@
 | 
			
		||||
				3703206927D2BB49007A0CB8 /* Alamofire */,
 | 
			
		||||
				37CF8B8528535E5A00B71E37 /* SDWebImage */,
 | 
			
		||||
				37A5DBC5285E06B100CA4DD1 /* SwiftUIPager */,
 | 
			
		||||
				372AA413286D06A10000B1DC /* Repeat */,
 | 
			
		||||
			);
 | 
			
		||||
			productName = "Yattee (macOS)";
 | 
			
		||||
			productReference = 37D4B0CF2671614900C925CA /* Yattee.app */;
 | 
			
		||||
@@ -2229,6 +2232,7 @@
 | 
			
		||||
				37FB285327220D8400A57617 /* SDWebImagePINPlugin */,
 | 
			
		||||
				3765917D27237D2A009F956E /* PINCache */,
 | 
			
		||||
				37CF8B8728535E6300B71E37 /* SDWebImage */,
 | 
			
		||||
				372AA411286D06950000B1DC /* Repeat */,
 | 
			
		||||
			);
 | 
			
		||||
			productName = Yattee;
 | 
			
		||||
			productReference = 37D4B158267164AE00C925CA /* Yattee.app */;
 | 
			
		||||
@@ -2325,6 +2329,7 @@
 | 
			
		||||
				3765917827237D07009F956E /* XCRemoteSwiftPackageReference "PINCache" */,
 | 
			
		||||
				37CF8B8228535E4F00B71E37 /* XCRemoteSwiftPackageReference "SDWebImage" */,
 | 
			
		||||
				37A5DBC2285DFF5400CA4DD1 /* XCRemoteSwiftPackageReference "SwiftUIPager" */,
 | 
			
		||||
				372AA40E286D067B0000B1DC /* XCRemoteSwiftPackageReference "Repeat" */,
 | 
			
		||||
			);
 | 
			
		||||
			productRefGroup = 37D4B0CA2671614900C925CA /* Products */;
 | 
			
		||||
			projectDirPath = "";
 | 
			
		||||
@@ -2660,7 +2665,6 @@
 | 
			
		||||
				37136CAC286273060095C0CF /* PersistentSystemOverlays+Backport.swift in Sources */,
 | 
			
		||||
				374C053527242D9F009BDDBE /* SponsorBlockSettings.swift in Sources */,
 | 
			
		||||
				37BF661C27308859008CCFB0 /* DropFavorite.swift in Sources */,
 | 
			
		||||
				371114EB27B94C8800C2EF7B /* RepeatingTimer.swift in Sources */,
 | 
			
		||||
				376A33E42720CB35000C1D6B /* Account.swift in Sources */,
 | 
			
		||||
				3756C2A62861131100E4B059 /* NetworkState.swift in Sources */,
 | 
			
		||||
				37BA794326DBA973002A0235 /* PlaylistsModel.swift in Sources */,
 | 
			
		||||
@@ -2845,7 +2849,6 @@
 | 
			
		||||
				37E2EEAC270656EC00170416 /* BrowserPlayerControls.swift in Sources */,
 | 
			
		||||
				37BF662027308884008CCFB0 /* DropFavoriteOutside.swift in Sources */,
 | 
			
		||||
				37E70924271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
 | 
			
		||||
				371114EC27B94C8800C2EF7B /* RepeatingTimer.swift in Sources */,
 | 
			
		||||
				374C0543272496E4009BDDBE /* AppDelegate.swift in Sources */,
 | 
			
		||||
				373CFACC26966264003CB2C6 /* SearchQuery.swift in Sources */,
 | 
			
		||||
				37AAF29126740715007FC770 /* Channel.swift in Sources */,
 | 
			
		||||
@@ -3030,7 +3033,6 @@
 | 
			
		||||
				376578872685429C00D4EA09 /* CaseIterable+Next.swift in Sources */,
 | 
			
		||||
				37D4B1802671650A00C925CA /* YatteeApp.swift in Sources */,
 | 
			
		||||
				3748187026A769D60084E870 /* DetailBadge.swift in Sources */,
 | 
			
		||||
				371114ED27B94C8800C2EF7B /* RepeatingTimer.swift in Sources */,
 | 
			
		||||
				3741A32C27E7EFFD00D266D1 /* PlayerControls.swift in Sources */,
 | 
			
		||||
				371B7E632759706A00D21217 /* CommentsView.swift in Sources */,
 | 
			
		||||
				37A9965C26D6F8CA006E3224 /* HorizontalCells.swift in Sources */,
 | 
			
		||||
@@ -4080,6 +4082,14 @@
 | 
			
		||||
				minimumVersion = 6.0.0;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		372AA40E286D067B0000B1DC /* XCRemoteSwiftPackageReference "Repeat" */ = {
 | 
			
		||||
			isa = XCRemoteSwiftPackageReference;
 | 
			
		||||
			repositoryURL = "https://github.com/malcommac/Repeat.git";
 | 
			
		||||
			requirement = {
 | 
			
		||||
				kind = upToNextMajorVersion;
 | 
			
		||||
				minimumVersion = 0.6.0;
 | 
			
		||||
			};
 | 
			
		||||
		};
 | 
			
		||||
		3765917827237D07009F956E /* XCRemoteSwiftPackageReference "PINCache" */ = {
 | 
			
		||||
			isa = XCRemoteSwiftPackageReference;
 | 
			
		||||
			repositoryURL = "https://github.com/pinterest/PINCache";
 | 
			
		||||
@@ -4221,6 +4231,21 @@
 | 
			
		||||
			package = 372915E22687E33E00F5A35B /* XCRemoteSwiftPackageReference "Defaults" */;
 | 
			
		||||
			productName = Defaults;
 | 
			
		||||
		};
 | 
			
		||||
		372AA40F286D067B0000B1DC /* Repeat */ = {
 | 
			
		||||
			isa = XCSwiftPackageProductDependency;
 | 
			
		||||
			package = 372AA40E286D067B0000B1DC /* XCRemoteSwiftPackageReference "Repeat" */;
 | 
			
		||||
			productName = Repeat;
 | 
			
		||||
		};
 | 
			
		||||
		372AA411286D06950000B1DC /* Repeat */ = {
 | 
			
		||||
			isa = XCSwiftPackageProductDependency;
 | 
			
		||||
			package = 372AA40E286D067B0000B1DC /* XCRemoteSwiftPackageReference "Repeat" */;
 | 
			
		||||
			productName = Repeat;
 | 
			
		||||
		};
 | 
			
		||||
		372AA413286D06A10000B1DC /* Repeat */ = {
 | 
			
		||||
			isa = XCSwiftPackageProductDependency;
 | 
			
		||||
			package = 372AA40E286D067B0000B1DC /* XCRemoteSwiftPackageReference "Repeat" */;
 | 
			
		||||
			productName = Repeat;
 | 
			
		||||
		};
 | 
			
		||||
		3765917B27237D21009F956E /* PINCache */ = {
 | 
			
		||||
			isa = XCSwiftPackageProductDependency;
 | 
			
		||||
			package = 3765917827237D07009F956E /* XCRemoteSwiftPackageReference "PINCache" */;
 | 
			
		||||
 
 | 
			
		||||
@@ -45,6 +45,15 @@
 | 
			
		||||
        "version" : "1.2.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "identity" : "repeat",
 | 
			
		||||
      "kind" : "remoteSourceControl",
 | 
			
		||||
      "location" : "https://github.com/malcommac/Repeat.git",
 | 
			
		||||
      "state" : {
 | 
			
		||||
        "revision" : "9df757d0936c15dfd9d01067766fab87e927f838",
 | 
			
		||||
        "version" : "0.6.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "identity" : "sdwebimage",
 | 
			
		||||
      "kind" : "remoteSourceControl",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user