1 // Copyright 2015-present 650 Industries. All rights reserved. 2 3 import EXStructuredHeaders 4 5 /** 6 * Data from the update response headers. 7 * For non-multipart responses, this is the data from the headers in the response. 8 * For multipart responses, this is the data from the headers in the outer response (as opposed to the headers in a part). 9 */ 10 public final class ResponseHeaderData { 11 /** 12 * expo-protocol-version header. Indicates which version of the expo-updates protocol the response is. 13 */ 14 private let protocolVersionRaw: String? 15 16 /** 17 * expo-server-defined-headers header. It defines headers that this library must store until overwritten by a newer dictionary. 18 * They must be included in every subsequent update request. 19 */ 20 private let serverDefinedHeadersRaw: String? 21 22 /** 23 * expo-manifest-filters header. It is used to filter updates stored by the client library by the 24 * `metadata` attribute found in the manifest. If a field is mentioned in the filter, the corresponding 25 * field in the metadata must either be missing or equal for the update to be included. 26 * The client library must store the manifest filters until it is overwritten by a newer response. 27 */ 28 private let manifestFiltersRaw: String? 29 30 /** 31 * Classic updates Expo Go manifest signature 32 */ 33 let manifestSignature: String? 34 35 public required init(protocolVersionRaw: String?, serverDefinedHeadersRaw: String?, manifestFiltersRaw: String?, manifestSignature: String?) { 36 self.protocolVersionRaw = protocolVersionRaw 37 self.serverDefinedHeadersRaw = serverDefinedHeadersRaw 38 self.manifestFiltersRaw = manifestFiltersRaw 39 self.manifestSignature = manifestSignature 40 } 41 42 lazy var protocolVersion: Int? = { 43 self.protocolVersionRaw.let { it in 44 Int(it) 45 } 46 }() 47 48 lazy var serverDefinedHeaders: [String: Any]? = { 49 self.serverDefinedHeadersRaw.let { it in 50 ResponseHeaderData.dictionaryWithStructuredHeader(it) 51 } 52 }() 53 54 public lazy var manifestFilters: [String: Any]? = { 55 self.manifestFiltersRaw.let { it in 56 ResponseHeaderData.dictionaryWithStructuredHeader(it) 57 } 58 }() 59 60 static func dictionaryWithStructuredHeader(_ headerString: String) -> [String: Any]? { 61 let parser = EXStructuredHeadersParser( 62 rawInput: headerString, 63 fieldType: EXStructuredHeadersParserFieldType.dictionary, 64 ignoringParameters: true 65 ) 66 let parserOutput: Any 67 do { 68 parserOutput = try parser.parseStructuredFields() 69 } catch let error as NSError { 70 NSLog("Error parsing header value: %@", error.localizedDescription) 71 return nil 72 } 73 74 guard let parserOutputDictionary = parserOutput as? [String: Any] else { 75 NSLog("Error parsing header value: %@", "Header was not a structured fields dictionary") 76 return nil 77 } 78 79 // ignore any dictionary entries whose type is not string, number, or boolean 80 // since this will be re-serialized to JSON 81 // The only way I can figure out how to detect numbers is to do a is NSNumber (is any Numeric didn't work) 82 // swiftlint:disable:next legacy_objc_type 83 return parserOutputDictionary.filter { $0.value is String || $0.value is NSNumber || $0.value is Bool } 84 } 85 } 86 87 /** 88 * Data from the update response part headers. 89 * For non-multipart responses, this is the data from the headers in the response. 90 * For multipart responses, this is the data from the headers in the part. 91 */ 92 internal final class ResponsePartHeaderData: NSObject { 93 /** 94 * Code signing signature for response part. 95 */ 96 let signature: String? 97 98 required init(signature: String?) { 99 self.signature = signature 100 } 101 } 102 103 /** 104 * Full info about a update response part. 105 * For non-multipart responses, this is the info about the full response. 106 * For multipart responses, this is the info about a single part (but includes the outer headers for processing). 107 */ 108 internal final class ResponsePartInfo { 109 let responseHeaderData: ResponseHeaderData 110 let responsePartHeaderData: ResponsePartHeaderData 111 let body: Data 112 113 required init(responseHeaderData: ResponseHeaderData, responsePartHeaderData: ResponsePartHeaderData, body: Data) { 114 self.responseHeaderData = responseHeaderData 115 self.responsePartHeaderData = responsePartHeaderData 116 self.body = body 117 } 118 } 119