📜 ⬆️ ⬇️

Developer iOS Notes: Sharing Experience, Part 1

image Hello, dear readers Habr!

I develop applications for iOS and Mac OS. For about a year I have been involved in freelancing and, moving from client to client, I began to notice that I understand the task once; and when a similar order appears, I simply use the modules already developed earlier. In the iOS Developer Notes series of articles, I will try to highlight some aspects that are often found in orders; I will write something like a cheat sheet, after reading which, you can quickly and painlessly introduce a new technology into your project. My notes do not in any way pretend to a deep understanding of the processes, but they describe an easy way to complete an order on time.

Content:
')
  1. Part 1: Working with Files; Template Singleton; Work with Audio; Work with Video; In-app purchases
  2. Part 2: Own popups (Popups); How to use Modal Segue in the Navigation Controller; Core Graphics; Working with UIWebView and ScrollView
  3. Part 3: Life without Autolayout; Splash Screen; Work with device orientation in iOS 6+; Shifting the contents of a UITextField
  4. Part 4: Google Analytics; Push Notifications; PSPDFKit; Login to the app via Facebook; Tell friends - Facebook, Twitter, Email
  5. Part 5: Core Data; UITableView and UICollectionView

Work with Files


Many novice iOS developers stumble on the following problem: the application works correctly in the simulator, but refuses to work or does not work correctly on a real device. One of the possible problems is that the application actively uses file resources that are not transferred to the application documents folder. Apple’s policy is: you cannot change application files. The only place where you can have fun is the document folder. You can read from anywhere (within your application), but you can only write in the document folder.
So, when you first start, you need to transfer all documents to write to the documents folder! Easily! Start by creating the Config.h file, add the following line to Your_App_Name-Prefix.pch (this file is automatically added to all files of your project):

#import "Config.h" 

Fine! Now all that is in Config.h is in the whole project! Let's fill this file:

 #define pathToApplicationDirectory [[NSBundle mainBundle] bundlePath] //     #define pathToDocuments [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] //    #define pathToSettings [[pathToDocuments URLByAppendingPathComponent:@"settings.plist"] path] //     #define pathToPopups [[NSBundle mainBundle] pathForResource:@"popups" ofType:@"plist"] //       

All this is done exclusively for our convenience with you in the future (we will work a lot with the files and it would be good to get rid of the magic lines).
Now you can start copying files to the folder with documents. We will often change the settings.plist and popups.plist files, so the copy item is necessary. Add the following code to our application: didFinishLaunchingWithOptions:

 //  ,   ,      ! [self placeResourcesToDocumentsDirectory:@{ @"settings" : @"plist", @"popups" : @"plist"}]; 

And, of course, the placeResourcesToDocumentsDirectory method itself:

Push me!
 /*! ,         /param     */ - (void)placeResourcesToDocumentsDirectory:(NSDictionary *)resources { //    ,    if (![[NSFileManager defaultManager] fileExistsAtPath:pathToSettings) { for (NSString *fileName in [resources allKeys]) { NSString *extension = resources[fileName]; NSURL *storeURL = [pathToDocuments URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", fileName, extension]]; NSURL *preloadURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:fileName ofType:extension]]; NSError *error = nil; [[NSFileManager defaultManager] copyItemAtURL:preloadURL toURL:storeURL error:&error]; } } } 

That's all! How easy it was to move files to the right folder, right? And you can access and work with the file like this:

 NSMutableDictionary *settings = [NSMutableDictionary dictionaryWithContentsOfFile:pathToSettings]; settings[@"isThisAppCool"] = @YES; [settings writeToFile:pathToSettings atomically:YES]; 

Singleton pattern


It is rather a small snippet that can make it easier for you to work with singles.
Singleton.h :

Push me!
 <...> //     #define coolSingleton [Singleton sharedSingleton] @interface Singleton : NSObject + (Singleton *)sharedSingleton; <...> 

Singleton.m :

Push me!
 <...> @implementation Singleton + (Singleton *)sharedSingleton { static Singleton *sharedSingleton = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedSingleton = [[self alloc] init]; }); return sharedSingleton; } <...> 

Everything is simple here. First of all, we have simplified the work with the singleton with one definition. Secondly, there is one static object of class Singleton. Thirdly, by calling the sharedSingleton class method, we will get either an existing object or initialize a new one. There will always be only one singleton object. Fourthly, our singleton is thread-safe (thanks to danilNik and AndrewShmig for the tip!).

