swift and c

I spent a few hours last night porting my Grand Central Dispatch wrapper GCDObjC to Swift. Although Craig Federighi described it as "Objective-C without the baggage of C," the C bindings seem to be really straightforward. In Xcode, Autocomplete and Quick Help both show the GCD API calls translated into Swift:

The only thing I found unclear was how to call OSAtomicIncrement32(), which expects a pointer to a non-const 32-bit integer, but digging around in the well-commented header files helped.

s for simple

I'm working on an iOS app that uses Amazon S3 to store images. It's just put/get/delete into a bucket, and without authentication on the bucket, it took less than 100 lines of NSURL* code to get working.

Now I need to restrict the bucket to only the app, so I figured I might as well try using Amazon's SDK. It's easy enough to set up; just add the AWSiOSSDK/S3 CocoaPod to the project. So how much code does that add?

$ cd Pods/AWSiOSSDK/src
$ find . -name "*.m" | xargs wc -l
      83 ./Amazon.Runtime/AmazonAbstractJsonWebServiceClient.m
     434 ./Amazon.Runtime/AmazonAbstractWebServiceClient.m
     272 ./Amazon.Runtime/AmazonAuthUtils.m
      59 ./Amazon.Runtime/AmazonBoolValueUnmarshaller.m
      59 ./Amazon.Runtime/AmazonClientException.m
      63 ./Amazon.Runtime/AmazonCredentials.m
      93 ./Amazon.Runtime/AmazonDictionaryUnmarshaller.m
     483 ./Amazon.Runtime/AmazonEndpoints.m
     116 ./Amazon.Runtime/AmazonErrorHandler.m
      43 ./Amazon.Runtime/AmazonJSON.m
      68 ./Amazon.Runtime/AmazonListUnmarshaller.m
      74 ./Amazon.Runtime/AmazonLogger.m
      90 ./Amazon.Runtime/AmazonMD5Util.m
      98 ./Amazon.Runtime/AmazonRequestDelegate.m
     705 ./Amazon.Runtime/AmazonSDKUtil.m
     104 ./Amazon.Runtime/AmazonServiceException.m
      81 ./Amazon.Runtime/AmazonServiceExceptionUnmarshaller.m
     278 ./Amazon.Runtime/AmazonServiceRequest.m
      46 ./Amazon.Runtime/AmazonServiceRequestConfig.m
     325 ./Amazon.Runtime/AmazonServiceResponse.m
      67 ./Amazon.Runtime/AmazonServiceResponseUnmarshaller.m
      20 ./Amazon.Runtime/AmazonSignatureException.m
      56 ./Amazon.Runtime/AmazonStaticCredentialsProvider.m
     107 ./Amazon.Runtime/AmazonUnmarshallerXMLParserDelegate.m
      32 ./Amazon.Runtime/AmazonURLRequest.m
      58 ./Amazon.Runtime/AmazonValueUnmarshaller.m
      32 ./Amazon.Runtime/AmazonWebServiceClient.m
     718 ./Amazon.S3/AmazonS3Client.m
      61 ./Amazon.S3/Model/BucketWebsiteConfiguration.m
      31 ./Amazon.S3/Model/DeletedObject.m
      32 ./Amazon.S3/Model/DeleteError.m
      54 ./Amazon.S3/Model/S3AbortMultipartUploadRequest.m
      21 ./Amazon.S3/Model/S3AbortMultipartUploadResponse.m
     164 ./Amazon.S3/Model/S3AbortMultipartUploadsOperation_Internal.m
     101 ./Amazon.S3/Model/S3AbstractPutRequest.m
     179 ./Amazon.S3/Model/S3AccessControlList.m
      77 ./Amazon.S3/Model/S3AccessControlListUnmarshaller.m
      32 ./Amazon.S3/Model/S3Bucket.m
      48 ./Amazon.S3/Model/S3BucketCrossOriginConfiguration.m
      51 ./Amazon.S3/Model/S3BucketCrossOriginConfigurationUnmarshaller.m
      48 ./Amazon.S3/Model/S3BucketLifecycleConfiguration.m
      99 ./Amazon.S3/Model/S3BucketLifecycleConfigurationRule.m
      96 ./Amazon.S3/Model/S3BucketLifecycleConfigurationRuleUnmarshaller.m
      84 ./Amazon.S3/Model/S3BucketLifecycleConfigurationTransition.m
      76 ./Amazon.S3/Model/S3BucketLifecycleConfigurationTransitionUnmarshaller.m
      51 ./Amazon.S3/Model/S3BucketLifecycleConfigurationUnmarshaller.m
      96 ./Amazon.S3/Model/S3BucketNameUtilities.m
      38 ./Amazon.S3/Model/S3BucketPolicy.m
      48 ./Amazon.S3/Model/S3BucketTaggingConfiguration.m
      51 ./Amazon.S3/Model/S3BucketTaggingConfigurationUnmarshaller.m
      51 ./Amazon.S3/Model/S3BucketTagSet.m
      79 ./Amazon.S3/Model/S3BucketTagSetUnmarshaller.m
      71 ./Amazon.S3/Model/S3BucketUnmarshaller.m
      64 ./Amazon.S3/Model/S3BucketVersioningConfiguration.m
      69 ./Amazon.S3/Model/S3BucketVersioningConfigurationUnmarshaller.m
     106 ./Amazon.S3/Model/S3CannedACL.m
      67 ./Amazon.S3/Model/S3CommonPrefixesUnmarshaller.m
      91 ./Amazon.S3/Model/S3CompleteMultipartUploadRequest.m
      55 ./Amazon.S3/Model/S3CompleteMultipartUploadResponse.m
      36 ./Amazon.S3/Model/S3CompleteMultipartUploadResult.m
      73 ./Amazon.S3/Model/S3CompleteMultipartUploadResultUnmarshaller.m
     120 ./Amazon.S3/Model/S3CopyObjectRequest.m
      44 ./Amazon.S3/Model/S3CopyObjectResponse.m
      32 ./Amazon.S3/Model/S3CopyObjectResult.m
      71 ./Amazon.S3/Model/S3CopyObjectResultUnmarshaller.m
      87 ./Amazon.S3/Model/S3CopyPartRequest.m
      47 ./Amazon.S3/Model/S3CopyPartResponse.m
      32 ./Amazon.S3/Model/S3CopyPartResult.m
      71 ./Amazon.S3/Model/S3CopyPartResultUnmarshaller.m
      71 ./Amazon.S3/Model/S3CORSRule.m
      95 ./Amazon.S3/Model/S3CORSRuleUnmarshaller.m
      99 ./Amazon.S3/Model/S3CreateBucketRequest.m
      20 ./Amazon.S3/Model/S3CreateBucketResponse.m
      47 ./Amazon.S3/Model/S3DeleteBucketCrossOriginRequest.m
      20 ./Amazon.S3/Model/S3DeleteBucketCrossOriginResponse.m
      47 ./Amazon.S3/Model/S3DeleteBucketLifecycleConfigurationRequest.m
      20 ./Amazon.S3/Model/S3DeleteBucketLifecycleConfigurationResponse.m
      39 ./Amazon.S3/Model/S3DeleteBucketPolicyRequest.m
      21 ./Amazon.S3/Model/S3DeleteBucketPolicyResponse.m
      59 ./Amazon.S3/Model/S3DeleteBucketRequest.m
      20 ./Amazon.S3/Model/S3DeleteBucketResponse.m
      47 ./Amazon.S3/Model/S3DeleteBucketTaggingRequest.m
      20 ./Amazon.S3/Model/S3DeleteBucketTaggingResponse.m
      47 ./Amazon.S3/Model/S3DeleteBucketWebsiteConfigurationRequest.m
      20 ./Amazon.S3/Model/S3DeleteBucketWebsiteConfigurationResponse.m
      79 ./Amazon.S3/Model/S3DeletedObjectUnmarshaller.m
      79 ./Amazon.S3/Model/S3DeleteErrorUnmarshaller.m
      40 ./Amazon.S3/Model/S3DeleteObjectRequest.m
      20 ./Amazon.S3/Model/S3DeleteObjectResponse.m
      81 ./Amazon.S3/Model/S3DeleteObjectsRequest.m
      46 ./Amazon.S3/Model/S3DeleteObjectsResponse.m
      74 ./Amazon.S3/Model/S3DeleteObjectsResultUnmarshaller.m
      50 ./Amazon.S3/Model/S3DeleteVersionRequest.m
      20 ./Amazon.S3/Model/S3DeleteVersionResponse.m
      75 ./Amazon.S3/Model/S3ErrorResponseHandler.m
      32 ./Amazon.S3/Model/S3GetACLRequest.m
      52 ./Amazon.S3/Model/S3GetACLResponse.m
      47 ./Amazon.S3/Model/S3GetBucketCrossOriginRequest.m
      37 ./Amazon.S3/Model/S3GetBucketCrossOriginResponse.m
      47 ./Amazon.S3/Model/S3GetBucketLifecycleConfigurationRequest.m
      37 ./Amazon.S3/Model/S3GetBucketLifecycleConfigurationResponse.m
      32 ./Amazon.S3/Model/S3GetBucketPolicyRequest.m
      41 ./Amazon.S3/Model/S3GetBucketPolicyResponse.m
      47 ./Amazon.S3/Model/S3GetBucketTaggingRequest.m
      37 ./Amazon.S3/Model/S3GetBucketTaggingResponse.m
      32 ./Amazon.S3/Model/S3GetBucketVersioningConfigurationRequest.m
      44 ./Amazon.S3/Model/S3GetBucketVersioningConfigurationResponse.m
      47 ./Amazon.S3/Model/S3GetBucketWebsiteConfigurationRequest.m
      37 ./Amazon.S3/Model/S3GetBucketWebsiteConfigurationResponse.m
      58 ./Amazon.S3/Model/S3GetBucketWebsiteConfigurationResultUnmarshaller.m
      31 ./Amazon.S3/Model/S3GetObjectMetadataRequest.m
      59 ./Amazon.S3/Model/S3GetObjectMetadataResponse.m
     570 ./Amazon.S3/Model/S3GetObjectOperation_Internal.m
     173 ./Amazon.S3/Model/S3GetObjectRequest.m
     106 ./Amazon.S3/Model/S3GetObjectResponse.m
     120 ./Amazon.S3/Model/S3GetPreSignedURLRequest.m
      53 ./Amazon.S3/Model/S3Grant.m
     132 ./Amazon.S3/Model/S3Grantee.m
      86 ./Amazon.S3/Model/S3GrantUnmarshaller.m
      99 ./Amazon.S3/Model/S3InitiateMultipartUploadRequest.m
      55 ./Amazon.S3/Model/S3InitiateMultipartUploadResponse.m
      69 ./Amazon.S3/Model/S3InitiateMultipartUploadResultUnmarshaller.m
      69 ./Amazon.S3/Model/S3KeyVersion.m
     118 ./Amazon.S3/Model/S3ListBucketResultUnmarshaller.m
      46 ./Amazon.S3/Model/S3ListBucketsRequest.m
      51 ./Amazon.S3/Model/S3ListBucketsResponse.m
      40 ./Amazon.S3/Model/S3ListBucketsResult.m
      83 ./Amazon.S3/Model/S3ListBucketsResultUnmarshaller.m
      83 ./Amazon.S3/Model/S3ListMultipartUploadsRequest.m
      45 ./Amazon.S3/Model/S3ListMultipartUploadsResponse.m
      63 ./Amazon.S3/Model/S3ListMultipartUploadsResult.m
     103 ./Amazon.S3/Model/S3ListMultipartUploadsResultUnmarshaller.m
      79 ./Amazon.S3/Model/S3ListObjectsRequest.m
      60 ./Amazon.S3/Model/S3ListObjectsResponse.m
      68 ./Amazon.S3/Model/S3ListObjectsResult.m
      70 ./Amazon.S3/Model/S3ListPartsRequest.m
      45 ./Amazon.S3/Model/S3ListPartsResponse.m
      45 ./Amazon.S3/Model/S3ListPartsResult.m
     103 ./Amazon.S3/Model/S3ListPartsResultUnmarshaller.m
      71 ./Amazon.S3/Model/S3ListVersionsRequest.m
      60 ./Amazon.S3/Model/S3ListVersionsResponse.m
      82 ./Amazon.S3/Model/S3ListVersionsResult.m
     126 ./Amazon.S3/Model/S3ListVersionsResultUnmarshaller.m
      48 ./Amazon.S3/Model/S3LocationConstraintUnmarshaller.m
      41 ./Amazon.S3/Model/S3MultiFactorAuthentication.m
      41 ./Amazon.S3/Model/S3MultipartUpload.m
     738 ./Amazon.S3/Model/S3MultipartUploadOperation_Internal.m
      93 ./Amazon.S3/Model/S3MultipartUploadUnmarshaller.m
      37 ./Amazon.S3/Model/S3ObjectSummary.m
     101 ./Amazon.S3/Model/S3ObjectSummaryUnmarshaller.m
      62 ./Amazon.S3/Model/S3Owner.m
      70 ./Amazon.S3/Model/S3OwnerUnmarshaller.m
      29 ./Amazon.S3/Model/S3Part.m
      73 ./Amazon.S3/Model/S3PartUnmarshaller.m
     112 ./Amazon.S3/Model/S3Permission.m
     239 ./Amazon.S3/Model/S3PutObjectOperation_Internal.m
     212 ./Amazon.S3/Model/S3PutObjectRequest.m
      20 ./Amazon.S3/Model/S3PutObjectResponse.m
     178 ./Amazon.S3/Model/S3Region.m
     152 ./Amazon.S3/Model/S3Request.m
     290 ./Amazon.S3/Model/S3Response.m
     103 ./Amazon.S3/Model/S3ResponseHeaderOverrides.m
      70 ./Amazon.S3/Model/S3RestoreObjectRequest.m
      20 ./Amazon.S3/Model/S3RestoreObjectResponse.m
      45 ./Amazon.S3/Model/S3SetACLRequest.m
      21 ./Amazon.S3/Model/S3SetACLResponse.m
      77 ./Amazon.S3/Model/S3SetBucketCrossOriginRequest.m
      22 ./Amazon.S3/Model/S3SetBucketCrossOriginResponse.m
      78 ./Amazon.S3/Model/S3SetBucketLifecycleConfigurationRequest.m
      22 ./Amazon.S3/Model/S3SetBucketLifecycleConfigurationResponse.m
      44 ./Amazon.S3/Model/S3SetBucketPolicyRequest.m
      21 ./Amazon.S3/Model/S3SetBucketPolicyResponse.m
      78 ./Amazon.S3/Model/S3SetBucketTaggingRequest.m
      22 ./Amazon.S3/Model/S3SetBucketTaggingResponse.m
      55 ./Amazon.S3/Model/S3SetBucketVersioningConfigurationRequest.m
      21 ./Amazon.S3/Model/S3SetBucketVersioningConfigurationResponse.m
      73 ./Amazon.S3/Model/S3SetBucketWebsiteConfigurationRequest.m
      22 ./Amazon.S3/Model/S3SetBucketWebsiteConfigurationResponse.m
      69 ./Amazon.S3/Model/S3TransferOperation.m
     151 ./Amazon.S3/Model/S3UploadInputStream.m
      74 ./Amazon.S3/Model/S3UploadPartRequest.m
      21 ./Amazon.S3/Model/S3UploadPartResponse.m
      51 ./Amazon.S3/Model/S3VersionSummary.m
     116 ./Amazon.S3/Model/S3VersionSummaryUnmarshaller.m
     748 ./Amazon.S3/S3TransferManager.m
     104 ./ThirdParty/JSON/AWS_SBJsonParser.m
     251 ./ThirdParty/JSON/AWS_SBJsonStreamParser.m
      51 ./ThirdParty/JSON/AWS_SBJsonStreamParserAccumulator.m
     171 ./ThirdParty/JSON/AWS_SBJsonStreamParserAdapter.m
     347 ./ThirdParty/JSON/AWS_SBJsonStreamParserState.m
     375 ./ThirdParty/JSON/AWS_SBJsonStreamWriter.m
      56 ./ThirdParty/JSON/AWS_SBJsonStreamWriterAccumulator.m
     139 ./ThirdParty/JSON/AWS_SBJsonStreamWriterState.m
     463 ./ThirdParty/JSON/AWS_SBJsonTokeniser.m
     143 ./ThirdParty/JSON/AWS_SBJsonUTF8Stream.m
     113 ./ThirdParty/JSON/AWS_SBJsonWriter.m
   19187 total

