Slow download with Apple sample code + progress - objective-c

I have implemented the following methods from the Apple site, available on this page:
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLDownload.html
//on my .h file:
#interface AppDelegate : NSObject <NSApplicationDelegate, NSWindowDelegate, NSURLDownloadDelegate>
{
BOOL allJobDone;
#private
NSURLResponse*downloadResponse;
long long bytesReceived;
}
//on my .m file:
#implementation AppDelegate
#synthesize downloadResponse = _downloadResponse;
#synthesize bytesReceived = _bytesReceived;
//.... the rest..
- (IBAction)startProcess:(id)sender {
// some code here..
[self startDownloadingURL];
}
// start below with the Apple code available here: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Tasks/UsingNSURLDownload.html
- (void)startDownloadingURL /*:sender*/
{
// Create the request.
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://freefr.dl.sourceforge.net/project/hpc/hpc/g95/gfortran-4.9-bin.tar.gz"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
// Create the download with the request and start loading the data.
NSURLDownload *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest delegate:self];
if (!theDownload) {
// Inform the user that the download failed.
NSLog(#"Download NOT started");
} else {
NSLog(#"Download started");
}
}
- (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename
{
NSString *destinationFilename;
destinationFilename = [[[_homeDirectory stringByAppendingPathComponent:#"Desktop"] stringByAppendingPathComponent:#"DOWN"] stringByAppendingPathComponent:filename];
[download setDestination:destinationFilename allowOverwrite:YES];
}
- (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error
{
// Dispose of any references to the download object
// that your app might keep.
// Inform the user.
NSLog(#"Download failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)downloadDidFinish:(NSURLDownload *)download
{
// Dispose of any references to the download object
// that your app might keep.
// Do something with the data.
NSLog(#"%#",#"downloadDidFinish");
}
- (void)setDownloadResponse:(NSURLResponse *)aDownloadResponse
{
NSLog(#"aDownloadResponse - %#",aDownloadResponse);
downloadResponse = aDownloadResponse;
NSLog(#"downloadResponse - %#",downloadResponse);
}
- (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)response
{
// Reset the progress, this might be called multiple times.
// bytesReceived is an instance variable defined elsewhere.
bytesReceived = 0;
// Store the response to use later.
[self setDownloadResponse:response];
}
- (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned long)length
{
long long expectedLength = [downloadResponse expectedContentLength];
bytesReceived = bytesReceived + length;
if (expectedLength != NSURLResponseUnknownLength) {
// If the expected content length is
// available, display percent complete.
float percentComplete = (bytesReceived/(float)expectedLength)*100.0;
NSLog(#"Percent complete - %f",percentComplete);
} else {
// If the expected content length is
// unknown, just log the progress.
NSLog(#"Bytes received - %lld",bytesReceived);
}
}
Everything seems to work, but the download is really slow. Trying the link in Safari, everything is very fast.
I get the impression that part of the code which calculates the progress (I will need for the progress indicator), has to do with the slowdown.
Does anyone know how to fix speed problems?

After countless attempts, since everything looked ok before, these two simple lines added:
NSString* userAgent = #"user";
[theRequest addValue:userAgent forHTTPHeaderField:#"User-Agent"];
they have speed up the download in a truly surprising. Perhaps even more things needs to be added, but now it's really satisfying.

Related

How to pull data point from Firebase using Obj-C?

I'm saving a users zip code to the Firebase database and want to query that database on app launch to see if the user has input their zip code already or if they're a brand new user.
I've posted my code before. I pulled the sample code from the Firebase docs, but it seems that my app is never even running the following code to get the value
[[[_ref child:#"user"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {...
What am I missing out on?
#import "FollowingVC.h"
#import <FirebaseDatabase/FirebaseDatabase.h>
#import Firebase;
#interface FollowingVC ()
#property NSString *uid;
#property FIRDatabaseReference *ref;
#property NSString *zipcode;
#end
#implementation FollowingVC
- (void)viewDidLoad {
[super viewDidLoad];
[self createAuthorizedUser];
[self checkForZipCode];
}
-(void)createAuthorizedUser
{
[[FIRAuth auth]
signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
if (!error) {
self.uid = user.uid;
self.ref=[[FIRDatabase database]reference];
}
}];
}
-(void)checkForZipCode
{
NSString *userID = [FIRAuth auth].currentUser.uid;
[[[_ref child:#"user"] child:userID] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
// Get user value
self.zipcode = snapshot.value[#"zip code"];
NSLog(#"It worked: %#", self.zipcode);
// ...
} withCancelBlock:^(NSError * _Nonnull error) {
NSLog(#"%#", error.localizedDescription);
}];
}
#end
Firebase is asynronous and you need to allow time for events to complete before moving on in the app.
In this case, you should call [self checkForZipCode] inside the sign-in block after self.uid is populated.
Otherwise you run the risk of the checkForZipCode function running before the self.uid is populated.
Let Firebase control the flow of the app - and don't try to use Firebase synchronously as it will get you into trouble due to internet lag etc.

NSPersistentDocument fails when saving a previously locked document

When opening a locked file using my NSPersistentDocument subclass I get the following message in the console:
Attempt to add read-only file at path [URL] read/write. Adding
it read-only instead. This will be a hard error in the future; you
must specify the NSReadOnlyPersistentStoreOption.
The document window title is '(document name) - Locked'. After the user unlocks it, makes a change and then attempts to save, the save fails with the error
An error occurred while saving.
It seems that NSPersistentDocument fails to recognize that the user has unlocked the document and doesn't reopen it in read/write mode. Is this a bug in NSPersistentDocument or am I missing something here?
I am not overriding any of the file I/O methods in NSPersistentDocument.
Ah, ok automatic file locking.
That happens for auto-save documents not accessed in a while.
The typical approach is to notice the lock before creating the core data stack and put up a dialog asking the user to unlock the file.
If they agree to unlock the file, you simply unlock it and run as normal.
If they don't agree to unlock it, you copy it or open it readonly. Of course, you could simply bypass the user's preference and automatically unlock the file anyway, but that's probably not very nice.
Here is a category that should help you determine if a file is locked, and also lock/unlock the file.
Note, that this is entirely separate from the files mode being changed to read-only, but you can handle it in a similar manner.
Category interface
#interface NSFileManager (MyFileLocking)
- (BOOL)isFileLockedAtPath:(NSString *)path;
- (BOOL)unlockFileAtPath:(NSString*)path error:(NSError**)error;
- (BOOL)lockFileAtPath:(NSString*)path error:(NSError**)error;
#end
Category implementation
#implementation NSFileManager (MyFileLocking)
- (BOOL)isFileLockedAtPath:(NSString *)path {
return [[[self attributesOfItemAtPath:path error:NULL]
objectForKey:NSFileImmutable] boolValue];
}
- (BOOL)unlockFileAtPath:(NSString*)path error:(NSError**)error {
return [self setAttributes:#{NSFileImmutable:#NO}
ofItemAtPath:path
error:error];
}
- (BOOL)lockFileAtPath:(NSString*)path error:(NSError**)error {
return [self setAttributes:#{NSFileImmutable:#YES}
ofItemAtPath:path
error:error];
}
#end
Then, you can call [[NSFileManager defaultManager] isFileLockedAtPath:path] to determine if it is locked, and if it is, throw up a dialog asking the user what to do about it. You can then unlock it and open the stack as normal, or leave it locked and open the stack read-only, which will prevent saves from changing the file store.
Note that you can also monitor the file, and know when it changes from locked/unlocked and respond accordingly.
For Apple's guidelines on this, see https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/DocBasedAppProgrammingGuideForOSX/StandardBehaviors/StandardBehaviors.html
EDIT
Ok. I would have liked for NSPersistentDocument to replicate the
behavior in NSDocument - where the prompt to unlock comes only when an
edit is attempted. What you're saying is that there is no such feature
in NSPersistentDocument? – Aderstedt
OK. I thought you were wanting to ask the user to unlock it so that it could be opened read/write.
If you want to "go with the flow" and open it read-only when necessary, then you should add a little customization to your NSPersistentDocument subclass.
First, you want to add a little state to keep track of whether or not the original options specified a read-only file.
#implementation MyDocument {
BOOL explicitReadOnly;
}
Then, you will want a couple of utility methods...
- (NSDictionary*)addReadOnlyOption:(NSDictionary*)options {
NSMutableDictionary *mutable = options ? [options mutableCopy]
: [NSMutableDictionary dictionary];
mutable[NSReadOnlyPersistentStoreOption] = #YES;
return [mutable copy];
}
- (NSDictionary*)removeReadOnlyOption:(NSDictionary*)options {
NSMutableDictionary *mutable = options ? [options mutableCopy]
: [NSMutableDictionary dictionary];
[mutable removeObjectForKey:NSReadOnlyPersistentStoreOption];
return [mutable copy];
}
Next, you want to provide your own persistent store coordinator configuration code. This allows you to provide the read-only option to the store when you create it. This method is automatically called when you build your document, all you need to do is provide an override implementation.
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url
ofType:(NSString *)fileType
modelConfiguration:(NSString *)configuration
storeOptions:(NSDictionary<NSString *,id> *)storeOptions
error:(NSError * _Nullable __autoreleasing *)error {
explicitReadOnly = [storeOptions[NSReadOnlyPersistentStoreOption] boolValue];
if (![[NSFileManager defaultManager] isWritableFileAtPath:url.path]) {
storeOptions = [self addReadOnlyOption:storeOptions];
}
return [super configurePersistentStoreCoordinatorForURL:url
ofType:fileType
modelConfiguration:configuration
storeOptions:storeOptions
error:error];
}
Also, notice that NSPersistentDocument implements the NSFilePresenter protocol. Thus, you can override a method and be notified whenever the file content or attributes are changed. This will notify you for any change to the file, including lock/unlock from within your application, the Finder, or any other mechanism.
- (void)presentedItemDidChange {
[self ensureReadOnlyConsistency];
[super presentedItemDidChange];
}
We then want to ensure that our persistent store remains consistent with the read-only properties of the file.
Here is one implementation, that just changes the store's readOnly property.
- (void)ensureReadOnlyConsistency {
NSURL *url = [self presentedItemURL];
BOOL fileIsReadOnly = ![[NSFileManager defaultManager] isWritableFileAtPath:url.path];
NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;
[psc performBlock:^{
NSPersistentStore *store = [psc persistentStoreForURL:url];
if (store) {
if (fileIsReadOnly) {
if (!store.isReadOnly) {
store.readOnly = YES;
}
} else if (!explicitReadOnly) {
if (store.isReadOnly) {
store.readOnly = NO;
}
}
}
}];
}
This works, but has one little hangup. If the store is originally opened with read-only options, then the very first time the readOnly attribute is set to NO, that first save throws (actually, it's the obtainPermanentIDsForObjects:error: call. Core data appears to catch the exception, but it is logged to the console.
The save continues, and nothing seems amiss. All the objects get saved, and the object IDs are properly obtained and recorded as well.
So, there is nothing that does not work that I can tell.
However, there is another more draconian option, but it avoids the aforementioned "issue." You can replace the store.
- (void)ensureReadOnlyConsistency {
NSURL *url = [self presentedItemURL];
BOOL fileIsReadOnly = ![[NSFileManager defaultManager] isWritableFileAtPath:url.path];
NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;
[psc performBlock:^{
NSPersistentStore *store = [psc persistentStoreForURL:url];
if (store) {
if (fileIsReadOnly != store.isReadOnly) {
NSString *type = store.type;
NSString *configuration = store.configurationName;
NSDictionary *options = store.options;
if (fileIsReadOnly) {
options = [self addReadOnlyOption:options];
} else if (!explicitReadOnly) {
options = [self removeReadOnlyOption:options];
}
NSError *error;
if (![psc removePersistentStore:store error:&error] ||
![psc addPersistentStoreWithType:type
configuration:configuration
URL:url
options:options
error:&error]) {
// Handle the error
}
}
}
}];
}
Finally, note that the notification happens when the operating system notices that the file has changed. When the file is locked/unlocked from within your application, you can get a faster notification.
You can override these two methods to get a little quicker response to the change...
- (void)lockWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler {
[super lockWithCompletionHandler:^(NSError * _Nullable error) {
if (completionHandler) completionHandler(error);
if (!error) [self ensureReadOnlyConsistency];
}];
}
- (void)unlockWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler {
[super unlockWithCompletionHandler:^(NSError * _Nullable error) {
if (completionHandler) completionHandler(error);
if (!error) [self ensureReadOnlyConsistency];
}];
}
I hope that's what you are looking for.

GCDWebServer run -addHandlerForMethod: twice time with different path, second request failed?

I use GCDWebServer, run method -addHandlerForMethod:path:requestClass: twice with different parameter "path". then NSLog webServer.serverURL,
first time it success:192.168.0.121:8080,
but second time it fail:nil
why? please help me.
#import "ServerMock.h"
#implementation ServerMock
+ (void)mockWithMethod:(NSString *)method path:(NSString *)path timeoutInterval:(NSTimeInterval)timeoutInterval JSONObject:(NSDictionary *)JSONObject port:(NSUInteger)port serverURL:(void (^)(NSURL *serverURL))block
{
GCDWebServer *webServer = [GCDWebServer new];
[webServer addHandlerForMethod:method path:path requestClass:[GCDWebServerRequest class] asyncProcessBlock:^(GCDWebServerRequest *request, GCDWebServerCompletionBlock completionBlock) {
GCD_DELAY_AFTER(timeoutInterval, ^{
GCDWebServerDataResponse *response = [GCDWebServerDataResponse responseWithJSONObject:JSONObject];
completionBlock(response);
});
}];
[webServer startWithPort:port bonjourName:nil];
block(webServer.serverURL);
}
#end
//////////////////////////////////////////////
- (void)viewDidLoad
{
NSDictionary *dict = #{
#"11111":
#"22222222"
};
[ServerMock mockWithMethod:#"GET"
path:#"/123"
timeoutInterval:0
JSONObject:dict
port:8080 serverURL:^(NSURL *serverURL) {
NSLog(#"________%#", serverURL);
}];
NSDictionary *dict2 = #{ #"2222222": #"111111111"};
[ServerMock mockWithMethod:#"GET" path:#"/321" timeoutInterval:0 JSONObject:dict2 port:8080 serverURL:^(NSURL *serverURL) {
NSLog(#"________%#", serverURL);
}];
}
Check the Xcode console for errors. The problem is most likely that you are you not stopping the GCDWebServer instance after calling the block, so it's still running and holding on port 8080, preventing new servers to start.

IOS App Action extension is not closing

I am facing app extension close issues , please tell me if anyone know what wrong am doing.I am using action extension after preform some action inside extension i need to return response back.
Sample Code
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
}
// With Error Case
- (void) completeActionWithError: (NSError *) error {
[self.extensionContext cancelRequestWithError: error];
}
With Success Case working fine but some time is not closing,
With Error Case not working above code.
Please let me know what went wrong.Thanks
When you create an action extension, this is the default method which will close the Action Extension View Controller:
- (IBAction)done {
// Return any edited content to the host app.
// This template doesn't do anything, so we just echo the passed in items.
[self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];
}
Since this method is already provided, you should just try calling it from your success method.
// With Success Case
- (void) completeActionWithItems: (NSString *) response {
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = #[[[NSItemProvider alloc] response typeIdentifier: (NSString *)kUTTypePlainText]];
[self.extensionContext completeRequestReturningItems: #[extensionItem] completionHandler: nil];
// Call to "done" method
[self done];
}

Connect to devices on local network using cocoa

I have been trying to develop an app for my ipad which i can use to connect to my local network devices in order to get files and so on without any progress.
What i want to do is to first detect the devices on the network and then select which i want to browse files from by connecting to it.
What i got so far is
#import "BrowseForNetworkDevices.h"
#implementation BrowseForNetworkDevices
- (id)init
{
self = [super init];
if (self)
{
services = [[NSMutableArray alloc] init];
serviceBrowser = [[NSNetServiceBrowser alloc] init];
[serviceBrowser setDelegate: delegateObject];
searching = NO;
}
return self;
}
- (void)dealloc
{
[services release];
[serviceBrowser release];
[super dealloc];
}
// Sent when browsing begins
- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser
{
searching = YES;
[serviceBrowser searchForServicesOfType:#" " inDomain:#"local."];
[self updateUI];
}
// Sent when browsing stops
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser
{
searching = NO;
[self updateUI];
}
// Sent if browsing fails
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didNotSearch:(NSDictionary *)errorDict
{
searching = NO;
[self handleError:[errorDict objectForKey:NSNetServicesErrorCode]];
}
// Sent when a service appears
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didFindService:(NSNetService *)aNetService
moreComing:(BOOL)moreComing
{
//[services addObject:aNetService];
if (![services containsObject:aNetService]) {
[self willChangeValueForKey:#"services"];
[services addObject:aNetService];
[self didChangeValueForKey:#"service"];
}
if(!moreComing)
{
[self updateUI];
}
}
// Sent when a service disappears
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
didRemoveService:(NSNetService *)aNetService
moreComing:(BOOL)moreComing
{
//[services removeObject:aNetService];
if ([services containsObject:aNetService]) {
[self willChangeValueForKey:#"service"];
[services removeObject:aNetService];
[self didChangeValueForKey:#"service"];
}
if(!moreComing)
{
[self updateUI];
}
}
// Error handling code
- (void)handleError:(NSNumber *)error
{
NSLog(#"An error occurred. Error code = %d", [error intValue]);
// Handle error here
}
// UI update code
- (void)updateUI
{
if(searching)
{
// Update the user interface to indicate searching
for(Class obj in services){
NSLog(#"service found %#", obj);
}
// Also update any UI that lists available services
}
else
{
// Update the user interface to indicate not searching
}
}
#end
Any good tutorials on how to perform such task?
Thanks in advance for all the help
You will need both the network devices and your iPad to be speaking the same protocol. You will need to pick an appropriate protocol. It sounds like what you want to do is file transfer - there are many protocols that can handle this that are implemented in libraries for iOS:
- WebDAV
- HTTP
- FTP
- SFTP
- AFP
- SAMBA
In order for the devices to detect one another you will need to use something like Apple's Bonjour