Work with Audio


Here we have to work with the AVAudioPlayer class from the AVFoundation framework. The principle of operation is simple: create an object of the AVAudioPlayer class with the name of a specific file, prepare it for playback and launch it when necessary. Let's create a simple singleton, which will contain all our audio players. We will have two audio players: one for background music, the second for playing a button click sound.

Look at SimpleAudioPlayer.h :

Push me!
 #import <Foundation/Foundation.h> #import <AVFoundation/AVFoundation.h> #define audioPlayer [SimpleAudioPlayer sharedAudioPlayer] @interface SimpleAudioPlayer : NSObject @property (nonatomic, retain) AVAudioPlayer *backgroundMusicPlayer; @property (nonatomic, retain) AVAudioPlayer *buttonSoundPlayer; + (SimpleAudioPlayer *)sharedAudioPlayer; @end 

And on SimpleAudioPlayer.m :

Push me!
 #import "SimpleAudioPlayer.h" @implementation SimpleAudioPlayer static SimpleAudioPlayer *sharedAudioPlayer; + (SimpleAudioPlayer *)sharedAudioPlayer { static SimpleAudioPlayer *sharedAudioPlayer = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedAudioPlayer = [[self alloc] init]; }); return sharedAudioPlayer; } - (id)init { self = [super init]; if (self) { [self initAudioPlayers]; } return self; } /*! ,   */ - (void)initAudioPlayers { NSURL *fileURL = [[NSURL alloc] initFileURLWithPath:pathToBackgroundAudio]; self.backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil]; [self.backgroundMusicPlayer prepareToPlay]; self.backgroundMusicPlayer.numberOfLoops = -1; fileURL = [[NSURL alloc] initFileURLWithPath:pathToButtonAudio]; self.buttonSoundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil]; [self.buttonSoundPlayer prepareToPlay]; } 

That's all. Definitions of paths to audio files can be specified in Config.h . It is worth noting that we indicated a negative number for the number of repetitions of background music. If you set a negative number for this property, then the audio file will repeat indefinitely. What you need! Also, do not forget about the prepareToPlay method - if you prepare all audio players as soon as the application is launched, there will not be a small delay before the first playback of the audio file. And you can use our audio player like this:

 [audioPlayer.backgroundMusicPlayer play]; <...> [audioPlayer.backgroundMusicPlayer stop]; 

Work with video


Let's work a bit with the MediaPlayer framework. In fact, the following code can be added even to the viewDidAppear method:

 NSURL *url = [[NSURL alloc] initFileURLWithPath:pathToMovie]; MPMoviePlayerViewController *movieController = [[MPMoviePlayerViewController alloc] initWithContentURL:url]; [self presentMoviePlayerViewControllerAnimated:movieController]; [movieController.moviePlayer play]; 

This code simply shows the video at the given URL. Apple's video player already has all the necessary buttons to close the video player. So the four lines above are the minimum set of characters to include video in your application.

In-app purchases


Well, and the most interesting for today: StoreKit framework! True, we will work not directly with him, but with MKStoreKit . Thank you so much MugunthKumar for the great framework!

It's simple: edit the MKStoreKitConfigs.plist file to suit your needs (everything is intuitively clear) and use the following code to check purchases:

 if ([MKStoreManager isFeaturePurchased:@"me.identifier.coolapp.somesinglefeature"]) { //     !    ! } if ([MKStoreManager isSubscriptionActive:@"me.identifier.coolapp.somesubscription"]) { // !      , , !     ,     } 

To make purchases, use the following:

 [[MKStoreManager sharedManager] buyFeature:@"me.identifier.coolapp.somesinglefeature" onComplete:^(NSString* purchasedFeature, NSData* purchasedReceipt, NSArray* availableDownloads) { NSLog(@" : %@", purchasedFeature); } onCancelled:^ { NSLog(@"   :( ."); }]; 

Conclusion


That's all for today. This part was a pilot, in the following articles we will consider examples more interesting. For example, how to create such a pop-up window so that our client can change his behavior directly in .xib files, without your participation.

I ask to write about all errors and inaccuracies of the article in my habracenter .

PS If you want to cooperate with me, then my profile is on one of the major exchanges of freelancers .

Should I continue the series of articles "Notes iOS developer?" Express your opinion in the comments.

Source: https://habr.com/ru/post/192062/


All Articles