Error thrown: NSConcreteMutableData replaceBytesInRange:withBytes:length:]: range {0, 73} exceeds data length 0
Workaround solution until a fix is available:
-> Attempts to replace replaceBytesInRange:withBytes:length: with setData:
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
swizzleWriteAvailableDataToStream()
return true
}
// ...
private func swizzleWriteAvailableDataToStream() {
// The modified implementation is approximatley equal to the Spotify SDKs -[SPTAppRemoteMessageStreamWriter writeAvailableDataToStream:]
// except instead of -[NSMutableData replaceBytesInRange:withBytes:length:], setData is called on the messageBuffer.
let exchangedImplementation: @convention(c) (NSObject, Selector, OutputStream) -> Void = { messageStreamWriter, selector, stream in
guard let messageBuffer = messageStreamWriter.value(forKey: "_messageBuffer") as? NSMutableData else {
return
}
while stream.hasSpaceAvailable {
if messageBuffer.isEmpty {
break
}
let buffer = messageBuffer.bytes.assumingMemoryBound(to: UInt8.self)
let result = stream.write(buffer, maxLength: messageBuffer.length)
if result > 0 {
messageBuffer.setData(Data())
}
}
}
// Make a reference to the class and selector for which to exchange the implementation.
guard let messageStreamWriterClass = NSClassFromString("SPTAppRemoteMessageStreamWriter") else {
return
}
let writeAvailableDataToStreamSelector = NSSelectorFromString("writeAvailableDataToStream:")
let writeAvailableDataToStreamSelectorExchanged = NSSelectorFromString("writeAvailableDataToStreamExchanged:")
// This string describes the types taken by the method. `@` is an instance (id), `:` is a selector. For a full list of types, see: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html#//apple_ref/doc/uid/TP40008048-CH100
let types = "@:@"
let exchangedFunctionPointer = unsafeBitCast(exchangedImplementation, to: UnsafeRawPointer.self)
let exchangedFunctionImplementation = IMP(exchangedFunctionPointer)
// Add the modified implementation under a different selector to the class.
class_addMethod(messageStreamWriterClass, writeAvailableDataToStreamSelectorExchanged, exchangedFunctionImplementation, types)
guard
let originalMethodPointer = class_getInstanceMethod(messageStreamWriterClass, writeAvailableDataToStreamSelector),
let targetMethodPointer = class_getInstanceMethod(messageStreamWriterClass, writeAvailableDataToStreamSelectorExchanged)
else {
return
}
// Exchange (Swizzle) the two methods. The selector in the SDK will point to the modified implementation,
// while the other selector (for the modified method) will point to the SDK implementation.
method_exchangeImplementations(originalMethodPointer, targetMethodPointer)
}
}
Error thrown:
NSConcreteMutableData replaceBytesInRange:withBytes:length:]: range {0, 73} exceeds data length 0
Workaround solution until a fix is available:
-> Attempts to replace
replaceBytesInRange:withBytes:length
: withsetData: