A starter project for Sample Project in Objective C. Objective C version of https://github.com/xeieshan/SwiftySampleProject
Just like software, this document will rot unless we take care of it. We encourage everyone to help us on that – just open an issue or send a pull request!
Getting on board with iOS can be intimidating. Neither Swift nor Objective-C are widely used elsewhere, the platform has its own names for almost everything, and it's a bumpy road for your code to actually make it onto a physical device. This living document is here to help you, whether you're taking your first steps in Cocoaland or you're curious about doing things "the right way". Everything below is just suggestions, so if you have a good reason to do something differently, by all means go for it!
A common question when beginning an iOS project is whether to write all views in code or use Interface Builder with Storyboards or XIB files. Both are known to occasionally result in working software. However, there are a few considerations:
Please follow the url [macbook-for-developers] [macbook-for-developers]:http://martiancraft.com/blog/2015/08/macbook-for-developers/
To keep all those hundreds of source files ending up in the same directory, it's a good idea to set up some folder structure depending on your architecture. For instance, you can use the following:
First, create them as groups (little yellow "folders") within the group with your project's name in Xcode's Project Navigator. Then, for each of the groups, link them to an actual directory in your project path by opening their File Inspector on the right, hitting the little gray folder icon, and creating a new subfolder with the name of the group in your project directory.
If you're planning on including external dependencies (e.g. third-party libraries) in your project, CocoaPods offers easy and fast integration. Install it like so:
sudo gem install cocoapods
To get started, move inside your iOS project folder and run
pod init
This creates a Podfile, which will hold all your dependencies in one place. After adding your dependencies to the Podfile, you run
pod install
to install the libraries and include them as part of a workspace which also holds your own project. It is generally recommended to commit the installed dependencies to your own repo, instead of relying on having each developer running pod install
after a fresh checkout.
Note that from now on, you'll need to open the .xcworkspace
file instead of .xcproject
, or your code will not compile. The command
pod update
will update all pods to the newest versions permitted by the Podfile. You can use a wealth of operators to specify your exact version requirements.
Keep app-wide constants in a Constants.h
file that is included in the prefix header.
Instead of preprocessor macro definitions (via #define
), use actual constants:
static CGFloat const kBrandingFontSizeSmall = 12.0f;
static NSString * const kAwesomenessDeliveredNotificationName = @"foo";
Actual constants are type-safe, have more explicit scope (they’re not available in all imported/included files until undefined), cannot be redefined or undefined in later parts of the code, and are available in the debugger.
These are the idiomatic ways for components to notify others about things:
Asset catalogs are the best way to manage all your project's visual assets. They can hold both universal and device-specific (iPhone 4-inch, iPhone Retina, iPad, etc.) assets and will automatically serve the correct ones for a given name. Teaching your designer(s) how to add and commit things there (Xcode has its own built-in Git client) can save a lot of time that would otherwise be spent copying stuff from emails or other channels to the codebase. It also allows them to instantly try out their changes and iterate if needed.
Asset catalogs expose only the names of image sets, abstracting away the actual file names within the set. This nicely prevents asset name conflicts, as files such as [email protected]
are now namespaced inside their image sets. However, some discipline when naming assets can make life easier:
The modifiers -568h
, @2x
, ~iphone
and ~ipad
are not required per se, but having them in the file name when dragging the file to an image set will automatically place them in the right "slot", thereby preventing assignment mistakes that can be hard to hunt down.
You can include the original vector graphics (PDFs) produced by designers into the asset catalogs, and have Xcode automatically generate the bitmaps from that. This reduces the complexity of your project (the number of files to manage.)
Apple pays great attention to keeping naming consistent, if sometimes a bit verbose, throughout their APIs. When developing for Cocoa, you make it much easier for new people to join the project if you follow Apple's naming conventions.
Here are some basic takeaways you can start using right away:
A method beginning with a verb indicates that it performs some side effects, but won't return anything:
- (void)loadView;
- (void)startAnimating;
Any method starting with a noun, however, returns that object and should do so without side effects:
- (UINavigationItem *)navigationItem;
+ (UILabel *)labelWithText:(NSString *)text;
It pays off to keep these two as separated as possible, i.e. not perform side effects when you transform data, and vice versa. That will keep your side effects contained to smaller sections of the code, which makes it more understandable and facilitates debugging.
Even simple apps can be built in different ways. The most basic separation that Xcode gives you is that between debug and release builds. For the latter, there is a lot more optimization going on at compile time, at the expense of debugging possibilities. Apple suggests that you use the debug build configuration for development, and create your App Store packages using the release build configuration. This is codified in the default scheme (the dropdown next to the Play and Stop buttons in Xcode), which commands that debug be used for Run and release for Archive.
However, this is a bit too simple for real-world applications. You might – no, should! – have different environments for testing, staging and other activities related to your service. Each might have its own base URL, log level, bundle identifier (so you can install them side-by-side), provisioning profile and so on. Therefore a simple debug/release distinction won't cut it. You can add more build configurations on the "Info" tab of your project settings in Xcode.
xcconfig
files for build settingsTypically build settings are specified in the Xcode GUI, but you can also use configuration settings files (“.xcconfig
files”) for them. The benefits of using these are:
#include
other build settings files, which helps you avoid repeating yourself:
Common.xcconfig
and #include
it in all the other files#include "MyApp_Debug.xcconfig"
and override one of the settingsFind more information about this topic in these presentation slides.
A target resides conceptually below the project level, i.e. a project can have several targets that may override its project settings. Roughly, each target corresponds to "an app" within the context of your codebase. For instance, you could have country-specific apps (built from the same codebase) for different countries' App Stores. Each of these will need development/staging/release builds, so it's better to handle those through build configurations, not targets. It's not uncommon at all for an app to only have a single target.
Schemes tell Xcode what should happen when you hit the Run, Test, Profile, Analyze or Archive action. Basically, they map each of these actions to a target and a build configuration. You can also pass launch arguments, such as the language the app should run in (handy for testing your localizations!) or set some diagnostic flags for debugging.
A suggested naming convention for schemes is MyApp (<Language>) [Environment]
:
MyApp (English) [Development]
MyApp (German) [Development]
MyApp [Testing]
MyApp [Staging]
MyApp [App Store]
For most environments the language is not needed, as the app will probably be installed through other means than Xcode, e.g. TestFlight, and the launch argument thus be ignored anyway. In that case, the device language should be set manually to test localization.
Deploying software on iOS devices isn't exactly straightforward. That being said, here are some central concepts that, once understood, will help you tremendously with it.
Whenever you want to run software on an actual device (as opposed to the simulator), you will need to sign your build with a certificate issued by Apple. Each certificate is linked to a private/public keypair, the private half of which resides in your Mac's Keychain. There are two types of certificates:
Development certificate: Every developer on a team has their own, and it is generated upon request. Xcode might do this for you, but it's better not to press the magic "Fix issue" button and understand what is actually going on. This certificate is needed to deploy development builds to devices. Naming Convention : CERT_DEV_XXX.p12
Distribution certificate: There can be several, but it's best to keep it to one per organization, and share its associated key through some internal channel. This certificate is needed to ship to the App Store, or your organization's internal "enterprise app store". Naming Convention : CERT_DIST_XXX.p12
Besides certificates, there are also provisioning profiles, which are basically the missing link between devices and certificates. Again, there are two types to distinguish between development and distribution purposes:
Development provisioning profile: It contains a list of all devices that are authorized to install and run the software. It is also linked to one or more development certificates, one for each developer that is allowed to use the profile. The profile can be tied to a specific app or use a wildcard App ID (*). The latter is discouraged, because Xcode is notoriously bad at picking the correct files for signing unless guided in the right direction. Also, certain capabilities like Push Notifications or App Groups require an explicit App ID. Naming Convention : PP_DEV_XXX.provisioningprofile
Distribution provisioning profile: There are three different ways of distribution, each for a different use case. Each distribution profile is linked to a distribution certificate, and will be invalid when the certificate expires.
To sync all certificates and profiles to your machine, go to Accounts in Xcode's Preferences, add your Apple ID if needed, and double-click your team name. There is a refresh button at the bottom, but sometimes you just need to restart Xcode to make everything show up.
Sometimes you need to debug a provisioning issue. For instance, Xcode may refuse to install the build to an attached device, because the latter is not on the (development or ad-hoc) profile's device list. In those cases, you can use Craig Hockenberry's excellent Provisioning plugin by browsing to ~/Library/MobileDevice/Provisioning Profiles
, selecting a .mobileprovision
file and hitting Space to launch Finder's Quick Look feature. It will show you a wealth of information such as devices, entitlements, certificates, and the App ID.
Use iPhone Configuration Utility
iTunes Connect is Apple's portal for managing your apps on the App Store. To upload a build, Xcode 6 requires an Apple ID that is part of the developer account used for signing. This can make things tricky when you are part of several developer accounts and want to upload their apps, as for mysterious reasons any given Apple ID can only be associated with a single iTunes Connect account. One workaround is to create a new Apple ID for each iTunes Connect account you need to be part of, and use Application Loader instead of Xcode to upload the builds. That effectively decouples the building and signing process from the upload of the resulting .app
file.
After uploading the build, be patient as it can take up to an hour for it to show up under the Builds section of your app version. When it appears, you can link it to the app version and submit your app for review.
Use following steps to make a signed ipa :
Following are the plugins that I suggest you to use :
To Give plugin support to any new Xcode version you need to know its application name. Follow these steps and you can enable all plugins on the new Xcode version. Write this command in terminal and tell me.
* find ~/Library/Application\ Support/Developer/Shared/Xcode/Plug-ins -name Info.plist -maxdepth 3 | xargs -I{} defaults write {} DVTPlugInCompatibilityUUIDs -array-add defaults read /Applications/Xcode.app/Contents/Info DVTPlugInCompatibilityUUID
Following the link StackOverFlow
Read Me in Russian : http://gargo.of.by/googlemapshelper/
A GOOGLE MAPS Helper that help you do multiple tasks like
// using AFNetworking
[[AFGoogleMapsHelper sharedAFGoogleMapsHelper] geocodeAddressString:@"Arsenal Emirates" components:@{} completionHandler:^(MOGoogleGeocodeList *googleGeoCodeList, SPGoogleGeoCoderResponse responseCode, NSString *message) {
}];
CLLocationCoordinate2D emiratesStadium = { 51.555747, -0.108309};
CLLocationCoordinate2D stamfordBridge = { 51.481690, -0.190999 };
[[AFGoogleMapsHelper sharedAFGoogleMapsHelper] reverseGeocodeCoordinate:(emiratesStadium) resultTypes:@[] locationTypes:@[] completionHandler:^(MOGoogleGeocodeList *googleGeoCodeList, SPGoogleGeoCoderResponse responseCode, NSString *message) {
}];
[[AFGoogleMapsHelper sharedAFGoogleMapsHelper] getAutoCompleteFromGoogle:@"Arsenal Emirates Stadium, london" andAutoComplete:^(MOGoogleAutoCompleteList *googleAutocompleteList, SPGoogleGeoCoderResponse responseCode, NSString *message) {
}];
[[AFGoogleMapsHelper sharedAFGoogleMapsHelper] getDirections:emiratesStadium andCoordinateDestination:stamfordBridge andDrawPoints:^{
} andPlaceMarks:^(MKPolyline *polyLine, NSString *distance, NSString *duration, NSString *startAddress, NSString *endAddress, NSMutableArray *polyLineSetArray, NSMutableArray *directionsSetArray, NSMutableArray *distanceSetArray) {
}];
// Using SVHTTPClient
[[SVGoogleMapsHelper sharedGoogleMapHelper] geocodeAddressString:@"Arsenal Emirates" components:@{} completionHandler:^(MOGoogleGeocodeList *googleGeoCodeList, SPGoogleGeoCoderResponse responseCode, NSString *message) {
}];
[[SVGoogleMapsHelper sharedGoogleMapHelper] reverseGeocodeCoordinate:(emiratesStadium) resultTypes:@[] locationTypes:@[] completionHandler:^(MOGoogleGeocodeList *googleGeoCodeList, SPGoogleGeoCoderResponse responseCode, NSString *message) {
}];
[[SVGoogleMapsHelper sharedGoogleMapHelper] getAutoCompleteFromGoogle:@"Arsenal Emirates Stadium, london" andAutoComplete:^(MOGoogleAutoCompleteList *googleAutocompleteList, SPGoogleGeoCoderResponse responseCode, NSString *message) {
}];
[[SVGoogleMapsHelper sharedGoogleMapHelper] getDirections:emiratesStadium andCoordinateDestination:stamfordBridge andDrawPoints:^{
} andPlaceMarks:^(MKPolyline *polyLine, NSString *distance, NSString *duration, NSString *startAddress, NSString *endAddress, NSMutableArray *polyLineSetArray, NSMutableArray *directionsSetArray, NSMutableArray *distanceSetArray) {
}];
It returns all these items :
I Geocode @"Arsenal Emirates" and I got Printing description of googleGeoCodeList->_results->[0]:
{ "formatted_address" = "Hornsey Rd, London N7 7AJ, UK"; geometry = { bounds = { }; location = { lat = "51.5548885"; lng = "-0.108438"; }; "location_type" = APPROXIMATE; viewport = { northeast = { lat = "51.55623748029149"; lng = "-0.107089019708498"; }; southwest = { lat = "51.5535395197085"; lng = "-0.109786980291502"; }; }; }; kMOGoogleGeocodePlacemarksAddressComponents = ( { kMOAddressComponentsTypes = ( route ); "long_name" = "Hornsey Road"; "short_name" = "Hornsey Rd"; }, { kMOAddressComponentsTypes = ( "postal_town" ); "long_name" = London; "short_name" = London; }, { kMOAddressComponentsTypes = ( "administrative_area_level_2", political ); "long_name" = "Greater London"; "short_name" = "Greater London"; }, { kMOAddressComponentsTypes = ( "administrative_area_level_1", political ); "long_name" = England; "short_name" = England; }, { kMOAddressComponentsTypes = ( country, political ); "long_name" = "United Kingdom"; "short_name" = GB; }, { kMOAddressComponentsTypes = ( "postal_code" ); "long_name" = "N7 7AJ"; "short_name" = "N7 7AJ"; } ); kMOGoogleGeocodePlacemarksTypes = ( establishment, "point_of_interest", stadium ); "place_id" = "ChIJO14pRXYbdkgRkM-CgzxxADY"; }
It returns all these items :
Printing description for first item :
<__NSArrayI 0x6080000b1b20>( { "formatted_address" = "Emirates Stadium, London, UK"; geometry = { bounds = { northeast = { lat = "51.5561569"; lng = "-0.1069905"; }; southwest = { lat = "51.5539356"; lng = "-0.1098853"; }; }; location = { lat = "51.55572979999999"; lng = "-0.1083118"; }; "location_type" = ROOFTOP; viewport = { northeast = { lat = "51.5563952302915"; lng = "-0.1069905"; }; southwest = { lat = "51.5536972697085"; lng = "-0.1098853"; }; }; }; kMOGoogleGeocodePlacemarksAddressComponents = ( { kMOAddressComponentsTypes = ( premise ); "long_name" = "Emirates Stadium"; "short_name" = "Emirates Stadium"; }, { kMOAddressComponentsTypes = ( locality, political ); "long_name" = London; "short_name" = London; }, { kMOAddressComponentsTypes = ( "postal_town" ); "long_name" = London; "short_name" = London; }, { kMOAddressComponentsTypes = ( "administrative_area_level_2", political ); "long_name" = "Greater London"; "short_name" = "Greater London"; }, { kMOAddressComponentsTypes = ( "administrative_area_level_1", political ); "long_name" = England; "short_name" = England; }, { kMOAddressComponentsTypes = ( country, political ); "long_name" = "United Kingdom"; "short_name" = GB; } ); kMOGoogleGeocodePlacemarksTypes = ( premise ); "place_id" = ChIJuaX4rXcbdkgRX7nJ4iCVzT0; }} )
It Returns all of these items :
I wanted to search @"Arsenal Emirates Stadium, london" and I got following 2 results, I am showing first item
Printing description of ((MOPredictions *)0x0000600000282b70): { description = "Arsenal Football Club, Emirates Stadium, Hornsey Road, London, United Kingdom"; id = 695fdbc199ef136a3674dc5c3946d0901be24cf2; kMOPredictionsMatchedSubstrings = ( { length = 7; offset = 0; }, { length = 16; offset = 23; }, { length = 6; offset = 55; } ); kMOPredictionsTerms = ( { offset = 0; value = "Arsenal Football Club"; }, { offset = 23; value = "Emirates Stadium"; }, { offset = 41; value = "Hornsey Road"; }, { offset = 55; value = London; }, { offset = 63; value = "United Kingdom"; } ); kMOPredictionsTypes = ( establishment ); "place_id" = ChIJq3Y4mXYbdkgRinA5RgGR5tA; reference = "CmRcAAAA3_03PcjmlvYYAMB56q1NSPHAa6o4s5OZlZzmqKWVzl6m8wQu8kIAHqSFzY8M_fJC6tbdt5vQSOylmlp6vu8hMJ0areyjFCiETtOb2e1qkM9a8TbnHRoIGK83-h0iy9EaEhCgUDC5ODRWWeKhZZmXh3wHGhRRAUwm4UFKR6a689AJXsADrqKFNA"; }
It Returns All these Items :
I found directions between following CLLocationCoordinate2D's CLLocationCoordinate2D emiratesStadium = { 51.555747, -0.108309}; CLLocationCoordinate2D stamfordBridge = { 51.481690, -0.190999 };
Printing description of duration:
42 mins
16.6 km
Citizen Rd, London N7, UK
19 Billing Pl, London SW10 9UN, UK
It also tells you Guidance strings which you can use :
- Head southwest on Citizen Rd toward Hornsey Rd/A103,
- Turn right onto Hornsey Rd/A103,
- Turn left onto Tollington Rd/A503Continue to follow A503,
- Continue straight onto Camden Rd/A503,
- Turn left onto Camden St/A400Continue to follow A400,
- Turn left onto Hampstead Rd/A400Continue to follow A400,
- Turn right onto Euston Rd,
- Merge onto Euston Rd/A501 via the ramp to Ring Road/A41/A40/KilburnContinue to follow A501,
- Keep right to continue on Marylebone Flyover/A40Continue to follow A40,
- Take the A3220 ramp to Hammersmith/Shepherd's Bush/White City/Earls Court,
- At the roundabout, take the 1st exit onto W Cross Rte/A3220,
- At the roundabout, take the 2nd exit onto Holland Rd/A3220Continue to follow A3220,
- Keep right to continue on Warwick Gardens/A3220,
- Turn left onto Pembroke Rd/A3220Continue to follow A3220,
- Continue straight onto Earls Ct Rd/A3220Continue to follow A3220,
- Turn right onto Fulham Rd/A308Continue to follow Fulham Rd,
- Turn right,
- Turn right,
- Turn left
- Destination will be on the left
I was using CocoaPods so I used
- pod 'SVHTTPRequest', '~> 0.5'
- pod 'AFNetworking', '~> 3.0'
- pod 'SVProgressHUD'
Dont forget to add condition in info.plist