Good grief.

Porting only the bits of code I needed to support authentication added another 70 lines to my original 100. I guess I'll stick with that until I really need an S3BucketLifecycleConfigurationTransitionUnmarshaller.

you can't go home again

Debugging multithreading race conditions being high on my list of Things I Don't Enjoy, I created this category:

#import "NSRecursiveLock+Extras.h"

@implementation NSRecursiveLock (Extras)

- (void)executeBlock:(void(^)())block {
  [self lock];
  block();
  [self unlock];
}

@end

Now it's easy to protect critical code sections. Just take something like this:

self.foo = [self doFoo];  
self.didFoo = YES;  

and wrap it in a lock:

[self.lock executeBlock:^{
  self.foo = [self doFoo];
  self.didFoo = YES;
}];

What could possibly go wrong? Let's wrap another benign bit of code in a lock:

if (self.foo != nil) {  
  return;
}
self.foo = [self gimmeANewFoo];

[self doSomethingWithNewFoo];

which becomes:

[self.lock executeBlock:^{
  if (self.foo != nil) {
    return;
  }
  self.foo = [self gimmeANewFoo];
}];

[self doSomethingWithNewFoo];

The obvious-in-retrospect problem is that return in a block just exits the block. If the block is being queued for asychronous execution, that probably does what you want, to quit early.

If the block is being executed synchronously, return exits the block, but execution carries on after the block. In this case, [self doSomethingWithNewFoo] will now be executed all of the time. Which is decidedly not what I wanted.

the static analyzer as perfect colleague

He's hardly ever wrong, documents everything scrupulously, and never mentions that thing you did last time.

stupid things I will probably do again

An ongoing series.

  1. NSString has an intValue method and a longLongValue method, but it doesn't have a longValue method. NSValue has a longValue method, which means that this will throw an exception at runtime rather than being flagged by the compiler:
NSArray *items = [aString componentsSeparatedByString:aSeparator];  
long value = [items[0] longValue];  

stupid things I will probably forget again

  1. You can add a gesture recognizer to a UIImageView, but it won't do anything until you set userInteractionEnabled on.

  2. NSString has a dataUsingEncoding: method, but NSData doesn't have a stringUsingEncoding: method. You have to use [[NSString alloc] initWithData:encoding:.

  3. [tableView indexPathForCell:cell] might work some of the time.
    [tableView indexPathForRowAtPoint:cell.center] will work all of the time.