I am building a feed with links to multimedia assets. If a link is bad it should not be in the feed as a video that does not play or image that does not load is a poor user experience. The feed is pulled by the phone via JSON from an endpoint without the benefit of a websocket.
I can test the links before putting them in the feed, however, waiting to verify a link takes time and if the feed has many items, I'm anticipating slow loading times.
Even accepting the delay for validating links, I am struggling with how to implement asynchronous calls to the links inside a synchronous conversion process once we have downloaded the data.
Here is the workflow.
Asynchronously import JSON as object. You get a JSON object such as:
{"feed":[{"title":"Check out this video","videosurl":"https://www.youtube.com/somevideo.mp4"}]}
Serialize JSON into Array of feed objects (synchronous)
Save the array of Feed objects to Core Data (synchronous)
Display the objects using an NSFetchedResultsController. (synchronous)
Inside 3-just before saving each item to Core Data would seem like a logical place to test/validate the links.
Here is my code so far inside of Step 3 (changed to use function B)
-(void) saveFeed:(NSArray *)items inContext: (NSManagedObjectContext*) context
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Videofeed" inManagedObjectContext:context];
items* item;
for (i=0;i<max;i++){
item = items[i];
NSString * link = item.videosurl;
__block BOOL validFile = NO;
[link foundValidFileAtSurlWithCompletion:^(BOOL __validFile) {
}];
if (validFile!=YES) {
// continue;//skip this one
}
NSManagedObject *record = [[NSManagedObject alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
[record setValue:title forKey:@"title"];
[record setValue:videosurl forKey:@"videosurl"];
}
[context save]
};//save context
}
Function A
@objc public extension NSString {
@available(iOS 13.0.0, *)
func fileExists(at surl: NSString) async throws -> Bool {
let str = self as String
guard let url = URL(string: str) else { return false }
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
request.timeoutInterval = 1.0 // Adjust to your needs
let (_, response) = try await URLSession.shared.data(for: request)
return (response as? HTTPURLResponse)?.statusCode == 200
}
}
Edit:
I have implemented the following code that uses a completionHandler to check for a valid URL and it returns a BOOL of yes or no, however, I don't have a way to hold up the synchronous code to wait for a response. How can I do this from Objective-C?
Function B
@objc public extension NSString {
func foundValidFileAtSurl(completion:@escaping (Bool) -> ()) {
let str = self as String
if (self == "") {
completion(false)
}
if let url = URL(string: str) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"//confirm header not complete file
request.timeoutInterval = 1.0 // Adjust to your needs
let task = URLSession.shared.dataTask(with: request) {
data,response,error in
guard error == nil else { print(error ?? "error",error!.localizedDescription); return }
guard data != nil else { print("Empty data"); return }
// flag = ((response as? HTTPURLResponse)?.statusCode == 200)
completion((response as? HTTPURLResponse)?.statusCode == 200)
}
task.resume()
}
}
}
saveFeed
in Swift, you can make thatasync
and you will be able toawait
. Otherwise you'll have to use completion handlers like the old days.await
is supported on Objective-C. If you have the choice, write everything in Swift.