Proyectos de Subversion Iphone Microlearning

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
//
2
//  RequestTaskMap.swift
3
//
4
//  Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
5
//
6
//  Permission is hereby granted, free of charge, to any person obtaining a copy
7
//  of this software and associated documentation files (the "Software"), to deal
8
//  in the Software without restriction, including without limitation the rights
9
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
//  copies of the Software, and to permit persons to whom the Software is
11
//  furnished to do so, subject to the following conditions:
12
//
13
//  The above copyright notice and this permission notice shall be included in
14
//  all copies or substantial portions of the Software.
15
//
16
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
//  THE SOFTWARE.
23
//
24
 
25
import Foundation
26
 
27
/// A type that maintains a two way, one to one map of `URLSessionTask`s to `Request`s.
28
struct RequestTaskMap {
29
    private typealias Events = (completed: Bool, metricsGathered: Bool)
30
 
31
    private var tasksToRequests: [URLSessionTask: Request]
32
    private var requestsToTasks: [Request: URLSessionTask]
33
    private var taskEvents: [URLSessionTask: Events]
34
 
35
    var requests: [Request] {
36
        Array(tasksToRequests.values)
37
    }
38
 
39
    init(tasksToRequests: [URLSessionTask: Request] = [:],
40
         requestsToTasks: [Request: URLSessionTask] = [:],
41
         taskEvents: [URLSessionTask: (completed: Bool, metricsGathered: Bool)] = [:]) {
42
        self.tasksToRequests = tasksToRequests
43
        self.requestsToTasks = requestsToTasks
44
        self.taskEvents = taskEvents
45
    }
46
 
47
    subscript(_ request: Request) -> URLSessionTask? {
48
        get { requestsToTasks[request] }
49
        set {
50
            guard let newValue = newValue else {
51
                guard let task = requestsToTasks[request] else {
52
                    fatalError("RequestTaskMap consistency error: no task corresponding to request found.")
53
                }
54
 
55
                requestsToTasks.removeValue(forKey: request)
56
                tasksToRequests.removeValue(forKey: task)
57
                taskEvents.removeValue(forKey: task)
58
 
59
                return
60
            }
61
 
62
            requestsToTasks[request] = newValue
63
            tasksToRequests[newValue] = request
64
            taskEvents[newValue] = (completed: false, metricsGathered: false)
65
        }
66
    }
67
 
68
    subscript(_ task: URLSessionTask) -> Request? {
69
        get { tasksToRequests[task] }
70
        set {
71
            guard let newValue = newValue else {
72
                guard let request = tasksToRequests[task] else {
73
                    fatalError("RequestTaskMap consistency error: no request corresponding to task found.")
74
                }
75
 
76
                tasksToRequests.removeValue(forKey: task)
77
                requestsToTasks.removeValue(forKey: request)
78
                taskEvents.removeValue(forKey: task)
79
 
80
                return
81
            }
82
 
83
            tasksToRequests[task] = newValue
84
            requestsToTasks[newValue] = task
85
            taskEvents[task] = (completed: false, metricsGathered: false)
86
        }
87
    }
88
 
89
    var count: Int {
90
        precondition(tasksToRequests.count == requestsToTasks.count,
91
                     "RequestTaskMap.count invalid, requests.count: \(tasksToRequests.count) != tasks.count: \(requestsToTasks.count)")
92
 
93
        return tasksToRequests.count
94
    }
95
 
96
    var eventCount: Int {
97
        precondition(taskEvents.count == count, "RequestTaskMap.eventCount invalid, count: \(count) != taskEvents.count: \(taskEvents.count)")
98
 
99
        return taskEvents.count
100
    }
101
 
102
    var isEmpty: Bool {
103
        precondition(tasksToRequests.isEmpty == requestsToTasks.isEmpty,
104
                     "RequestTaskMap.isEmpty invalid, requests.isEmpty: \(tasksToRequests.isEmpty) != tasks.isEmpty: \(requestsToTasks.isEmpty)")
105
 
106
        return tasksToRequests.isEmpty
107
    }
108
 
109
    var isEventsEmpty: Bool {
110
        precondition(taskEvents.isEmpty == isEmpty, "RequestTaskMap.isEventsEmpty invalid, isEmpty: \(isEmpty) != taskEvents.isEmpty: \(taskEvents.isEmpty)")
111
 
112
        return taskEvents.isEmpty
113
    }
114
 
115
    mutating func disassociateIfNecessaryAfterGatheringMetricsForTask(_ task: URLSessionTask) -> Bool {
116
        guard let events = taskEvents[task] else {
117
            fatalError("RequestTaskMap consistency error: no events corresponding to task found.")
118
        }
119
 
120
        switch (events.completed, events.metricsGathered) {
121
        case (_, true): fatalError("RequestTaskMap consistency error: duplicate metricsGatheredForTask call.")
122
        case (false, false): taskEvents[task] = (completed: false, metricsGathered: true); return false
123
        case (true, false): self[task] = nil; return true
124
        }
125
    }
126
 
127
    mutating func disassociateIfNecessaryAfterCompletingTask(_ task: URLSessionTask) -> Bool {
128
        guard let events = taskEvents[task] else {
129
            fatalError("RequestTaskMap consistency error: no events corresponding to task found.")
130
        }
131
 
132
        switch (events.completed, events.metricsGathered) {
133
        case (true, _): fatalError("RequestTaskMap consistency error: duplicate completionReceivedForTask call.")
134
        #if os(Linux) // Linux doesn't gather metrics, so unconditionally remove the reference and return true.
135
        default: self[task] = nil; return true
136
        #else
137
        case (false, false):
138
            if #available(macOS 10.12, iOS 10, watchOS 7, tvOS 10, *) {
139
                taskEvents[task] = (completed: true, metricsGathered: false); return false
140
            } else {
141
                // watchOS < 7 doesn't gather metrics, so unconditionally remove the reference and return true.
142
                self[task] = nil; return true
143
            }
144
        case (false, true):
145
            self[task] = nil; return true
146
        #endif
147
        }
148
    }
149
}